001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    /**
058     * @author Brian Wing Shun Chan
059     * @author Connor McKay
060     */
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                                    /*if (value.startsWith("Raymond Aug")) {
158                                            value = new String(
159                                                    "Raymond Aug\u00c3\u00a9".getBytes(), StringPool.UTF8);
160                                    }*/
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                    // Trim whitespace inside paragraph tags or in the first paragraph
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");
571    
572                    String comment = rootElement.elementText("comment");
573    
574                    if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
575                            sb.append(_wrapText(comment, indent));
576                    }
577    
578                    String docletTags = _addDocletTags(
579                            rootElement,
580                            new String[] {
581                                    "author", "version", "see", "since", "serial", "deprecated"
582                            },
583                            indent);
584    
585                    if (docletTags.length() > 0) {
586                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
587                                    sb.append(" *\n");
588                            }
589    
590                            sb.append(docletTags);
591                    }
592    
593                    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");
658    
659                    String comment = fieldElement.elementText("comment");
660    
661                    if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
662                            sb.append(_wrapText(comment, indent));
663                    }
664    
665                    String docletTags = _addDocletTags(
666                            fieldElement,
667                            new String[] {"version", "see", "since", "deprecated"}, indent);
668    
669                    if (docletTags.length() > 0) {
670                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
671                                    sb.append(indent);
672                                    sb.append(" *\n");
673                            }
674    
675                            sb.append(docletTags);
676                    }
677    
678                    sb.append(indent);
679                    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");
719    
720                    String comment = methodElement.elementText("comment");
721    
722                    if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
723                            sb.append(_wrapText(comment, indent));
724                    }
725    
726                    String docletTags = _addDocletTags(
727                            methodElement,
728                            new String[] {
729                                    "version", "param", "return", "throws", "see", "since",
730                                    "deprecated"
731                            },
732                            indent);
733    
734                    if (docletTags.length() > 0) {
735                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
736                                    sb.append(indent);
737                                    sb.append(" *\n");
738                            }
739    
740                            sb.append(docletTags);
741                    }
742    
743                    sb.append(indent);
744                    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("/**")) {
839                                                    break;
840                                            }
841    
842                                            line = lines[--pos].trim();
843                                    }
844                            }
845                    }
846    
847                    StringBuilder sb = new StringBuilder(content.length());
848    
849                    for (String line : lines) {
850                            if (line != null) {
851                                    sb.append(line);
852                                    sb.append("\n");
853                            }
854                    }
855    
856                    return sb.toString().trim();
857            }
858    
859            private void _format(String fileName) throws Exception {
860                    FileInputStream fis = new FileInputStream(
861                            new File(_basedir + fileName));
862    
863                    byte[] bytes = new byte[fis.available()];
864    
865                    fis.read(bytes);
866    
867                    fis.close();
868    
869                    String originalContent = new String(bytes);
870    
871                    if (!fileName.endsWith("JavadocFormatter.java") &&
872                            _isGenerated(originalContent)) {
873    
874                            return;
875                    }
876    
877                    JavaClass javaClass = _getJavaClass(
878                            fileName, new UnsyncStringReader(originalContent));
879    
880                    String javadocLessContent = _removeJavadocFromJava(
881                            javaClass, originalContent);
882    
883                    Document document = _getJavadocDocument(javaClass);
884    
885                    _updateJavaFromDocument(
886                            fileName, originalContent, javadocLessContent, document);
887            }
888    
889            private String _formatInlines(String text) {
890    
891                    // Wrap special constants in code tags
892    
893                    text = text.replaceAll(
894                            "(?i)(?<!<code>)(null|false|true)", "<code>$1</code>");
895    
896                    return text;
897            }
898    
899            private int _getJavaClassLineNumber(JavaClass javaClass) {
900                    int lineNumber = javaClass.getLineNumber();
901    
902                    Annotation[] annotations = javaClass.getAnnotations();
903    
904                    if (annotations.length == 0) {
905                            return lineNumber;
906                    }
907    
908                    for (Annotation annotation : annotations) {
909                            int annotationLineNumber = annotation.getLineNumber();
910    
911                            if (annotation.getPropertyMap().isEmpty()) {
912                                    annotationLineNumber--;
913                            }
914    
915                            if (annotationLineNumber < lineNumber) {
916                                    lineNumber = annotationLineNumber;
917                            }
918                    }
919    
920                    return lineNumber;
921            }
922    
923            private String _trimMultilineText(String text) {
924                    String[] textArray = StringUtil.split(text, "\n");
925    
926                    for (int i = 0; i < textArray.length; i++) {
927                            textArray[i] = textArray[i].trim();
928                    }
929    
930                    return StringUtil.merge(textArray, " ");
931            }
932    
933            private void _updateJavaFromDocument(
934                            String fileName, String originalContent, String javadocLessContent,
935                            Document document)
936                    throws Exception {
937    
938                    String[] lines = StringUtil.split(javadocLessContent, "\n");
939    
940                    JavaClass javaClass = _getJavaClass(
941                            fileName, new UnsyncStringReader(javadocLessContent));
942    
943                    Element rootElement = document.getRootElement();
944    
945                    Map<Integer, String> commentsMap = new TreeMap<Integer, String>();
946    
947                    commentsMap.put(
948                            _getJavaClassLineNumber(javaClass),
949                            _getJavaClassComment(rootElement, javaClass));
950    
951                    Map<String, Element> methodElementsMap = new HashMap<String, Element>();
952    
953                    List<Element> methodElements = rootElement.elements("method");
954    
955                    for (Element methodElement : methodElements) {
956                            String methodKey = _getMethodKey(methodElement);
957    
958                            methodElementsMap.put(methodKey, methodElement);
959                    }
960    
961                    JavaMethod[] javaMethods = javaClass.getMethods();
962    
963                    for (JavaMethod javaMethod : javaMethods) {
964                            if (commentsMap.containsKey(javaMethod.getLineNumber())) {
965                                    continue;
966                            }
967    
968                            commentsMap.put(
969                                    javaMethod.getLineNumber(),
970                                    _getJavaMethodComment(lines, methodElementsMap, javaMethod));
971                    }
972    
973                    Map<String, Element> fieldElementsMap = new HashMap<String, Element>();
974    
975                    List<Element> fieldElements = rootElement.elements("field");
976    
977                    for (Element fieldElement : fieldElements) {
978                            String fieldKey = _getFieldKey(fieldElement);
979    
980                            fieldElementsMap.put(fieldKey, fieldElement);
981                    }
982    
983                    JavaField[] javaFields = javaClass.getFields();
984    
985                    for (JavaField javaField : javaFields) {
986                            if (commentsMap.containsKey(javaField.getLineNumber())) {
987                                    continue;
988                            }
989    
990                            commentsMap.put(
991                                    javaField.getLineNumber(),
992                                    _getJavaFieldComment(lines, fieldElementsMap, javaField));
993                    }
994    
995                    StringBuilder sb = new StringBuilder(javadocLessContent.length());
996    
997                    for (int lineNumber = 1; lineNumber <= lines.length; lineNumber++) {
998                            String line = lines[lineNumber - 1];
999    
1000                            String comments = commentsMap.get(lineNumber);
1001    
1002                            if (comments != null) {
1003                                    sb.append(comments);
1004                            }
1005    
1006                            sb.append(line);
1007                            sb.append("\n");
1008                    }
1009    
1010                    String formattedContent = sb.toString().trim();
1011    
1012                    if (!originalContent.equals(formattedContent)) {
1013                            File file = new File(_basedir + fileName);
1014    
1015                            _fileUtil.write(file, formattedContent.getBytes());
1016    
1017                            System.out.println("Writing " + file);
1018                    }
1019            }
1020    
1021            private String _wrapText(String text, String indent) {
1022                    int indentLength = _getIndentLength(indent);
1023    
1024                    // Do not wrap text inside <pre>
1025    
1026                    if (text.contains("<pre>")) {
1027                            Pattern pattern = Pattern.compile(
1028                                    "(?<=^|</pre>).+?(?=$|<pre>)", Pattern.DOTALL);
1029    
1030                            Matcher matcher = pattern.matcher(text);
1031    
1032                            StringBuffer sb = new StringBuffer();
1033    
1034                            while (matcher.find()) {
1035                                    String wrapped = _formatInlines(matcher.group());
1036    
1037                                    wrapped = StringUtil.wrap(
1038                                            wrapped, 80 - indentLength - 3, "\n");
1039    
1040                                    matcher.appendReplacement(sb, wrapped);
1041                            }
1042    
1043                            matcher.appendTail(sb);
1044    
1045                            sb.append("\n");
1046    
1047                            text = sb.toString();
1048                    }
1049                    else {
1050                            text = _formatInlines(text);
1051    
1052                            text = StringUtil.wrap(text, 80 - indentLength - 3, "\n");
1053                    }
1054    
1055                    text = text.replaceAll("(?m)^", indent + " * ");
1056                    text = text.replaceAll("(?m) +$", StringPool.BLANK);
1057    
1058                    return text;
1059            }
1060    
1061            private static FileImpl _fileUtil = FileImpl.getInstance();
1062            private static SAXReaderImpl _saxReaderUtil = SAXReaderImpl.getInstance();
1063    
1064            private String _basedir = "./";
1065            private boolean _initializeMissingJavadocs;
1066    
1067    }