View Javadoc

1   /*
2    * Copyright 2010 Capgemini
3    * Licensed under the Apache License, Version 2.0 (the "License"); 
4    * you may not use this file except in compliance with the License. 
5    * You may obtain a copy of the License at 
6    * 
7    * http://www.apache.org/licenses/LICENSE-2.0 
8    * 
9    * Unless required by applicable law or agreed to in writing, software 
10   * distributed under the License is distributed on an "AS IS" BASIS, 
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
12   * See the License for the specific language governing permissions and 
13   * limitations under the License. 
14   * 
15   */
16  package org.xmlfield.core.internal;
17  
18  import static org.apache.commons.lang.StringUtils.isBlank;
19  
20  import java.lang.reflect.Method;
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.Map.Entry;
24  
25  import org.w3c.dom.Node;
26  import org.xmlfield.annotations.Association;
27  import org.xmlfield.annotations.ExplicitCollection;
28  import org.xmlfield.annotations.FieldXPath;
29  import org.xmlfield.annotations.Namespaces;
30  import org.xmlfield.annotations.ResourceXPath;
31  import org.xmlfield.core.XmlField;
32  import org.xmlfield.core.api.XmlFieldNode;
33  import org.xmlfield.core.api.XmlFieldNodeModifier;
34  import org.xmlfield.core.api.XmlFieldObject;
35  
36  /**
37   * Xml manipulation node utility class.
38   * 
39   * @author David Andrianavalontsalama
40   * @author Nicolas Richeton <nicolas.richeton@capgemini.com>
41   * @author Guillaume Mary <guillaume.mary@capgemini.com>
42   */
43  public abstract class XmlFieldUtils {
44  
45  	/**
46  	 * All the method prefixes known by xmlfield.
47  	 */
48  	private static final String[] METHOD_PREFIXES = { "set", "get", "has",
49  			"is", "addTo", "sizeOf", "isNull", "new", "removeFrom" };
50  
51  	/**
52  	 * 
53  	 * @param namespaces
54  	 * @param contextNode
55  	 * @param elementName
56  	 * @param stringValue
57  	 * @return
58  	 */
59  	public static XmlFieldNode createComplexElement(NamespaceMap namespaces,
60  			XmlFieldNode contextNode, String elementName, String stringValue,
61  			XmlField xf) {
62  
63  		XmlFieldNode result = contextNode;
64  
65  		XmlFieldNodeModifier modifier = xf._getModifier();
66  
67  		// Create required node
68  		switch (XPathUtils.getElementType(elementName)) {
69  		case XPathUtils.TYPE_ATTRIBUTE:
70  			modifier.createAttribute(contextNode, elementName.substring(1),
71  					stringValue);
72  			break;
73  		case XPathUtils.TYPE_TAG:
74  
75  			result = modifier.createElement(namespaces, contextNode,
76  					elementName, stringValue);
77  			break;
78  
79  		case XPathUtils.TYPE_TAG_WITH_ATTRIBUTE:
80  			// Create tag
81  			String tagName = XPathUtils.getElementName(elementName);
82  			result = modifier.createElement(namespaces, contextNode, tagName,
83  					stringValue);
84  
85  			// Then create attributes in selector
86  			Map<String, String> attributes = XPathUtils
87  					.getElementSelectorAttributes(elementName);
88  			for (String key : attributes.keySet()) {
89  				modifier.createAttribute(result, key, attributes.get(key));
90  			}
91  
92  			break;
93  		}
94  
95  		return result;
96  
97  	}
98  
99  	/**
100 	 * Create an empty tag matching the given data.
101 	 * 
102 	 * @param tag
103 	 *            an xml tag, can be of the form "ns:name" or "name"
104 	 * @param namespaces
105 	 *            the namespaces to use or null if none
106 	 * @return a string representing the given tag with the given namespaces
107 	 */
108 	public static String emptyTag(String tag, NamespaceMap namespaces) {
109 		StringBuilder builder = new StringBuilder("<");
110 		builder.append(tag);
111 
112 		if (namespaces != null) {
113 			for (Entry<String, String> entry : namespaces) {
114 				builder.append(" xmlns:");
115 				builder.append(entry.getKey());
116 				builder.append("=\"");
117 				builder.append(entry.getValue());
118 				builder.append("\"");
119 			}
120 		}
121 		builder.append(" />");
122 
123 		return builder.toString();
124 	}
125 
126 	/**
127 	 * Used to get an element xpath expression from an attribute expression
128 	 * 
129 	 * @param fieldXPath
130 	 *            field xpath
131 	 * @return element xpath expression
132 	 */
133 	public static String getElementXPath(String fieldXPath, Class<?> type) {
134 		String elementXPath = XPathUtils.getElementXPath(fieldXPath);
135 		if (elementXPath == null) {
136 			elementXPath = XPathUtils.getElementXPath(getResourceXPath(type)
137 					+ "/" + fieldXPath);
138 		}
139 		return elementXPath;
140 	}
141 
142 	/**
143 	 * Function to get all the associations of explicit collection
144 	 * 
145 	 * @param method
146 	 *            method to get the Collection
147 	 * @return map with an association of xpath name and classes
148 	 */
149 	public static Map<String, Class<?>> getExplicitCollections(
150 			final Method method) {
151 
152 		Map<String, Class<?>> explicitAssociations = new HashMap<String, Class<?>>();
153 
154 		if (method == null) {
155 			return null;
156 		}
157 
158 		final ExplicitCollection explicitCollection = method
159 				.getAnnotation(ExplicitCollection.class);
160 
161 		if (explicitCollection != null) {
162 
163 			for (int j = 0; j < explicitCollection.value().length; j++) {
164 				// looking for ExplicitCollectionAssociation in a
165 				// ExplicitCollection
166 				if (explicitCollection.value()[j] != null) {
167 					Association explicitCollectionAssociation = explicitCollection
168 							.value()[j];
169 					explicitAssociations.put(
170 							explicitCollectionAssociation.xpath(),
171 							explicitCollectionAssociation.targetClass());
172 				}
173 
174 			}
175 
176 			return explicitAssociations;
177 		}
178 
179 		final String methodName = method.getName();
180 
181 		if (methodName.startsWith("get")) {
182 			return explicitAssociations;
183 		}
184 
185 		for (final String prefix : METHOD_PREFIXES) {
186 
187 			if (methodName.startsWith(prefix)) {
188 
189 				final String methodSuffix = methodName.substring(prefix
190 						.length());
191 
192 				final Method getterMethod;
193 
194 				try {
195 
196 					getterMethod = method.getDeclaringClass().getMethod(
197 							"get" + methodSuffix);
198 
199 				} catch (final NoSuchMethodException e) {
200 
201 					return null;
202 				}
203 
204 				return getExplicitCollections(getterMethod);
205 			}
206 		}
207 
208 		return explicitAssociations;
209 	}
210 
211 	/**
212 	 * récupère le contenu de l'annotation @{@link FieldFormat} sur une méthode,
213 	 * ou <tt>null</tt>. Si la méthode passée en paramètre est un
214 	 * <em>setter</em>, cette méthode regarde aussi du côté du <em>getter</em>
215 	 * correspondant.
216 	 * 
217 	 * @param method
218 	 *            la méthode pour laquelle on veut le contenu de l'annotation.
219 	 * @return la valeur de l'annotation, ou <tt>null</tt>.
220 	 */
221 	public static String getFieldFormat(final Method method) {
222 
223 		final FieldXPath fieldXPath = getFieldXPathAnnotation(method);
224 
225 		if (fieldXPath == null) {
226 
227 			return null;
228 		}
229 
230 		final String format = fieldXPath.format();
231 
232 		if (isBlank(format)) {
233 
234 			return null;
235 		}
236 
237 		return format;
238 	}
239 
240 	/**
241 	 * récupère le contenu de l'annotation @{@link FieldXPath} sur une méthode,
242 	 * ou <tt>null</tt>. Si la méthode passée en paramètre est un
243 	 * <em>setter</em>, cette méthode regarde aussi du côté du <em>getter</em>
244 	 * correspondant.
245 	 * 
246 	 * @param method
247 	 *            la méthode pour laquelle on veut le contenu de l'annotation.
248 	 * @return la valeur de l'annotation, ou <tt>null</tt>.
249 	 */
250 	public static String getFieldXPath(final Method method) {
251 
252 		final FieldXPath fieldXPath = getFieldXPathAnnotation(method);
253 
254 		if (fieldXPath == null) {
255 
256 			return null;
257 		}
258 
259 		return fieldXPath.value();
260 	}
261 
262 	private static FieldXPath getFieldXPathAnnotation(final Method method) {
263 
264 		if (method == null) {
265 			return null;
266 		}
267 
268 		final FieldXPath fieldXPath = method.getAnnotation(FieldXPath.class);
269 
270 		if (fieldXPath != null) {
271 
272 			return fieldXPath;
273 		}
274 
275 		final String methodName = method.getName();
276 
277 		if (methodName.startsWith("get")) {
278 			return null;
279 		}
280 
281 		for (final String prefix : METHOD_PREFIXES) {
282 
283 			if (methodName.startsWith(prefix)) {
284 
285 				final String methodSuffix = methodName.substring(prefix
286 						.length());
287 
288 				final Method getterMethod;
289 
290 				try {
291 
292 					getterMethod = method.getDeclaringClass().getMethod(
293 							"get" + methodSuffix);
294 
295 				} catch (final NoSuchMethodException e) {
296 
297 					return null;
298 				}
299 
300 				return getFieldXPathAnnotation(getterMethod);
301 			}
302 		}
303 
304 		return null;
305 	}
306 
307 	/**
308 	 * récupère le contenu de l'annotation @{@link FieldXPathType} sur une
309 	 * méthode, ou <tt>null</tt>.
310 	 * 
311 	 * @param method
312 	 *            la méthode pour laquelle on veut le contenu de l'annotation.
313 	 * @return la valeur de l'annotation, ou <tt>null</tt>.
314 	 */
315 	public static Class<?> getFieldXPathType(final Method method) {
316 
317 		final FieldXPath fieldXPath = getFieldXPathAnnotation(method);
318 
319 		if (fieldXPath == null) {
320 
321 			return null;
322 		}
323 
324 		final Class<?> xpathType = fieldXPath.xpathType();
325 
326 		if (xpathType == null || void.class.equals(xpathType)) {
327 
328 			return null;
329 		}
330 
331 		return xpathType;
332 	}
333 
334 	/**
335 	 * récupère l'annotation @{@link Namespaces} sur une class, ou <tt>null</tt>
336 	 * .
337 	 * 
338 	 * @param type
339 	 *            la classe pour laquelle on veut le contenu de l'annotation.
340 	 * @return l'annotation, ou <tt>null</tt>.
341 	 */
342 	public static NamespaceMap getResourceNamespaces(final Class<?> type) {
343 
344 		if (type == null) {
345 			return null;
346 		}
347 
348 		final Namespaces namespaces = type.getAnnotation(Namespaces.class);
349 
350 		final Class<?>[] interfaces = type.getInterfaces();
351 
352 		if (interfaces == null || interfaces.length == 0) {
353 
354 			return namespaces == null ? null : new NamespaceMap(namespaces);
355 		}
356 
357 		final NamespaceMap nMap = new NamespaceMap(namespaces);
358 
359 		for (final Class<?> c : interfaces) {
360 
361 			final NamespaceMap ns = getResourceNamespaces(c);
362 
363 			nMap.addNamespaces(ns);
364 		}
365 
366 		return nMap.isEmpty() ? null : nMap;
367 	}
368 
369 	/**
370 	 * récupère le contenu de l'annotation @{@link ResourceXPath} sur une class,
371 	 * ou <tt>null</tt>.
372 	 * 
373 	 * @param type
374 	 *            la classe pour laquelle on veut le contenu de l'annotation.
375 	 * @return la valeur de l'annotation, ou <tt>null</tt>.
376 	 */
377 	public static String getResourceXPath(final Class<?> type) {
378 
379 		if (type == null) {
380 			return null;
381 		}
382 
383 		final ResourceXPath resourceXPathDeclaration = type
384 				.getAnnotation(ResourceXPath.class);
385 
386 		if (resourceXPathDeclaration == null) {
387 
388 			return null;
389 		}
390 
391 		return resourceXPathDeclaration.value();
392 	}
393 
394 	/**
395 	 * récupérer le nœud XML qui correspond à un objet.
396 	 * 
397 	 * @param object
398 	 *            l'objet.
399 	 * @return le nœud XML qui correspond à l'objet.
400 	 */
401 	public static XmlFieldNode getXmlFieldNode(final Object object) {
402 		if (object instanceof XmlFieldObject) {
403 			return ((XmlFieldObject) object).toNode();
404 		}
405 		if (object instanceof XmlFieldNode) {
406 			return (XmlFieldNode) object;
407 		}
408 		return null;
409 	}
410 
411 	/**
412 	 * Remove element from document. item must have been acquired either by
413 	 * calling {@link XmlField#nodeToObject(Node, Class)} or any get or addTo
414 	 * method.
415 	 * 
416 	 * @param item
417 	 *            Item to remove
418 	 */
419 	public static void remove(final Object item, XmlField xf) {
420 		remove(getXmlFieldNode(item), xf);
421 	}
422 
423 	/**
424 	 * retirer un nœud XML de son parent.
425 	 * 
426 	 * @param node
427 	 *            le nœud à retirer.
428 	 */
429 	public static void remove(final XmlFieldNode node, XmlField xf) {
430 
431 		if (node == null) {
432 			return;
433 		}
434 
435 		final XmlFieldNode parent = node.getParentNode();
436 
437 		if (parent != null) {
438 			xf._getModifier().removeChild(parent, node);
439 		}
440 	}
441 
442 }