1   package org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc;
2   
3   import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.location.ClassLocation;
4   import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.location.FieldLocation;
5   import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.location.MethodLocation;
6   import org.w3c.dom.CDATASection;
7   import org.w3c.dom.Document;
8   import org.w3c.dom.Element;
9   import org.w3c.dom.NamedNodeMap;
10  import org.w3c.dom.Node;
11  
12  import javax.xml.XMLConstants;
13  import java.util.ArrayList;
14  import java.util.Arrays;
15  import java.util.Collections;
16  import java.util.List;
17  import java.util.ListIterator;
18  import java.util.Set;
19  import java.util.SortedMap;
20  
21  
22  
23  
24  
25  
26  
27  public final class DomHelper {
28  
29      private static final String NAME_ATTRIBUTE = "name";
30      private static final String VALUE_ATTRIBUTE = "value";
31  
32      
33  
34  
35      public static final String ANNOTATION_ELEMENT_NAME = "annotation";
36  
37      
38  
39  
40      public static final String DOCUMENTATION_ELEMENT_NAME = "documentation";
41  
42      
43  
44  
45  
46  
47  
48      public static final String XSD_SCHEMA_NAMESPACE_PREFIX = "xs";
49  
50      
51  
52  
53      public static final List<String> CLASS_FIELD_METHOD_ELEMENT_NAMES = Arrays.asList("element", "attribute");
54  
55      
56  
57  
58      public static final List<String> ENUMERATION_FIELD_METHOD_ELEMENT_NAMES = Collections.singletonList("enumeration");
59  
60      
61  
62  
63      private DomHelper() {
64      }
65  
66      
67  
68  
69  
70  
71  
72      public static String getNameAttribute(final Node aNode) {
73          return getNamedAttribute(aNode, NAME_ATTRIBUTE);
74      }
75  
76      
77  
78  
79  
80  
81  
82      public static String getValueAttribute(final Node aNode) {
83          return getNamedAttribute(aNode, VALUE_ATTRIBUTE);
84      }
85  
86      
87  
88  
89  
90  
91  
92      public static boolean isNamedElement(final Node aNode) {
93  
94          final boolean isElementNode = aNode != null && aNode.getNodeType() == Node.ELEMENT_NODE;
95  
96          return isElementNode
97                  && getNamedAttribute(aNode, NAME_ATTRIBUTE) != null
98                  && !getNamedAttribute(aNode, NAME_ATTRIBUTE).isEmpty();
99      }
100 
101     
102 
103 
104 
105 
106 
107     public static String getElementTagName(final Node aNode) {
108 
109         if (aNode != null && aNode.getNodeType() == Node.ELEMENT_NODE) {
110 
111             final Element theElement = (Element) aNode;
112             return theElement.getTagName();
113         }
114 
115         
116         return null;
117     }
118 
119     
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134     public static void addXmlDocumentAnnotationTo(final Node aNode, final String formattedDocumentation) {
135 
136         if (aNode != null && formattedDocumentation != null && !formattedDocumentation.isEmpty()) {
137 
138             
139             final Document doc = aNode.getOwnerDocument();
140             final Element annotation = doc.createElementNS(
141                     XMLConstants.W3C_XML_SCHEMA_NS_URI, ANNOTATION_ELEMENT_NAME);
142             final Element docElement = doc.createElementNS(
143                     XMLConstants.W3C_XML_SCHEMA_NS_URI, DOCUMENTATION_ELEMENT_NAME);
144             final CDATASection xsdDocumentation = doc.createCDATASection(formattedDocumentation);
145 
146             
147             annotation.setPrefix(XSD_SCHEMA_NAMESPACE_PREFIX);
148             docElement.setPrefix(XSD_SCHEMA_NAMESPACE_PREFIX);
149 
150             
151             annotation.appendChild(docElement);
152             final Node firstChildOfCurrentNode = aNode.getFirstChild();
153             if (firstChildOfCurrentNode == null) {
154                 aNode.appendChild(annotation);
155             } else {
156                 aNode.insertBefore(annotation, firstChildOfCurrentNode);
157             }
158 
159             
160             docElement.appendChild(xsdDocumentation);
161         }
162     }
163 
164     
165 
166 
167 
168 
169 
170     public static String getXPathFor(final Node aNode) {
171 
172         List<String> nodeNameList = new ArrayList<String>();
173 
174         for (Node current = aNode; current != null; current = current.getParentNode()) {
175 
176             final String currentNodeName = current.getNodeName();
177             final String nameAttribute = DomHelper.getNameAttribute(current);
178 
179             if (currentNodeName.toLowerCase().endsWith("enumeration")) {
180 
181                 
182                 nodeNameList.add(currentNodeName + "[@value='" + getValueAttribute(current) + "']");
183 
184             } else if (nameAttribute == null) {
185 
186                 
187                 nodeNameList.add(current.getNodeName());
188 
189             } else {
190 
191                 
192                 nodeNameList.add(current.getNodeName() + "[@name='" + nameAttribute + "']");
193             }
194         }
195 
196         StringBuilder builder = new StringBuilder();
197         for (ListIterator<String> it = nodeNameList.listIterator(nodeNameList.size()); it.hasPrevious(); ) {
198             builder.append(it.previous());
199             if (it.hasPrevious()) {
200                 builder.append("/");
201             }
202         }
203 
204         return builder.toString();
205     }
206 
207     
208 
209 
210 
211 
212 
213 
214     public static ClassLocation getClassLocation(final Node aNode, final Set<ClassLocation> classLocations) {
215 
216 
217         if (aNode != null) {
218 
219             
220             final String nodeLocalName = aNode.getLocalName();
221             final boolean acceptableType = "complexType".equalsIgnoreCase(nodeLocalName)
222                     || "simpleType".equalsIgnoreCase(nodeLocalName);
223 
224             if (acceptableType) {
225 
226                 final String nodeClassName = DomHelper.getNameAttribute(aNode);
227                 for (ClassLocation current : classLocations) {
228 
229                     
230 
231                     
232                     final String effectiveClassName = current.getAnnotationRenamedTo() == null
233                             ? current.getClassName()
234                             : current.getAnnotationRenamedTo();
235                     if (effectiveClassName.equalsIgnoreCase(nodeClassName)) {
236                         return current;
237                     }
238                 }
239             }
240         }
241 
242         
243         return null;
244     }
245 
246     
247 
248 
249 
250 
251 
252 
253     public static MethodLocation getMethodLocation(final Node aNode, final Set<MethodLocation> methodLocations) {
254 
255         MethodLocation toReturn = null;
256 
257         if (aNode != null && CLASS_FIELD_METHOD_ELEMENT_NAMES.contains(aNode.getLocalName().toLowerCase())) {
258 
259             final MethodLocation validLocation = getFieldOrMethodLocationIfValid(aNode,
260                     getContainingClassOrNull(aNode),
261                     methodLocations);
262 
263             
264             if (validLocation != null
265                     && MethodLocation.NO_PARAMETERS.equalsIgnoreCase(validLocation.getParametersAsString())) {
266                 toReturn = validLocation;
267             }
268         }
269 
270         
271         return toReturn;
272     }
273 
274     
275 
276 
277 
278 
279 
280 
281     public static FieldLocation getFieldLocation(final Node aNode, final Set<FieldLocation> fieldLocations) {
282 
283         FieldLocation toReturn = null;
284 
285         if (aNode != null) {
286 
287             if (CLASS_FIELD_METHOD_ELEMENT_NAMES.contains(aNode.getLocalName().toLowerCase())) {
288 
289                 
290                 toReturn = getFieldOrMethodLocationIfValid(aNode, getContainingClassOrNull(aNode), fieldLocations);
291             } else if (ENUMERATION_FIELD_METHOD_ELEMENT_NAMES.contains(aNode.getLocalName().toLowerCase())) {
292 
293                 
294                 toReturn = getFieldOrMethodLocationIfValid(aNode, getContainingClassOrNull(aNode), fieldLocations);
295             }
296         }
297 
298         
299         return toReturn;
300     }
301 
302     
303 
304 
305 
306 
307 
308 
309 
310 
311 
312     public static <T extends FieldLocation> T getFieldOrMethodLocationIfValid(
313             final Node aNode,
314             final Node containingClassNode,
315             final Set<? extends FieldLocation> locations) {
316 
317         T toReturn = null;
318 
319         if (containingClassNode != null) {
320 
321             
322             for (FieldLocation current : locations) {
323 
324                 
325                 
326                 
327                 
328                 
329                 
330                 
331                 
332                 
333                 
334 
335                 
336                 final String fieldName = current.getAnnotationRenamedTo() == null
337                         ? current.getMemberName()
338                         : current.getAnnotationRenamedTo();
339                 final String className = current.getClassName();
340 
341                 try {
342 
343                     
344                     
345                     
346                     
347                     
348                     
349                     
350                     
351                     
352                     final String attributeValue = DomHelper.getNameAttribute(aNode) == null
353                             ? DomHelper.getValueAttribute(aNode)
354                             : DomHelper.getNameAttribute(aNode);
355                     if (fieldName.equalsIgnoreCase(attributeValue)
356                             && className.equalsIgnoreCase(DomHelper.getNameAttribute(containingClassNode))) {
357                         toReturn = (T) current;
358                     }
359                 } catch (Exception e) {
360                     throw new IllegalStateException("Could not acquire FieldLocation for fieldName ["
361                             + fieldName + "] and className [" + className + "]", e);
362                 }
363             }
364         }
365 
366         
367         return toReturn;
368     }
369 
370     
371 
372 
373 
374 
375 
376 
377 
378 
379     public static void insertXmlDocumentationAnnotationsFor(
380             final Node aNode,
381             final SortedMap<ClassLocation, JavaDocData> classJavaDocs,
382             final SortedMap<FieldLocation, JavaDocData> fieldJavaDocs,
383             final SortedMap<MethodLocation, JavaDocData> methodJavaDocs,
384             final JavaDocRenderer renderer) {
385 
386         JavaDocData javaDocData = null;
387         SortableLocation location = null;
388 
389         
390         final ClassLocation classLocation = DomHelper.getClassLocation(aNode, classJavaDocs.keySet());
391         if (classLocation != null) {
392             javaDocData = classJavaDocs.get(classLocation);
393             location = classLocation;
394         } else {
395 
396             final FieldLocation fieldLocation = DomHelper.getFieldLocation(aNode, fieldJavaDocs.keySet());
397             if (fieldLocation != null) {
398                 javaDocData = fieldJavaDocs.get(fieldLocation);
399                 location = fieldLocation;
400             } else {
401 
402                 final MethodLocation methodLocation = DomHelper.getMethodLocation(aNode, methodJavaDocs.keySet());
403                 if (methodLocation != null) {
404                     javaDocData = methodJavaDocs.get(methodLocation);
405                     location = methodLocation;
406                 }
407             }
408         }
409 
410         
411         if (javaDocData == null) {
412 
413             final String nodeName = aNode.getNodeName();
414             String humanReadableName = DomHelper.getNameAttribute(aNode);
415 
416             if (humanReadableName == null && nodeName.toLowerCase().endsWith("enumeration")) {
417                 humanReadableName = "enumeration#" + getValueAttribute(aNode);
418             }
419 
420             throw new IllegalStateException("Could not find JavaDocData for XSD node ["
421                     + humanReadableName + "] with XPath [" + DomHelper.getXPathFor(aNode) + "]");
422         }
423 
424         
425         final String processedJavaDoc = renderer.render(javaDocData, location).trim();
426         DomHelper.addXmlDocumentAnnotationTo(aNode, processedJavaDoc);
427     }
428 
429     
430     
431     
432 
433     private static Node getContainingClassOrNull(final Node aNode) {
434 
435         for (Node current = aNode.getParentNode(); current != null; current = current.getParentNode()) {
436 
437             final String localName = current.getLocalName();
438             final boolean foundClassMatch = "complexType".equalsIgnoreCase(localName)
439                     || "simpleType".equalsIgnoreCase(localName);
440 
441             if (foundClassMatch) {
442                 return current;
443             }
444         }
445 
446         
447         return null;
448     }
449 
450     private static String getNamedAttribute(final Node aNode, final String attributeName) {
451 
452         
453         if (aNode == null) {
454             return null;
455         }
456 
457         final NamedNodeMap attributes = aNode.getAttributes();
458         if (attributes != null) {
459 
460             final Node nameNode = attributes.getNamedItem(attributeName);
461             if (nameNode != null) {
462                 return nameNode.getNodeValue().trim();
463             }
464         }
465 
466         
467         return null;
468     }
469 }