[nuiton-converter] branch develop updated (84a820b -> 2e2d1ce)
This is an automated email from the git hooks/post-receive script. New change to branch develop in repository nuiton-converter. See http://git.nuiton.org/nuiton-converter.git from 84a820b let's build the site in english + improve the readme file new 2e2d1ce - Introduce a NuitonConverter to get converter type and avoid any weak reflection - Write javadoc in english - Clean code The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 2e2d1ced221977798382838712f867ad1c61077f Author: Tony CHEMIT <chemit@codelutin.com> Date: Wed Jul 23 13:31:18 2014 +0200 - Introduce a NuitonConverter to get converter type and avoid any weak reflection - Write javadoc in english - Clean code Summary of changes: .../java/org/nuiton/converter/ColorConverter.java | 16 +-- .../java/org/nuiton/converter/ConverterUtil.java | 131 +++++++++++---------- .../java/org/nuiton/converter/EnumConverter.java | 44 ++++--- .../org/nuiton/converter/KeyStrokeConverter.java | 8 +- .../java/org/nuiton/converter/LocaleConverter.java | 8 +- .../java/org/nuiton/converter/NuitonConverter.java | 19 +++ .../java/org/nuiton/converter/URIConverter.java | 10 +- .../java/org/nuiton/converter/URLConverter.java | 10 +- .../java/org/nuiton/converter/package-info.java | 3 + ...verter => org.nuiton.converter.NuitonConverter} | 0 .../nuiton/converter/UnregistreableConverter.java | 40 ------- .../org.apache.commons.beanutils.Converter | 1 - 12 files changed, 146 insertions(+), 144 deletions(-) create mode 100644 src/main/java/org/nuiton/converter/NuitonConverter.java rename src/main/resources/META-INF/services/{org.apache.commons.beanutils.Converter => org.nuiton.converter.NuitonConverter} (100%) delete mode 100644 src/test/java/org/nuiton/converter/UnregistreableConverter.java delete mode 100644 src/test/resources/META-INF/services/org.apache.commons.beanutils.Converter -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository nuiton-converter. See http://git.nuiton.org/nuiton-converter.git commit 2e2d1ced221977798382838712f867ad1c61077f Author: Tony CHEMIT <chemit@codelutin.com> Date: Wed Jul 23 13:31:18 2014 +0200 - Introduce a NuitonConverter to get converter type and avoid any weak reflection - Write javadoc in english - Clean code --- .../java/org/nuiton/converter/ColorConverter.java | 16 +-- .../java/org/nuiton/converter/ConverterUtil.java | 131 +++++++++++---------- .../java/org/nuiton/converter/EnumConverter.java | 44 ++++--- .../org/nuiton/converter/KeyStrokeConverter.java | 8 +- .../java/org/nuiton/converter/LocaleConverter.java | 8 +- .../java/org/nuiton/converter/NuitonConverter.java | 19 +++ .../java/org/nuiton/converter/URIConverter.java | 10 +- .../java/org/nuiton/converter/URLConverter.java | 10 +- .../java/org/nuiton/converter/package-info.java | 3 + ...verter => org.nuiton.converter.NuitonConverter} | 0 .../nuiton/converter/UnregistreableConverter.java | 40 ------- .../org.apache.commons.beanutils.Converter | 1 - 12 files changed, 146 insertions(+), 144 deletions(-) diff --git a/src/main/java/org/nuiton/converter/ColorConverter.java b/src/main/java/org/nuiton/converter/ColorConverter.java index 2356308..9e5d8af 100644 --- a/src/main/java/org/nuiton/converter/ColorConverter.java +++ b/src/main/java/org/nuiton/converter/ColorConverter.java @@ -24,19 +24,18 @@ package org.nuiton.converter; import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.Converter; import java.awt.Color; import java.util.Scanner; /** - * Converter of {@link Color}. + * To convert {@link Color} from and to {@link String}. * * @author mallon - mallon@codelutin.com * @author Tony Chemit - chemit@codelutin.com * @since 1.0 */ -public class ColorConverter implements Converter { +public class ColorConverter implements NuitonConverter<Color> { @Override public <T> T convert(Class<T> aClass, Object value) { @@ -54,7 +53,7 @@ public class ColorConverter implements Converter { Color result; try { if (valueToString.length() == 7 && valueToString.charAt(0) == '#') { - result= new Color(Integer.parseInt(valueToString.substring(1), 16)); + result = new Color(Integer.parseInt(valueToString.substring(1), 16)); } else { Scanner sc = new Scanner(valueToString); sc.useDelimiter("\\D+"); @@ -70,11 +69,12 @@ public class ColorConverter implements Converter { } } - protected boolean isEnabled(Class<?> aClass) { - return Color.class.equals(aClass); + @Override + public Class<Color> getType() { + return Color.class; } - public Class<?> getType() { - return Color.class; + protected boolean isEnabled(Class<?> aClass) { + return Color.class.equals(aClass); } } diff --git a/src/main/java/org/nuiton/converter/ConverterUtil.java b/src/main/java/org/nuiton/converter/ConverterUtil.java index 22fbb4e..4b9f33f 100644 --- a/src/main/java/org/nuiton/converter/ConverterUtil.java +++ b/src/main/java/org/nuiton/converter/ConverterUtil.java @@ -27,11 +27,11 @@ import org.apache.commons.beanutils.Converter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import java.lang.reflect.Method; import java.util.ServiceLoader; /** - * Une classe contenant des méthodes utiles sur les converters et les conversions + * Useful class to get / register converter from {@link ConvertUtils} + * or trying to get it from this package. * * @author Tony Chemit - chemit@codelutin.com * @since 1.0 @@ -42,28 +42,33 @@ public class ConverterUtil { static private Log log = LogFactory.getLog(ConverterUtil.class); /** - * le paquetage où chercher les implentations de Converter, si non présents - * dans le système + * Default package where to find the converter. */ protected static final String CONVERTER_PACKAGE = "org.nuiton.converter"; - /** un drapeau pour savoir si on doit charger les converters specifiques */ + /** + * State of default converters registering. If set to {@code false}, then the will invoke the method {@link #initConverters()} + * the vry first time while calling methods {@link #getConverter(Class)} or {@link #convert(Class, Object)}. + * <p/> + * Method {@link #deregister()} will set back the flag value to {@code false}. + */ protected static Boolean WAS_INIT = Boolean.FALSE; /** - * Cherche un converter pour un <code>type</code> donné. + * Try to find amatching converter for the given {@code type}. * <p/> - * Recherche dans un premier temps dans les converteurs déjà connus. + * If required, init defaults converters (see {@link #initConverters()}). * <p/> - * Si le type est une énum et qu'aucun converter, n'a été trouvé, on - * enregistre un nouveau convert d'enum. + * If not found and type is an enum, then will register a enum type converter. * <p/> - * Sinon on tente d'instancier un converteur dans le paquetage dédié aux - * converteurs {@link #CONVERTER_PACKAGE}. + * If still not found, try to register a converter having the {@link NuitonConverter} contract named {@code typeConverter} + * in the current package ({@link #CONVERTER_PACKAGE}). + * <p/> + * For example with type {@code MyType}, will try to find the converter {@code org.nuiton.converter.MyTypeConverter}. * - * @param <T> le type a convertir - * @param type le type a convertir - * @return le converter trouvé, ou null si non trouvé + * @param <T> type to convert + * @param type type to convert + * @return the found converter, or {@code null} if not found */ public static <T> Converter getConverter(Class<T> type) { if (!WAS_INIT) { @@ -74,10 +79,10 @@ public class ConverterUtil { return converter; } if (type.isEnum()) { - registerEnumConverter(type); + registerEnumConverter((Class) type); return ConvertUtils.lookup(type); } - // on essaye de trouver un converter dans le paquetage des converters + // try in the converter package try { registerConverter0(type); converter = ConvertUtils.lookup(type); @@ -88,14 +93,17 @@ public class ConverterUtil { } /** - * Convertir une valeur! + * Try to convert a value. + * <p/> + * Will first try to find a matching converter, if not found return then {@code null}. + * <p/> + * Note: will init default converters or matching data type converter if required. * - * @param <T> le type de donnee recherchee - * @param type le type de donnee recherchee - * @param toConvert l'object a convertir - * @return la nouvelle instance de l'objet converti type ou null + * @param <T> type of data to convert + * @param type type of data to convert + * @param toConvert data to convert + * @return the converted data or {@code null} if no converter found for the type of data. */ - @SuppressWarnings({"unchecked"}) public static <T> T convert(Class<T> type, Object toConvert) { if (!WAS_INIT) { initConverters(); @@ -103,7 +111,7 @@ public class ConverterUtil { T result = null; Converter converter = getConverter(type); if (converter != null) { - return (T) converter.convert(type, toConvert); + result = converter.convert(type, toConvert); } return result; } @@ -129,32 +137,41 @@ public class ConverterUtil { } /** - * Enregistre un nouveau converter pour un type d'enum donné, avec une - * valeur par defaut. + * Register a new converter for a given enum type, + * using the given default value. * - * @param type le type d'enum à convertir - * @param defaultValue la valeur par defaut. + * @param type enum type to convert + * @param defaultValue default value to use. + * @see EnumConverter */ - public static void registerEnumConverter(Class<?> type, - Object defaultValue) { - if (EnumConverter.isEnabled(type, type) && - ConvertUtils.lookup(type) == null) { - Converter converter = new EnumConverter(type, defaultValue); + public static <E extends Enum> void registerEnumConverter(Class<E> type, + Object defaultValue) { + if (EnumConverter.isEnabled(type, type) && ConvertUtils.lookup(type) == null) { + Converter converter = new EnumConverter<E>(type, defaultValue); log.info("for type : " + type + " : " + converter); ConvertUtils.register(converter, type); } } /** - * Enregistre un nouveau converter pour un type d'enum donné, sans utiliser - * de valeur par defaut. + * Register a new converter for a given enum type, + * not using any default value. * - * @param type le type d'enum à convertir + * @param type enum type to convert + * @see EnumConverter */ - public static void registerEnumConverter(Class<?> type) { + public static <E extends Enum> void registerEnumConverter(Class<E> type) { registerEnumConverter(type, null); } + /** + * Converts a array of char to an array of bytes. + * <p/> + * TODO Find out who uses this ? + * + * @param chars array of characters to converter + * @return the array of bytes + */ public static byte[] convert(char[] chars) { byte[] bytes = new byte[chars.length]; for (int i = 0; i < chars.length; i++) { @@ -163,41 +180,37 @@ public class ConverterUtil { return bytes; } + /** + * To remove any registered converter. + * <p/> + * At the next call to method {@link #convert(Class, Object)} or {@link #getConverter(Class)}, default converters + * will be registred again. + */ public static synchronized void deregister() { ConvertUtils.deregister(); WAS_INIT = false; } + /** + * To init default converters (using the service loader mecanism on contract {@link NuitonConverter}). + * <p/> + * If was already init, then will do nothing. + * <p/> + * Note: the {@link #getConverter(Class)} or {@link #convert(Class, Object)} methods always invoke this method if + * the class was not initialized (see the {@link #WAS_INIT} internal flag). + */ public static synchronized void initConverters() { if (WAS_INIT != null && WAS_INIT) { return; } - ServiceLoader<Converter> converters = - ServiceLoader.load(Converter.class); - for (Converter converter : converters) { + ServiceLoader<NuitonConverter> converters = ServiceLoader.load(NuitonConverter.class); + for (NuitonConverter converter : converters) { if (log.isDebugEnabled()) { log.debug("discovered converter " + converter); } - try { - Method m = converter.getClass().getDeclaredMethod("getType"); - m.setAccessible(true); - try { - Class<?> returnType = (Class<?>) m.invoke(converter); - log.info("register converter " + converter); - ConvertUtils.register(converter, returnType); - } catch (Exception ex) { - log.warn("could not obtain type of converter " + - converter + " for reason : " + ex.getMessage(), - ex); - } - } catch (NoSuchMethodException ex) { - log.warn("could not find method getType on converter " + - converter + ", will not be registred..."); - } catch (SecurityException ex) { - log.warn("could not find method getType on converter " + - converter + ", will not be registred..."); - } - + Class<?> type = converter.getType(); + log.info("register converter " + converter); + ConvertUtils.register(converter, type); } WAS_INIT = true; } diff --git a/src/main/java/org/nuiton/converter/EnumConverter.java b/src/main/java/org/nuiton/converter/EnumConverter.java index ce3d49e..e89c958 100644 --- a/src/main/java/org/nuiton/converter/EnumConverter.java +++ b/src/main/java/org/nuiton/converter/EnumConverter.java @@ -23,7 +23,6 @@ package org.nuiton.converter; */ import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.Converter; import org.apache.commons.logging.Log; import java.util.EnumSet; @@ -31,32 +30,40 @@ import java.util.EnumSet; import static org.apache.commons.logging.LogFactory.getLog; /** - * classe pour convertir une chaine en un objet Enum type-safe en - * connaissant le type d'enumération utilisée {@link #enumType}. + * To convert {@link Enum} type-safe (exact type is {@link #enumType} + * from and to {@link String}. * <p/> - * Il est possible aussi de convertir une Enum à partir de sa valeur ordinal. + * It is possible also to convert the enum object from his ordinal using method {@link #convertFromOrdinal(Class, Object)}. * <p/> - * Pour enregister un nouveau convertissemnt pour un type d'Enum utiliser les - * méthodes * {@link ConverterUtil#registerEnumConverter(Class)}, - * et {@link ConverterUtil#registerEnumConverter(Class, Object)} . + * you can also define a default value while trying to convert {@code null} value. + * <p/> + * To register a new such converter use methods {@link ConverterUtil#registerEnumConverter(Class)}, + * or {@link ConverterUtil#registerEnumConverter(Class, Object)} . * * @author Tony Chemit - chemit@codelutin.com * @see Enum * @see Enum#ordinal() + * @since 1.0 */ -public class EnumConverter implements Converter { +public class EnumConverter<E extends Enum> implements NuitonConverter<E> { /** Logger. */ static Log log = getLog(EnumConverter.class); - /** valeur par default à utiliser, si pas non trouvée et {@link #useDefault} actif. */ + /** + * Default value to use if try to convert {@code null} value and flag {@link #useDefault} is on. + */ protected Object defaultValue; - /** flag pour utiliser la valeur par defaut {@link #defaultValue} si non trouvé. */ + /** + * flag to use or not default value. + */ protected boolean useDefault; - /** le type de l'énumération à convertir */ - protected Class<?> enumType; + /** + * Enum type-safe. + */ + protected Class<E> enumType; @Override public <T> T convert(Class<T> aClass, Object value) { @@ -91,7 +98,7 @@ public class EnumConverter implements Converter { String.format("no convertor found for type %2$s and objet '%1$s'", aClass.getName(), value)); } - public EnumConverter(Class<?> enumType, Object defaultValue) { + public EnumConverter(Class<E> enumType, Object defaultValue) { this.enumType = enumType; this.defaultValue = defaultValue; useDefault = defaultValue != null; @@ -100,10 +107,15 @@ public class EnumConverter implements Converter { } } - public EnumConverter(Class<?> enumType) { + public EnumConverter(Class<E> enumType) { this(enumType, null); } + @Override + public Class<E> getType() { + return enumType; + } + protected static boolean isEnabled(Class<?> aClass, Class<?> enumType) { return aClass != null && aClass.isEnum() && aClass == enumType; } @@ -140,9 +152,5 @@ public class EnumConverter implements Converter { return vals; } - public Class<?> getType() { - return enumType; - } - } diff --git a/src/main/java/org/nuiton/converter/KeyStrokeConverter.java b/src/main/java/org/nuiton/converter/KeyStrokeConverter.java index fc58cd6..9ba281d 100644 --- a/src/main/java/org/nuiton/converter/KeyStrokeConverter.java +++ b/src/main/java/org/nuiton/converter/KeyStrokeConverter.java @@ -23,7 +23,6 @@ package org.nuiton.converter; */ import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.Converter; import org.apache.commons.logging.Log; import javax.swing.KeyStroke; @@ -31,12 +30,12 @@ import javax.swing.KeyStroke; import static org.apache.commons.logging.LogFactory.getLog; /** - * Used to convert a {@link String} to {@link KeyStroke}. + * To convert {@link KeyStroke} from and to {@link String}. * * @author Sylvain Letellier - letellier@codelutin.com * @since 1.0 */ -public class KeyStrokeConverter implements Converter { +public class KeyStrokeConverter implements NuitonConverter<KeyStroke> { /** Logger. */ static Log log = getLog(KeyStrokeConverter.class); @@ -66,7 +65,8 @@ public class KeyStrokeConverter implements Converter { return KeyStroke.class.equals(aClass); } - public Class<?> getType() { + @Override + public Class<KeyStroke> getType() { return KeyStroke.class; } } diff --git a/src/main/java/org/nuiton/converter/LocaleConverter.java b/src/main/java/org/nuiton/converter/LocaleConverter.java index 95c4d1e..51595a3 100644 --- a/src/main/java/org/nuiton/converter/LocaleConverter.java +++ b/src/main/java/org/nuiton/converter/LocaleConverter.java @@ -23,7 +23,6 @@ package org.nuiton.converter; */ import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.Converter; import org.apache.commons.logging.Log; import java.util.Locale; @@ -33,12 +32,12 @@ import java.util.regex.Pattern; import static org.apache.commons.logging.LogFactory.getLog; /** - * classe pour convertir une chaine en un objet {@link Locale}. + * To convert {@link Locale} from and to {@link String}. * * @author Tony Chemit - chemit@codelutin.com * @since 1.0 */ -public class LocaleConverter implements Converter { +public class LocaleConverter implements NuitonConverter<Locale> { private static final Pattern FULL_SCOPE_PATTERN = Pattern.compile("([a-zA-Z]{2})_([a-zA-Z]{2})"); @@ -126,7 +125,8 @@ public class LocaleConverter implements Converter { return Locale.class.equals(aClass); } - public Class<?> getType() { + @Override + public Class<Locale> getType() { return Locale.class; } diff --git a/src/main/java/org/nuiton/converter/NuitonConverter.java b/src/main/java/org/nuiton/converter/NuitonConverter.java new file mode 100644 index 0000000..bab9bbe --- /dev/null +++ b/src/main/java/org/nuiton/converter/NuitonConverter.java @@ -0,0 +1,19 @@ +package org.nuiton.converter; + +import org.apache.commons.beanutils.Converter; + +/** + * Over {@link Converter} contract to be able to get main type of a converter. + * <p/> + * Created on 7/23/14. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 1.0 + */ +public interface NuitonConverter<O> extends Converter { + + /** + * @return the main type of the converter. + */ + Class<O> getType(); +} diff --git a/src/main/java/org/nuiton/converter/URIConverter.java b/src/main/java/org/nuiton/converter/URIConverter.java index 2779258..f1d4c20 100644 --- a/src/main/java/org/nuiton/converter/URIConverter.java +++ b/src/main/java/org/nuiton/converter/URIConverter.java @@ -23,7 +23,6 @@ package org.nuiton.converter; */ import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.Converter; import org.apache.commons.logging.Log; import java.net.URI; @@ -32,12 +31,12 @@ import java.net.URISyntaxException; import static org.apache.commons.logging.LogFactory.getLog; /** - * classe pour convertir une chaine en un objet URI. + * To convert {@link URI} from and to {@link String}. * * @author Tony Chemit - chemit@codelutin.com - * @since 1.3 (replace {@code org.nuiton.util.URIConverter}). + * @since 1.0 */ -public class URIConverter implements Converter { +public class URIConverter implements NuitonConverter<URI> { /** Logger. */ static Log log = getLog(URIConverter.class); @@ -84,7 +83,8 @@ public class URIConverter implements Converter { return URI.class.equals(aClass); } - public Class<?> getType() { + @Override + public Class<URI> getType() { return URI.class; } } diff --git a/src/main/java/org/nuiton/converter/URLConverter.java b/src/main/java/org/nuiton/converter/URLConverter.java index 4ebd868..5caecd7 100644 --- a/src/main/java/org/nuiton/converter/URLConverter.java +++ b/src/main/java/org/nuiton/converter/URLConverter.java @@ -23,7 +23,6 @@ package org.nuiton.converter; */ import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.Converter; import org.apache.commons.logging.Log; import java.net.MalformedURLException; @@ -32,12 +31,12 @@ import java.net.URL; import static org.apache.commons.logging.LogFactory.getLog; /** - * classe pour convertir une chaine en un objet URL. + * To convert {@link URL} from and to {@link String}. * * @author Tony Chemit - chemit@codelutin.com - * @since 1.3 (replace {@code org.nuiton.util.URLConverter}). + * @since 1.0 */ -public class URLConverter implements Converter { +public class URLConverter implements NuitonConverter<URL> { /** Logger. */ static Log log = getLog(URLConverter.class); @@ -84,7 +83,8 @@ public class URLConverter implements Converter { return URL.class.equals(aClass); } - public Class<?> getType() { + @Override + public Class<URL> getType() { return URL.class; } diff --git a/src/main/java/org/nuiton/converter/package-info.java b/src/main/java/org/nuiton/converter/package-info.java index 13836da..9558534 100644 --- a/src/main/java/org/nuiton/converter/package-info.java +++ b/src/main/java/org/nuiton/converter/package-info.java @@ -5,6 +5,9 @@ * <h1>Converter api</h1> * This converter api is based on the * http://commons.apache.org/beanutils {@code commons-beanutils}. + * + * With a thin over layer (see {@link org.nuiton.converter.NuitonConverter} to get at loading + * the type of converter to register. * <p/> * Use the {@link org.nuiton.converter.ConverterUtil} to register or obtain a * converter. diff --git a/src/main/resources/META-INF/services/org.apache.commons.beanutils.Converter b/src/main/resources/META-INF/services/org.nuiton.converter.NuitonConverter similarity index 100% rename from src/main/resources/META-INF/services/org.apache.commons.beanutils.Converter rename to src/main/resources/META-INF/services/org.nuiton.converter.NuitonConverter diff --git a/src/test/java/org/nuiton/converter/UnregistreableConverter.java b/src/test/java/org/nuiton/converter/UnregistreableConverter.java deleted file mode 100644 index d3ffef1..0000000 --- a/src/test/java/org/nuiton/converter/UnregistreableConverter.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.nuiton.converter; - -/* - * #%L - * Nuiton Converter - * %% - * Copyright (C) 2014 CodeLutin, Tony Chemit - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ - -import org.apache.commons.beanutils.Converter; - -/** - * A converter with no getType method, so not registreable via {@link ConverterUtil#initConverters()} - * - * @author Tony Chemit - chemit@codelutin.com - * @since 1.0 - */ -public class UnregistreableConverter implements Converter { - - @Override - public <T> T convert(Class<T> aClass, Object value) { - return aClass.cast(value); - } - -} \ No newline at end of file diff --git a/src/test/resources/META-INF/services/org.apache.commons.beanutils.Converter b/src/test/resources/META-INF/services/org.apache.commons.beanutils.Converter deleted file mode 100644 index c22d6cb..0000000 --- a/src/test/resources/META-INF/services/org.apache.commons.beanutils.Converter +++ /dev/null @@ -1 +0,0 @@ -org.nuiton.converter.UnregistreableConverter \ No newline at end of file -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
participants (1)
-
nuiton.org scm