1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.tools;
16  
17  import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
18  import com.liferay.portal.kernel.util.GetterUtil;
19  import com.liferay.portal.kernel.util.StringPool;
20  import com.liferay.portal.kernel.util.StringUtil;
21  import com.liferay.portal.kernel.util.Validator;
22  import com.liferay.portal.kernel.xml.Document;
23  import com.liferay.portal.kernel.xml.Element;
24  import com.liferay.portal.tools.servicebuilder.ServiceBuilder;
25  import com.liferay.portal.util.FileImpl;
26  import com.liferay.portal.xml.SAXReaderImpl;
27  import com.liferay.util.xml.DocUtil;
28  
29  import com.thoughtworks.qdox.JavaDocBuilder;
30  import com.thoughtworks.qdox.model.AbstractJavaEntity;
31  import com.thoughtworks.qdox.model.Annotation;
32  import com.thoughtworks.qdox.model.DocletTag;
33  import com.thoughtworks.qdox.model.JavaClass;
34  import com.thoughtworks.qdox.model.JavaField;
35  import com.thoughtworks.qdox.model.JavaMethod;
36  import com.thoughtworks.qdox.model.JavaParameter;
37  import com.thoughtworks.qdox.model.Type;
38  
39  import jargs.gnu.CmdLineParser;
40  
41  import java.io.File;
42  import java.io.FileInputStream;
43  import java.io.Reader;
44  
45  import java.util.ArrayList;
46  import java.util.HashMap;
47  import java.util.HashSet;
48  import java.util.List;
49  import java.util.Map;
50  import java.util.Set;
51  import java.util.TreeMap;
52  
53  import org.apache.tools.ant.DirectoryScanner;
54  
55  /**
56   * <a href="JavadocFormatter.java.html"><b><i>View Source</i></b></a>
57   *
58   * @author Brian Wing Shun Chan
59   */
60  public class JavadocFormatter {
61  
62      public static void main(String[] args) {
63          try {
64              new JavadocFormatter(args);
65          }
66          catch (Exception e) {
67              e.printStackTrace();
68          }
69      }
70  
71      public JavadocFormatter(String[] args) throws Exception {
72          CmdLineParser cmdLineParser = new CmdLineParser();
73  
74          CmdLineParser.Option limitOption = cmdLineParser.addStringOption(
75              "limit");
76          CmdLineParser.Option initOption = cmdLineParser.addStringOption(
77              "init");
78  
79          cmdLineParser.parse(args);
80  
81          String limit = (String)cmdLineParser.getOptionValue(limitOption);
82          String init = (String)cmdLineParser.getOptionValue(initOption);
83  
84          if (!init.startsWith("$")) {
85              _initializeMissingJavadocs = GetterUtil.getBoolean(init);
86          }
87  
88          DirectoryScanner ds = new DirectoryScanner();
89  
90          ds.setBasedir(_basedir);
91          ds.setExcludes(
92              new String[] {
93                  "**\\classes\\**", "**\\portal-client\\**"
94              });
95  
96          List<String> includes = new ArrayList<String>();
97  
98          if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
99              String[] limitArray = StringUtil.split(limit, "/");
100 
101             for (String curLimit : limitArray) {
102                 includes.add(
103                     "**\\" + StringUtil.replace(curLimit, ".", "\\") +
104                         "\\**\\*.java");
105                 includes.add("**\\" + curLimit + ".java");
106             }
107         }
108         else {
109             includes.add("**\\*.java");
110         }
111 
112         ds.setIncludes(includes.toArray(new String[includes.size()]));
113 
114         ds.scan();
115 
116         String[] fileNames = ds.getIncludedFiles();
117 
118         for (String fileName : fileNames) {
119             fileName = StringUtil.replace(fileName, "\\", "/");
120 
121             _format(fileName);
122         }
123     }
124 
125     private void _addClassCommentElement(
126         Element rootElement, JavaClass javaClass) {
127 
128         Element commentElement = rootElement.addElement("comment");
129 
130         String comment = _getCDATA(javaClass);
131 
132         if (comment.startsWith("Copyright (c) 2000-2010 Liferay, Inc.")) {
133             comment = StringPool.BLANK;
134         }
135 
136         if (comment.startsWith(
137                 "<a href=\"" + javaClass.getName() + ".java.html\">")) {
138 
139             int pos = comment.indexOf("</a>");
140 
141             comment = comment.substring(pos + 4).trim();
142         }
143 
144         commentElement.addCDATA(comment);
145     }
146 
147     private void _addDocletElements(
148             Element parentElement, AbstractJavaEntity abstractJavaEntity,
149             String name)
150         throws Exception {
151 
152         DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);
153 
154         for (DocletTag docletTag : docletTags) {
155             String value = docletTag.getValue();
156 
157             value = _trimMultilineText(value);
158 
159             value = StringUtil.replace(value, " </", "</");
160 
161             if (name.equals("author") || name.equals("see") ||
162                 name.equals("since") || name.equals("version")) {
163 
164                 /*if (value.startsWith("Raymond Aug")) {
165                     value = new String(
166                         "Raymond Augé".getBytes(), StringPool.UTF8);
167                 }*/
168             }
169 
170             Element element = parentElement.addElement(name);
171 
172             element.addCDATA(value);
173         }
174 
175         if ((docletTags.length == 0) && name.equals("author")) {
176             Element element = parentElement.addElement(name);
177 
178             element.addCDATA(ServiceBuilder.AUTHOR);
179         }
180     }
181 
182     private String _addDocletTags(
183         Element parentElement, String[] names, String indent) {
184 
185         StringBuilder sb = new StringBuilder();
186 
187         int maxNameLength = 0;
188 
189         for (String name : names) {
190             if (name.length() < maxNameLength) {
191                 continue;
192             }
193 
194             List<Element> elements = parentElement.elements(name);
195 
196             for (Element element : elements) {
197                 Element commentElement = element.element("comment");
198 
199                 String comment = null;
200 
201                 if (commentElement != null) {
202                     comment = commentElement.getText();
203                 }
204                 else {
205                     comment = element.getText();
206                 }
207 
208                 if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
209                     Validator.isNull(comment)) {
210 
211                     continue;
212                 }
213 
214                 maxNameLength = name.length();
215 
216                 break;
217             }
218         }
219 
220         int indentLength =_getIndentLength(indent) + maxNameLength;
221 
222         String maxNameIndent = "\t ";
223 
224         for (int i = 0; i < maxNameLength; i++) {
225             maxNameIndent += " ";
226         }
227 
228         maxNameIndent = StringUtil.replace(
229             maxNameIndent, StringPool.FOUR_SPACES, "\t");
230 
231         for (String name : names) {
232             String curNameIndent = " ";
233 
234             if (name.length() < maxNameLength) {
235                 int firstTab = 4 - (name.length() % 4);
236 
237                 int delta = (maxNameLength + 1) - (name.length() + firstTab);
238 
239                 if (delta == 0) {
240                     curNameIndent = "\t";
241                 }
242                 else if (delta < 0) {
243                     for (int i = 0; i < (maxNameLength - name.length()); i++) {
244                         curNameIndent += " ";
245                     }
246                 }
247                 else if (delta > 0) {
248                     curNameIndent = "\t";
249 
250                     int numberOfTabs = delta / 4;
251 
252                     if (numberOfTabs > 0) {
253                         for (int i = 0; i < numberOfTabs; i++) {
254                             curNameIndent += "\t";
255                         }
256                     }
257 
258                     int numberOfSpaces = delta % 4;
259 
260                     if (numberOfSpaces > 0) {
261                         for (int i = 0; i < numberOfSpaces; i++) {
262                             curNameIndent += " ";
263                         }
264                     }
265                 }
266             }
267 
268             List<Element> elements = parentElement.elements(name);
269 
270             for (Element element : elements) {
271                 Element commentElement = element.element("comment");
272 
273                 String comment = null;
274 
275                 if (commentElement != null) {
276                     comment = commentElement.getText();
277                 }
278                 else {
279                     comment = element.getText();
280                 }
281 
282                 if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
283                     Validator.isNull(comment)) {
284 
285                     continue;
286                 }
287 
288                 sb.append(indent);
289                 sb.append(" * @");
290                 sb.append(name);
291 
292                 if (Validator.isNotNull(comment) || (commentElement != null)) {
293                     sb.append(curNameIndent);
294                 }
295 
296                 if (commentElement != null) {
297                     comment = element.elementText("name") + " " + comment;
298                 }
299 
300                 comment = StringUtil.wrap(comment, 80 - indentLength - 5, "\n");
301 
302                 comment = comment.trim();
303 
304                 comment = StringUtil.replace(
305                     comment, "\n", "\n" + indent + " *" + maxNameIndent);
306 
307                 while (comment.contains(" \n")) {
308                     comment = StringUtil.replace(comment, " \n", "\n");
309                 }
310 
311                 while (comment.startsWith("\n")) {
312                     comment = comment.substring(1, comment.length());
313                 }
314 
315                 sb.append(comment);
316                 sb.append("\n");
317             }
318         }
319 
320         return sb.toString();
321     }
322 
323     private void _addFieldElement(Element rootElement, JavaField javaField)
324         throws Exception {
325 
326         Element fieldElement = rootElement.addElement("field");
327 
328         DocUtil.add(fieldElement, "name", javaField.getName());
329 
330         Element commentElement = fieldElement.addElement("comment");
331 
332         commentElement.addCDATA(_getCDATA(javaField));
333 
334         _addDocletElements(fieldElement, javaField, "version");
335         _addDocletElements(fieldElement, javaField, "see");
336         _addDocletElements(fieldElement, javaField, "since");
337         _addDocletElements(fieldElement, javaField, "deprecated");
338     }
339 
340     private void _addMethodElement(Element rootElement, JavaMethod javaMethod)
341         throws Exception {
342 
343         Element methodElement = rootElement.addElement("method");
344 
345         DocUtil.add(methodElement, "name", javaMethod.getName());
346 
347         Element commentElement = methodElement.addElement("comment");
348 
349         commentElement.addCDATA(_getCDATA(javaMethod));
350 
351         _addDocletElements(methodElement, javaMethod, "version");
352         _addParamElements(methodElement, javaMethod);
353         _addReturnElement(methodElement, javaMethod);
354         _addThrowsElements(methodElement, javaMethod);
355         _addDocletElements(methodElement, javaMethod, "see");
356         _addDocletElements(methodElement, javaMethod, "since");
357         _addDocletElements(methodElement, javaMethod, "deprecated");
358     }
359 
360     private void _addParamElement(
361         Element methodElement, JavaParameter javaParameter,
362         DocletTag[] paramDocletTags) {
363 
364         String name = javaParameter.getName();
365         String type = javaParameter.getType().getValue();
366         String value = null;
367 
368         for (DocletTag paramDocletTag : paramDocletTags) {
369             String curValue = paramDocletTag.getValue();
370 
371             if (!curValue.startsWith(name)) {
372                 continue;
373             }
374             else {
375                 value = curValue;
376 
377                 break;
378             }
379         }
380 
381         Element paramElement = methodElement.addElement("param");
382 
383         DocUtil.add(paramElement, "name", name);
384         DocUtil.add(paramElement, "type", type);
385 
386         if (value != null) {
387             value = value.substring(name.length());
388         }
389 
390         Element commentElement = paramElement.addElement("comment");
391 
392         commentElement.addCDATA(_getCDATA(value));
393     }
394 
395     private void _addParamElements(
396         Element methodElement, JavaMethod javaMethod) {
397 
398         JavaParameter[] javaParameters = javaMethod.getParameters();
399 
400         DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");
401 
402         for (JavaParameter javaParameter : javaParameters) {
403             _addParamElement(methodElement, javaParameter, paramDocletTags);
404         }
405     }
406 
407     private void _addReturnElement(
408             Element methodElement, JavaMethod javaMethod)
409         throws Exception {
410 
411         Type returns = javaMethod.getReturns();
412 
413         if ((returns == null) || returns.getValue().equals("void")) {
414             return;
415         }
416 
417         _addDocletElements(methodElement, javaMethod, "return");
418     }
419 
420     private void _addThrowsElement(
421         Element methodElement, Type exception, DocletTag[] throwsDocletTags) {
422 
423         String name = exception.getJavaClass().getName();
424         String value = null;
425 
426         for (DocletTag throwsDocletTag : throwsDocletTags) {
427             String curValue = throwsDocletTag.getValue();
428 
429             if (!curValue.startsWith(name)) {
430                 continue;
431             }
432             else {
433                 value = curValue;
434 
435                 break;
436             }
437         }
438 
439         Element throwsElement = methodElement.addElement("throws");
440 
441         DocUtil.add(throwsElement, "name", name);
442         DocUtil.add(throwsElement, "type", exception.getValue());
443 
444         if (value != null) {
445             value = value.substring(name.length());
446         }
447 
448         Element commentElement = throwsElement.addElement("comment");
449 
450         commentElement.addCDATA(_getCDATA(value));
451 
452     }
453 
454     private void _addThrowsElements(
455         Element methodElement, JavaMethod javaMethod) {
456 
457         Type[] exceptions = javaMethod.getExceptions();
458 
459         DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");
460 
461         for (Type exception : exceptions) {
462             _addThrowsElement(methodElement, exception, throwsDocletTags);
463         }
464     }
465 
466     private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
467         return _getCDATA(abstractJavaEntity.getComment());
468     }
469 
470     private String _getCDATA(String cdata) {
471         if (cdata == null) {
472             return StringPool.BLANK;
473         }
474 
475         cdata = _trimMultilineText(cdata);
476 
477         cdata = StringUtil.replace(
478             cdata,
479             new String[] {
480                 "\n", "<p>", "</p>", "<li>", "</li>"
481             },
482             new String[] {
483                 " ", " \n<p>\n", "\n</p>\n", " \n<li>\n", "\n</li>\n"
484             });
485 
486         while (cdata.contains("\n ")) {
487             cdata = StringUtil.replace(cdata, "\n ", "\n");
488         }
489 
490         while (cdata.contains("  ")) {
491             cdata = StringUtil.replace(cdata, "  ", " ");
492         }
493 
494         cdata = StringUtil.replace(cdata, "</p>\n<p>", "</p>\n\n<p>");
495         cdata = StringUtil.replace(cdata, "</li>\n<li>", "</li>\n\n<li>");
496 
497         return cdata.trim();
498     }
499 
500     private String _getFieldKey(Element fieldElement) {
501         return fieldElement.elementText("name");
502     }
503 
504     private String _getFieldKey(JavaField javaField) {
505         return javaField.getName();
506     }
507 
508     private int _getIndentLength(String indent) {
509         int indentLength = 0;
510 
511         for (char c : indent.toCharArray()) {
512             if (c == '\t') {
513                 indentLength = indentLength + 4;
514             }
515             else {
516                 indentLength++;
517             }
518         }
519 
520         return indentLength;
521     }
522 
523     private JavaClass _getJavaClass(String fileName, Reader reader)
524         throws Exception {
525 
526         int pos = fileName.indexOf("src/");
527 
528         if (pos == -1) {
529             pos = fileName.indexOf("test/");
530         }
531 
532         if (pos == -1) {
533             throw new RuntimeException(fileName);
534         }
535 
536         pos = fileName.indexOf("/", pos);
537 
538         String srcFile = fileName.substring(pos + 1, fileName.length());
539         String className = StringUtil.replace(
540             srcFile.substring(0, srcFile.length() - 5), "/", ".");
541 
542         JavaDocBuilder builder = new JavaDocBuilder();
543 
544         if (reader == null) {
545             File file = new File(fileName);
546 
547             if (!file.exists()) {
548                 return null;
549             }
550 
551             builder.addSource(file);
552         }
553         else {
554             builder.addSource(reader);
555         }
556 
557         return builder.getClassByName(className);
558     }
559 
560     private String _getJavaClassComment(
561         Element rootElement, JavaClass javaClass) {
562 
563         StringBuilder sb = new StringBuilder();
564 
565         String indent = StringPool.BLANK;
566 
567         sb.append("/**\n");
568 
569         String viewSourceHREF =
570             " * <a href=\"" + javaClass.getName() +
571                 ".java.html\"><b><i>View Source</i></b></a>";
572 
573         sb.append(ServiceBuilder.wrapViewSourceHREF(viewSourceHREF));
574         sb.append("\n");
575 
576         String comment = rootElement.elementText("comment");
577 
578         if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
579             sb.append(" *\n");
580             sb.append(_wrapText(comment, indent));
581             sb.append("\n");
582         }
583 
584         String docletTags = _addDocletTags(
585             rootElement,
586             new String[] {
587                 "author", "version", "see", "since", "serial", "deprecated"
588             },
589             indent);
590 
591         if (docletTags.length() > 0) {
592             //if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
593                 sb.append(" *\n");
594             //}
595 
596             sb.append(docletTags);
597         }
598 
599         sb.append(" */\n");
600 
601         return sb.toString();
602     }
603 
604     private Document _getJavadocDocument(JavaClass javaClass) throws Exception {
605         Element rootElement = _saxReaderUtil.createElement("javadoc");
606 
607         Document document = _saxReaderUtil.createDocument(rootElement);
608 
609         DocUtil.add(rootElement, "name", javaClass.getName());
610         DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());
611 
612         _addClassCommentElement(rootElement, javaClass);
613         _addDocletElements(rootElement, javaClass, "author");
614         _addDocletElements(rootElement, javaClass, "version");
615         _addDocletElements(rootElement, javaClass, "see");
616         _addDocletElements(rootElement, javaClass, "since");
617         _addDocletElements(rootElement, javaClass, "serial");
618         _addDocletElements(rootElement, javaClass, "deprecated");
619 
620         JavaMethod[] javaMethods = javaClass.getMethods();
621 
622         for (JavaMethod javaMethod : javaMethods) {
623             _addMethodElement(rootElement, javaMethod);
624         }
625 
626         JavaField[] javaFields = javaClass.getFields();
627 
628         for (JavaField javaField : javaFields) {
629             _addFieldElement(rootElement, javaField);
630         }
631 
632         return document;
633     }
634 
635     private String _getJavaFieldComment(
636         String[] lines, Map<String, Element> fieldElementsMap,
637         JavaField javaField) {
638 
639         String fieldKey = _getFieldKey(javaField);
640 
641         Element fieldElement = fieldElementsMap.get(fieldKey);
642 
643         if (fieldElement == null) {
644             return null;
645         }
646 
647         String line = lines[javaField.getLineNumber() - 1];
648 
649         String indent = StringPool.BLANK;
650 
651         for (char c : line.toCharArray()) {
652             if (Character.isWhitespace(c)) {
653                 indent += c;
654             }
655             else {
656                 break;
657             }
658         }
659 
660         StringBuilder sb = new StringBuilder();
661 
662         sb.append(indent);
663         sb.append("/**\n");
664 
665         String comment = fieldElement.elementText("comment");
666 
667         if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
668             sb.append(_wrapText(comment, indent));
669             sb.append("\n");
670         }
671 
672         String docletTags = _addDocletTags(
673             fieldElement,
674             new String[] {"version", "see", "since", "deprecated"}, indent);
675 
676         if (docletTags.length() > 0) {
677             if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
678                 sb.append(indent);
679                 sb.append(" *\n");
680             }
681 
682             sb.append(docletTags);
683         }
684 
685         sb.append(indent);
686         sb.append(" */\n");
687 
688         if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
689             Validator.isNull(docletTags)) {
690 
691             return null;
692         }
693 
694         return sb.toString();
695     }
696 
697     private String _getJavaMethodComment(
698         String[] lines, Map<String, Element> methodElementsMap,
699         JavaMethod javaMethod) {
700 
701         String methodKey = _getMethodKey(javaMethod);
702 
703         Element methodElement = methodElementsMap.get(methodKey);
704 
705         if (methodElement == null) {
706             return null;
707         }
708 
709         String line = lines[javaMethod.getLineNumber() - 1];
710 
711         String indent = StringPool.BLANK;
712 
713         for (char c : line.toCharArray()) {
714             if (Character.isWhitespace(c)) {
715                 indent += c;
716             }
717             else {
718                 break;
719             }
720         }
721 
722         StringBuilder sb = new StringBuilder();
723 
724         sb.append(indent);
725         sb.append("/**\n");
726 
727         String comment = methodElement.elementText("comment");
728 
729         if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
730             sb.append(_wrapText(comment, indent));
731             sb.append("\n");
732         }
733 
734         String docletTags = _addDocletTags(
735             methodElement,
736             new String[] {
737                 "version", "param", "return", "throws", "see", "since",
738                 "deprecated"
739             },
740             indent);
741 
742         if (docletTags.length() > 0) {
743             if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
744                 sb.append(indent);
745                 sb.append(" *\n");
746             }
747 
748             sb.append(docletTags);
749         }
750 
751         sb.append(indent);
752         sb.append(" */\n");
753 
754         if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
755             Validator.isNull(docletTags)) {
756 
757             return null;
758         }
759 
760         return sb.toString();
761     }
762 
763     private String _getMethodKey(Element methodElement) {
764         StringBuilder sb = new StringBuilder();
765 
766         sb.append(methodElement.elementText("name"));
767         sb.append("(");
768 
769         List<Element> paramElements = methodElement.elements("param");
770 
771         for (Element paramElement : paramElements) {
772             sb.append(paramElement.elementText("name"));
773             sb.append("|");
774             sb.append(paramElement.elementText("type"));
775             sb.append(",");
776         }
777 
778         sb.append(")");
779 
780         return sb.toString();
781     }
782 
783     private String _getMethodKey(JavaMethod javaMethod) {
784         StringBuilder sb = new StringBuilder();
785 
786         sb.append(javaMethod.getName());
787         sb.append("(");
788 
789         JavaParameter[] javaParameters = javaMethod.getParameters();
790 
791         for (JavaParameter javaParameter : javaParameters) {
792             sb.append(javaParameter.getName());
793             sb.append("|");
794             sb.append(javaParameter.getType().getValue());
795             sb.append(",");
796         }
797 
798         sb.append(")");
799 
800         return sb.toString();
801     }
802 
803     private boolean _isGenerated(String content) {
804         if (content.contains("* @generated")) {
805             return true;
806         }
807         else {
808             return false;
809         }
810     }
811 
812     private String _removeJavadocFromJava(
813         JavaClass javaClass, String content) {
814 
815         Set<Integer> lineNumbers = new HashSet<Integer>();
816 
817         lineNumbers.add(_getJavaClassLineNumber(javaClass));
818 
819         JavaMethod[] javaMethods = javaClass.getMethods();
820 
821         for (JavaMethod javaMethod : javaMethods) {
822             lineNumbers.add(javaMethod.getLineNumber());
823         }
824 
825         JavaField[] javaFields = javaClass.getFields();
826 
827         for (JavaField javaField : javaFields) {
828             lineNumbers.add(javaField.getLineNumber());
829         }
830 
831         String[] lines = StringUtil.split(content, "\n");
832 
833         for (int lineNumber : lineNumbers) {
834             if (lineNumber == 0) {
835                 continue;
836             }
837 
838             int pos = lineNumber - 2;
839 
840             String line = lines[pos].trim();
841 
842             if (line.endsWith("*/")) {
843                 while (true) {
844                     lines[pos] = null;
845 
846                     if (line.startsWith("/**")) {
847                         break;
848                     }
849 
850                     line = lines[--pos].trim();
851                 }
852             }
853         }
854 
855         StringBuilder sb = new StringBuilder(content.length());
856 
857         for (String line : lines) {
858             if (line != null) {
859                 sb.append(line);
860                 sb.append("\n");
861             }
862         }
863 
864         return sb.toString().trim();
865     }
866 
867     private void _format(String fileName) throws Exception {
868         FileInputStream fis = new FileInputStream(
869             new File(_basedir + fileName));
870 
871         byte[] bytes = new byte[fis.available()];
872 
873         fis.read(bytes);
874 
875         fis.close();
876 
877         String originalContent = new String(bytes);
878 
879         if (!fileName.endsWith("JavadocFormatter.java") &&
880             _isGenerated(originalContent)) {
881 
882             return;
883         }
884 
885         JavaClass javaClass = _getJavaClass(
886             fileName, new UnsyncStringReader(originalContent));
887 
888         String javadocLessContent = _removeJavadocFromJava(
889             javaClass, originalContent);
890 
891         Document document = _getJavadocDocument(javaClass);
892 
893         _updateJavaFromDocument(
894             fileName, originalContent, javadocLessContent, document);
895     }
896 
897     private int _getJavaClassLineNumber(JavaClass javaClass) {
898         int lineNumber = javaClass.getLineNumber();
899 
900         Annotation[] annotations = javaClass.getAnnotations();
901 
902         if (annotations.length == 0) {
903             return lineNumber;
904         }
905 
906         for (Annotation annotation : annotations) {
907             int annotationLineNumber = annotation.getLineNumber();
908 
909             if (annotation.getPropertyMap().isEmpty()) {
910                 annotationLineNumber--;
911             }
912 
913             if (annotationLineNumber < lineNumber) {
914                 lineNumber = annotationLineNumber;
915             }
916         }
917 
918         return lineNumber;
919     }
920 
921     private String _trimMultilineText(String text) {
922         String[] textArray = StringUtil.split(text, "\n");
923 
924         for (int i = 0; i < textArray.length; i++) {
925             textArray[i] = textArray[i].trim();
926         }
927 
928         return StringUtil.merge(textArray, " ");
929     }
930 
931     private void _updateJavaFromDocument(
932             String fileName, String originalContent, String javadocLessContent,
933             Document document)
934         throws Exception {
935 
936         String[] lines = StringUtil.split(javadocLessContent, "\n");
937 
938         JavaClass javaClass = _getJavaClass(
939             fileName, new UnsyncStringReader(javadocLessContent));
940 
941         Element rootElement = document.getRootElement();
942 
943         Map<Integer, String> commentsMap = new TreeMap<Integer, String>();
944 
945         commentsMap.put(
946             _getJavaClassLineNumber(javaClass),
947             _getJavaClassComment(rootElement, javaClass));
948 
949         Map<String, Element> methodElementsMap = new HashMap<String, Element>();
950 
951         List<Element> methodElements = rootElement.elements("method");
952 
953         for (Element methodElement : methodElements) {
954             String methodKey = _getMethodKey(methodElement);
955 
956             methodElementsMap.put(methodKey, methodElement);
957         }
958 
959         JavaMethod[] javaMethods = javaClass.getMethods();
960 
961         for (JavaMethod javaMethod : javaMethods) {
962             if (commentsMap.containsKey(javaMethod.getLineNumber())) {
963                 continue;
964             }
965 
966             commentsMap.put(
967                 javaMethod.getLineNumber(),
968                 _getJavaMethodComment(lines, methodElementsMap, javaMethod));
969         }
970 
971         Map<String, Element> fieldElementsMap = new HashMap<String, Element>();
972 
973         List<Element> fieldElements = rootElement.elements("field");
974 
975         for (Element fieldElement : fieldElements) {
976             String fieldKey = _getFieldKey(fieldElement);
977 
978             fieldElementsMap.put(fieldKey, fieldElement);
979         }
980 
981         JavaField[] javaFields = javaClass.getFields();
982 
983         for (JavaField javaField : javaFields) {
984             if (commentsMap.containsKey(javaField.getLineNumber())) {
985                 continue;
986             }
987 
988             commentsMap.put(
989                 javaField.getLineNumber(),
990                 _getJavaFieldComment(lines, fieldElementsMap, javaField));
991         }
992 
993         StringBuilder sb = new StringBuilder(javadocLessContent.length());
994 
995         for (int lineNumber = 1; lineNumber <= lines.length; lineNumber++) {
996             String line = lines[lineNumber - 1];
997 
998             String comments = commentsMap.get(lineNumber);
999 
1000            if (comments != null) {
1001                sb.append(comments);
1002            }
1003
1004            sb.append(line);
1005            sb.append("\n");
1006        }
1007
1008        String formattedContent = sb.toString().trim();
1009
1010        if (!originalContent.equals(formattedContent)) {
1011            File file = new File(_basedir + fileName);
1012
1013            _fileUtil.write(file, formattedContent.getBytes());
1014
1015            System.out.println("Writing " + file);
1016        }
1017    }
1018
1019    private String _wrapText(String text, String indent) {
1020        int indentLength = _getIndentLength(indent);
1021
1022        text = StringUtil.wrap(text, 80 - indentLength - 3, "\n");
1023
1024        text = "\n" + text.trim();
1025
1026        text = StringUtil.replace(text, "\n", "\n" + indent + " * ");
1027
1028        while (text.contains(" \n")) {
1029            text = StringUtil.replace(text, " \n", "\n");
1030        }
1031
1032        while (text.startsWith("\n")) {
1033            text = text.substring(1, text.length());
1034        }
1035
1036        return text;
1037    }
1038
1039    private static FileImpl _fileUtil = FileImpl.getInstance();
1040    private static SAXReaderImpl _saxReaderUtil = SAXReaderImpl.getInstance();
1041
1042    private String _basedir = "./";
1043    private boolean _initializeMissingJavadocs;
1044
1045}