Author: tchemit Date: 2009-09-08 18:02:25 +0200 (Tue, 08 Sep 2009) New Revision: 570 Modified: trunk/src/main/java/org/nuiton/io/xpp3/AbstractXpp3Reader.java trunk/src/main/java/org/nuiton/io/xpp3/PropertyMapper.java trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Helper.java trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Reader.java trunk/src/test/java/org/nuiton/util/BasePluginTestCase.java Log: [FEATURE #46] ajout d'un contrat pour faire des parseurs xpp3 Modified: trunk/src/main/java/org/nuiton/io/xpp3/AbstractXpp3Reader.java =================================================================== --- trunk/src/main/java/org/nuiton/io/xpp3/AbstractXpp3Reader.java 2009-09-07 22:54:20 UTC (rev 569) +++ trunk/src/main/java/org/nuiton/io/xpp3/AbstractXpp3Reader.java 2009-09-08 16:02:25 UTC (rev 570) @@ -3,10 +3,12 @@ import java.beans.IntrospectionException; import java.io.IOException; import java.io.Reader; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.TreeMap; import org.codehaus.plexus.util.xml.pull.MXParser; import org.codehaus.plexus.util.xml.pull.XmlPullParser; @@ -22,20 +24,22 @@ * The logic of setting properties from xml (tag and attributes) is done in * * <ul> - * <li>{@link #read(org.codehaus.plexus.util.xml.pull.XmlPullParser, boolean)} </li> - * <li>{@link #readArray(org.codehaus.plexus.util.xml.pull.XmlPullParser, boolean)} </li> + * <li>{@link #read(String, Class, org.codehaus.plexus.util.xml.pull.XmlPullParser, boolean)} </li> + * <li>{@link #readArray(String, String, Class, org.codehaus.plexus.util.xml.pull.XmlPullParser, boolean)}</li> * </ul> * * The default implementation is to map tag text to a pojo's property. * * If you want to do something more complex, override these methods. * + * @param <O> the type of object to be build by the reader + * * @author chemit * @since 1.0.0 * @see PropertyMapper * @see Xpp3Reader */ -public abstract class AbstractXpp3Reader implements Xpp3Reader { +public abstract class AbstractXpp3Reader<O> implements Xpp3Reader<O> { /** * If set the parser will be loaded with all single characters @@ -51,13 +55,17 @@ /** * the type of the object to produce from the xml streams. */ - protected final Class<?> type; + protected final Class<O> type; /** * the root tag of an object to retreave from xml streams. * */ - protected final String rootTagName; + protected String rootTagName; /** + * the root tag of an array of objets to retreave from xml streams. + */ + protected String arrayRootTagName; + /** * the univers of mappers availables, initialized in {@link #initMappers()}. * * Each mapper of the dictionary is associated to the fully qualified name @@ -69,18 +77,22 @@ * org.nuiton.util.MyPojo#my-attribute * </pre> */ - protected final Map<String, PropertyMapper> allMappers; - /** - * a flag to display in console the generated code : - * an easy way to build to generate java pojos mocks from xml file (to test the parser for example :) - */ - protected boolean showGeneratedCode; + protected Map<String, PropertyMapper> allMappers; protected abstract void initMappers() throws IntrospectionException; - protected AbstractXpp3Reader(Class<?> type, String rootTagName) { + protected AbstractXpp3Reader(Class<O> type) { + this(type, null, null); + } + + protected AbstractXpp3Reader(Class<O> type, String rootTagName) { + this(type, null, rootTagName); + } + + protected AbstractXpp3Reader(Class<O> type, String arrayRootTagName, String rootTagName) { this.type = type; this.rootTagName = rootTagName; + this.arrayRootTagName = arrayRootTagName; this.allMappers = new TreeMap<String, PropertyMapper>(); try { initMappers(); @@ -90,29 +102,33 @@ } @Override - public Class<?> getType() { + public Class<O> getType() { return type; } + @Override public String getRootTagName() { return rootTagName; } - public String getArrayRootTagName() { - return rootTagName + "s"; + @Override + public void setRootTagName(String rootTagName) { + this.rootTagName = rootTagName; } @Override - public boolean isAddDefaultEntities() { - return addDefaultEntities; + public String getArrayRootTagName() { + return arrayRootTagName; } - public boolean isShowGeneratedCode() { - return showGeneratedCode; + @Override + public void setParentRootTagName(String parentRootTagName) { + this.arrayRootTagName = parentRootTagName; } - public void setShowGeneratedCode(boolean showGeneratedCode) { - this.showGeneratedCode = showGeneratedCode; + @Override + public boolean isAddDefaultEntities() { + return addDefaultEntities; } @Override @@ -121,12 +137,12 @@ } @Override - public Object read(Reader reader) throws IOException, XmlPullParserException { + public O read(Reader reader) throws IOException, XmlPullParserException { return read(reader, true); } @Override - public Object read(Reader reader, boolean strict) throws IOException, XmlPullParserException { + public O read(Reader reader, boolean strict) throws IOException, XmlPullParserException { XmlPullParser parser = new MXParser(); parser.setInput(reader); @@ -138,16 +154,26 @@ } parser.next(); - return read(parser, strict); + + // read the first open tag getRootTagName() and consume the matching ending tag + O result = read(getRootTagName(), getType(), parser, strict); + + // go after the ending tag getRootTagName() + parser.next(); + + // must be at the end of the document + checkEndOfXml(parser); + + return result; } @Override - public Object[] readArray(Reader reader) throws IOException, XmlPullParserException { + public O[] readArray(Reader reader) throws IOException, XmlPullParserException { return readArray(reader, true); } @Override - public Object[] readArray(Reader reader, boolean strict) throws IOException, XmlPullParserException { + public O[] readArray(Reader reader, boolean strict) throws IOException, XmlPullParserException { XmlPullParser parser = new MXParser(); parser.setInput(reader); @@ -155,11 +181,19 @@ if (addDefaultEntities) { Xpp3Helper.addDefaultEntities(parser); - } parser.next(); - return readArray(parser, strict); + + parser.getEventType(); + + O[] result = readArray(getArrayRootTagName(), getRootTagName(), getType(), parser, strict); + + parser.next(); + + checkEndOfXml(parser); + + return result; } /** @@ -194,57 +228,78 @@ * * No work is done on attribute values here. * - * Note: The xml stream must contains one object to build on the root node. - * - * the root node name is given by <code>getRootTagName()</code> + * Note: The parser must accept as a next open tag the required one . * + * the next node name is given by <code>getRootTagName()</code> + * * Example : * <pre> + * ... * <my-pojo> * <my-property>myValue</my-property> * </my-pojo> * </pre> - * + * + * @param <T> the type of object to build + * @param rootTagName the name of the root tag matching the object to build + * @param type the type of object to build * @param parser the xpp3 parser * @param strict flag to indicate if should fail if a unknown tag (or attribute ?) is scanned * @return the single object build from the xml stream. * @throws IOException if any io pb * @throws XmlPullParserException if any parsing pb */ - protected Object read(XmlPullParser parser, boolean strict) throws IOException, XmlPullParserException { + protected <T> T read(String rootTagName, Class<T> type, XmlPullParser parser, boolean strict) throws XmlPullParserException, IOException { - Object result; + // search open tag rootTagName + gotoNextOpenTag(rootTagName, parser); + + // can init result + T result = null; try { - result = getType().newInstance(); + result = type.newInstance(); } catch (Exception ex) { // should never happens! throw new RuntimeException("could not instanciate a new " + getType().getName() + " for reason : " + ex.getMessage(), ex); } - java.util.Set<String> parsed = new java.util.HashSet<String>(); - int eventType = parser.getEventType(); - boolean foundRoot = false; + // prepare alvailable mappers for the given type + Map<String, PropertyMapper> mappers = getMappers(type); - Map<String, PropertyMapper> mappers = getMappers(getType()); - if (showGeneratedCode) { - System.out.println(getType().getName() + " i = new " + getType().getSimpleName() + "();"); - } - while (eventType != XmlPullParser.END_DOCUMENT) { - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals(rootTagName)) { - foundRoot = true; - } else { + Set<String> parsed = parsed = new java.util.HashSet<String>(); + try { + // go to next tag + int eventType = parser.next(); + + while (true) { + + checkNotEndOfXml(parser, rootTagName); + + if (eventType == XmlPullParser.START_TAG) { +// System.out.println("reading tag " + parser.getName()); PropertyMapper mapper = mappers.get(parser.getName()); if (mapper != null) { mapper.setProperty(result, parser, parsed, strict); } else if (strict) { throw new XmlPullParserException("Unrecognised tag: '" + parser.getName() + "'", parser, null); } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(rootTagName)) { + // have reach the end of the object + break; + } } + + // try next event + eventType = parser.next(); } - eventType = parser.next(); + return result; + } finally { + parsed.clear(); + if (mappers != null) { + mappers.clear(); + } } - return result; } /** @@ -254,14 +309,12 @@ * * No work is done on attribute values here. * - * Note: The xml stream must contains a root node. + * Note: The next node of the parser must be the one given by + * {@code parentRootTagName} and sub nodes with names {@code rootTagName}. * - * the root node name is given by <code>getArrayRootTagName()</code> - * - * and sub nodes with names <code>getRootTagName()</code> - * * Example : * <pre> + * ... * <my-pojos> * <my-pojo> * <my-property>myValue</my-property> @@ -272,67 +325,117 @@ * </my-pojos> * </pre> * + * @param <T> the type of objects to build + * @param parentRootTagName the tag's name of the array container + * @param rootTagName the tag's name of each object to build + * @param type the type of objects to build * @param parser the xpp3 parser * @param strict flag to indicate if should fail if a unknown tag (or attribute ?) is scanned * @return the single object build from the xml stream. * @throws IOException if any io pb * @throws XmlPullParserException if any parsing pb */ - protected Object[] readArray(XmlPullParser parser, boolean strict) throws IOException, XmlPullParserException { + protected <T> T[] readArray(String parentRootTagName, String rootTagName, Class<T> type, XmlPullParser parser, boolean strict) throws XmlPullParserException, IOException { - List<Object> results = new ArrayList<Object>(); + // search open tag parentRootTagName + // if not found, will raise an parsing exception + gotoNextOpenTag(parentRootTagName, parser); + // can init result + List<T> results = new ArrayList<T>(); - java.util.Set<String> parsed = new java.util.HashSet<String>(); - int eventType = parser.getEventType(); - boolean foundRoot = false; + boolean addChild = false; + boolean quit = false; - Map<String, PropertyMapper> mappers = getMappers(getType()); - Object result = null; - String root = getArrayRootTagName(); - if (showGeneratedCode) { - System.out.println(getType().getSimpleName() + " i = null;"); - } - while (eventType != XmlPullParser.END_DOCUMENT) { - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals(root)) { - foundRoot = true; - if (showGeneratedCode) { - System.out.println("List<" + getType().getSimpleName() + "> list = new ArrayList<" + getType().getSimpleName() + ">();"); - } - } else if (parser.getName().equals(this.rootTagName)) { - try { - result = getType().newInstance(); - if (showGeneratedCode) { - System.out.println("i = new " + getType().getSimpleName() + "();"); - } - } catch (Exception ex) { - // should never happens! - throw new RuntimeException("could not instanciate a new " + getType().getName() + " for reason : " + ex.getMessage(), ex); - } + while (!quit) { - } else { - PropertyMapper mapper = mappers.get(parser.getName()); - if (mapper != null) { - mapper.setProperty(result, parser, parsed, strict); + addChild = false; + quit = false; - } else if (strict) { - throw new XmlPullParserException("Unrecognised tag: '" + parser.getName() + "'", parser, null); - } + // search next opening tag (rootTagName) or ending tag (parentRootTagName) + + while (true) { + + checkNotEndOfXml(parser, parentRootTagName); + + int eventType = parser.getEventType(); + + if (eventType == XmlPullParser.START_TAG && rootTagName.equals(parser.getName())) { + // there is a child to read + addChild = true; + break; } - } else if (eventType == XmlPullParser.END_TAG) { - if (parser.getName().equals(this.rootTagName)) { - // save it - results.add(result); - // clear parsed attributes - parsed.clear(); - if (showGeneratedCode) { - System.out.println("list.add(i);"); - } + + if (eventType == XmlPullParser.END_TAG && parentRootTagName.equals(parser.getName())) { + // can quit main loop, end of work + quit = true; + break; } + + eventType = parser.next(); } + + if (addChild) { + + // find an object to add + T result = read(rootTagName, type, parser, strict); + + results.add(result); + + // go for another round + continue; + } + + // no more child + // must be at the end of tag + if (!quit) { + throw new XmlPullParserException("should be on " + parentRootTagName + " but was not : " + parser.getName()); + } + } + + // can not directly instanciate a generic array (or don't known how ?) + return results.toArray((T[]) Array.newInstance(type, results.size())); + } + + protected int gotoNextOpenTag(String tagName, XmlPullParser parser) throws XmlPullParserException, IOException { + // search next open tag tagName + + int eventType = parser.getEventType(); + + while (eventType != XmlPullParser.START_TAG || !parser.getName().equals(tagName)) { + + checkNotEndOfXml(parser, tagName); + eventType = parser.next(); } - return results.toArray(new Object[results.size()]); + + return eventType; } + + /** + * Checks that a given parser is not at the end of the xml document. + * + * @param parser the parser to check + * @param tagName the endign tag's name + * @throws XmlPullParserException if the parser is at the end of the xml stream, instead of the {@code tagName} ending tag + */ + protected void checkNotEndOfXml(XmlPullParser parser, String tagName) throws XmlPullParserException { + if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { + // can not be here ? + throw new XmlPullParserException("end of document found... but required at least the ending tag " + tagName); + } + } + + /** + * Checks that a given parser is at the end of the xml document. + * + * @param parser the parser to check + * @throws XmlPullParserException if the parser is not at the end of the xml stream. + */ + protected void checkEndOfXml(XmlPullParser parser) throws XmlPullParserException { + // must be at the end of the document + if (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + throw new XmlPullParserException("should be at the end of document but was not... : " + parser.getName()); + } + } } Modified: trunk/src/main/java/org/nuiton/io/xpp3/PropertyMapper.java =================================================================== --- trunk/src/main/java/org/nuiton/io/xpp3/PropertyMapper.java 2009-09-07 22:54:20 UTC (rev 569) +++ trunk/src/main/java/org/nuiton/io/xpp3/PropertyMapper.java 2009-09-08 16:02:25 UTC (rev 570) @@ -4,6 +4,7 @@ import java.io.IOException; import java.text.ParseException; import java.util.Set; +import org.apache.commons.lang.builder.ToStringBuilder; import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; @@ -16,6 +17,13 @@ * <li>{@link TagTextContentToProperty} to map the text content of a tag to a pojo's property</li> * <li>{@link AttributeValueToProperty} to map the text content of a tag to a pojo's property</li> * </ul> + * + * There is two convinient factory methods in {@link Xpp3Helper} to add some new mappers into a given + * dictionnary of mappers. + * <ul> + * <li> {@link Xpp3Helper#addTagTextContentMappers(Class, org.nuiton.io.xpp3.DataConverter, boolean, java.util.Map, String[])}</li> + * <li>{@link Xpp3Helper#addAttributeValueMappers(Class, org.nuiton.io.xpp3.DataConverter, boolean, java.util.Map, String[])}</li> + * </ul> * * @author chemit * @since 1.0.3 @@ -24,8 +32,8 @@ public static class TagTextContentToProperty extends PropertyMapper { - public TagTextContentToProperty(String tagName, String propertyName, DataConverter type, boolean onlyOne, PropertyDescriptor descriptor) { - super(tagName, propertyName, type, onlyOne, descriptor); + public TagTextContentToProperty(String tagName, String propertyName, Class<?> containerType, DataConverter type, boolean onlyOne, PropertyDescriptor descriptor) { + super(tagName, propertyName, containerType, type, onlyOne, descriptor); } @Override @@ -41,8 +49,8 @@ public static class AttributeValueToProperty extends PropertyMapper { - public AttributeValueToProperty(String tagName, String propertyName, DataConverter type, boolean onlyOne, PropertyDescriptor descriptor) { - super(tagName, propertyName, type, onlyOne, descriptor); + public AttributeValueToProperty(String tagName, String propertyName, Class<?> containerType, DataConverter type, boolean onlyOne, PropertyDescriptor descriptor) { + super(tagName, propertyName, containerType, type, onlyOne, descriptor); } @Override @@ -68,6 +76,10 @@ */ protected final DataConverter type; /** + * the type of the pojo container of the property + */ + protected final Class<?> containerType; + /** * the pojo's property descriptor */ protected final PropertyDescriptor descriptor; @@ -77,11 +89,12 @@ */ protected final boolean onlyOne; - protected PropertyMapper(String tagName, String propertyName, DataConverter type, boolean onlyOne, PropertyDescriptor descriptor) { + protected PropertyMapper(String tagName, String propertyName, Class<?> containerType, DataConverter type, boolean onlyOne, PropertyDescriptor descriptor) { this.name = tagName; this.propertyName = propertyName; this.type = type; this.onlyOne = onlyOne; + this.containerType = containerType; this.descriptor = descriptor; } @@ -131,4 +144,23 @@ public DataConverter getType() { return type; } + + public Class<?> getContainerType() { + return containerType; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + ToStringBuilder toStringBuilder = new ToStringBuilder(this); + toStringBuilder.append("name", name); + toStringBuilder.append("propertyName", propertyName); + toStringBuilder.append("onlyOne", onlyOne); + toStringBuilder.append("type", type); + toStringBuilder.append("containerType", containerType); + return toStringBuilder.toString(); + } } Modified: trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Helper.java =================================================================== --- trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Helper.java 2009-09-07 22:54:20 UTC (rev 569) +++ trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Helper.java 2009-09-08 16:02:25 UTC (rev 570) @@ -1,5 +1,9 @@ package org.nuiton.io.xpp3; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; @@ -7,11 +11,16 @@ import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.ServiceLoader; +import org.apache.commons.lang.StringUtils; import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.nuiton.io.xpp3.PropertyMapper.AttributeValueToProperty; +import org.nuiton.io.xpp3.PropertyMapper.TagTextContentToProperty; /** * A Helper to read some data stored in xml with a {@link Xpp3Reader}. @@ -29,20 +38,21 @@ * les readers enregistres via un {@link ServiceLoader} * sur le contrat {@link Xpp3Reader}. */ - protected static Map<Class<?>, Xpp3Reader> readers; + protected static Map<Class<?>, Xpp3Reader<?>> readers; /** * Read a single object from a xml stream. * + * @param <O> the type of object to read * @param klass the type of object to read * @param reader the reader where to parse the xml * @return the loaded object * @throws IOException if any io pb * @throws XmlPullParserException if any parsing pb */ - public static Object readObject(Class<?> klass, Reader reader) throws IOException, XmlPullParserException { + public static <O> O readObject(Class<O> klass, Reader reader) throws IOException, XmlPullParserException { - Object result = null; + O result = null; try { @@ -54,7 +64,7 @@ StringReader sReader = new StringReader(rawInput); - Xpp3Reader modelReader = getReader(klass); + Xpp3Reader<O> modelReader = getReader(klass); result = modelReader.read(sReader); @@ -68,15 +78,16 @@ /** * Read an array of objects from a xml stream. * + * @param <O> the type of objects to return * @param klass the type of object to read * @param reader the reader where to parse the xml * @return the loaded objects * @throws IOException if any io pb * @throws XmlPullParserException if any parsing pb */ - public static Object[] readObjects(Class<?> klass, Reader reader) throws IOException, XmlPullParserException { + public static <O> O[] readObjects(Class<O> klass, Reader reader) throws IOException, XmlPullParserException { - Object[] result = null; + O[] result = null; try { @@ -88,7 +99,7 @@ StringReader sReader = new StringReader(rawInput); - Xpp3Reader modelReader = getReader(klass); + Xpp3Reader<O> modelReader = getReader(klass); result = modelReader.readArray(sReader); @@ -103,7 +114,7 @@ * * @return an iterator on all registred {@link Xpp3Reader}. */ - public static Iterator<Xpp3Reader> getReaderItetator() { + public static Iterator<Xpp3Reader<?>> getReaderItetator() { return getReaders().values().iterator(); } @@ -115,8 +126,8 @@ * @param klass the type of the data which should be parsed by the researched parser * @return the parser for the given type */ - public static <T> Xpp3Reader getReader(Class<?> klass) { - Xpp3Reader reader = getReaders().get(klass); + public static <T> Xpp3Reader<T> getReader(Class<T> klass) { + Xpp3Reader<T> reader = (Xpp3Reader<T>) getReaders().get(klass); return reader; } @@ -405,17 +416,72 @@ parser.defineEntityReplacementText("diams", "\u2666"); } + public static void addTagTextContentMappers(Class<?> containerType, DataConverter type, boolean onlyOne, Map<String, PropertyMapper> allMappers, String... tagNames) throws IntrospectionException { + try { + addMappers(TagTextContentToProperty.class, containerType, type, onlyOne, allMappers, tagNames); + } catch (Exception ex) { + throw new RuntimeException("could not addMappers for reason : " + ex.getMessage(), ex); + } + } + + public static void addAttributeValueMappers(Class<?> containerType, DataConverter type, boolean onlyOne, Map<String, PropertyMapper> allMappers, String... tagNames) throws IntrospectionException { + try { + addMappers(AttributeValueToProperty.class, containerType, type, onlyOne, allMappers, tagNames); + } catch (Exception ex) { + throw new RuntimeException("could not addMappers for reason : " + ex.getMessage(), ex); + } + } + + protected static void addMappers(Class<? extends PropertyMapper> mapperClass, Class<?> containerType, DataConverter type, boolean onlyOne, Map<String, PropertyMapper> allMappers, String... tagNames) throws IntrospectionException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + for (String tagName : tagNames) { + + // the tag-name is transformed to tagName + String[] parts = tagName.split("-"); + StringBuilder buffer = new StringBuilder(); + for (int i = 0, j = parts.length; i < j; i++) { + if (i == 0) { + buffer.append(parts[i]); + } else { + buffer.append(StringUtils.capitalize(parts[i])); + } + } + String propertyName = buffer.toString(); + BeanInfo beanInfo = Introspector.getBeanInfo(containerType); + PropertyDescriptor descriptor = null; + for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { + if (propertyDescriptor.getName().equals(propertyName)) { + descriptor = propertyDescriptor; + break; + } + } + if (descriptor == null) { + throw new IllegalArgumentException("could not find a property descriptor for property " + propertyName + " of " + containerType); + } + Constructor<? extends PropertyMapper> constructor = mapperClass.getConstructor(String.class, String.class, Class.class, DataConverter.class, boolean.class, PropertyDescriptor.class); + + PropertyMapper mapper = constructor.newInstance(tagName, propertyName, containerType, type, onlyOne, descriptor); +// System.out.println("adding a mapper " + mapper); + + allMappers.put(containerType.getName() + "#" + tagName, mapper); + } + } + /** + * Load (if {@link #readers} is {@code null} the readers via a + * {@link ServiceLoader} of contract {@link Xpp3Reader} and returns + * the dictionnary of discovered associated to their type + * ({@link Xpp3Reader#getType()}). * + * * @return all the {@link Xpp3Reader} registred via a {@link ServiceLoader} - * on the contract {@link Xpp3Reader}. + * on the contract {@link Xpp3Reader} associated to their type + * ({@link Xpp3Reader#getType()}). * */ - protected static Map<Class<?>, Xpp3Reader> getReaders() { + protected static Map<Class<?>, Xpp3Reader<?>> getReaders() { if (readers == null) { - readers = new HashMap<Class<?>, Xpp3Reader>(); - ServiceLoader<Xpp3Reader> load = ServiceLoader.load(Xpp3Reader.class); - for (Xpp3Reader r : load) { + readers = new HashMap<Class<?>, Xpp3Reader<?>>(); + for (Xpp3Reader<?> r : ServiceLoader.load(Xpp3Reader.class)) { readers.put(r.getType(), r); } } Modified: trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Reader.java =================================================================== --- trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Reader.java 2009-09-07 22:54:20 UTC (rev 569) +++ trunk/src/main/java/org/nuiton/io/xpp3/Xpp3Reader.java 2009-09-08 16:02:25 UTC (rev 570) @@ -2,7 +2,6 @@ import java.io.IOException; import java.io.Reader; -import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; /** @@ -17,73 +16,147 @@ * * to make possible auto-discovering of availables reader at runtime. * - * See {@link Xpp3Helper#getReader(java.lang.Class)}. + * See {@link Xpp3Helper#getReader(java.lang.Class)} and + * {@link Xpp3Helper#getReaders()}. * * + * @param <O> the type of object to be build by the reader. + * * @author chemit * @since 1.0.3 */ -public interface Xpp3Reader { +public interface Xpp3Reader<O> { /** * * @return the type of main object to read */ - Class<?> getType(); + Class<O> getType(); /** - * @see ReaderFactory#newXmlReader + * Read a single instance of the typed object and return it. * + * Note : this is a convinient method to call + * {@link #read(java.io.Reader, boolean)} in strict mode. + * + * In the xml stream, the root tag must be the {@link #getRootTagName()}. + * + * Example : + * <pre> + * <issue>: + * ... + * </issue>: + * </pre> + * * @param reader * @throws IOException * @throws XmlPullParserException * @return Settings */ - Object read(Reader reader) throws IOException, XmlPullParserException; + O read(Reader reader) throws IOException, XmlPullParserException; /** - * @see ReaderFactory#newXmlReader + * Read a single instance of the typed object and return it. * - * @param reader - * @param strict - * @throws IOException - * @throws XmlPullParserException - * @return Settings + * In the xml stream, the root tag must be the {@link #getRootTagName()}. + * + * Example : + * <pre> + * <issue>: + * ... + * </issue>: + * </pre> + * + * @param reader the xml input reader + * @param strict flag to be strict while parsing + * @throws IOException if any io pb + * @throws XmlPullParserException if any parsing pb + * @return the */ - Object read(Reader reader, boolean strict) throws IOException, XmlPullParserException; + O read(Reader reader, boolean strict) throws IOException, XmlPullParserException; /** - * @see ReaderFactory#newXmlReader + * Read some instances of the typed object and return it. * - * @param reader - * @throws IOException - * @throws XmlPullParserException - * @return Settings + * In the xml stream, the root tag must be the {@link #getArrayRootTagName()}. + * + * Note : this is a convinient method to call : + * {@link #readArray(java.io.Reader, boolean)} in stritc mode. + * + * Example : + * <pre> + * <issues>: + * <issue>: + * ... + * </issue>: + * </issues>: + * </pre> + * + * @param reader the xml input reader + * @throws IOException if any io pb + * @throws XmlPullParserException if any parsing pb + * @return the array of read objects. */ - Object[] readArray(Reader reader) throws IOException, XmlPullParserException; + O[] readArray(Reader reader) throws IOException, XmlPullParserException; /** - * @see ReaderFactory#newXmlReader + * Read some instances of the typed object and return it. * - * @param reader - * @param strict - * @throws IOException - * @throws XmlPullParserException - * @return Settings + * In the xml stream, the root tag must be the {@link #getArrayRootTagName()}. + * + * Example : + * <pre> + * <issues>: + * <issue>: + * ... + * </issue>: + * </issues>: + * </pre> + * + * @param reader the xml input reader + * @param strict flag to be strict while parsing + * @throws IOException if any io pb + * @throws XmlPullParserException if any parsing pb + * @return the array of read objects. */ - Object[] readArray(Reader reader, boolean strict) throws IOException, XmlPullParserException; + O[] readArray(Reader reader, boolean strict) throws IOException, XmlPullParserException; /** - * Returns the state of the "add default entities" flag. * - * @return boolean + * @return the name of the root tag of a object to read */ + String getRootTagName(); + + /** + * Set the name of the root tag of an object to read. + * + * @param rootTagName the name of the tag + */ + void setRootTagName(String rootTagName); + + /** + * + * @return the name of the root tag of an array of objets to read + */ + String getArrayRootTagName(); + + /** + * Set the name of the root tag for an array of object to read. + * + * @param parentRootTagName the name of the tag + */ + void setParentRootTagName(String parentRootTagName); + + /** + * @return <code>true</code> if parser will load the default entities, + * <code>false</code> otherwise. + */ boolean isAddDefaultEntities(); /** - * Sets the state of the "add default entities" flag. + * Set the new value of the {@code defaultEntities} flag. * - * @param addDefaultEntities + * @param addDefaultEntities the new value. */ void setAddDefaultEntities(boolean addDefaultEntities); } Modified: trunk/src/test/java/org/nuiton/util/BasePluginTestCase.java =================================================================== --- trunk/src/test/java/org/nuiton/util/BasePluginTestCase.java 2009-09-07 22:54:20 UTC (rev 569) +++ trunk/src/test/java/org/nuiton/util/BasePluginTestCase.java 2009-09-08 16:02:25 UTC (rev 570) @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.apache.maven.plugin.logging.SystemStreamLog; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.PlexusTestCase; import org.junit.Ignore; @@ -41,6 +42,7 @@ * @author chemit */ public abstract class BasePluginTestCase<P extends AbstractPlugin> { + protected static SystemStreamLog log; protected static File basedir; protected static File testDir; protected static Iterator<PluginConfig> configItr; @@ -63,6 +65,7 @@ } protected static <P extends AbstractPlugin> void initConfigs(Class<? extends BasePluginTestCase<P>> klass, String... testNames) throws Exception { + log = new SystemStreamLog(); List<PluginConfig> configs = new ArrayList<PluginConfig>(); String rep = klass.getName(); rep = rep.replaceAll("\\.", File.separator); @@ -70,7 +73,7 @@ File f = new File(getBaseDir(), "target" + File.separator + "test-classes"); //File f = new File(getBaseDir(), "src" + File.separator + "test" + File.separator + "resources"); testDir = new File(f, rep); - System.out.println("test dir : " + testDir); + log.info("test dir : " + testDir.getAbsolutePath().substring(getBaseDir().getAbsolutePath().length()+1)); if (testNames.length == 0) { @@ -85,8 +88,8 @@ if (annotation != null) { Test t = m.getAnnotation(Test.class); if (t != null) { - Ignore i = m.getAnnotation(Ignore.class); - if (i==null) { + Ignore i = m.getAnnotation(Ignore.class); + if (i == null) { testNamesList.add(m.getName()); } }