View Javadoc

1   /*
2    Copyright (C) 2007 Grid Systems, S.A.
3   
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8   
9    This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13  
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18  package com.gridsystems.nextgrid.api.pom.expr;
19  
20  import java.io.Serializable;
21  import java.util.Iterator;
22  import java.util.Map;
23  
24  import org.apache.commons.jxpath.JXPathContext;
25  import org.apache.commons.jxpath.Variables;
26  
27  import com.gridsystems.nextgrid.api.env.AbstractTransientAttribute;
28  
29  import nextgrid.api.env.ProcessEnvironment;
30  import nextgrid.api.pom.Expression;
31  import nextgrid.api.pom.ExpressionException;
32  
33  /**
34   * XPath expression wrapper to simplify expression handling.
35   * <p>
36   * XPath is focused on retrieving and selecting nodes from an XML document.
37   * In this case we use its ability in the following way:
38   *
39   * <ul>
40   *   <li>Jakarta Commons JXPath is used to use arbitrary Java objects as data
41   *       sources, instead of plain XML files.</li>
42   *   <li>We will consider that an XPath expression is equivalent to <tt>true</tt>
43   *       if (and only if) it returns a minimum number of results, which is 1 by
44   *       default.</li>
45   * </ul>
46   *
47   * @author Rodrigo Ruiz
48   */
49  public final class XPathExpression extends Expression {
50  
51    /**
52     * <code>serialVersionUID</code> attribute.
53     */
54    private static final long serialVersionUID = -3368997983579800617L;
55  
56    /**
57     * Minimum accepted number of returned objects.
58     */
59    private int minValues;
60  
61    /**
62     * Maximum accepted number of returned objects.
63     */
64    private int maxValues;
65  
66    /**
67     * Creates a new instance.
68     *
69     * @param text The XPath expression
70     * @throws ExpressionException If a compilation error occurs
71     */
72    public XPathExpression(String text) throws ExpressionException {
73      this(text, 1, Integer.MAX_VALUE);
74    }
75  
76    /**
77     * Creates a new instance.
78     *
79     * @param text   The XPath expression
80     * @param min    The minimum number of results
81     * @param max    The maximum number of results
82     * @throws ExpressionException If a compilation error occurs
83     */
84    public XPathExpression(String text, int min, int max) throws ExpressionException {
85      super("XPath", text);
86      this.minValues = min;
87      this.maxValues = max;
88    }
89  
90    /**
91     * {@inheritDoc}
92     */
93    @Override public void validate() throws ExpressionException {
94      // TODO Implement this method
95    }
96  
97    /**
98     * {@inheritDoc}
99     */
100   @SuppressWarnings("unchecked")
101   @Override public boolean boolEval(ProcessEnvironment env, Object o)
102     throws ExpressionException {
103     JXPathContext root = getRootContext(env);
104     JXPathContext context = JXPathContext.newContext(root, o);
105 
106     context.setLenient(true);
107     Iterator it = context.iterate(this.getText());
108 
109     int count = 0;
110     while (it.hasNext()) {
111       count++;
112       it.next();
113     }
114 
115     return (count >= minValues) && (count <= maxValues);
116   }
117 
118   /**
119    * Gets a shared context instance for the specified Process Environment.
120    * <p>
121    * This context will contain common variable definitions.
122    *
123    * @param env ProcessEnvironment for which to get a context
124    * @return The context
125    */
126   public synchronized JXPathContext getRootContext(ProcessEnvironment env) {
127     Serializable attr = env.getAttribute("XPathExpression.rootContext");
128     ContextHolder holder = null;
129     if (attr instanceof ContextHolder) {
130       holder = (ContextHolder)attr;
131     } else {
132       holder = new ContextHolder();
133       env.setAttribute("XPathExpression.rootContext", holder);
134     }
135 
136     return holder.getValue(env);
137   }
138 
139   /**
140    * Context attribute type. This class is a workaround for using
141    */
142   static class ContextHolder extends AbstractTransientAttribute<JXPathContext> {
143 
144     /**
145      * <code>serialVersionUID</code> attribute.
146      */
147     private static final long serialVersionUID = -4973174396783312256L;
148 
149     /**
150      * {@inheritDoc}
151      */
152     @Override public JXPathContext createValue(ProcessEnvironment env) {
153       JXPathContext ctx = JXPathContext.newContext(null);
154 
155       // Declare global variables
156       Variables vars = ctx.getVariables();
157       for (Map.Entry<String, Serializable> entry : env.getAttributes().entrySet()) {
158         vars.declareVariable(entry.getKey(), entry.getValue());
159       }
160 
161       // Declare utility constants
162       vars.declareVariable("true", Integer.valueOf(1));
163       vars.declareVariable("false", Integer.valueOf(0));
164 
165       return ctx;
166     }
167   }
168 }