1
22
23 package com.liferay.portal.tools;
24
25 import com.liferay.portal.kernel.util.StringPool;
26 import com.liferay.portal.kernel.util.StringUtil;
27 import com.liferay.portal.kernel.util.Validator;
28 import com.liferay.portal.kernel.xml.Document;
29 import com.liferay.portal.kernel.xml.Element;
30 import com.liferay.portal.util.FileImpl;
31 import com.liferay.portal.xml.SAXReaderImpl;
32 import com.liferay.util.xml.DocUtil;
33
34 import com.thoughtworks.qdox.JavaDocBuilder;
35 import com.thoughtworks.qdox.model.AbstractJavaEntity;
36 import com.thoughtworks.qdox.model.DocletTag;
37 import com.thoughtworks.qdox.model.JavaClass;
38 import com.thoughtworks.qdox.model.JavaField;
39 import com.thoughtworks.qdox.model.JavaMethod;
40 import com.thoughtworks.qdox.model.JavaParameter;
41 import com.thoughtworks.qdox.model.Type;
42
43 import jargs.gnu.CmdLineParser;
44
45 import java.io.File;
46 import java.io.Reader;
47 import java.io.StringReader;
48
49 import java.util.ArrayList;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Set;
55 import java.util.TreeMap;
56
57 import org.apache.tools.ant.DirectoryScanner;
58
59
64 public class JavadocBuilder {
65
66 public static void main(String[] args) {
67 try {
68 new JavadocBuilder(args);
69 }
70 catch (Exception e) {
71 e.printStackTrace();
72 }
73 }
74
75 public JavadocBuilder(String[] args) throws Exception {
76 CmdLineParser cmdLineParser = new CmdLineParser();
77
78 CmdLineParser.Option commandOption = cmdLineParser.addStringOption(
79 "command");
80 CmdLineParser.Option limitOption = cmdLineParser.addStringOption(
81 "limit");
82 CmdLineParser.Option ignoreAutogeneratedOption =
83 cmdLineParser.addBooleanOption("ignoreAutogenerated");
84
85 cmdLineParser.parse(args);
86
87 String command = (String)cmdLineParser.getOptionValue(commandOption);
88 String limit = (String)cmdLineParser.getOptionValue(limitOption);
89 Boolean ignoreAutogenerated = (Boolean)cmdLineParser.getOptionValue(
90 ignoreAutogeneratedOption);
91
92 _process(command, limit, ignoreAutogenerated);
93 }
94
95 private void _addClassCommentElement(
96 Element rootElement, JavaClass javaClass) {
97
98 Element commentElement = rootElement.addElement("comment");
99
100 String comment = _getCDATA(javaClass);
101
102 if (comment.startsWith("Copyright (c) 2000-2009 Liferay, Inc.")) {
103 comment = StringPool.BLANK;
104 }
105
106 if (comment.startsWith(
107 "<a href=\"" + javaClass.getName() + ".java.html\">")) {
108
109 int pos = comment.indexOf("</a>");
110
111 comment = comment.substring(pos + 4).trim();
112 }
113
114 commentElement.addCDATA(comment);
115 }
116
117 private void _addDocletElements(
118 Element parentElement, AbstractJavaEntity abstractJavaEntity,
119 String name) {
120
121 DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);
122
123 for (DocletTag docletTag : docletTags) {
124 String value = docletTag.getValue();
125
126 if (name.equals("author") || name.equals("see") ||
127 name.equals("since") || name.equals("version")) {
128
129 if (value.startsWith("Raymond Aug")) {
130 value = "Raymond Aug\u00c3\u00a9";
131 }
132
133 DocUtil.add(parentElement, name, value);
134 }
135 else {
136 Element element = parentElement.addElement(name);
137
138 element.addCDATA(value);
139 }
140 }
141 }
142
143 private void _addDocletTags(
144 Element parentElement, String name, String indent, StringBuilder sb) {
145
146 List<Element> elements = parentElement.elements(name);
147
148 for (Element element : elements) {
149 sb.append(indent);
150 sb.append(" * @");
151 sb.append(name);
152 sb.append(" ");
153
154 Element commentElement = element.element("comment");
155
156 if (commentElement != null) {
157 sb.append(element.elementText("name"));
158 sb.append(" ");
159 sb.append(_getCDATA(element.elementText("comment")));
160 }
161 else {
162 sb.append(_getCDATA(element.getText()));
163 }
164
165 sb.append("\n");
166 }
167 }
168
169 private void _addFieldElement(Element rootElement, JavaField javaField) {
170 Element fieldElement = rootElement.addElement("field");
171
172 DocUtil.add(fieldElement, "name", javaField.getName());
173
174 Element commentElement = fieldElement.addElement("comment");
175
176 commentElement.addCDATA(_getCDATA(javaField));
177
178 _addDocletElements(fieldElement, javaField, "deprecated");
179 _addDocletElements(fieldElement, javaField, "see");
180 _addDocletElements(fieldElement, javaField, "since");
181 _addDocletElements(fieldElement, javaField, "version");
182 }
183
184 private void _addMethodElement(Element rootElement, JavaMethod javaMethod) {
185 Element methodElement = rootElement.addElement("method");
186
187 DocUtil.add(methodElement, "name", javaMethod.getName());
188
189 Element commentElement = methodElement.addElement("comment");
190
191 commentElement.addCDATA(_getCDATA(javaMethod));
192
193 _addDocletElements(methodElement, javaMethod, "deprecated");
194 _addParamElements(methodElement, javaMethod);
195 _addReturnElement(methodElement, javaMethod);
196 _addDocletElements(methodElement, javaMethod, "see");
197 _addDocletElements(methodElement, javaMethod, "since");
198 _addThrowsElements(methodElement, javaMethod);
199 _addDocletElements(methodElement, javaMethod, "version");
200 }
201
202 private void _addParamElement(
203 Element methodElement, JavaParameter javaParameter,
204 DocletTag[] paramDocletTags) {
205
206 String name = javaParameter.getName();
207 String type = javaParameter.getType().getValue();
208 String value = null;
209
210 for (DocletTag paramDocletTag : paramDocletTags) {
211 String curValue = paramDocletTag.getValue();
212
213 if (!curValue.startsWith(name)) {
214 continue;
215 }
216 else {
217 curValue = value;
218
219 break;
220 }
221 }
222
223 Element paramElement = methodElement.addElement("param");
224
225 DocUtil.add(paramElement, "name", name);
226 DocUtil.add(paramElement, "type", type);
227
228 if (value != null) {
229 value = value.substring(name.length());
230 }
231
232 Element commentElement = paramElement.addElement("comment");
233
234 commentElement.addCDATA(_getCDATA(value));
235 }
236
237 private void _addParamElements(
238 Element methodElement, JavaMethod javaMethod) {
239
240 JavaParameter[] javaParameters = javaMethod.getParameters();
241
242 DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");
243
244 for (JavaParameter javaParameter : javaParameters) {
245 _addParamElement(methodElement, javaParameter, paramDocletTags);
246 }
247 }
248
249 private void _addReturnElement(
250 Element methodElement, JavaMethod javaMethod) {
251
252 Type returns = javaMethod.getReturns();
253
254 if ((returns == null) || returns.getValue().equals("void")) {
255 return;
256 }
257
258 _addDocletElements(methodElement, javaMethod, "return");
259 }
260
261 private void _addThrowsElement(
262 Element methodElement, Type exception, DocletTag[] throwsDocletTags) {
263
264 String name = exception.getJavaClass().getName();
265 String value = null;
266
267 for (DocletTag throwsDocletTag : throwsDocletTags) {
268 String curValue = throwsDocletTag.getValue();
269
270 if (!curValue.startsWith(name)) {
271 continue;
272 }
273 else {
274 curValue = value;
275
276 break;
277 }
278 }
279
280 Element throwsElement = methodElement.addElement("throws");
281
282 DocUtil.add(throwsElement, "name", name);
283 DocUtil.add(throwsElement, "type", exception.getValue());
284
285 if (value != null) {
286 value = value.substring(name.length());
287 }
288
289 Element commentElement = throwsElement.addElement("comment");
290
291 commentElement.addCDATA(_getCDATA(value));
292
293 }
294
295 private void _addThrowsElements(
296 Element methodElement, JavaMethod javaMethod) {
297
298 Type[] exceptions = javaMethod.getExceptions();
299
300 DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");
301
302 for (Type exception : exceptions) {
303 _addThrowsElement(methodElement, exception, throwsDocletTags);
304 }
305 }
306
307 private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
308 return _getCDATA(abstractJavaEntity.getComment());
309 }
310
311 private String _getCDATA(String cdata) {
312 if (cdata == null) {
313 return StringPool.BLANK;
314 }
315
316 cdata = StringUtil.replace(
317 cdata,
318 new String[] {"\n", "<p>", "</p>"},
319 new String[] {" ", " <p> ", " </p> "});
320
321 while (cdata.contains(" ")) {
322 cdata = StringUtil.replace(cdata, " ", " ");
323 }
324
325 return cdata.trim();
326 }
327
328 private String _getFieldKey(Element fieldElement) {
329 return fieldElement.elementText("name");
330 }
331
332 private String _getFieldKey(JavaField javaField) {
333 return javaField.getName();
334 }
335
336 private JavaClass _getJavaClass(String fileName) throws Exception {
337 return _getJavaClass(fileName, null);
338 }
339
340 private JavaClass _getJavaClass(String fileName, Reader reader)
341 throws Exception {
342
343 int pos = fileName.indexOf("src/");
344
345 if (pos == -1) {
346 pos = fileName.indexOf("test/");
347 }
348
349 if (pos == -1) {
350 throw new RuntimeException(fileName);
351 }
352
353 pos = fileName.indexOf("/", pos);
354
355 String srcFile = fileName.substring(pos + 1, fileName.length());
356 String className = StringUtil.replace(
357 srcFile.substring(0, srcFile.length() - 5), "/", ".");
358
359 JavaDocBuilder builder = new JavaDocBuilder();
360
361 if (reader == null) {
362 File file = new File(fileName);
363
364 if (!file.exists()) {
365 return null;
366 }
367
368 builder.addSource(file);
369 }
370 else {
371 builder.addSource(reader);
372 }
373
374 return builder.getClassByName(className);
375 }
376
377 private String _getJavaClassComment(
378 Element rootElement, JavaClass javaClass) {
379
380 StringBuilder sb = new StringBuilder();
381
382 sb.append("/**\n");
383 sb.append(" * <a href=\"");
384 sb.append(javaClass.getName());
385 sb.append(".java.html\"><b><i>View Source</i></b></a>\n");
386 sb.append(" *\n");
387 sb.append(" * ");
388 sb.append(_getCDATA(rootElement.elementText("comment")));
389 sb.append("\n");
390 sb.append(" *\n");
391
392 String indent = StringPool.BLANK;
393
394 _addDocletTags(rootElement, "author", indent, sb);
395 _addDocletTags(rootElement, "deprecated", indent, sb);
396 _addDocletTags(rootElement, "see", indent, sb);
397 _addDocletTags(rootElement, "serial", indent, sb);
398 _addDocletTags(rootElement, "since", indent, sb);
399 _addDocletTags(rootElement, "version", indent, sb);
400
401 sb.append(" */\n");
402
403 return sb.toString();
404 }
405
406 private String _getJavadocXml(JavaClass javaClass) throws Exception {
407 Element rootElement = _saxReaderUtil.createElement("javadoc");
408
409 Document document = _saxReaderUtil.createDocument(rootElement);
410
411 DocUtil.add(rootElement, "name", javaClass.getName());
412 DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());
413
414 _addClassCommentElement(rootElement, javaClass);
415 _addDocletElements(rootElement, javaClass, "author");
416 _addDocletElements(rootElement, javaClass, "deprecated");
417 _addDocletElements(rootElement, javaClass, "see");
418 _addDocletElements(rootElement, javaClass, "serial");
419 _addDocletElements(rootElement, javaClass, "since");
420 _addDocletElements(rootElement, javaClass, "version");
421
422 JavaMethod[] javaMethods = javaClass.getMethods();
423
424 for (JavaMethod javaMethod : javaMethods) {
425 _addMethodElement(rootElement, javaMethod);
426 }
427
428 JavaField[] javaFields = javaClass.getFields();
429
430 for (JavaField javaField : javaFields) {
431 _addFieldElement(rootElement, javaField);
432 }
433
434 return document.formattedString();
435 }
436
437 private String _getJavaFieldComment(
438 String[] lines, Map<String, Element> fieldElementsMap,
439 JavaField javaField) {
440
441 String fieldKey = _getFieldKey(javaField);
442
443 Element fieldElement = fieldElementsMap.get(fieldKey);
444
445 if (fieldElement == null) {
446 return null;
447 }
448
449 String line = lines[javaField.getLineNumber() - 1];
450
451 String indent = StringPool.BLANK;
452
453 for (char c : line.toCharArray()) {
454 if (Character.isWhitespace(c)) {
455 indent += c;
456 }
457 else {
458 break;
459 }
460 }
461
462 StringBuilder sb = new StringBuilder();
463
464 sb.append(indent);
465 sb.append("/**\n");
466 sb.append(indent);
467 sb.append(" * ");
468 sb.append(fieldElement.elementText("comment"));
469 sb.append("\n");
470 sb.append(indent);
471 sb.append(" *\n");
472
473 _addDocletTags(fieldElement, "deprecated", indent, sb);
474 _addDocletTags(fieldElement, "see", indent, sb);
475 _addDocletTags(fieldElement, "since", indent, sb);
476 _addDocletTags(fieldElement, "version", indent, sb);
477
478 sb.append(indent);
479 sb.append(" */\n");
480
481 return sb.toString();
482 }
483
484 private String _getJavaMethodComment(
485 String[] lines, Map<String, Element> methodElementsMap,
486 JavaMethod javaMethod) {
487
488 String methodKey = _getMethodKey(javaMethod);
489
490 Element methodElement = methodElementsMap.get(methodKey);
491
492 if (methodElement == null) {
493 return null;
494 }
495
496 String line = lines[javaMethod.getLineNumber() - 1];
497
498 String indent = StringPool.BLANK;
499
500 for (char c : line.toCharArray()) {
501 if (Character.isWhitespace(c)) {
502 indent += c;
503 }
504 else {
505 break;
506 }
507 }
508
509 StringBuilder sb = new StringBuilder();
510
511 sb.append(indent);
512 sb.append("/**\n");
513 sb.append(indent);
514 sb.append(" * ");
515 sb.append(methodElement.elementText("comment"));
516 sb.append("\n");
517 sb.append(indent);
518 sb.append(" *\n");
519
520 _addDocletTags(methodElement, "deprecated", indent, sb);
521 _addDocletTags(methodElement, "param", indent, sb);
522 _addDocletTags(methodElement, "return", indent, sb);
523 _addDocletTags(methodElement, "see", indent, sb);
524 _addDocletTags(methodElement, "since", indent, sb);
525 _addDocletTags(methodElement, "throws", indent, sb);
526 _addDocletTags(methodElement, "version", indent, sb);
527
528 sb.append(indent);
529 sb.append(" */\n");
530
531 return sb.toString();
532 }
533
534 private String _getMethodKey(Element methodElement) {
535 StringBuilder sb = new StringBuilder();
536
537 sb.append(methodElement.elementText("name"));
538 sb.append("(");
539
540 List<Element> paramElements = methodElement.elements("param");
541
542 for (Element paramElement : paramElements) {
543 sb.append(paramElement.elementText("name"));
544 sb.append("|");
545 sb.append(paramElement.elementText("type"));
546 sb.append(",");
547 }
548
549 sb.append(")");
550
551 return sb.toString();
552 }
553
554 private String _getMethodKey(JavaMethod javaMethod) {
555 StringBuilder sb = new StringBuilder();
556
557 sb.append(javaMethod.getName());
558 sb.append("(");
559
560 JavaParameter[] javaParameters = javaMethod.getParameters();
561
562 for (JavaParameter javaParameter : javaParameters) {
563 sb.append(javaParameter.getName());
564 sb.append("|");
565 sb.append(javaParameter.getType().getValue());
566 sb.append(",");
567 }
568
569 sb.append(")");
570
571 return sb.toString();
572 }
573
574 private boolean _isGenerated(String content) {
575 if (content.contains("<javadoc autogenerated=\"true\">")) {
576 return true;
577 }
578 else {
579 return false;
580 }
581 }
582
583 private void _process(
584 String command, String limit, Boolean ignoreAutogenerated)
585 throws Exception {
586
587 DirectoryScanner ds = new DirectoryScanner();
588
589 ds.setBasedir(_basedir);
590 ds.setExcludes(
591 new String[] {
592 "**\\classes\\**", "**\\portal-client\\**", "**\\portal-web\\**"
593 });
594
595 List<String> includes = new ArrayList<String>();
596
597 if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
598 String[] limitArray = StringUtil.split(limit, "/");
599
600 for (String curLimit : limitArray) {
601 includes.add(
602 "**\\" + StringUtil.replace(curLimit, ".", "\\") +
603 "\\**\\*.java");
604 includes.add("**\\" + curLimit + ".java");
605 }
606 }
607 else {
608 includes.add("**\\*.java");
609 }
610
611 ds.setIncludes(includes.toArray(new String[includes.size()]));
612
613 ds.scan();
614
615 String[] fileNames = ds.getIncludedFiles();
616
617 for (String fileName : fileNames) {
618 fileName = StringUtil.replace(fileName, "\\", "/");
619
620
623
624 if ((ignoreAutogenerated != null) &&
625 (ignoreAutogenerated.booleanValue())) {
626
627 File file = new File(_basedir + fileName);
628
629 if (file.exists()) {
630 String oldContent = _fileUtil.read(
631 _basedir + fileName + "doc");
632
633 if (_isGenerated(oldContent)) {
634 continue;
635 }
636 }
637 }
638
639 if (command.equals("cleanup")) {
640 _processGet(fileName);
641 _processSave(fileName);
642 _processDelete(fileName);
643 }
644 else if (command.equals("commit")) {
645 _processSave(fileName);
646 _processDelete(fileName);
647 }
648 else if (command.equals("delete")) {
649 _processDelete(fileName);
650 }
651 else if (command.equals("get")) {
652 _processGet(fileName);
653 }
654 else if (command.equals("save")) {
655 _processSave(fileName);
656 }
657 }
658 }
659
660 private void _processDelete(String fileName) throws Exception {
661 _removeJavadocFromJava(fileName, true);
662 }
663
664 private void _processGet(String fileName) throws Exception {
665 File javadocFile = new File(_basedir + fileName + "doc");
666
667 if (!javadocFile.exists()) {
668 _updateJavadocFromJava(fileName);
669 }
670
671 String javaWithoutJavadoc = _removeJavadocFromJava(fileName, false);
672
673 _updateJavaFromJavadoc(fileName, javaWithoutJavadoc);
674 }
675
676 private void _processSave(String fileName) throws Exception {
677 _updateJavadocFromJava(fileName);
678 }
679
680 private String _removeJavadocFromJava(String fileName, boolean log)
681 throws Exception {
682
683 File file = new File(_basedir + fileName);
684
685 String oldContent = _fileUtil.read(file);
686
687 String[] lines = StringUtil.split(oldContent, "\n");
688
689 JavaClass javaClass = _getJavaClass(
690 fileName, new StringReader(oldContent));
691
692 Set<Integer> lineNumbers = new HashSet<Integer>();
693
694 lineNumbers.add(javaClass.getLineNumber());
695
696 JavaMethod[] javaMethods = javaClass.getMethods();
697
698 for (JavaMethod javaMethod : javaMethods) {
699 lineNumbers.add(javaMethod.getLineNumber());
700 }
701
702 JavaField[] javaFields = javaClass.getFields();
703
704 for (JavaField javaField : javaFields) {
705 lineNumbers.add(javaField.getLineNumber());
706 }
707
708 for (int lineNumber : lineNumbers) {
709 int pos = lineNumber - 2;
710
711 String line = lines[pos].trim();
712
713 if (line.endsWith("*/")) {
714 while (true) {
715 lines[pos] = null;
716
717 if (line.startsWith("/**")) {
718 break;
719 }
720
721 line = lines[--pos].trim();
722 }
723 }
724 }
725
726 StringBuilder sb = new StringBuilder(oldContent.length());
727
728 for (String line : lines) {
729 if (line != null) {
730 sb.append(line);
731 sb.append("\n");
732 }
733 }
734
735 String newContent = sb.toString().trim();
736
737 if ((oldContent == null) || !oldContent.equals(newContent)) {
738 _fileUtil.write(file, newContent);
739
740 if (log) {
741 System.out.println("Writing " + file);
742 }
743 }
744
745 return newContent;
746 }
747
748 private void _updateJavadocFromJava(String fileName) throws Exception {
749 File file = new File(_basedir + fileName + "doc");
750
751 String oldContent = null;
752
753 if (file.exists()) {
754 oldContent = _fileUtil.read(file);
755
756 if (_isGenerated(oldContent)) {
757 return;
758 }
759 }
760
761 JavaClass javaClass = _getJavaClass(fileName);
762
763 String newContent = _getJavadocXml(javaClass);
764
765 if ((oldContent == null) || !oldContent.equals(newContent)) {
766 _fileUtil.write(file, newContent.getBytes());
767
768 System.out.println("Writing " + file);
769 }
770 }
771
772 private void _updateJavaFromJavadoc(String fileName, String oldContent)
773 throws Exception {
774
775 File javadocFile = new File(_basedir + fileName + "doc");
776
777 if (!javadocFile.exists()) {
778 return;
779 }
780
781 File file = new File(_basedir + fileName);
782
783 if (oldContent == null) {
784 oldContent = _fileUtil.read(file);
785 }
786
787 String[] lines = StringUtil.split(oldContent, "\n");
788
789 JavaClass javaClass = _getJavaClass(
790 fileName, new StringReader(oldContent));
791
792 Document document = _saxReaderUtil.read(javadocFile);
793
794 Element rootElement = document.getRootElement();
795
796 Map<Integer, String> commentsMap = new TreeMap<Integer, String>();
797
798 commentsMap.put(
799 javaClass.getLineNumber(),
800 _getJavaClassComment(rootElement, javaClass));
801
802 Map<String, Element> methodElementsMap = new HashMap<String, Element>();
803
804 List<Element> methodElements = rootElement.elements("method");
805
806 for (Element methodElement : methodElements) {
807 String methodKey = _getMethodKey(methodElement);
808
809 methodElementsMap.put(methodKey, methodElement);
810 }
811
812 JavaMethod[] javaMethods = javaClass.getMethods();
813
814 for (JavaMethod javaMethod : javaMethods) {
815 if (commentsMap.containsKey(javaMethod.getLineNumber())) {
816 continue;
817 }
818
819 commentsMap.put(
820 javaMethod.getLineNumber(),
821 _getJavaMethodComment(lines, methodElementsMap, javaMethod));
822 }
823
824 Map<String, Element> fieldElementsMap = new HashMap<String, Element>();
825
826 List<Element> fieldElements = rootElement.elements("field");
827
828 for (Element fieldElement : fieldElements) {
829 String fieldKey = _getFieldKey(fieldElement);
830
831 fieldElementsMap.put(fieldKey, fieldElement);
832 }
833
834 JavaField[] javaFields = javaClass.getFields();
835
836 for (JavaField javaField : javaFields) {
837 if (commentsMap.containsKey(javaField.getLineNumber())) {
838 continue;
839 }
840
841 commentsMap.put(
842 javaField.getLineNumber(),
843 _getJavaFieldComment(lines, fieldElementsMap, javaField));
844 }
845
846 StringBuilder sb = new StringBuilder(oldContent.length());
847
848 for (int lineNumber = 1; lineNumber <= lines.length; lineNumber++) {
849 String line = lines[lineNumber - 1];
850
851 String comments = commentsMap.get(lineNumber);
852
853 if (comments != null) {
854 sb.append(comments);
855 }
856
857 sb.append(line);
858 sb.append("\n");
859 }
860
861 String newContent = sb.toString().trim();
862
863 if ((oldContent == null) || !oldContent.equals(newContent)) {
864 _fileUtil.write(file, newContent);
865
866 System.out.println("Writing " + file);
867 }
868 }
869
870 private static FileImpl _fileUtil = FileImpl.getInstance();
871 private static SAXReaderImpl _saxReaderUtil = SAXReaderImpl.getInstance();
872
873 private String _basedir = "./";
874
875 }