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.AbstractJavaEntity;
031 import com.thoughtworks.qdox.model.Annotation;
032 import com.thoughtworks.qdox.model.DocletTag;
033 import com.thoughtworks.qdox.model.JavaClass;
034 import com.thoughtworks.qdox.model.JavaField;
035 import com.thoughtworks.qdox.model.JavaMethod;
036 import com.thoughtworks.qdox.model.JavaParameter;
037 import com.thoughtworks.qdox.model.Type;
038
039 import jargs.gnu.CmdLineParser;
040
041 import java.io.File;
042 import java.io.FileInputStream;
043 import java.io.Reader;
044
045 import java.util.ArrayList;
046 import java.util.HashMap;
047 import java.util.HashSet;
048 import java.util.List;
049 import java.util.Map;
050 import java.util.Set;
051 import java.util.TreeMap;
052 import java.util.regex.Matcher;
053 import java.util.regex.Pattern;
054
055 import org.apache.tools.ant.DirectoryScanner;
056
057
061 public class JavadocFormatter {
062
063 public static void main(String[] args) {
064 try {
065 new JavadocFormatter(args);
066 }
067 catch (Exception e) {
068 e.printStackTrace();
069 }
070 }
071
072 public JavadocFormatter(String[] args) throws Exception {
073 CmdLineParser cmdLineParser = new CmdLineParser();
074
075 CmdLineParser.Option limitOption = cmdLineParser.addStringOption(
076 "limit");
077 CmdLineParser.Option initOption = cmdLineParser.addStringOption(
078 "init");
079
080 cmdLineParser.parse(args);
081
082 String limit = (String)cmdLineParser.getOptionValue(limitOption);
083 String init = (String)cmdLineParser.getOptionValue(initOption);
084
085 if (!init.startsWith("$")) {
086 _initializeMissingJavadocs = GetterUtil.getBoolean(init);
087 }
088
089 DirectoryScanner ds = new DirectoryScanner();
090
091 ds.setBasedir(_basedir);
092 ds.setExcludes(
093 new String[] {
094 "**\\classes\\**", "**\\portal-client\\**", "**\\tools\\**"
095 });
096
097 List<String> includes = new ArrayList<String>();
098
099 if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
100 String[] limitArray = StringUtil.split(limit, "/");
101
102 for (String curLimit : limitArray) {
103 includes.add(
104 "**\\" + StringUtil.replace(curLimit, ".", "\\") +
105 "\\**\\*.java");
106 includes.add("**\\" + curLimit + ".java");
107 }
108 }
109 else {
110 includes.add("**\\*.java");
111 }
112
113 ds.setIncludes(includes.toArray(new String[includes.size()]));
114
115 ds.scan();
116
117 String[] fileNames = ds.getIncludedFiles();
118
119 for (String fileName : fileNames) {
120 fileName = StringUtil.replace(fileName, "\\", "/");
121
122 _format(fileName);
123 }
124 }
125
126 private void _addClassCommentElement(
127 Element rootElement, JavaClass javaClass) {
128
129 Element commentElement = rootElement.addElement("comment");
130
131 String comment = _getCDATA(javaClass);
132
133 if (comment.startsWith("Copyright (c) 2000-2010 Liferay, Inc.")) {
134 comment = StringPool.BLANK;
135 }
136
137 commentElement.addCDATA(comment);
138 }
139
140 private void _addDocletElements(
141 Element parentElement, AbstractJavaEntity abstractJavaEntity,
142 String name)
143 throws Exception {
144
145 DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);
146
147 for (DocletTag docletTag : docletTags) {
148 String value = docletTag.getValue();
149
150 value = _trimMultilineText(value);
151
152 value = StringUtil.replace(value, " </", "</");
153
154 if (name.equals("author") || name.equals("see") ||
155 name.equals("since") || name.equals("version")) {
156
157
161 }
162
163 Element element = parentElement.addElement(name);
164
165 element.addCDATA(value);
166 }
167
168 if ((docletTags.length == 0) && name.equals("author")) {
169 Element element = parentElement.addElement(name);
170
171 element.addCDATA(ServiceBuilder.AUTHOR);
172 }
173 }
174
175 private String _addDocletTags(
176 Element parentElement, String[] names, String indent) {
177
178 StringBuilder sb = new StringBuilder();
179
180 int maxNameLength = 0;
181
182 for (String name : names) {
183 if (name.length() < maxNameLength) {
184 continue;
185 }
186
187 List<Element> elements = parentElement.elements(name);
188
189 for (Element element : elements) {
190 Element commentElement = element.element("comment");
191
192 String comment = null;
193
194 if (commentElement != null) {
195 comment = commentElement.getText();
196 }
197 else {
198 comment = element.getText();
199 }
200
201 if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
202 Validator.isNull(comment)) {
203
204 continue;
205 }
206
207 maxNameLength = name.length();
208
209 break;
210 }
211 }
212
213 int indentLength =_getIndentLength(indent) + maxNameLength;
214
215 String maxNameIndent = "\t ";
216
217 for (int i = 0; i < maxNameLength; i++) {
218 maxNameIndent += " ";
219 }
220
221 maxNameIndent = StringUtil.replace(
222 maxNameIndent, StringPool.FOUR_SPACES, "\t");
223
224 for (String name : names) {
225 String curNameIndent = " ";
226
227 if (name.length() < maxNameLength) {
228 int firstTab = 4 - (name.length() % 4);
229
230 int delta = (maxNameLength + 1) - (name.length() + firstTab);
231
232 if (delta == 0) {
233 curNameIndent = "\t";
234 }
235 else if (delta < 0) {
236 for (int i = 0; i < (maxNameLength - name.length()); i++) {
237 curNameIndent += " ";
238 }
239 }
240 else if (delta > 0) {
241 curNameIndent = "\t";
242
243 int numberOfTabs = delta / 4;
244
245 if (numberOfTabs > 0) {
246 for (int i = 0; i < numberOfTabs; i++) {
247 curNameIndent += "\t";
248 }
249 }
250
251 int numberOfSpaces = delta % 4;
252
253 if (numberOfSpaces > 0) {
254 for (int i = 0; i < numberOfSpaces; i++) {
255 curNameIndent += " ";
256 }
257 }
258 }
259 }
260
261 List<Element> elements = parentElement.elements(name);
262
263 for (Element element : elements) {
264 Element commentElement = element.element("comment");
265
266 String comment = null;
267
268 if (commentElement != null) {
269 comment = commentElement.getText();
270 }
271 else {
272 comment = element.getText();
273 }
274
275 if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
276 Validator.isNull(comment)) {
277
278 continue;
279 }
280
281 sb.append(indent);
282 sb.append(" * @");
283 sb.append(name);
284
285 if (Validator.isNotNull(comment) || (commentElement != null)) {
286 sb.append(curNameIndent);
287 }
288
289 if (commentElement != null) {
290 comment = element.elementText("name") + " " + comment;
291 }
292
293 comment = StringUtil.wrap(comment, 80 - indentLength - 5, "\n");
294
295 comment = comment.trim();
296
297 comment = StringUtil.replace(
298 comment, "\n", "\n" + indent + " *" + maxNameIndent);
299
300 while (comment.contains(" \n")) {
301 comment = StringUtil.replace(comment, " \n", "\n");
302 }
303
304 while (comment.startsWith("\n")) {
305 comment = comment.substring(1, comment.length());
306 }
307
308 sb.append(comment);
309 sb.append("\n");
310 }
311 }
312
313 return sb.toString();
314 }
315
316 private void _addFieldElement(Element rootElement, JavaField javaField)
317 throws Exception {
318
319 Element fieldElement = rootElement.addElement("field");
320
321 DocUtil.add(fieldElement, "name", javaField.getName());
322
323 Element commentElement = fieldElement.addElement("comment");
324
325 commentElement.addCDATA(_getCDATA(javaField));
326
327 _addDocletElements(fieldElement, javaField, "version");
328 _addDocletElements(fieldElement, javaField, "see");
329 _addDocletElements(fieldElement, javaField, "since");
330 _addDocletElements(fieldElement, javaField, "deprecated");
331 }
332
333 private void _addMethodElement(Element rootElement, JavaMethod javaMethod)
334 throws Exception {
335
336 Element methodElement = rootElement.addElement("method");
337
338 DocUtil.add(methodElement, "name", javaMethod.getName());
339
340 Element commentElement = methodElement.addElement("comment");
341
342 commentElement.addCDATA(_getCDATA(javaMethod));
343
344 _addDocletElements(methodElement, javaMethod, "version");
345 _addParamElements(methodElement, javaMethod);
346 _addReturnElement(methodElement, javaMethod);
347 _addThrowsElements(methodElement, javaMethod);
348 _addDocletElements(methodElement, javaMethod, "see");
349 _addDocletElements(methodElement, javaMethod, "since");
350 _addDocletElements(methodElement, javaMethod, "deprecated");
351 }
352
353 private void _addParamElement(
354 Element methodElement, JavaParameter javaParameter,
355 DocletTag[] paramDocletTags) {
356
357 String name = javaParameter.getName();
358 String type = javaParameter.getType().getValue();
359 String value = null;
360
361 for (DocletTag paramDocletTag : paramDocletTags) {
362 String curValue = paramDocletTag.getValue();
363
364 if (!curValue.startsWith(name)) {
365 continue;
366 }
367 else {
368 value = curValue;
369
370 break;
371 }
372 }
373
374 Element paramElement = methodElement.addElement("param");
375
376 DocUtil.add(paramElement, "name", name);
377 DocUtil.add(paramElement, "type", type);
378
379 if (value != null) {
380 value = value.substring(name.length());
381 }
382
383 value = _trimMultilineText(value);
384
385 Element commentElement = paramElement.addElement("comment");
386
387 commentElement.addCDATA(value);
388 }
389
390 private void _addParamElements(
391 Element methodElement, JavaMethod javaMethod) {
392
393 JavaParameter[] javaParameters = javaMethod.getParameters();
394
395 DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");
396
397 for (JavaParameter javaParameter : javaParameters) {
398 _addParamElement(methodElement, javaParameter, paramDocletTags);
399 }
400 }
401
402 private void _addReturnElement(
403 Element methodElement, JavaMethod javaMethod)
404 throws Exception {
405
406 Type returns = javaMethod.getReturns();
407
408 if ((returns == null) || returns.getValue().equals("void")) {
409 return;
410 }
411
412 _addDocletElements(methodElement, javaMethod, "return");
413 }
414
415 private void _addThrowsElement(
416 Element methodElement, Type exception, DocletTag[] throwsDocletTags) {
417
418 String name = exception.getJavaClass().getName();
419 String value = null;
420
421 for (DocletTag throwsDocletTag : throwsDocletTags) {
422 String curValue = throwsDocletTag.getValue();
423
424 if (!curValue.startsWith(name)) {
425 continue;
426 }
427 else {
428 value = curValue;
429
430 break;
431 }
432 }
433
434 Element throwsElement = methodElement.addElement("throws");
435
436 DocUtil.add(throwsElement, "name", name);
437 DocUtil.add(throwsElement, "type", exception.getValue());
438
439 if (value != null) {
440 value = value.substring(name.length());
441 }
442
443 value = _trimMultilineText(value);
444
445 Element commentElement = throwsElement.addElement("comment");
446
447 commentElement.addCDATA(_getCDATA(value));
448
449 }
450
451 private void _addThrowsElements(
452 Element methodElement, JavaMethod javaMethod) {
453
454 Type[] exceptions = javaMethod.getExceptions();
455
456 DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");
457
458 for (Type exception : exceptions) {
459 _addThrowsElement(methodElement, exception, throwsDocletTags);
460 }
461 }
462
463 private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
464 return _getCDATA(abstractJavaEntity.getComment());
465 }
466
467 private String _getCDATA(String cdata) {
468 if (cdata == null) {
469 return StringPool.BLANK;
470 }
471
472 cdata = cdata.replaceAll(
473 "(?s)\\s*<(p|pre|[ou]l)>\\s*(.*?)\\s*</\\1>\\s*",
474 "\n\n<$1>\n$2\n</$1>\n\n");
475 cdata = cdata.replaceAll(
476 "(?s)\\s*<li>\\s*(.*?)\\s*</li>\\s*", "\n<li>\n$1\n</li>\n");
477 cdata = StringUtil.replace(cdata, "</li>\n\n<li>", "</li>\n<li>");
478 cdata = cdata.replaceAll("\n\\s+\n", "\n\n");
479 cdata = cdata.replaceAll(" +", " ");
480
481
482
483 Pattern pattern = Pattern.compile(
484 "(^.*?(?=\n\n|$)+|(?<=<p>\n).*?(?=\n</p>))", Pattern.DOTALL);
485
486 Matcher matcher = pattern.matcher(cdata);
487
488 StringBuffer sb = new StringBuffer();
489
490 while (matcher.find()) {
491 String trimmed = _trimMultilineText(matcher.group());
492
493 matcher.appendReplacement(sb, trimmed);
494 }
495
496 matcher.appendTail(sb);
497
498 cdata = sb.toString();
499
500 return cdata.trim();
501 }
502
503 private String _getFieldKey(Element fieldElement) {
504 return fieldElement.elementText("name");
505 }
506
507 private String _getFieldKey(JavaField javaField) {
508 return javaField.getName();
509 }
510
511 private int _getIndentLength(String indent) {
512 int indentLength = 0;
513
514 for (char c : indent.toCharArray()) {
515 if (c == '\t') {
516 indentLength = indentLength + 4;
517 }
518 else {
519 indentLength++;
520 }
521 }
522
523 return indentLength;
524 }
525
526 private JavaClass _getJavaClass(String fileName, Reader reader)
527 throws Exception {
528
529 int pos = fileName.indexOf("src/");
530
531 if (pos == -1) {
532 pos = fileName.indexOf("test/");
533 }
534
535 if (pos == -1) {
536 throw new RuntimeException(fileName);
537 }
538
539 pos = fileName.indexOf("/", pos);
540
541 String srcFile = fileName.substring(pos + 1, fileName.length());
542 String className = StringUtil.replace(
543 srcFile.substring(0, srcFile.length() - 5), "/", ".");
544
545 JavaDocBuilder builder = new JavaDocBuilder();
546
547 if (reader == null) {
548 File file = new File(fileName);
549
550 if (!file.exists()) {
551 return null;
552 }
553
554 builder.addSource(file);
555 }
556 else {
557 builder.addSource(reader);
558 }
559
560 return builder.getClassByName(className);
561 }
562
563 private String _getJavaClassComment(
564 Element rootElement, JavaClass javaClass) {
565
566 StringBuilder sb = new StringBuilder();
567
568 String indent = StringPool.BLANK;
569
570 sb.append("\n");
594
595 return sb.toString();
596 }
597
598 private Document _getJavadocDocument(JavaClass javaClass) throws Exception {
599 Element rootElement = _saxReaderUtil.createElement("javadoc");
600
601 Document document = _saxReaderUtil.createDocument(rootElement);
602
603 DocUtil.add(rootElement, "name", javaClass.getName());
604 DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());
605
606 _addClassCommentElement(rootElement, javaClass);
607 _addDocletElements(rootElement, javaClass, "author");
608 _addDocletElements(rootElement, javaClass, "version");
609 _addDocletElements(rootElement, javaClass, "see");
610 _addDocletElements(rootElement, javaClass, "since");
611 _addDocletElements(rootElement, javaClass, "serial");
612 _addDocletElements(rootElement, javaClass, "deprecated");
613
614 JavaMethod[] javaMethods = javaClass.getMethods();
615
616 for (JavaMethod javaMethod : javaMethods) {
617 _addMethodElement(rootElement, javaMethod);
618 }
619
620 JavaField[] javaFields = javaClass.getFields();
621
622 for (JavaField javaField : javaFields) {
623 _addFieldElement(rootElement, javaField);
624 }
625
626 return document;
627 }
628
629 private String _getJavaFieldComment(
630 String[] lines, Map<String, Element> fieldElementsMap,
631 JavaField javaField) {
632
633 String fieldKey = _getFieldKey(javaField);
634
635 Element fieldElement = fieldElementsMap.get(fieldKey);
636
637 if (fieldElement == null) {
638 return null;
639 }
640
641 String line = lines[javaField.getLineNumber() - 1];
642
643 String indent = StringPool.BLANK;
644
645 for (char c : line.toCharArray()) {
646 if (Character.isWhitespace(c)) {
647 indent += c;
648 }
649 else {
650 break;
651 }
652 }
653
654 StringBuilder sb = new StringBuilder();
655
656 sb.append(indent);
657 sb.append("\n");
680
681 if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
682 Validator.isNull(docletTags)) {
683
684 return null;
685 }
686
687 return sb.toString();
688 }
689
690 private String _getJavaMethodComment(
691 String[] lines, Map<String, Element> methodElementsMap,
692 JavaMethod javaMethod) {
693
694 String methodKey = _getMethodKey(javaMethod);
695
696 Element methodElement = methodElementsMap.get(methodKey);
697
698 if (methodElement == null) {
699 return null;
700 }
701
702 String line = lines[javaMethod.getLineNumber() - 1];
703
704 String indent = StringPool.BLANK;
705
706 for (char c : line.toCharArray()) {
707 if (Character.isWhitespace(c)) {
708 indent += c;
709 }
710 else {
711 break;
712 }
713 }
714
715 StringBuilder sb = new StringBuilder();
716
717 sb.append(indent);
718 sb.append("\n");
745
746 if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
747 Validator.isNull(docletTags)) {
748
749 return null;
750 }
751
752 return sb.toString();
753 }
754
755 private String _getMethodKey(Element methodElement) {
756 StringBuilder sb = new StringBuilder();
757
758 sb.append(methodElement.elementText("name"));
759 sb.append("(");
760
761 List<Element> paramElements = methodElement.elements("param");
762
763 for (Element paramElement : paramElements) {
764 sb.append(paramElement.elementText("name"));
765 sb.append("|");
766 sb.append(paramElement.elementText("type"));
767 sb.append(",");
768 }
769
770 sb.append(")");
771
772 return sb.toString();
773 }
774
775 private String _getMethodKey(JavaMethod javaMethod) {
776 StringBuilder sb = new StringBuilder();
777
778 sb.append(javaMethod.getName());
779 sb.append("(");
780
781 JavaParameter[] javaParameters = javaMethod.getParameters();
782
783 for (JavaParameter javaParameter : javaParameters) {
784 sb.append(javaParameter.getName());
785 sb.append("|");
786 sb.append(javaParameter.getType().getValue());
787 sb.append(",");
788 }
789
790 sb.append(")");
791
792 return sb.toString();
793 }
794
795 private boolean _isGenerated(String content) {
796 if (content.contains("* @generated")) {
797 return true;
798 }
799 else {
800 return false;
801 }
802 }
803
804 private String _removeJavadocFromJava(
805 JavaClass javaClass, String content) {
806
807 Set<Integer> lineNumbers = new HashSet<Integer>();
808
809 lineNumbers.add(_getJavaClassLineNumber(javaClass));
810
811 JavaMethod[] javaMethods = javaClass.getMethods();
812
813 for (JavaMethod javaMethod : javaMethods) {
814 lineNumbers.add(javaMethod.getLineNumber());
815 }
816
817 JavaField[] javaFields = javaClass.getFields();
818
819 for (JavaField javaField : javaFields) {
820 lineNumbers.add(javaField.getLineNumber());
821 }
822
823 String[] lines = StringUtil.split(content, "\n");
824
825 for (int lineNumber : lineNumbers) {
826 if (lineNumber == 0) {
827 continue;
828 }
829
830 int pos = lineNumber - 2;
831
832 String line = lines[pos].trim();
833
834 if (line.endsWith("*/")) {
835 while (true) {
836 lines[pos] = null;
837
838 if (line.startsWith("