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