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.GetterUtil;
019 import com.liferay.portal.kernel.util.StringPool;
020 import com.liferay.portal.kernel.util.StringUtil;
021 import com.liferay.portal.kernel.util.Validator;
022 import com.liferay.portal.kernel.xml.Document;
023 import com.liferay.portal.kernel.xml.Element;
024 import com.liferay.portal.tools.servicebuilder.ServiceBuilder;
025 import com.liferay.portal.util.FileImpl;
026 import com.liferay.portal.xml.SAXReaderImpl;
027 import com.liferay.util.xml.DocUtil;
028
029 import com.thoughtworks.qdox.JavaDocBuilder;
030 import com.thoughtworks.qdox.model.AbstractBaseJavaEntity;
031 import com.thoughtworks.qdox.model.AbstractJavaEntity;
032 import com.thoughtworks.qdox.model.Annotation;
033 import com.thoughtworks.qdox.model.DocletTag;
034 import com.thoughtworks.qdox.model.JavaClass;
035 import com.thoughtworks.qdox.model.JavaField;
036 import com.thoughtworks.qdox.model.JavaMethod;
037 import com.thoughtworks.qdox.model.JavaPackage;
038 import com.thoughtworks.qdox.model.JavaParameter;
039 import com.thoughtworks.qdox.model.Type;
040
041 import jargs.gnu.CmdLineParser;
042
043 import java.io.File;
044 import java.io.FileInputStream;
045 import java.io.Reader;
046
047 import java.util.ArrayList;
048 import java.util.Collection;
049 import java.util.HashMap;
050 import java.util.HashSet;
051 import java.util.List;
052 import java.util.Map;
053 import java.util.Set;
054 import java.util.TreeMap;
055 import java.util.regex.Matcher;
056 import java.util.regex.Pattern;
057
058 import org.apache.tools.ant.DirectoryScanner;
059
060
065 public class JavadocFormatter {
066
067 public static void main(String[] args) {
068 try {
069 new JavadocFormatter(args);
070 }
071 catch (Exception e) {
072 e.printStackTrace();
073 }
074 }
075
076 public JavadocFormatter(String[] args) throws Exception {
077 CmdLineParser cmdLineParser = new CmdLineParser();
078
079 CmdLineParser.Option limitOption = cmdLineParser.addStringOption(
080 "limit");
081 CmdLineParser.Option initOption = cmdLineParser.addStringOption(
082 "init");
083
084 cmdLineParser.parse(args);
085
086 String limit = (String)cmdLineParser.getOptionValue(limitOption);
087 String init = (String)cmdLineParser.getOptionValue(initOption);
088
089 if (Validator.isNotNull(init) && !init.startsWith("$")) {
090 _initializeMissingJavadocs = GetterUtil.getBoolean(init);
091 }
092
093 DirectoryScanner ds = new DirectoryScanner();
094
095 ds.setBasedir(_basedir);
096 ds.setExcludes(
097 new String[] {"**\\classes\\**", "**\\portal-client\\**"});
098
099 List<String> includes = new ArrayList<String>();
100
101 if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
102 System.out.println("Limit on " + limit);
103
104 String[] limitArray = StringUtil.split(limit, '/');
105
106 for (String curLimit : limitArray) {
107 includes.add(
108 "**\\" + StringUtil.replace(curLimit, ".", "\\") +
109 "\\**\\*.java");
110 includes.add("**\\" + curLimit + ".java");
111 }
112 }
113 else {
114 includes.add("**\\*.java");
115 }
116
117 ds.setIncludes(includes.toArray(new String[includes.size()]));
118
119 ds.scan();
120
121 String[] fileNames = ds.getIncludedFiles();
122
123 if ((fileNames.length == 0) && Validator.isNotNull(limit) &&
124 !limit.startsWith("$")) {
125
126 StringBuilder sb = new StringBuilder("Limit file not found: ");
127
128 sb.append(limit);
129
130 if (limit.contains(".")) {
131 sb.append(" Specify limit filename without package path or ");
132 sb.append("file type suffix.");
133 }
134
135 System.out.println(sb.toString());
136 }
137
138 for (String fileName : fileNames) {
139 fileName = StringUtil.replace(fileName, "\\", "/");
140
141 _format(fileName);
142 }
143 }
144
145 private void _addClassCommentElement(
146 Element rootElement, JavaClass javaClass) {
147
148 Element commentElement = rootElement.addElement("comment");
149
150 String comment = _getCDATA(javaClass);
151
152 if (comment.startsWith("Copyright (c) 2000-2010 Liferay, Inc.")) {
153 comment = StringPool.BLANK;
154 }
155
156 commentElement.addCDATA(comment);
157 }
158
159 private void _addDocletElements(
160 Element parentElement, AbstractJavaEntity abstractJavaEntity,
161 String name)
162 throws Exception {
163
164 DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);
165
166 for (DocletTag docletTag : docletTags) {
167 String value = docletTag.getValue();
168
169 value = _trimMultilineText(value);
170
171 value = StringUtil.replace(value, " </", "</");
172
173 if (name.equals("author") || name.equals("see") ||
174 name.equals("since") || name.equals("version")) {
175
176
180 }
181
182 Element element = parentElement.addElement(name);
183
184 element.addCDATA(value);
185 }
186
187 if ((docletTags.length == 0) && name.equals("author")) {
188 Element element = parentElement.addElement(name);
189
190 element.addCDATA(ServiceBuilder.AUTHOR);
191 }
192 }
193
194 private String _addDocletTags(
195 Element parentElement, String[] names, String indent) {
196
197 StringBuilder sb = new StringBuilder();
198
199 int maxNameLength = 0;
200
201 for (String name : names) {
202 if (name.length() < maxNameLength) {
203 continue;
204 }
205
206 List<Element> elements = parentElement.elements(name);
207
208 for (Element element : elements) {
209 Element commentElement = element.element("comment");
210
211 String comment = null;
212
213 if (commentElement != null) {
214 comment = commentElement.getText();
215 }
216 else {
217 comment = element.getText();
218 }
219
220 if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
221 Validator.isNull(comment)) {
222
223 continue;
224 }
225
226 maxNameLength = name.length();
227
228 break;
229 }
230 }
231
232
233
234 maxNameLength += 2;
235
236 String nameIndent = _getSpacesIndent(maxNameLength);
237
238 for (String name : names) {
239 List<Element> elements = parentElement.elements(name);
240
241 for (Element element : elements) {
242 Element commentElement = element.element("comment");
243
244 String comment = null;
245
246 if (commentElement != null) {
247 comment = commentElement.getText();
248 }
249 else {
250 comment = element.getText();
251 }
252
253 if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
254 Validator.isNull(comment)) {
255
256 continue;
257 }
258
259 if (commentElement != null) {
260 comment = element.elementText("name") + " " + comment;
261 }
262
263 if (Validator.isNull(comment)) {
264 sb.append(indent);
265 sb.append(StringPool.AT);
266 sb.append(name);
267 sb.append(StringPool.NEW_LINE);
268 }
269 else {
270 comment = _wrapText(comment, indent + nameIndent);
271
272 String firstLine = indent + "@" + name;
273
274 comment = firstLine + comment.substring(firstLine.length());
275
276 sb.append(comment);
277 }
278 }
279 }
280
281 return sb.toString();
282 }
283
284 private void _addFieldElement(Element rootElement, JavaField javaField)
285 throws Exception {
286
287 Element fieldElement = rootElement.addElement("field");
288
289 DocUtil.add(fieldElement, "name", javaField.getName());
290
291 Element commentElement = fieldElement.addElement("comment");
292
293 commentElement.addCDATA(_getCDATA(javaField));
294
295 _addDocletElements(fieldElement, javaField, "version");
296 _addDocletElements(fieldElement, javaField, "see");
297 _addDocletElements(fieldElement, javaField, "since");
298 _addDocletElements(fieldElement, javaField, "deprecated");
299 }
300
301 private void _addMethodElement(Element rootElement, JavaMethod javaMethod)
302 throws Exception {
303
304 Element methodElement = rootElement.addElement("method");
305
306 DocUtil.add(methodElement, "name", javaMethod.getName());
307
308 Element commentElement = methodElement.addElement("comment");
309
310 commentElement.addCDATA(_getCDATA(javaMethod));
311
312 _addDocletElements(methodElement, javaMethod, "version");
313 _addParamElements(methodElement, javaMethod);
314 _addReturnElement(methodElement, javaMethod);
315 _addThrowsElements(methodElement, javaMethod);
316 _addDocletElements(methodElement, javaMethod, "see");
317 _addDocletElements(methodElement, javaMethod, "since");
318 _addDocletElements(methodElement, javaMethod, "deprecated");
319 }
320
321 private void _addParamElement(
322 Element methodElement, JavaParameter javaParameter,
323 DocletTag[] paramDocletTags) {
324
325 String name = javaParameter.getName();
326 String type = javaParameter.getType().getValue();
327 String value = null;
328
329 for (DocletTag paramDocletTag : paramDocletTags) {
330 String curValue = paramDocletTag.getValue();
331
332 if (!curValue.startsWith(name)) {
333 continue;
334 }
335 else {
336 value = curValue;
337
338 break;
339 }
340 }
341
342 Element paramElement = methodElement.addElement("param");
343
344 DocUtil.add(paramElement, "name", name);
345 DocUtil.add(paramElement, "type", type);
346
347 if (value != null) {
348 value = value.substring(name.length());
349 }
350
351 value = _trimMultilineText(value);
352
353 Element commentElement = paramElement.addElement("comment");
354
355 commentElement.addCDATA(value);
356 }
357
358 private void _addParamElements(
359 Element methodElement, JavaMethod javaMethod) {
360
361 JavaParameter[] javaParameters = javaMethod.getParameters();
362
363 DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");
364
365 for (JavaParameter javaParameter : javaParameters) {
366 _addParamElement(methodElement, javaParameter, paramDocletTags);
367 }
368 }
369
370 private void _addReturnElement(
371 Element methodElement, JavaMethod javaMethod)
372 throws Exception {
373
374 Type returns = javaMethod.getReturns();
375
376 if ((returns == null) || returns.getValue().equals("void")) {
377 return;
378 }
379
380 _addDocletElements(methodElement, javaMethod, "return");
381 }
382
383 private void _addThrowsElement(
384 Element methodElement, Type exception, DocletTag[] throwsDocletTags) {
385
386 String name = exception.getJavaClass().getName();
387 String value = null;
388
389 for (DocletTag throwsDocletTag : throwsDocletTags) {
390 String curValue = throwsDocletTag.getValue();
391
392 if (!curValue.startsWith(name)) {
393 continue;
394 }
395 else {
396 value = curValue;
397
398 break;
399 }
400 }
401
402 Element throwsElement = methodElement.addElement("throws");
403
404 DocUtil.add(throwsElement, "name", name);
405 DocUtil.add(throwsElement, "type", exception.getValue());
406
407 if (value != null) {
408 value = value.substring(name.length());
409 }
410
411 value = _trimMultilineText(value);
412
413 Element commentElement = throwsElement.addElement("comment");
414
415 commentElement.addCDATA(_getCDATA(value));
416
417 }
418
419 private void _addThrowsElements(
420 Element methodElement, JavaMethod javaMethod) {
421
422 Type[] exceptions = javaMethod.getExceptions();
423
424 DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");
425
426 for (Type exception : exceptions) {
427 _addThrowsElement(methodElement, exception, throwsDocletTags);
428 }
429 }
430
431 private List<JavaClass> _getAncestorJavaClasses(JavaClass javaClass) {
432 List<JavaClass> ancestorJavaClasses = new ArrayList<JavaClass>();
433
434 while ((javaClass = javaClass.getSuperJavaClass()) != null) {
435 ancestorJavaClasses.add(javaClass);
436 }
437
438 return ancestorJavaClasses;
439 }
440
441 private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
442 return _getCDATA(abstractJavaEntity.getComment());
443 }
444
445 private String _getCDATA(String cdata) {
446 if (cdata == null) {
447 return StringPool.BLANK;
448 }
449
450 cdata = cdata.replaceAll(
451 "(?s)\\s*<(p|pre|[ou]l)>\\s*(.*?)\\s*</\\1>\\s*",
452 "\n\n<$1>\n$2\n</$1>\n\n");
453 cdata = cdata.replaceAll(
454 "(?s)\\s*<li>\\s*(.*?)\\s*</li>\\s*", "\n<li>\n$1\n</li>\n");
455 cdata = StringUtil.replace(cdata, "</li>\n\n<li>", "</li>\n<li>");
456 cdata = cdata.replaceAll("\n\\s+\n", "\n\n");
457 cdata = cdata.replaceAll(" +", " ");
458
459
460
461 Pattern pattern = Pattern.compile(
462 "(^.*?(?=\n\n|$)+|(?<=<p>\n).*?(?=\n</p>))", Pattern.DOTALL);
463
464 Matcher matcher = pattern.matcher(cdata);
465
466 StringBuffer sb = new StringBuffer();
467
468 while (matcher.find()) {
469 String trimmed = _trimMultilineText(matcher.group());
470
471
472
473 trimmed = trimmed.replaceAll("\\$", "\\\\\\$");
474
475 matcher.appendReplacement(sb, trimmed);
476 }
477
478 matcher.appendTail(sb);
479
480 cdata = sb.toString();
481
482 return cdata.trim();
483 }
484
485 private String _getClassName(String fileName) {
486 int pos = fileName.indexOf("src/");
487
488 if (pos == -1) {
489 pos = fileName.indexOf("test/");
490 }
491
492 if (pos == -1) {
493 pos = fileName.indexOf("service/");
494 }
495
496 if (pos == -1) {
497 throw new RuntimeException(fileName);
498 }
499
500 pos = fileName.indexOf("/", pos);
501
502 String srcFile = fileName.substring(pos + 1, fileName.length());
503
504 return StringUtil.replace(
505 srcFile.substring(0, srcFile.length() - 5), "/", ".");
506 }
507
508 private String _getFieldKey(Element fieldElement) {
509 return fieldElement.elementText("name");
510 }
511
512 private String _getFieldKey(JavaField javaField) {
513 return javaField.getName();
514 }
515
516 private String _getIndent(
517 String[] lines, AbstractBaseJavaEntity abstractBaseJavaEntity) {
518
519 String line = lines[abstractBaseJavaEntity.getLineNumber() - 1];
520
521 String indent = StringPool.BLANK;
522
523 for (char c : line.toCharArray()) {
524 if (Character.isWhitespace(c)) {
525 indent += c;
526 }
527 else {
528 break;
529 }
530 }
531
532 return indent;
533 }
534
535 private int _getIndentLength(String indent) {
536 int indentLength = 0;
537
538 for (char c : indent.toCharArray()) {
539 if (c == '\t') {
540 indentLength = indentLength + 4;
541 }
542 else {
543 indentLength++;
544 }
545 }
546
547 return indentLength;
548 }
549
550 private JavaClass _getJavaClass(String fileName, Reader reader)
551 throws Exception {
552
553 String className = _getClassName(fileName);
554
555 JavaDocBuilder builder = new JavaDocBuilder();
556
557 if (reader == null) {
558 File file = new File(fileName);
559
560 if (!file.exists()) {
561 return null;
562 }
563
564 builder.addSource(file);
565 }
566 else {
567 builder.addSource(reader);
568 }
569
570 return builder.getClassByName(className);
571 }
572
573 private String _getJavaClassComment(
574 Element rootElement, JavaClass javaClass) {
575
576 StringBuilder sb = new StringBuilder();
577
578 String indent = StringPool.BLANK;
579
580 sb.append("\n");
604
605 return sb.toString();
606 }
607
608 private int _getJavaClassLineNumber(JavaClass javaClass) {
609 int lineNumber = javaClass.getLineNumber();
610
611 Annotation[] annotations = javaClass.getAnnotations();
612
613 if (annotations.length == 0) {
614 return lineNumber;
615 }
616
617 for (Annotation annotation : annotations) {
618 int annotationLineNumber = annotation.getLineNumber();
619
620 if (annotation.getPropertyMap().isEmpty()) {
621 annotationLineNumber--;
622 }
623
624 if (annotationLineNumber < lineNumber) {
625 lineNumber = annotationLineNumber;
626 }
627 }
628
629 return lineNumber;
630 }
631
632 private Document _getJavadocDocument(JavaClass javaClass) throws Exception {
633 Element rootElement = _saxReaderUtil.createElement("javadoc");
634
635 Document document = _saxReaderUtil.createDocument(rootElement);
636
637 DocUtil.add(rootElement, "name", javaClass.getName());
638 DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());
639
640 _addClassCommentElement(rootElement, javaClass);
641 _addDocletElements(rootElement, javaClass, "author");
642 _addDocletElements(rootElement, javaClass, "version");
643 _addDocletElements(rootElement, javaClass, "see");
644 _addDocletElements(rootElement, javaClass, "since");
645 _addDocletElements(rootElement, javaClass, "serial");
646 _addDocletElements(rootElement, javaClass, "deprecated");
647
648 JavaMethod[] javaMethods = javaClass.getMethods();
649
650 for (JavaMethod javaMethod : javaMethods) {
651 _addMethodElement(rootElement, javaMethod);
652 }
653
654 JavaField[] javaFields = javaClass.getFields();
655
656 for (JavaField javaField : javaFields) {
657 _addFieldElement(rootElement, javaField);
658 }
659
660 return document;
661 }
662
663 private String _getJavaFieldComment(
664 String[] lines, Map<String, Element> fieldElementsMap,
665 JavaField javaField) {
666
667 String fieldKey = _getFieldKey(javaField);
668
669 Element fieldElement = fieldElementsMap.get(fieldKey);
670
671 if (fieldElement == null) {
672 return null;
673 }
674
675 String indent = _getIndent(lines, javaField);
676
677 StringBuilder sb = new StringBuilder();
678
679 sb.append(indent);
680 sb.append("\n");
704
705 if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
706 Validator.isNull(docletTags)) {
707
708 return null;
709 }
710
711 return sb.toString();
712 }
713
714 private String _getJavaMethodComment(
715 String[] lines, Map<String, Element> methodElementsMap,
716 JavaMethod javaMethod) {
717
718 String methodKey = _getMethodKey(javaMethod);
719
720 Element methodElement = methodElementsMap.get(methodKey);
721
722 if (methodElement == null) {
723 return null;
724 }
725
726 String indent = _getIndent(lines, javaMethod);
727
728 StringBuilder sb = new StringBuilder();
729
730 sb.append(indent);
731 sb.append("\n");
758
759 if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
760 Validator.isNull(docletTags)) {
761
762 return null;
763 }
764
765 return sb.toString();
766 }
767
768 private String _getMethodKey(Element methodElement) {
769 StringBuilder sb = new StringBuilder();
770
771 sb.append(methodElement.elementText("name"));
772 sb.append("(");
773
774 List<Element> paramElements = methodElement.elements("param");
775
776 for (Element paramElement : paramElements) {
777 sb.append(paramElement.elementText("name"));
778 sb.append("|");
779 sb.append(paramElement.elementText("type"));
780 sb.append(",");
781 }
782
783 sb.append(")");
784
785 return sb.toString();
786 }
787
788 private String _getMethodKey(JavaMethod javaMethod) {
789 StringBuilder sb = new StringBuilder();
790
791 sb.append(javaMethod.getName());
792 sb.append("(");
793
794 JavaParameter[] javaParameters = javaMethod.getParameters();
795
796 for (JavaParameter javaParameter : javaParameters) {
797 sb.append(javaParameter.getName());
798 sb.append("|");
799 sb.append(javaParameter.getType().getValue());
800 sb.append(",");
801 }
802
803 sb.append(")");
804
805 return sb.toString();
806 }
807
808 private String _getSpacesIndent(int length) {
809 String indent = StringPool.BLANK;
810
811 for (int i = 0; i < length; i++) {
812 indent += StringPool.SPACE;
813 }
814
815 return indent;
816 }
817
818 private boolean _hasAnnotation(
819 AbstractBaseJavaEntity abstractBaseJavaEntity, String annotationName) {
820
821 Annotation[] annotations = abstractBaseJavaEntity.getAnnotations();
822
823 if (annotations == null) {
824 return false;
825 }
826
827 for (int i = 0; i < annotations.length; i++) {
828 Type type = annotations[i].getType();
829
830 JavaClass javaClass = type.getJavaClass();
831
832 if (annotationName.equals(javaClass.getName())) {
833 return true;
834 }
835 }
836
837 return false;
838 }
839
840 private boolean _isGenerated(String content) {
841 if (content.contains("* @generated") || content.contains("$ANTLR")) {
842 return true;
843 }
844 else {
845 return false;
846 }
847 }
848
849 private boolean _isOverrideMethod(
850 JavaClass javaClass, JavaMethod javaMethod,
851 Collection<JavaClass> ancestorJavaClasses) {
852
853 if (javaClass.isInterface() || javaMethod.isConstructor() ||
854 javaMethod.isPrivate() || javaMethod.isStatic()) {
855
856 return false;
857 }
858
859 String methodName = javaMethod.getName();
860
861 JavaParameter[] javaParameters = javaMethod.getParameters();
862
863 Type[] types = new Type[javaParameters.length];
864
865 for (int i = 0; i < javaParameters.length; i++) {
866 types[i] = javaParameters[i].getType();
867 }
868
869
870
871 for (JavaClass ancestorJavaClass : ancestorJavaClasses) {
872 JavaMethod ancestorJavaMethod =
873 ancestorJavaClass.getMethodBySignature(methodName, types);
874
875 if (ancestorJavaMethod == null) {
876 continue;
877 }
878
879 boolean samePackage = false;
880
881 JavaPackage ancestorJavaPackage = ancestorJavaClass.getPackage();
882
883 if (ancestorJavaPackage != null) {
884 samePackage = ancestorJavaPackage.equals(
885 javaClass.getPackage());
886 }
887
888
889
890 if (samePackage) {
891 return !ancestorJavaMethod.isPrivate();
892 }
893 else {
894 if (ancestorJavaMethod.isProtected() ||
895 ancestorJavaMethod.isPublic()) {
896
897 return true;
898 }
899 else {
900 return false;
901 }
902 }
903 }
904
905 return false;
906 }
907
908 private String _removeJavadocFromJava(
909 JavaClass javaClass, String content) {
910
911 Set<Integer> lineNumbers = new HashSet<Integer>();
912
913 lineNumbers.add(_getJavaClassLineNumber(javaClass));
914
915 JavaMethod[] javaMethods = javaClass.getMethods();
916
917 for (JavaMethod javaMethod : javaMethods) {
918 lineNumbers.add(javaMethod.getLineNumber());
919 }
920
921 JavaField[] javaFields = javaClass.getFields();
922
923 for (JavaField javaField : javaFields) {
924 lineNumbers.add(javaField.getLineNumber());
925 }
926
927 String[] lines = StringUtil.splitLines(content);
928
929 for (int lineNumber : lineNumbers) {
930 if (lineNumber == 0) {
931 continue;
932 }
933
934 int pos = lineNumber - 2;
935
936 String line = lines[pos];
937
938 if (line == null) {
939 continue;
940 }
941
942 line = line.trim();
943
944 if (line.endsWith("*/")) {
945 while (true) {
946 lines[pos] = null;
947
948 if (line.startsWith("