All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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); } } }