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

jaxx.runtime.JXPathDecorator Maven / Gradle / Ivy

The newest version!
package jaxx.runtime;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * JXPath decorator based on {@link String#format(String, Object[])} method.
 * 

* To use it, give to him a expression where all jxpath to apply on bean are boxed in ${}. *

* After the jxpath token you must specifiy the formatter to apply of the jxpath token. *

* For example : *

 * Decorator<Object> d = JXPathDecorator.newDecorator(JXPathDecorator.class,"expr = ${expressions}$s");
 * assert "expr = %1$s" == d.getExpression();
 * assert 1 == d.getNbToken();
 * assert java.util.Arrays.asList("expression") == d.getTokens();
 * assert "expr = %1$s" == d.toString(d);
 * 
* * @param type of data * @author chemit * @see Decorator */ public class JXPathDecorator extends Decorator { /** to use log facility, just put in your code: log.info(\"...\"); */ private static final Log log = LogFactory.getLog(JXPathDecorator.class); private static final long serialVersionUID = 1L; /** * Factory method to instanciate a new {@link JXPathDecorator} for the given class {@link O} and expression. * * @param internalClass the class of the objects decorated by the new decorator * @param expression the expression to use to decorated objects * @param the generic type of class to be decorated by the new decorator * @return the new instanciated decorator * @throws IllegalArgumentException if the expression is not valid, says: *

* - a missing right brace was detected. *

* - a ${ was found in a jxpath token. * @throws NullPointerException if internalClass parameter is null. */ public static JXPathDecorator newDecorator(Class internalClass, String expression) throws IllegalArgumentException, NullPointerException { return new JXPathDecorator(internalClass, expression, true); } /** * Sort a list of data based on the first token property of a given context * in a given decorator. * * @param type of data to sort * @param decorator the decorator to use to sort * @param datas the list of data to sort * @param pos the index of context to used in decorator to obtain sorted property. */ public static void sort(JXPathDecorator decorator, List datas, int pos) { Comparator c = null; boolean cachedComparator = false; try { c = decorator.getComparator(pos); cachedComparator = c instanceof JXPathComparator; if (cachedComparator) { ((JXPathComparator) c).init(decorator, datas); } Collections.sort(datas, c); } finally { if (cachedComparator) { ((JXPathComparator) c).clear(); } } } public static class JXPathComparator implements Comparator { protected Map>> valueCache; private final String expression; public JXPathComparator(String expression) { this.expression = expression; this.valueCache = new HashMap>>(); } @Override public int compare(O o1, O o2) { Comparable> c1 = valueCache.get(o1); Comparable> c2 = valueCache.get(o2); return c1.compareTo(c2); } public void clear() { valueCache.clear(); } public void init(JXPathDecorator decorator, List datas) { clear(); for (O data : datas) { JXPathContext jxcontext = JXPathContext.newContext(data); Comparable> key = decorator.getTokenValue(jxcontext, expression); valueCache.put(data, key); } } } public static class Context implements java.io.Serializable { /** * expression to format using {@link String#format(String, Object[])}, all variables are compute * using using the jxpath tokens. */ protected String expression; /** list of jxpath tokens to apply on expression */ protected String[] tokens; protected transient Comparator comparator; private static final long serialVersionUID = 1L; public Context(String expression, String[] tokens) { this.expression = expression; this.tokens = tokens; } public String getFirstProperty() { return tokens[0]; } public Comparator getComparator(int pos) { if (comparator == null) { comparator = new JXPathComparator(tokens[pos]); } return comparator; } public void setComparator(Comparator comparator) { this.comparator = comparator; } @Override public String toString() { return ""; } } /** the computed context of the decorator */ protected Context context; /** nb jxpath tokens to compute */ protected int nbToken; /** the initial expression used to compute the decorator context. */ protected String initialExpression; @Override public String toString(Object bean) { if (bean == null) { return null; } JXPathContext jxcontext = JXPathContext.newContext(bean); Object[] args = new Object[nbToken]; for (int i = 0; i < nbToken; i++) { try { args[i] = getTokenValue(jxcontext, context.tokens[i]); } catch (Exception e) { log.error("can not obtain token " + context.tokens[i] + "on object " + bean + " for reason " + e.getMessage(), e); } } return String.format(context.expression, args); } @SuppressWarnings({"unchecked"}) protected Comparable> getTokenValue(JXPathContext jxcontext, String token) { // assume all values are comparable return (Comparable>) jxcontext.getValue(token); } public String getProperty(int pos) { return getTokens()[pos]; } public String getExpression() { return context.expression; } public String[] getTokens() { return context.tokens; } public int getNbToken() { return nbToken; } public String getInitialExpression() { return initialExpression; } @Override public String toString() { return super.toString() + "<" + context + ">"; } public void setContext(Context context) { this.context = context; this.nbToken = context.tokens.length; // always reset comparator //this.context.comparator = null; if (log.isDebugEnabled()) { log.debug(context); } } public JXPathDecorator(Class internalClass, String expression, boolean creatContext) throws IllegalArgumentException, NullPointerException { super(internalClass); this.initialExpression = expression; if (creatContext) { setContext(JXPathDecorator.createInitialContext(expression)); if (log.isDebugEnabled()) { log.debug(expression + " --> " + this.context); } } } @SuppressWarnings({"unchecked"}) protected Comparator getComparator(int pos) { ensureTokenIndex(this, pos); return context.getComparator(pos); } public static Context createInitialContext(String expression) { List lTokens = new ArrayList(); StringBuilder buffer = new StringBuilder(); int size = expression.length(); int end = -1; int start; while ((start = expression.indexOf("${", end + 1)) > -1) { if (start > end + 1) { // prefix of next jxpath token buffer.append(expression.substring(end + 1, start)); } // seek end of jxpath end = expression.indexOf("}", start + 1); if (end == -1) { throw new IllegalArgumentException("could not find the rigth brace starting at car " + start + " : " + expression.substring(start + 2)); } String jxpath = expression.substring(start + 2, end); // not allowed ${ inside a jxpath token if (jxpath.indexOf("${") > -1) { throw new IllegalArgumentException("could not find a ${ inside a jxpath expression at car " + (start + 2) + " : " + jxpath); } // save the jxpath token lTokens.add(jxpath); // replace jxpath token in expresion with a string format variable buffer.append("%").append(lTokens.size()); } if (size > (end + 1)) { // suffix after end jxpath (or all expression if no jxpath) buffer.append(expression.substring(end + 1)); } return new Context(buffer.toString(), lTokens.toArray(new String[lTokens.size()])); } protected static void ensureTokenIndex(JXPathDecorator decorator, int pos) { if (pos < -1 || pos > decorator.getNbToken()) { throw new ArrayIndexOutOfBoundsException("token index " + pos + " is out of bound, can be inside [" + 0 + "," + decorator.nbToken + "]"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy