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 com.thoughtworks.qdox.JavaProjectBuilder;
23 import com.thoughtworks.qdox.model.JavaAnnotatedElement;
24 import com.thoughtworks.qdox.model.JavaClass;
25 import com.thoughtworks.qdox.model.JavaField;
26 import com.thoughtworks.qdox.model.JavaMethod;
27 import com.thoughtworks.qdox.model.JavaPackage;
28 import com.thoughtworks.qdox.model.JavaSource;
29 import org.apache.maven.plugin.logging.Log;
30 import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.location.ClassLocation;
31 import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.location.FieldLocation;
32 import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.location.MethodLocation;
33 import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.location.PackageLocation;
34 import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
35 import org.codehaus.mojo.jaxb2.shared.Validate;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.net.URL;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.SortedMap;
45 import java.util.SortedSet;
46 import java.util.TreeMap;
47
48
49
50
51
52
53
54
55
56
57
58 public class JavaDocExtractor {
59
60
61 private JavaProjectBuilder builder;
62 private Log log;
63
64
65
66
67
68
69 public JavaDocExtractor(final Log log) {
70
71
72 Validate.notNull(log, "log");
73
74
75 this.log = log;
76 this.builder = new JavaProjectBuilder();
77 }
78
79
80
81
82
83
84
85
86 public JavaDocExtractor addSourceFiles(final List<File> sourceCodeFiles) throws IllegalArgumentException {
87
88
89 Validate.notNull(sourceCodeFiles, "addSourceFiles");
90
91
92 for (File current : sourceCodeFiles) {
93 try {
94 builder.addSource(current);
95 } catch (IOException e) {
96 throw new IllegalArgumentException("Could not add file ["
97 + FileSystemUtilities.getCanonicalPath(current) + "]", e);
98 }
99 }
100
101
102 return this;
103 }
104
105
106
107
108
109
110
111
112 public JavaDocExtractor addSourceURLs(final List<URL> sourceCodeURLs) throws IllegalArgumentException {
113
114
115 Validate.notNull(sourceCodeURLs, "sourceCodeURLs");
116
117
118 for (URL current : sourceCodeURLs) {
119 try {
120 builder.addSource(current);
121 } catch (IOException e) {
122 throw new IllegalArgumentException("Could not add URL [" + current.toString() + "]", e);
123 }
124 }
125
126
127 return this;
128 }
129
130
131
132
133
134
135
136 public SearchableDocumentation process() {
137
138
139 final SortedMap<SortableLocation, JavaDocData> dataHolder = new TreeMap<SortableLocation, JavaDocData>();
140 final Collection<JavaSource> sources = builder.getSources();
141
142 if (log.isInfoEnabled()) {
143 log.info("Processing [" + sources.size() + "] java sources.");
144 }
145
146 for (JavaSource current : sources) {
147
148
149 final JavaPackage currentPackage = current.getPackage();
150 final String packageName = currentPackage.getName();
151 addEntry(dataHolder, new PackageLocation(packageName), currentPackage);
152
153 if (log.isDebugEnabled()) {
154 log.debug("Added package-level JavaDoc for [" + packageName + "]");
155 }
156
157 for (JavaClass currentClass : current.getClasses()) {
158
159
160 final String simpleClassName = currentClass.getName();
161 final ClassLocation classLocation = new ClassLocation(packageName, simpleClassName);
162 addEntry(dataHolder, classLocation, currentClass);
163
164 if (log.isDebugEnabled()) {
165 log.debug("Added class-level JavaDoc for [" + classLocation + "]");
166 }
167
168 for (JavaField currentField : currentClass.getFields()) {
169
170
171 final FieldLocation fieldLocation = new FieldLocation(
172 packageName,
173 simpleClassName,
174 currentField.getName());
175
176 addEntry(dataHolder, fieldLocation, currentField);
177
178 if (log.isDebugEnabled()) {
179 log.debug("Added field-level JavaDoc for [" + fieldLocation + "]");
180 }
181 }
182
183 for (JavaMethod currentMethod : currentClass.getMethods()) {
184
185
186 final MethodLocation location = new MethodLocation(packageName,
187 simpleClassName,
188 currentMethod.getName(),
189 currentMethod.getParameters());
190 addEntry(dataHolder, location, currentMethod);
191
192 if (log.isDebugEnabled()) {
193 log.debug("Added method-level JavaDoc for [" + location + "]");
194 }
195 }
196 }
197 }
198
199
200 return new ReadOnlySearchableDocumentation(dataHolder);
201 }
202
203
204
205
206
207 private void addEntry(final SortedMap<SortableLocation, JavaDocData> map,
208 final SortableLocation key,
209 final JavaAnnotatedElement value) {
210
211
212 if (map.containsKey(key)) {
213
214
215 final JavaDocData existing = map.get(key);
216
217
218 if (key instanceof PackageLocation) {
219
220 final boolean emptyExisting = existing.getComment() == null || existing.getComment().isEmpty();
221 final boolean emptyGiven = value.getComment() == null || value.getComment().isEmpty();
222
223 if (emptyGiven) {
224 if (log.isDebugEnabled()) {
225 log.debug("Skipping processing empty Package javadoc from [" + key + "]");
226 }
227 return;
228 } else if (emptyExisting && log.isWarnEnabled()) {
229 log.warn("Overwriting empty Package javadoc from [" + key + "]");
230 }
231 } else {
232 final String given = "[" + value.getClass().getName() + "]: " + value.getComment();
233 throw new IllegalArgumentException("Not processing duplicate SortableLocation [" + key + "]. "
234 + "\n Existing: " + existing
235 + ".\n Given: [" + given + "]");
236 }
237 }
238
239
240
241
242 map.put(key, new JavaDocData(value.getComment(), value.getTags()));
243 }
244
245
246
247
248 class ReadOnlySearchableDocumentation implements SearchableDocumentation {
249
250
251 private TreeMap<String, SortableLocation> keyMap;
252 private SortedMap<? extends SortableLocation, JavaDocData> valueMap;
253
254 ReadOnlySearchableDocumentation(final SortedMap<SortableLocation, JavaDocData> valueMap) {
255
256
257 this.valueMap = valueMap;
258
259 keyMap = new TreeMap<String, SortableLocation>();
260 for (Map.Entry<SortableLocation, JavaDocData> current : valueMap.entrySet()) {
261
262 final SortableLocation key = current.getKey();
263 keyMap.put(key.getPath(), key);
264 }
265 }
266
267
268
269
270 @Override
271 public SortedSet<String> getPaths() {
272 return Collections.unmodifiableSortedSet(keyMap.navigableKeySet());
273 }
274
275
276
277
278 @Override
279 public JavaDocData getJavaDoc(final String path) {
280
281
282 Validate.notNull(path, "path");
283
284
285 final SortableLocation location = getLocation(path);
286 return (location == null) ? null : valueMap.get(location);
287 }
288
289
290
291
292 @Override
293 @SuppressWarnings("unchecked")
294 public <T extends SortableLocation> T getLocation(final String path) {
295
296
297 Validate.notNull(path, "path");
298
299
300 return (T) keyMap.get(path);
301 }
302
303
304
305
306 @Override
307 @SuppressWarnings("unchecked")
308 public SortedMap<SortableLocation, JavaDocData> getAll() {
309 return (SortedMap<SortableLocation, JavaDocData>) Collections.unmodifiableSortedMap(valueMap);
310 }
311
312
313
314
315 @Override
316 @SuppressWarnings("unchecked")
317 public <T extends SortableLocation> SortedMap<T, JavaDocData> getAll(final Class<T> type) {
318
319
320 Validate.notNull(type, "type");
321
322
323 final SortedMap<T, JavaDocData> toReturn = new TreeMap<T, JavaDocData>();
324 for (Map.Entry<? extends SortableLocation, JavaDocData> current : valueMap.entrySet()) {
325 if (type == current.getKey().getClass()) {
326 toReturn.put((T) current.getKey(), current.getValue());
327 }
328 }
329
330
331 return toReturn;
332 }
333 }
334 }