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 final String standardXsPrefix = "xs";
239 final Document doc = aNode.getOwnerDocument();
240 final Element annotation = doc.createElementNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, ANNOTATION_ELEMENT_NAME);
241 final Element docElement = doc.createElementNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, DOCUMENTATION_ELEMENT_NAME);
242 final CDATASection xsdDocumentation = doc.createCDATASection(renderer.render(javaDocData, location).trim());
243
244 annotation.setPrefix(standardXsPrefix);
245 docElement.setPrefix(standardXsPrefix);
246
247 annotation.appendChild(docElement);
248 final Node firstChildOfCurrentNode = aNode.getFirstChild();
249 if (firstChildOfCurrentNode == null) {
250 aNode.appendChild(annotation);
251 } else {
252 aNode.insertBefore(annotation, firstChildOfCurrentNode);
253 }
254
255 docElement.appendChild(xsdDocumentation);
256 }
257
258
259
260
261
262 private static MethodLocation getMethodLocation(final Node aNode, final Set<MethodLocation> methodLocations) {
263
264 MethodLocation toReturn = null;
265
266 if (aNode != null && FIELD_METHOD_ELEMENT_NAMES.contains(aNode.getLocalName().toLowerCase())) {
267
268 final MethodLocation validLocation = getFieldOrMethodLocationIfValid(aNode,
269 getContainingClassOrNull(aNode),
270 methodLocations);
271
272
273 if (validLocation != null
274 && MethodLocation.NO_PARAMETERS.equalsIgnoreCase(validLocation.getParametersAsString())) {
275 toReturn = validLocation;
276 }
277 }
278
279
280 return toReturn;
281 }
282
283 private static FieldLocation getFieldLocation(final Node aNode, final Set<FieldLocation> fieldLocations) {
284
285 FieldLocation toReturn = null;
286
287 if (aNode != null && FIELD_METHOD_ELEMENT_NAMES.contains(aNode.getLocalName().toLowerCase())) {
288 toReturn = getFieldOrMethodLocationIfValid(aNode, getContainingClassOrNull(aNode), fieldLocations);
289 }
290
291
292 return toReturn;
293 }
294
295 private static <T extends FieldLocation> T getFieldOrMethodLocationIfValid(
296 final Node aNode,
297 final Node containingClassNode,
298 final Set<? extends FieldLocation> locations) {
299
300 T toReturn = null;
301
302 if (containingClassNode != null) {
303
304
305 for (FieldLocation current : locations) {
306
307
308
309
310
311
312
313
314
315 final String fieldName = current.getMemberName();
316 final String className = current.getClassName();
317
318 try {
319 if (fieldName.equalsIgnoreCase(getName(aNode))
320 && className.equalsIgnoreCase(getName(containingClassNode))) {
321 toReturn = (T) current;
322 }
323 } catch (Exception e) {
324 throw new IllegalStateException("Could not acquire FieldLocation for fieldName ["
325 + fieldName + "] and className [" + className + "]", e);
326 }
327 }
328 }
329
330
331 return toReturn;
332 }
333
334 private static ClassLocation getClassLocation(final Node aNode, final Set<ClassLocation> classLocations) {
335
336 if (aNode != null && "complexType".equalsIgnoreCase(aNode.getLocalName())) {
337
338 final String nodeClassName = getName(aNode);
339 for (ClassLocation current : classLocations) {
340
341
342 if (current.getClassName().equalsIgnoreCase(nodeClassName)) {
343 return current;
344 }
345 }
346 }
347
348
349 return null;
350 }
351
352 private static String getName(final Node aNode) {
353
354 final NamedNodeMap attributes = aNode.getAttributes();
355 if (attributes != null) {
356
357 final Node nameNode = attributes.getNamedItem("name");
358 if (nameNode != null) {
359 return nameNode.getNodeValue().trim();
360 }
361 }
362
363
364 return null;
365 }
366
367 private static Node getContainingClassOrNull(final Node aNode) {
368
369 for (Node current = aNode.getParentNode(); current != null; current = current.getParentNode()) {
370
371 final String localName = current.getLocalName();
372 if ("complexType".equalsIgnoreCase(localName)) {
373 return current;
374 }
375 }
376
377
378 return null;
379 }
380
381 private static String getXPathFor(final Node aNode) {
382
383 List<String> nodeNameList = new ArrayList<String>();
384
385 for (Node current = aNode; current != null; current = current.getParentNode()) {
386 nodeNameList.add(current.getNodeName() + "[@name='" + getName(current) + "]");
387 }
388
389 StringBuilder builder = new StringBuilder();
390 for (ListIterator<String> it = nodeNameList.listIterator(nodeNameList.size()); it.hasPrevious(); ) {
391 builder.append(it.previous());
392 if (it.hasPrevious()) {
393 builder.append("/");
394 }
395 }
396
397 return builder.toString();
398 }
399 }