001
014
015 package com.liferay.portal.tools;
016
017 import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
018 import com.liferay.portal.kernel.util.StringPool;
019 import com.liferay.portal.kernel.util.StringUtil;
020 import com.liferay.portal.kernel.util.Validator;
021 import com.liferay.portal.kernel.xml.Document;
022 import com.liferay.portal.kernel.xml.Element;
023 import com.liferay.portal.util.FileImpl;
024 import com.liferay.portal.xml.SAXReaderImpl;
025 import com.liferay.util.xml.DocUtil;
026
027 import com.thoughtworks.qdox.JavaDocBuilder;
028 import com.thoughtworks.qdox.model.AbstractJavaEntity;
029 import com.thoughtworks.qdox.model.DocletTag;
030 import com.thoughtworks.qdox.model.JavaClass;
031 import com.thoughtworks.qdox.model.JavaField;
032 import com.thoughtworks.qdox.model.JavaMethod;
033 import com.thoughtworks.qdox.model.JavaParameter;
034 import com.thoughtworks.qdox.model.Type;
035
036 import jargs.gnu.CmdLineParser;
037
038 import java.io.File;
039 import java.io.Reader;
040
041 import java.util.ArrayList;
042 import java.util.HashMap;
043 import java.util.HashSet;
044 import java.util.List;
045 import java.util.Map;
046 import java.util.Set;
047 import java.util.TreeMap;
048
049 import org.apache.tools.ant.DirectoryScanner;
050
051
054 public class JavadocBuilder {
055
056 public static void main(String[] args) {
057 try {
058 new JavadocBuilder(args);
059 }
060 catch (Exception e) {
061 e.printStackTrace();
062 }
063 }
064
065 public JavadocBuilder(String[] args) throws Exception {
066 CmdLineParser cmdLineParser = new CmdLineParser();
067
068 cmdLineParser.parse(args);
069
070 CmdLineParser.Option commandOption = cmdLineParser.addStringOption(
071 "command");
072
073 String command = (String)cmdLineParser.getOptionValue(commandOption);
074
075 CmdLineParser.Option limitOption = cmdLineParser.addStringOption(
076 "limit");
077
078 String limit = (String)cmdLineParser.getOptionValue(limitOption);
079
080 CmdLineParser.Option ignoreAutogeneratedOption =
081 cmdLineParser.addBooleanOption("ignoreAutogenerated");
082
083 Boolean ignoreAutogenerated = (Boolean)cmdLineParser.getOptionValue(
084 ignoreAutogeneratedOption);
085
086 _process(command, limit, ignoreAutogenerated);
087 }
088
089 private void _addClassCommentElement(
090 Element rootElement, JavaClass javaClass) {
091
092 Element commentElement = rootElement.addElement("comment");
093
094 String comment = _getCDATA(javaClass);
095
096 if (comment.startsWith("Copyright (c) 2000-2010 Liferay, Inc.")) {
097 comment = StringPool.BLANK;
098 }
099
100 if (comment.startsWith(
101 "<a href=\"" + javaClass.getName() + ".java.html\">")) {
102
103 int pos = comment.indexOf("</a>");
104
105 comment = comment.substring(pos + 4).trim();
106 }
107
108 commentElement.addCDATA(comment);
109 }
110
111 private void _addDocletElements(
112 Element parentElement, AbstractJavaEntity abstractJavaEntity,
113 String name) {
114
115 DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);
116
117 for (DocletTag docletTag : docletTags) {
118 String value = docletTag.getValue();
119
120 if (name.equals("author") || name.equals("see") ||
121 name.equals("since") || name.equals("version")) {
122
123
126
127 DocUtil.add(parentElement, name, value);
128 }
129 else {
130 Element element = parentElement.addElement(name);
131
132 element.addCDATA(value);
133 }
134 }
135 }
136
137 private void _addDocletTags(
138 Element parentElement, String name, String indent, StringBuilder sb) {
139
140 List<Element> elements = parentElement.elements(name);
141
142 for (Element element : elements) {
143 sb.append(indent);
144 sb.append(" * @");
145 sb.append(name);
146 sb.append(" ");
147
148 Element commentElement = element.element("comment");
149
150 if (commentElement != null) {
151 sb.append(element.elementText("name"));
152 sb.append(" ");
153 sb.append(_getCDATA(element.elementText("comment")));
154 }
155 else {
156 sb.append(_getCDATA(element.getText()));
157 }
158
159 sb.append("\n");
160 }
161 }
162
163 private void _addFieldElement(Element rootElement, JavaField javaField) {
164 Element fieldElement = rootElement.addElement("field");
165
166 DocUtil.add(fieldElement, "name", javaField.getName());
167
168 Element commentElement = fieldElement.addElement("comment");
169
170 commentElement.addCDATA(_getCDATA(javaField));
171
172 _addDocletElements(fieldElement, javaField, "deprecated");
173 _addDocletElements(fieldElement, javaField, "see");
174 _addDocletElements(fieldElement, javaField, "since");
175 _addDocletElements(fieldElement, javaField, "version");
176 }
177
178 private void _addMethodElement(Element rootElement, JavaMethod javaMethod) {
179 Element methodElement = rootElement.addElement("method");
180
181 DocUtil.add(methodElement, "name", javaMethod.getName());
182
183 Element commentElement = methodElement.addElement("comment");
184
185 commentElement.addCDATA(_getCDATA(javaMethod));
186
187 _addDocletElements(methodElement, javaMethod, "deprecated");
188 _addParamElements(methodElement, javaMethod);
189 _addReturnElement(methodElement, javaMethod);
190 _addDocletElements(methodElement, javaMethod, "see");
191 _addDocletElements(methodElement, javaMethod, "since");
192 _addThrowsElements(methodElement, javaMethod);
193 _addDocletElements(methodElement, javaMethod, "version");
194 }
195
196 private void _addParamElement(
197 Element methodElement, JavaParameter javaParameter,
198 DocletTag[] paramDocletTags) {
199
200 String name = javaParameter.getName();
201 String type = javaParameter.getType().getValue();
202 String value = null;
203
204 for (DocletTag paramDocletTag : paramDocletTags) {
205 String curValue = paramDocletTag.getValue();
206
207 if (!curValue.startsWith(name)) {
208 continue;
209 }
210 else {
211 curValue = value;
212
213 break;
214 }
215 }
216
217 Element paramElement = methodElement.addElement("param");
218
219 DocUtil.add(paramElement, "name", name);
220 DocUtil.add(paramElement, "type", type);
221
222 if (value != null) {
223 value = value.substring(name.length());
224 }
225
226 Element commentElement = paramElement.addElement("comment");
227
228 commentElement.addCDATA(_getCDATA(value));
229 }
230
231 private void _addParamElements(
232 Element methodElement, JavaMethod javaMethod) {
233
234 JavaParameter[] javaParameters = javaMethod.getParameters();
235
236 DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");
237
238 for (JavaParameter javaParameter : javaParameters) {
239 _addParamElement(methodElement, javaParameter, paramDocletTags);
240 }
241 }
242
243 private void _addReturnElement(
244 Element methodElement, JavaMethod javaMethod) {
245
246 Type returns = javaMethod.getReturns();
247
248 if ((returns == null) || returns.getValue().equals("void")) {
249 return;
250 }
251
252 _addDocletElements(methodElement, javaMethod, "return");
253 }
254
255 private void _addThrowsElement(
256 Element methodElement, Type exception, DocletTag[] throwsDocletTags) {
257
258 String name = exception.getJavaClass().getName();
259 String value = null;
260
261 for (DocletTag throwsDocletTag : throwsDocletTags) {
262 String curValue = throwsDocletTag.getValue();
263
264 if (!curValue.startsWith(name)) {
265 continue;
266 }
267 else {
268 curValue = value;
269
270 break;
271 }
272 }
273
274 Element throwsElement = methodElement.addElement("throws");
275
276 DocUtil.add(throwsElement, "name", name);
277 DocUtil.add(throwsElement, "type", exception.getValue());
278
279 if (value != null) {
280 value = value.substring(name.length());
281 }
282
283 Element commentElement = throwsElement.addElement("comment");
284
285 commentElement.addCDATA(_getCDATA(value));
286 }
287
288 private void _addThrowsElements(
289 Element methodElement, JavaMethod javaMethod) {
290
291 Type[] exceptions = javaMethod.getExceptions();
292
293 DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");
294
295 for (Type exception : exceptions) {
296 _addThrowsElement(methodElement, exception, throwsDocletTags);
297 }
298 }
299
300 private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
301 return _getCDATA(abstractJavaEntity.getComment());
302 }
303
304 private String _getCDATA(String cdata) {
305 if (cdata == null) {
306 return StringPool.BLANK;
307 }
308
309 cdata = StringUtil.replace(
310 cdata, new String[] {"\n", "<p>", "</p>"},
311 new String[] {" ", " <p> ", " </p> "});
312
313 while (cdata.contains(" ")) {
314 cdata = StringUtil.replace(cdata, " ", " ");
315 }
316
317 return cdata.trim();
318 }
319
320 private String _getFieldKey(Element fieldElement) {
321 return fieldElement.elementText("name");
322 }
323
324 private String _getFieldKey(JavaField javaField) {
325 return javaField.getName();
326 }
327
328 private JavaClass _getJavaClass(String fileName) throws Exception {
329 return _getJavaClass(fileName, null);
330 }
331
332 private JavaClass _getJavaClass(String fileName, Reader reader)
333 throws Exception {
334
335 int pos = fileName.indexOf("src/");
336
337 if (pos == -1) {
338 pos = fileName.indexOf("test/");
339 }
340
341 if (pos == -1) {
342 throw new RuntimeException(fileName);
343 }
344
345 pos = fileName.indexOf("/", pos);
346
347 String srcFile = fileName.substring(pos + 1);
348 String className = StringUtil.replace(
349 srcFile.substring(0, srcFile.length() - 5), "/", ".");
350
351 JavaDocBuilder builder = new JavaDocBuilder();
352
353 if (reader == null) {
354 File file = new File(fileName);
355
356 if (!file.exists()) {
357 return null;
358 }
359
360 builder.addSource(file);
361 }
362 else {
363 builder.addSource(reader);
364 }
365
366 return builder.getClassByName(className);
367 }
368
369 private String _getJavaClassComment(
370 Element rootElement, JavaClass javaClass) {
371
372 StringBuilder sb = new StringBuilder();
373
374 sb.append("\n");
390
391 return sb.toString();
392 }
393
394 private String _getJavadocXml(JavaClass javaClass) throws Exception {
395 Element rootElement = _saxReaderUtil.createElement("javadoc");
396
397 Document document = _saxReaderUtil.createDocument(rootElement);
398
399 DocUtil.add(rootElement, "name", javaClass.getName());
400 DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());
401
402 _addClassCommentElement(rootElement, javaClass);
403 _addDocletElements(rootElement, javaClass, "author");
404 _addDocletElements(rootElement, javaClass, "deprecated");
405 _addDocletElements(rootElement, javaClass, "see");
406 _addDocletElements(rootElement, javaClass, "serial");
407 _addDocletElements(rootElement, javaClass, "since");
408 _addDocletElements(rootElement, javaClass, "version");
409
410 JavaMethod[] javaMethods = javaClass.getMethods();
411
412 for (JavaMethod javaMethod : javaMethods) {
413 _addMethodElement(rootElement, javaMethod);
414 }
415
416 JavaField[] javaFields = javaClass.getFields();
417
418 for (JavaField javaField : javaFields) {
419 _addFieldElement(rootElement, javaField);
420 }
421
422 return document.formattedString();
423 }
424
425 private String _getJavaFieldComment(
426 String[] lines, Map<String, Element> fieldElementsMap,
427 JavaField javaField) {
428
429 String fieldKey = _getFieldKey(javaField);
430
431 Element fieldElement = fieldElementsMap.get(fieldKey);
432
433 if (fieldElement == null) {
434 return null;
435 }
436
437 String line = lines[javaField.getLineNumber() - 1];
438
439 String indent = StringPool.BLANK;
440
441 for (char c : line.toCharArray()) {
442 if (Character.isWhitespace(c)) {
443 indent += c;
444 }
445 else {
446 break;
447 }
448 }
449
450 StringBuilder sb = new StringBuilder();
451
452 sb.append(indent);
453 sb.append("\n");
468
469 return sb.toString();
470 }
471
472 private String _getJavaMethodComment(
473 String[] lines, Map<String, Element> methodElementsMap,
474 JavaMethod javaMethod) {
475
476 String methodKey = _getMethodKey(javaMethod);
477
478 Element methodElement = methodElementsMap.get(methodKey);
479
480 if (methodElement == null) {
481 return null;
482 }
483
484 String line = lines[javaMethod.getLineNumber() - 1];
485
486 String indent = StringPool.BLANK;
487
488 for (char c : line.toCharArray()) {
489 if (Character.isWhitespace(c)) {
490 indent += c;
491 }
492 else {
493 break;
494 }
495 }
496
497 StringBuilder sb = new StringBuilder();
498
499 sb.append(indent);
500 sb.append("\n");
518
519 return sb.toString();
520 }
521
522 private String _getMethodKey(Element methodElement) {
523 StringBuilder sb = new StringBuilder();
524
525 sb.append(methodElement.elementText("name"));
526 sb.append(StringPool.OPEN_PARENTHESIS);
527
528 List<Element> paramElements = methodElement.elements("param");
529
530 for (Element paramElement : paramElements) {
531 sb.append(paramElement.elementText("name"));
532 sb.append("|");
533 sb.append(paramElement.elementText("type"));
534 sb.append(",");
535 }
536
537 sb.append(StringPool.CLOSE_PARENTHESIS);
538
539 return sb.toString();
540 }
541
542 private String _getMethodKey(JavaMethod javaMethod) {
543 StringBuilder sb = new StringBuilder();
544
545 sb.append(javaMethod.getName());
546 sb.append(StringPool.OPEN_PARENTHESIS);
547
548 JavaParameter[] javaParameters = javaMethod.getParameters();
549
550 for (JavaParameter javaParameter : javaParameters) {
551 sb.append(javaParameter.getName());
552 sb.append("|");
553 sb.append(javaParameter.getType().getValue());
554 sb.append(",");
555 }
556
557 sb.append(StringPool.CLOSE_PARENTHESIS);
558
559 return sb.toString();
560 }
561
562 private boolean _isGenerated(String content) {
563 if (content.contains("<javadoc autogenerated=\"true\">")) {
564 return true;
565 }
566 else {
567 return false;
568 }
569 }
570
571 private void _process(
572 String command, String limit, Boolean ignoreAutogenerated)
573 throws Exception {
574
575 DirectoryScanner ds = new DirectoryScanner();
576
577 ds.setBasedir(_basedir);
578 ds.setExcludes(
579 new String[] {
580 "**\\classes\\**", "**\\portal-client\\**", "**\\portal-web\\**"
581 });
582
583 List<String> includes = new ArrayList<String>();
584
585 if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
586 String[] limitArray = StringUtil.split(limit, '/');
587
588 for (String curLimit : limitArray) {
589 includes.add(
590 "**\\" + StringUtil.replace(curLimit, ".", "\\") +
591 "\\**\\*.java");
592 includes.add("**\\" + curLimit + ".java");
593 }
594 }
595 else {
596 includes.add("**\\*.java");
597 }
598
599 ds.setIncludes(includes.toArray(new String[includes.size()]));
600
601 ds.scan();
602
603 String[] fileNames = ds.getIncludedFiles();
604
605 for (String fileName : fileNames) {
606 fileName = StringUtil.replace(fileName, "\\", "/");
607
608
611
612 if ((ignoreAutogenerated != null) &&
613 ignoreAutogenerated.booleanValue()) {
614
615 File file = new File(_basedir + fileName);
616
617 if (file.exists()) {
618 String oldContent = _fileUtil.read(
619 _basedir + fileName + "doc");
620
621 if (_isGenerated(oldContent)) {
622 continue;
623 }
624 }
625 }
626
627 if (command.equals("cleanup")) {
628 _processGet(fileName);
629 _processSave(fileName);
630 _processDelete(fileName);
631 }
632 else if (command.equals("commit")) {
633 _processSave(fileName);
634 _processDelete(fileName);
635 }
636 else if (command.equals("delete")) {
637 _processDelete(fileName);
638 }
639 else if (command.equals("get")) {
640 _processGet(fileName);
641 }
642 else if (command.equals("save")) {
643 _processSave(fileName);
644 }
645 }
646 }
647
648 private void _processDelete(String fileName) throws Exception {
649 _removeJavadocFromJava(fileName, true);
650 }
651
652 private void _processGet(String fileName) throws Exception {
653 File javadocFile = new File(_basedir + fileName + "doc");
654
655 if (!javadocFile.exists()) {
656 _updateJavadocFromJava(fileName);
657 }
658
659 String javaWithoutJavadoc = _removeJavadocFromJava(fileName, false);
660
661 _updateJavaFromJavadoc(fileName, javaWithoutJavadoc);
662 }
663
664 private void _processSave(String fileName) throws Exception {
665 _updateJavadocFromJava(fileName);
666 }
667
668 private String _removeJavadocFromJava(String fileName, boolean log)
669 throws Exception {
670
671 File file = new File(_basedir + fileName);
672
673 String oldContent = _fileUtil.read(file);
674
675 String[] lines = StringUtil.splitLines(oldContent);
676
677 JavaClass javaClass = _getJavaClass(
678 fileName, new UnsyncStringReader(oldContent));
679
680 Set<Integer> lineNumbers = new HashSet<Integer>();
681
682 lineNumbers.add(javaClass.getLineNumber());
683
684 JavaMethod[] javaMethods = javaClass.getMethods();
685
686 for (JavaMethod javaMethod : javaMethods) {
687 lineNumbers.add(javaMethod.getLineNumber());
688 }
689
690 JavaField[] javaFields = javaClass.getFields();
691
692 for (JavaField javaField : javaFields) {
693 lineNumbers.add(javaField.getLineNumber());
694 }
695
696 for (int lineNumber : lineNumbers) {
697 int pos = lineNumber - 2;
698
699 String line = lines[pos].trim();
700
701 if (line.endsWith("*/")) {
702 while (true) {
703 lines[pos] = null;
704
705 if (line.startsWith("