1 package org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc;
2
3 import org.codehaus.mojo.jaxb2.AbstractJaxbMojo;
4 import org.codehaus.mojo.jaxb2.BufferingLog;
5 import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.NodeProcessor;
6 import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
7 import org.codehaus.mojo.jaxb2.shared.Validate;
8 import org.custommonkey.xmlunit.Diff;
9 import org.custommonkey.xmlunit.XMLUnit;
10 import org.junit.Assert;
11 import org.junit.Before;
12 import org.w3c.dom.Document;
13 import org.w3c.dom.NamedNodeMap;
14 import org.w3c.dom.Node;
15 import org.w3c.dom.NodeList;
16 import org.xml.sax.InputSource;
17 import org.xml.sax.SAXException;
18
19 import javax.xml.bind.JAXBContext;
20 import javax.xml.bind.SchemaOutputResolver;
21 import javax.xml.parsers.DocumentBuilderFactory;
22 import javax.xml.transform.OutputKeys;
23 import javax.xml.transform.Result;
24 import javax.xml.transform.Transformer;
25 import javax.xml.transform.TransformerFactory;
26 import javax.xml.transform.dom.DOMSource;
27 import javax.xml.transform.stream.StreamResult;
28 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.StringReader;
34 import java.io.StringWriter;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.SortedMap;
40 import java.util.TreeMap;
41
42
43
44
45 public abstract class AbstractSourceCodeAwareNodeProcessingTest {
46
47
48
49
50 public static final String DEFAULT_EMPTY_NAMESPACE_SYSTEM_ID = "emptyNamespaceSystemId.xsd";
51
52
53 protected BufferingLog log;
54 protected SearchableDocumentation docs;
55 protected SortedMap<String, String> namespace2GeneratedSchemaMap;
56 protected SortedMap<String, Document> namespace2DocumentMap;
57 protected Map<String, String> namespace2SystemIdMap;
58 protected List<String> xsdGenerationWarnings;
59 protected final File basedir;
60 protected final File testJavaDir;
61 protected JAXBContext jaxbContext;
62 protected SortedMap<String, Throwable> xsdGenerationLog;
63
64
65 private List<Class<?>> jaxbClasses;
66
67 public AbstractSourceCodeAwareNodeProcessingTest() {
68
69
70 basedir = getBasedir();
71 testJavaDir = new File(basedir, "src/test/java");
72 Assert.assertTrue(testJavaDir.exists() && testJavaDir.isDirectory());
73 }
74
75 @Before
76 public final void setupSharedState() throws Exception {
77
78 log = new BufferingLog(BufferingLog.LogLevel.DEBUG);
79
80
81 namespace2SystemIdMap = new TreeMap<String, String>();
82 xsdGenerationWarnings = new ArrayList<String>();
83 namespace2DocumentMap = new TreeMap<String, Document>();
84 namespace2GeneratedSchemaMap = new TreeMap<String, String>();
85
86
87 namespace2SystemIdMap.put(SomewhatNamedPerson.NAMESPACE, "somewhatNamedPerson.xsd");
88 namespace2SystemIdMap.put("http://jaxb.mojohaus.org/wrappers", "wrapperExample.xsd");
89 namespace2SystemIdMap.put("http://gnat.west.se/foods", "anotherExample.xsd");
90 namespace2SystemIdMap.put("", DEFAULT_EMPTY_NAMESPACE_SYSTEM_ID);
91
92
93 jaxbClasses = getJaxbAnnotatedClassesForJaxbContext();
94 Assert.assertNotNull("getJaxbAnnotatedClassesForJaxbContext() should not return a null List.", jaxbClasses);
95 final Class<?>[] classArray = jaxbClasses.toArray(new Class<?>[jaxbClasses.size()]);
96 jaxbContext = JAXBContext.newInstance(classArray);
97
98
99 final SortedMap<String, StringWriter> tmpSchemaMap = new TreeMap<String, StringWriter>();
100
101 try {
102 jaxbContext.generateSchema(new SchemaOutputResolver() {
103 @Override
104 public Result createOutput(final String namespaceUri,
105 final String suggestedFileName)
106 throws IOException {
107
108
109
110
111
112 if (namespaceUri.isEmpty()) {
113 xsdGenerationWarnings.add("Got empty namespaceUri for suggestedFileName ["
114 + suggestedFileName + "].");
115 }
116
117
118 final StringWriter out = new StringWriter();
119 final StreamResult toReturn = new StreamResult(out);
120
121
122
123 final String effectiveSystemId = namespace2SystemIdMap.get(namespaceUri) == null
124 ? suggestedFileName
125 : namespace2SystemIdMap.get(namespaceUri);
126 toReturn.setSystemId(effectiveSystemId);
127
128
129 tmpSchemaMap.put(namespaceUri, out);
130
131
132 return toReturn;
133 }
134 });
135 } catch (IOException e) {
136 throw new IllegalArgumentException("Could not acquire Schema snippets.", e);
137 }
138
139
140 for (Map.Entry<String, StringWriter> current : tmpSchemaMap.entrySet()) {
141 namespace2GeneratedSchemaMap.put(current.getKey(), current.getValue().toString());
142 }
143
144
145 for (Map.Entry<String, String> current : namespace2GeneratedSchemaMap.entrySet()) {
146 final Document document = createDocument(current.getValue());
147 namespace2DocumentMap.put(current.getKey(), document);
148 }
149
150
151 final JavaDocExtractor extractor = new JavaDocExtractor(log);
152 extractor.addSourceFiles(resolveSourceFiles());
153 docs = extractor.process();
154
155
156 xsdGenerationLog = log.getAndResetLogBuffer();
157 }
158
159
160
161
162 protected abstract List<Class<?>> getJaxbAnnotatedClassesForJaxbContext();
163
164
165
166
167 protected File getBasedir() {
168
169
170 String basedirPath = System.getProperty("basedir");
171 if (basedirPath == null) {
172 basedirPath = new File("").getAbsolutePath();
173 }
174
175 final File toReturn = new File(basedirPath);
176 Assert.assertNotNull("Could not find 'basedir'. Please set the system property 'basedir'.", toReturn);
177 Assert.assertTrue("'basedir' must be an existing directory. ", toReturn.exists() && toReturn.isDirectory());
178
179
180 return toReturn;
181 }
182
183
184
185
186
187
188
189 protected final Document createDocument(final String xmlContent) {
190
191
192 Validate.notEmpty(xmlContent, "xmlContent");
193
194
195 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
196 factory.setNamespaceAware(true);
197
198 try {
199 return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xmlContent)));
200 } catch (Exception e) {
201 throw new IllegalArgumentException("Could not create DOM Document", e);
202 }
203 }
204
205
206
207
208
209
210
211
212
213
214 public final void process(final Node node, final boolean recurseToChildren, final NodeProcessor visitor) {
215
216
217 if (visitor.accept(node)) {
218 onAcceptedNode(node);
219 visitor.process(node);
220 }
221
222 NamedNodeMap attributes = node.getAttributes();
223 for (int i = 0; i < attributes.getLength(); i++) {
224 Node attribute = attributes.item(i);
225
226
227 if (visitor.accept(attribute)) {
228 onAcceptedAttribute(attribute);
229 visitor.process(attribute);
230 }
231 }
232
233 if (recurseToChildren) {
234 NodeList children = node.getChildNodes();
235 for (int i = 0; i < children.getLength(); i++) {
236 Node child = children.item(i);
237
238
239 if (child.getNodeType() == Node.ELEMENT_NODE) {
240 process(child, true, visitor);
241 }
242 }
243 }
244 }
245
246
247
248
249
250
251 protected void onAcceptedNode(final Node aNode) {
252
253
254 final Node nameAttribute = aNode.getAttributes().getNamedItem("name");
255 if(nameAttribute != null) {
256
257 final String nodeName = nameAttribute.getNodeValue();
258 log.info("Accepted node [" + aNode.getNodeName() + "] " + nodeName);
259 }
260 }
261
262
263
264
265
266
267 protected void onAcceptedAttribute(final Node anAttribute) {
268 log.info("Accepted attribute [" + anAttribute.getNodeName() + "]");
269 }
270
271
272
273
274
275
276
277
278
279
280
281
282 protected static String readFully(final String path) {
283
284 final StringBuilder toReturn = new StringBuilder(50);
285
286 try {
287
288
289 final InputStream resource = AbstractSourceCodeAwareNodeProcessingTest
290 .class
291 .getClassLoader()
292 .getResourceAsStream(path);
293 final BufferedReader tmp = new BufferedReader(new InputStreamReader(resource));
294
295 for (String line = tmp.readLine(); line != null; line = tmp.readLine()) {
296 toReturn.append(line).append(AbstractJaxbMojo.NEWLINE);
297 }
298 } catch (final Exception e) {
299 throw new IllegalArgumentException("Resource [" + path + "] not readable.");
300 }
301
302
303 return toReturn.toString();
304 }
305
306
307
308
309
310
311
312
313
314
315 protected static Diff compareXmlIgnoringWhitespace(final String expected, final String actual) throws SAXException,
316 IOException {
317
318
319 Validate.notNull(expected, "Cannot handle null expected argument.");
320 Validate.notNull(actual, "Cannot handle null actual argument.");
321
322
323 XMLUnit.setNormalize(true);
324 XMLUnit.setIgnoreWhitespace(true);
325 XMLUnit.setNormalize(true);
326
327
328 return XMLUnit.compareXML(expected, actual);
329 }
330
331 private List<File> resolveSourceFiles() {
332
333 final List<File> sourceDirs = Arrays.<File>asList(new File(basedir, "src/main/java"), testJavaDir);
334 final List<File> candidates = FileSystemUtilities.resolveRecursively(sourceDirs, null, log);
335 final List<File> toReturn = new ArrayList<File>();
336
337 for (File current : candidates) {
338 for (Class<?> currentClass : jaxbClasses) {
339
340 final String expectedFileName = currentClass.getSimpleName() + ".java";
341 if (expectedFileName.equalsIgnoreCase(current.getName())) {
342
343 final String transmutedCanonicalPath = FileSystemUtilities.getCanonicalPath(current)
344 .replace("/", ".")
345 .replace(File.separator, ".");
346
347 if (transmutedCanonicalPath.contains(currentClass.getPackage().getName())) {
348 toReturn.add(current);
349 }
350 }
351 }
352 }
353
354
355 return toReturn;
356 }
357
358
359
360
361
362
363
364 public static String printDocument(final Document doc) {
365
366 try {
367
368 final TransformerFactory tf = TransformerFactory.newInstance();
369 final Transformer transformer = tf.newTransformer();
370
371
372 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
373 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
374 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
375 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
376 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
377
378
379 final StringWriter out = new StringWriter();
380 transformer.transform(new DOMSource(doc), new StreamResult(out));
381 return out.toString();
382 } catch (Exception e) {
383 throw new IllegalArgumentException("Could not print document", e);
384 }
385 }
386 }