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 }