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