1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.jelly.tags.xml;
17
18 import org.apache.commons.jelly.JellyTagException;
19 import org.apache.commons.jelly.MissingAttributeException;
20 import org.apache.commons.jelly.XMLOutput;
21 import org.apache.commons.jelly.xpath.XPathComparator;
22 import org.apache.commons.jelly.xpath.XPathTagSupport;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 import org.dom4j.Node;
28
29 import org.jaxen.XPath;
30 import org.jaxen.JaxenException;
31
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.ListIterator;
37
38 /*** A tag which defines a variable from an XPath expression.
39 * This function creates a variable of type {@link List} or {@link org.dom4j.Node}
40 * (for example {@link org.dom4j.Element} or {@link org.dom4j.Attribute}).
41 * Thus, the variable created from xml:set can be
42 * used from the other xml library functions.
43 *
44 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
45 * @version $Revision: 155420 $
46 */
47 public class SetTag extends XPathTagSupport {
48
49 private static final int RETURN_NODE_LIST = 0;
50 private static final int RETURN_FIRST_NODE = 1;
51 private static final int RETURN_STRING_LIST = 2;
52 private static final int RETURN_DELIMITED_STRING_LIST = 3;
53 private static final int RETURN_FIRST_AS_STRING = 4;
54
55 /*** The Log to which logging calls will be made. */
56 private Log log = LogFactory.getLog(SetTag.class);
57
58 /*** The variable name to export. */
59 private String var;
60
61 /*** The XPath expression to evaluate. */
62 private XPath select;
63
64 /*** Xpath comparator for sorting */
65 private XPathComparator xpCmp = null;
66
67 private Boolean single = null;
68
69 private Boolean asString = null;
70
71 private String delimiter = null;
72
73 private String delim = null;
74
75 public SetTag() {
76
77 }
78
79
80
81 public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException {
82 if (var == null) {
83 throw new MissingAttributeException( "var" );
84 }
85 if (select == null) {
86 throw new MissingAttributeException( "select" );
87 }
88
89 Object xpathContext = getXPathContext();
90 Object value = null;
91 try {
92 if( single != null && single.booleanValue() == true ) {
93 value = select.selectSingleNode(xpathContext);
94 } else {
95 value = select.evaluate(xpathContext);
96 }
97 }
98 catch (JaxenException e) {
99 throw new JellyTagException(e);
100 }
101
102 if (value instanceof List) {
103 List list = (List) value;
104
105 if (xpCmp != null && (xpCmp.getXpath() != null)) {
106 Collections.sort(list, xpCmp);
107 }
108 if(list.isEmpty()) {
109 value = null;
110 }
111 }
112
113
114
115 if (single!=null) {
116 if (single.booleanValue() == true) {
117 if(value instanceof List) {
118 List l = (List) value;
119 if (l.size() == 0)
120 value=null;
121 else
122 value=l.get(0);
123 }
124 } else {
125 if(! (value instanceof List) ) {
126 List l = null;
127 if (value==null) {
128 l = new ArrayList(0);
129 } else {
130 l = new ArrayList(1);
131 l.add(value);
132 }
133 value = l;
134 }
135 }
136 }
137
138
139 if(asString != null && asString.booleanValue()) {
140 if(value instanceof Node) {
141 value = ((Node) value).getStringValue();
142 } else if(value instanceof List) {
143 for(ListIterator it = ((List) value).listIterator(); it.hasNext(); ) {
144 Object v = it.next();
145 if(v instanceof Node) {
146 v = ((Node) v).getStringValue();
147 it.set(v);
148 }
149 }
150 }
151 }
152
153
154 if(delimiter != null && value instanceof List) {
155 StringBuffer buff = new StringBuffer();
156 for(Iterator it = ((List) value).iterator(); it.hasNext(); ) {
157 Object v = it.next();
158 if (v instanceof Node) {
159 buff.append( ((Node) v).getStringValue());
160 } else {
161 buff.append(v.toString());
162 }
163 if(it.hasNext()) {
164 buff.append(delimiter);
165 }
166 }
167 buff.setLength(buff.length());
168 value = buff.toString();
169 }
170
171
172
173
174 context.setVariable(var, value);
175 }
176
177 private List valueAsList( final Object value ) {
178 if (value instanceof List) {
179 return (List)value;
180 } else {
181 if (value == null) {
182 return Collections.EMPTY_LIST;
183 } else {
184 return Collections.singletonList(value);
185 }
186 }
187 }
188
189 private Object valueAsSingle( final Object value ) {
190 if (value instanceof List) {
191 List l = (List) value;
192 if (l.isEmpty())
193 return null;
194 else
195 return l.get(0);
196 } else {
197 return value;
198 }
199 }
200
201 private String singleValueAsString( final Object value ) {
202 if (value instanceof Node) {
203 return ((Node) value).getStringValue();
204 } else {
205 return null;
206 }
207 }
208
209 private List nodeListToStringList( final List values ) {
210 List l = new ArrayList(values.size());
211 for (Iterator it = values.iterator(); it.hasNext(); ) {
212 Object v = it.next();
213 String s = singleValueAsString(v);
214 if (s != null) {
215 l.add(s);
216 }
217 }
218 return l;
219 }
220
221 private String joinDelimitedElements( final List values ) {
222 StringBuffer sb = new StringBuffer();
223 int sz = values.size();
224 for (int i = 0; i < sz; i++) {
225 String s = (String)values.get(i);
226 sb.append(s);
227 if (i < sz - 1)
228 sb.append(delim);
229 }
230 return sb.toString();
231 }
232
233 private int determineReturnType() {
234 int resultType;
235 if (single != null && single.booleanValue()) {
236 if (asString != null && asString.booleanValue()) {
237 resultType = RETURN_FIRST_AS_STRING;
238 } else {
239 resultType = RETURN_FIRST_NODE;
240 }
241 } else {
242 if (asString != null && asString.booleanValue()) {
243 if (delim != null) {
244 resultType = RETURN_DELIMITED_STRING_LIST;
245 } else {
246 resultType = RETURN_STRING_LIST;
247 }
248 } else {
249 resultType = RETURN_NODE_LIST;
250 }
251 }
252 return resultType;
253 }
254
255
256
257
258 /*** Sets the variable name to define for this expression
259 */
260 public void setVar(String var) {
261 this.var = var;
262 }
263
264 /*** Sets the XPath expression to evaluate. */
265 public void setSelect(XPath select) {
266 this.select = select;
267 }
268
269 /*** If set to true will only take the first element matching.
270 It then guarantees that the result is of type
271 {@link org.dom4j.Node} thereby making sure that, for example,
272 when an element is selected, one can directly call such methods
273 as setAttribute.<br/>
274 If set to false, guarantees that a list is returned.
275 If set to false, guarantees that a list is returned.
276 */
277 public void setSingle(boolean single) {
278 this.single = new Boolean(single);
279 }
280
281 /*** If set to true, will ensure that the (XPath) text-value
282 * of the selected node is taken instead of the node
283 * itself.
284 * This ensures that, thereafter, string manipulations
285 * can be performed on the result.
286 */
287 public void setAsString(boolean asString) {
288 this.asString = new Boolean(asString);
289 }
290
291 /*** If set, returns a string delimited by this delimiter.
292 * Implies <code>asString</code> to be true.
293 */
294 public void setDelim(String delim) {
295 this.delimiter = delim;
296 if( delim!=null ) {
297 this.asString = Boolean.TRUE;
298 }
299 }
300
301 /*** Sets the xpath expression to use to sort selected nodes.
302 * Ignored if single is true.
303 */
304 public void setSort(XPath sortXPath) throws JaxenException {
305 if (xpCmp == null) xpCmp = new XPathComparator();
306 xpCmp.setXpath(sortXPath);
307 }
308
309 /***
310 * Set whether to sort ascending or descending.
311 */
312 public void setDescending(boolean descending) {
313 if (xpCmp == null) xpCmp = new XPathComparator();
314 xpCmp.setDescending(descending);
315 }
316 }