com.publicobject.misc.xml.Processors Maven / Gradle / Ivy
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package com.publicobject.misc.xml;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.impl.beans.BeanProperty;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.Matchers;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
/**
* This factory class produces common implementations of the {@link PushProcessor}
* and {@link PopProcessor} interfaces.
*
* @author James Lemieux
*/
public final class Processors {
private Processors() {}
/**
* Returns a {@link PushProcessor} that uses reflection to instantiate a new
* Object via a no-arg constructor in the given clazz
. It then
* associates the new Object in the XML context with the XMLTagPath for the
* end of the current path.
*
* Typical usage of this Processor would be to build a new Customer
* object each time <customer> is seen. The new Customer
* objects would then be placed in the parse context Map using the
* {@link XMLTagPath} of </customer>.
*/
public static PushProcessor createNewObject(Class clazz) {
return createNewObject(clazz, null, null);
}
/**
* Returns a {@link PushProcessor} that uses reflection to instantiate a new
* Object for the given clazz
with arguments that match the
* given params
using the given args
. It then
* associates the new Object in the XML context with the XMLTagPath for the
* end of the current path.
*
*
Typical usage of this Processor would be to build a new Customer
* object each time <customer> is seen. The new Customer
* objects would then be placed in the parse context Map using the
* {@link XMLTagPath} of </customer>.
*/
public static PushProcessor createNewObject(Class clazz, Class[] params, Object[] args) {
return new CreateNewObjectProcessor(clazz, params, args);
}
/**
* Returns a {@link PopProcessor} that takes the active object and adds it
* to a collection from the current context object.
*
*
Typical usage of this Processor would be to insert a newly built
* Customer object into the target EventList each time </customer>
* is seen. The Customer object to insert would be associated with the path
* to </customer> in the parse context map.
*/
public static PopProcessor, B> addObjectToTargetList() {
return new AddObjectToTargetListProcessor();
}
/**
* Returns a {@link PopProcessor} that retrieves an object from the parse
* context Map using the end tag of the parent XMLTagPath, which is
* assumed to be an instance of the given clazz
. It then uses
* reflection to call a setter method for the given
* propertyName
using the object at the end tag for the current
* XMLTagPath within the parse context Map.
*
* Typical usage of this Processor would be to set the name property of
* a Customer object when </name> is seen. The parent XMLTagPath would
* be the </customer> and would be used to fetch the
* partially built Customer object from the parse context Map. That Customer
* would then have setName(...) called on it with the String value associated
* to the current XMLTagPath, </name>.
*/
public static PopProcessor setterMethod(Class clazz, String propertyName) {
return setterMethod(clazz, propertyName, (Converter) Converters.identityConverter());
}
/**
* Functions exactly the same as {@link #setterMethod(Class, String)}
* with the exception that the raw String value is run through a
* {@link Converter} first to produce a more appropriate argument to the
* setter.
*/
public static PopProcessor setterMethod(Class clazz, String propertyName, Converter converter) {
return new CallSetterMethodProcessor(new BeanProperty(clazz, propertyName, false, true), converter);
}
/**
* Returns a {@link PopProcessor} that retrieves an object from the parse
* context Map using the end tag of the parent XMLTagPath, which is
* assumed to be an instance of the given clazz
. It then uses
* reflection to call a getter method for the given
* propertyName
and assumes that the return type is some sort
* of {@link Collection}. The object at the end tag for the current
* XMLTagPath within the parse context Map is added to that {@link Collection}.
*
* Typical usage of this Processor would be to add each Item object
* to a Collection owned by the object, e.g. an Order object, when
* </item> is seen. The parent XMLTagPath would be the </order>
* and would be used to fetch the partially built Order object from the
* parse context Map. That Order would then have
*
*
Collection getItems()
*
*
called on it to retrieve the Collection of Item objects, and then
* the latest Item object would be added to that Collection.
*/
public static PopProcessor addToCollection(Class clazz, String propertyName) {
return addToCollection(clazz, propertyName, (Converter) Converters.identityConverter(), Matchers.trueMatcher());
}
/**
* Functions exactly the same as {@link #addToCollection(Class, String)}
* with the exception that the value is run through a {@link Converter}
* first to produce a more appropriate object to add to the {@link Collection}.
*/
public static PopProcessor addToCollection(Class clazz, String propertyName, Converter converter) {
return addToCollection(clazz, propertyName, converter, (Matcher) Matchers.trueMatcher());
}
/**
* Functions exactly the same as {@link #addToCollection(Class, String, Converter)}
* with the exception that the value is only added if it is matched by the
* given matcher
.
*/
public static PopProcessor addToCollection(Class clazz, String propertyName, Converter converter, Matcher matcher) {
return new AddToCollectionProcessor(new BeanProperty(clazz, propertyName, true, false), converter, matcher);
}
private static class CreateNewObjectProcessor implements PushProcessor {
private final Constructor constructor;
private final Object[] args;
public CreateNewObjectProcessor(Class clazz, Class[] params, Object[] args) {
try {
this.args = args;
this.constructor = clazz.getConstructor(params);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@Override
public T evaluate() {
try {
return constructor.newInstance(args);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
private static class AddObjectToTargetListProcessor implements PopProcessor,T> {
@Override
public void process(EventList baseObject, T value) {
// add the object to the targetList in a thread-safe manner
baseObject.getReadWriteLock().writeLock().lock();
try {
baseObject.add(value);
} finally {
baseObject.getReadWriteLock().writeLock().unlock();
}
}
}
private static class CallSetterMethodProcessor implements PopProcessor {
private final BeanProperty beanProperty;
private final Converter converter;
public CallSetterMethodProcessor(BeanProperty beanProperty, Converter converter) {
if(beanProperty == null) throw new IllegalArgumentException();
if(converter == null) throw new IllegalArgumentException();
this.beanProperty = beanProperty;
this.converter = converter;
}
@Override
public void process(T baseObject, O value) {
// if a converter has been specified, run the value through the converter
C convertedValue = converter.convert(value);
// call setXXX(...) on the setterOwner
beanProperty.set(baseObject, convertedValue);
}
}
private static class AddToCollectionProcessor implements PopProcessor {
private final BeanProperty beanProperty;
private final Converter converter;
private final Matcher matcher;
public AddToCollectionProcessor(BeanProperty beanProperty, Converter converter, Matcher matcher) {
if(beanProperty == null) throw new IllegalArgumentException();
if(converter == null) throw new IllegalArgumentException();
if(matcher == null) throw new IllegalArgumentException();
this.beanProperty = beanProperty;
this.converter = converter;
this.matcher = matcher;
}
@Override
public void process(T baseObject, V value) {
// if a converter has been specified, run the value through the converter
value = converter.convert(value);
// if element doesn't pass the matcher, return early
if (!matcher.matches(value)) return;
// get the Collection
final Collection c = (Collection) beanProperty.get(baseObject);
// add the value to the Collection
c.add(value);
}
}
}