001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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.AbstractBaseJavaEntity;
031    import com.thoughtworks.qdox.model.AbstractJavaEntity;
032    import com.thoughtworks.qdox.model.Annotation;
033    import com.thoughtworks.qdox.model.DocletTag;
034    import com.thoughtworks.qdox.model.JavaClass;
035    import com.thoughtworks.qdox.model.JavaField;
036    import com.thoughtworks.qdox.model.JavaMethod;
037    import com.thoughtworks.qdox.model.JavaPackage;
038    import com.thoughtworks.qdox.model.JavaParameter;
039    import com.thoughtworks.qdox.model.Type;
040    
041    import jargs.gnu.CmdLineParser;
042    
043    import java.io.File;
044    import java.io.FileInputStream;
045    import java.io.Reader;
046    
047    import java.util.ArrayList;
048    import java.util.Collection;
049    import java.util.HashMap;
050    import java.util.HashSet;
051    import java.util.List;
052    import java.util.Map;
053    import java.util.Set;
054    import java.util.TreeMap;
055    import java.util.regex.Matcher;
056    import java.util.regex.Pattern;
057    
058    import org.apache.tools.ant.DirectoryScanner;
059    
060    /**
061     * @author Brian Wing Shun Chan
062     * @author Connor McKay
063     * @author James Hinkey
064     */
065    public class JavadocFormatter {
066    
067            public static void main(String[] args) {
068                    try {
069                            new JavadocFormatter(args);
070                    }
071                    catch (Exception e) {
072                            e.printStackTrace();
073                    }
074            }
075    
076            public JavadocFormatter(String[] args) throws Exception {
077                    CmdLineParser cmdLineParser = new CmdLineParser();
078    
079                    CmdLineParser.Option limitOption = cmdLineParser.addStringOption(
080                            "limit");
081                    CmdLineParser.Option initOption = cmdLineParser.addStringOption(
082                            "init");
083    
084                    cmdLineParser.parse(args);
085    
086                    String limit = (String)cmdLineParser.getOptionValue(limitOption);
087                    String init = (String)cmdLineParser.getOptionValue(initOption);
088    
089                    if (Validator.isNotNull(init) && !init.startsWith("$")) {
090                            _initializeMissingJavadocs = GetterUtil.getBoolean(init);
091                    }
092    
093                    DirectoryScanner ds = new DirectoryScanner();
094    
095                    ds.setBasedir(_basedir);
096                    ds.setExcludes(
097                            new String[] {"**\\classes\\**", "**\\portal-client\\**"});
098    
099                    List<String> includes = new ArrayList<String>();
100    
101                    if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
102                            System.out.println("Limit on " + limit);
103    
104                            String[] limitArray = StringUtil.split(limit, '/');
105    
106                            for (String curLimit : limitArray) {
107                                    includes.add(
108                                            "**\\" + StringUtil.replace(curLimit, ".", "\\") +
109                                                    "\\**\\*.java");
110                                    includes.add("**\\" + curLimit + ".java");
111                            }
112                    }
113                    else {
114                            includes.add("**\\*.java");
115                    }
116    
117                    ds.setIncludes(includes.toArray(new String[includes.size()]));
118    
119                    ds.scan();
120    
121                    String[] fileNames = ds.getIncludedFiles();
122    
123                    if ((fileNames.length == 0) && Validator.isNotNull(limit) &&
124                            !limit.startsWith("$")) {
125    
126                            StringBuilder sb = new StringBuilder("Limit file not found: ");
127    
128                            sb.append(limit);
129    
130                            if (limit.contains(".")) {
131                                    sb.append(" Specify limit filename without package path or ");
132                                    sb.append("file type suffix.");
133                            }
134    
135                            System.out.println(sb.toString());
136                    }
137    
138                    for (String fileName : fileNames) {
139                            fileName = StringUtil.replace(fileName, "\\", "/");
140    
141                            _format(fileName);
142                    }
143            }
144    
145            private void _addClassCommentElement(
146                    Element rootElement, JavaClass javaClass) {
147    
148                    Element commentElement = rootElement.addElement("comment");
149    
150                    String comment = _getCDATA(javaClass);
151    
152                    if (comment.startsWith("Copyright (c) 2000-2010 Liferay, Inc.")) {
153                            comment = StringPool.BLANK;
154                    }
155    
156                    commentElement.addCDATA(comment);
157            }
158    
159            private void _addDocletElements(
160                            Element parentElement, AbstractJavaEntity abstractJavaEntity,
161                            String name)
162                    throws Exception {
163    
164                    DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);
165    
166                    for (DocletTag docletTag : docletTags) {
167                            String value = docletTag.getValue();
168    
169                            value = _trimMultilineText(value);
170    
171                            value = StringUtil.replace(value, " </", "</");
172    
173                            if (name.equals("author") || name.equals("see") ||
174                                    name.equals("since") || name.equals("version")) {
175    
176                                    /*if (value.startsWith("Raymond Aug")) {
177                                            value = new String(
178                                                    "Raymond Aug\u00c3\u00a9".getBytes(), StringPool.UTF8);
179                                    }*/
180                            }
181    
182                            Element element = parentElement.addElement(name);
183    
184                            element.addCDATA(value);
185                    }
186    
187                    if ((docletTags.length == 0) && name.equals("author")) {
188                            Element element = parentElement.addElement(name);
189    
190                            element.addCDATA(ServiceBuilder.AUTHOR);
191                    }
192            }
193    
194            private String _addDocletTags(
195                    Element parentElement, String[] names, String indent) {
196    
197                    StringBuilder sb = new StringBuilder();
198    
199                    int maxNameLength = 0;
200    
201                    for (String name : names) {
202                            if (name.length() < maxNameLength) {
203                                    continue;
204                            }
205    
206                            List<Element> elements = parentElement.elements(name);
207    
208                            for (Element element : elements) {
209                                    Element commentElement = element.element("comment");
210    
211                                    String comment = null;
212    
213                                    if (commentElement != null) {
214                                            comment = commentElement.getText();
215                                    }
216                                    else {
217                                            comment = element.getText();
218                                    }
219    
220                                    if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
221                                            Validator.isNull(comment)) {
222    
223                                            continue;
224                                    }
225    
226                                    maxNameLength = name.length();
227    
228                                    break;
229                            }
230                    }
231    
232                    // There should be one space after the name and an @ before it
233    
234                    maxNameLength += 2;
235    
236                    String nameIndent = _getSpacesIndent(maxNameLength);
237    
238                    for (String name : names) {
239                            List<Element> elements = parentElement.elements(name);
240    
241                            for (Element element : elements) {
242                                    Element commentElement = element.element("comment");
243    
244                                    String comment = null;
245    
246                                    if (commentElement != null) {
247                                            comment = commentElement.getText();
248                                    }
249                                    else {
250                                            comment = element.getText();
251                                    }
252    
253                                    if (!name.equals("deprecated") && !_initializeMissingJavadocs &&
254                                            Validator.isNull(comment)) {
255    
256                                            continue;
257                                    }
258    
259                                    if (commentElement != null) {
260                                            comment = element.elementText("name") + " " + comment;
261                                    }
262    
263                                    if (Validator.isNull(comment)) {
264                                            sb.append(indent);
265                                            sb.append(StringPool.AT);
266                                            sb.append(name);
267                                            sb.append(StringPool.NEW_LINE);
268                                    }
269                                    else {
270                                            comment = _wrapText(comment, indent + nameIndent);
271    
272                                            String firstLine = indent + "@" + name;
273    
274                                            comment = firstLine + comment.substring(firstLine.length());
275    
276                                            sb.append(comment);
277                                    }
278                            }
279                    }
280    
281                    return sb.toString();
282            }
283    
284            private void _addFieldElement(Element rootElement, JavaField javaField)
285                    throws Exception {
286    
287                    Element fieldElement = rootElement.addElement("field");
288    
289                    DocUtil.add(fieldElement, "name", javaField.getName());
290    
291                    Element commentElement = fieldElement.addElement("comment");
292    
293                    commentElement.addCDATA(_getCDATA(javaField));
294    
295                    _addDocletElements(fieldElement, javaField, "version");
296                    _addDocletElements(fieldElement, javaField, "see");
297                    _addDocletElements(fieldElement, javaField, "since");
298                    _addDocletElements(fieldElement, javaField, "deprecated");
299            }
300    
301            private void _addMethodElement(Element rootElement, JavaMethod javaMethod)
302                    throws Exception {
303    
304                    Element methodElement = rootElement.addElement("method");
305    
306                    DocUtil.add(methodElement, "name", javaMethod.getName());
307    
308                    Element commentElement = methodElement.addElement("comment");
309    
310                    commentElement.addCDATA(_getCDATA(javaMethod));
311    
312                    _addDocletElements(methodElement, javaMethod, "version");
313                    _addParamElements(methodElement, javaMethod);
314                    _addReturnElement(methodElement, javaMethod);
315                    _addThrowsElements(methodElement, javaMethod);
316                    _addDocletElements(methodElement, javaMethod, "see");
317                    _addDocletElements(methodElement, javaMethod, "since");
318                    _addDocletElements(methodElement, javaMethod, "deprecated");
319            }
320    
321            private void _addParamElement(
322                    Element methodElement, JavaParameter javaParameter,
323                    DocletTag[] paramDocletTags) {
324    
325                    String name = javaParameter.getName();
326                    String type = javaParameter.getType().getValue();
327                    String value = null;
328    
329                    for (DocletTag paramDocletTag : paramDocletTags) {
330                            String curValue = paramDocletTag.getValue();
331    
332                            if (!curValue.startsWith(name)) {
333                                    continue;
334                            }
335                            else {
336                                    value = curValue;
337    
338                                    break;
339                            }
340                    }
341    
342                    Element paramElement = methodElement.addElement("param");
343    
344                    DocUtil.add(paramElement, "name", name);
345                    DocUtil.add(paramElement, "type", type);
346    
347                    if (value != null) {
348                            value = value.substring(name.length());
349                    }
350    
351                    value = _trimMultilineText(value);
352    
353                    Element commentElement = paramElement.addElement("comment");
354    
355                    commentElement.addCDATA(value);
356            }
357    
358            private void _addParamElements(
359                    Element methodElement, JavaMethod javaMethod) {
360    
361                    JavaParameter[] javaParameters = javaMethod.getParameters();
362    
363                    DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");
364    
365                    for (JavaParameter javaParameter : javaParameters) {
366                            _addParamElement(methodElement, javaParameter, paramDocletTags);
367                    }
368            }
369    
370            private void _addReturnElement(
371                            Element methodElement, JavaMethod javaMethod)
372                    throws Exception {
373    
374                    Type returns = javaMethod.getReturns();
375    
376                    if ((returns == null) || returns.getValue().equals("void")) {
377                            return;
378                    }
379    
380                    _addDocletElements(methodElement, javaMethod, "return");
381            }
382    
383            private void _addThrowsElement(
384                    Element methodElement, Type exception, DocletTag[] throwsDocletTags) {
385    
386                    String name = exception.getJavaClass().getName();
387                    String value = null;
388    
389                    for (DocletTag throwsDocletTag : throwsDocletTags) {
390                            String curValue = throwsDocletTag.getValue();
391    
392                            if (!curValue.startsWith(name)) {
393                                    continue;
394                            }
395                            else {
396                                    value = curValue;
397    
398                                    break;
399                            }
400                    }
401    
402                    Element throwsElement = methodElement.addElement("throws");
403    
404                    DocUtil.add(throwsElement, "name", name);
405                    DocUtil.add(throwsElement, "type", exception.getValue());
406    
407                    if (value != null) {
408                            value = value.substring(name.length());
409                    }
410    
411                    value = _trimMultilineText(value);
412    
413                    Element commentElement = throwsElement.addElement("comment");
414    
415                    commentElement.addCDATA(_getCDATA(value));
416    
417            }
418    
419            private void _addThrowsElements(
420                    Element methodElement, JavaMethod javaMethod) {
421    
422                    Type[] exceptions = javaMethod.getExceptions();
423    
424                    DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");
425    
426                    for (Type exception : exceptions) {
427                            _addThrowsElement(methodElement, exception, throwsDocletTags);
428                    }
429            }
430    
431            private List<JavaClass> _getAncestorJavaClasses(JavaClass javaClass) {
432                    List<JavaClass> ancestorJavaClasses = new ArrayList<JavaClass>();
433    
434                    while ((javaClass = javaClass.getSuperJavaClass()) != null) {
435                            ancestorJavaClasses.add(javaClass);
436                    }
437    
438                    return ancestorJavaClasses;
439            }
440    
441            private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
442                    return _getCDATA(abstractJavaEntity.getComment());
443            }
444    
445            private String _getCDATA(String cdata) {
446                    if (cdata == null) {
447                            return StringPool.BLANK;
448                    }
449    
450                    cdata = cdata.replaceAll(
451                            "(?s)\\s*<(p|pre|[ou]l)>\\s*(.*?)\\s*</\\1>\\s*",
452                            "\n\n<$1>\n$2\n</$1>\n\n");
453                    cdata = cdata.replaceAll(
454                            "(?s)\\s*<li>\\s*(.*?)\\s*</li>\\s*", "\n<li>\n$1\n</li>\n");
455                    cdata = StringUtil.replace(cdata, "</li>\n\n<li>", "</li>\n<li>");
456                    cdata = cdata.replaceAll("\n\\s+\n", "\n\n");
457                    cdata = cdata.replaceAll(" +", " ");
458    
459                    // Trim whitespace inside paragraph tags or in the first paragraph
460    
461                    Pattern pattern = Pattern.compile(
462                            "(^.*?(?=\n\n|$)+|(?<=<p>\n).*?(?=\n</p>))", Pattern.DOTALL);
463    
464                    Matcher matcher = pattern.matcher(cdata);
465    
466                    StringBuffer sb = new StringBuffer();
467    
468                    while (matcher.find()) {
469                            String trimmed = _trimMultilineText(matcher.group());
470    
471                            // Escape dollar signs so they are not treated as replacement groups
472    
473                            trimmed = trimmed.replaceAll("\\$", "\\\\\\$");
474    
475                            matcher.appendReplacement(sb, trimmed);
476                    }
477    
478                    matcher.appendTail(sb);
479    
480                    cdata = sb.toString();
481    
482                    return cdata.trim();
483            }
484    
485            private String _getClassName(String fileName) {
486                    int pos = fileName.indexOf("src/");
487    
488                    if (pos == -1) {
489                            pos = fileName.indexOf("test/");
490                    }
491    
492                    if (pos == -1) {
493                            pos = fileName.indexOf("service/");
494                    }
495    
496                    if (pos == -1) {
497                            throw new RuntimeException(fileName);
498                    }
499    
500                    pos = fileName.indexOf("/", pos);
501    
502                    String srcFile = fileName.substring(pos + 1, fileName.length());
503    
504                    return StringUtil.replace(
505                            srcFile.substring(0, srcFile.length() - 5), "/", ".");
506            }
507    
508            private String _getFieldKey(Element fieldElement) {
509                    return fieldElement.elementText("name");
510            }
511    
512            private String _getFieldKey(JavaField javaField) {
513                    return javaField.getName();
514            }
515    
516            private String _getIndent(
517                    String[] lines, AbstractBaseJavaEntity abstractBaseJavaEntity) {
518    
519                    String line = lines[abstractBaseJavaEntity.getLineNumber() - 1];
520    
521                    String indent = StringPool.BLANK;
522    
523                    for (char c : line.toCharArray()) {
524                            if (Character.isWhitespace(c)) {
525                                    indent += c;
526                            }
527                            else {
528                                    break;
529                            }
530                    }
531    
532                    return indent;
533            }
534    
535            private int _getIndentLength(String indent) {
536                    int indentLength = 0;
537    
538                    for (char c : indent.toCharArray()) {
539                            if (c == '\t') {
540                                    indentLength = indentLength + 4;
541                            }
542                            else {
543                                    indentLength++;
544                            }
545                    }
546    
547                    return indentLength;
548            }
549    
550            private JavaClass _getJavaClass(String fileName, Reader reader)
551                    throws Exception {
552    
553                    String className = _getClassName(fileName);
554    
555                    JavaDocBuilder builder = new JavaDocBuilder();
556    
557                    if (reader == null) {
558                            File file = new File(fileName);
559    
560                            if (!file.exists()) {
561                                    return null;
562                            }
563    
564                            builder.addSource(file);
565                    }
566                    else {
567                            builder.addSource(reader);
568                    }
569    
570                    return builder.getClassByName(className);
571            }
572    
573            private String _getJavaClassComment(
574                    Element rootElement, JavaClass javaClass) {
575    
576                    StringBuilder sb = new StringBuilder();
577    
578                    String indent = StringPool.BLANK;
579    
580                    sb.append("/**\n");
581    
582                    String comment = rootElement.elementText("comment");
583    
584                    if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
585                            sb.append(_wrapText(comment, indent + " * "));
586                    }
587    
588                    String docletTags = _addDocletTags(
589                            rootElement,
590                            new String[] {
591                                    "author", "version", "see", "since", "serial", "deprecated"
592                            },
593                            indent + " * ");
594    
595                    if (docletTags.length() > 0) {
596                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
597                                    sb.append(" *\n");
598                            }
599    
600                            sb.append(docletTags);
601                    }
602    
603                    sb.append(" */\n");
604    
605                    return sb.toString();
606            }
607    
608            private int _getJavaClassLineNumber(JavaClass javaClass) {
609                    int lineNumber = javaClass.getLineNumber();
610    
611                    Annotation[] annotations = javaClass.getAnnotations();
612    
613                    if (annotations.length == 0) {
614                            return lineNumber;
615                    }
616    
617                    for (Annotation annotation : annotations) {
618                            int annotationLineNumber = annotation.getLineNumber();
619    
620                            if (annotation.getPropertyMap().isEmpty()) {
621                                    annotationLineNumber--;
622                            }
623    
624                            if (annotationLineNumber < lineNumber) {
625                                    lineNumber = annotationLineNumber;
626                            }
627                    }
628    
629                    return lineNumber;
630            }
631    
632            private Document _getJavadocDocument(JavaClass javaClass) throws Exception {
633                    Element rootElement = _saxReaderUtil.createElement("javadoc");
634    
635                    Document document = _saxReaderUtil.createDocument(rootElement);
636    
637                    DocUtil.add(rootElement, "name", javaClass.getName());
638                    DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());
639    
640                    _addClassCommentElement(rootElement, javaClass);
641                    _addDocletElements(rootElement, javaClass, "author");
642                    _addDocletElements(rootElement, javaClass, "version");
643                    _addDocletElements(rootElement, javaClass, "see");
644                    _addDocletElements(rootElement, javaClass, "since");
645                    _addDocletElements(rootElement, javaClass, "serial");
646                    _addDocletElements(rootElement, javaClass, "deprecated");
647    
648                    JavaMethod[] javaMethods = javaClass.getMethods();
649    
650                    for (JavaMethod javaMethod : javaMethods) {
651                            _addMethodElement(rootElement, javaMethod);
652                    }
653    
654                    JavaField[] javaFields = javaClass.getFields();
655    
656                    for (JavaField javaField : javaFields) {
657                            _addFieldElement(rootElement, javaField);
658                    }
659    
660                    return document;
661            }
662    
663            private String _getJavaFieldComment(
664                    String[] lines, Map<String, Element> fieldElementsMap,
665                    JavaField javaField) {
666    
667                    String fieldKey = _getFieldKey(javaField);
668    
669                    Element fieldElement = fieldElementsMap.get(fieldKey);
670    
671                    if (fieldElement == null) {
672                            return null;
673                    }
674    
675                    String indent = _getIndent(lines, javaField);
676    
677                    StringBuilder sb = new StringBuilder();
678    
679                    sb.append(indent);
680                    sb.append("/**\n");
681    
682                    String comment = fieldElement.elementText("comment");
683    
684                    if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
685                            sb.append(_wrapText(comment, indent + " * "));
686                    }
687    
688                    String docletTags = _addDocletTags(
689                            fieldElement,
690                            new String[] {"version", "see", "since", "deprecated"},
691                            indent + " * ");
692    
693                    if (docletTags.length() > 0) {
694                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
695                                    sb.append(indent);
696                                    sb.append(" *\n");
697                            }
698    
699                            sb.append(docletTags);
700                    }
701    
702                    sb.append(indent);
703                    sb.append(" */\n");
704    
705                    if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
706                            Validator.isNull(docletTags)) {
707    
708                            return null;
709                    }
710    
711                    return sb.toString();
712            }
713    
714            private String _getJavaMethodComment(
715                    String[] lines, Map<String, Element> methodElementsMap,
716                    JavaMethod javaMethod) {
717    
718                    String methodKey = _getMethodKey(javaMethod);
719    
720                    Element methodElement = methodElementsMap.get(methodKey);
721    
722                    if (methodElement == null) {
723                            return null;
724                    }
725    
726                    String indent = _getIndent(lines, javaMethod);
727    
728                    StringBuilder sb = new StringBuilder();
729    
730                    sb.append(indent);
731                    sb.append("/**\n");
732    
733                    String comment = methodElement.elementText("comment");
734    
735                    if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
736                            sb.append(_wrapText(comment, indent + " * "));
737                    }
738    
739                    String docletTags = _addDocletTags(
740                            methodElement,
741                            new String[] {
742                                    "version", "param", "return", "throws", "see", "since",
743                                    "deprecated"
744                            },
745                            indent + " * ");
746    
747                    if (docletTags.length() > 0) {
748                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
749                                    sb.append(indent);
750                                    sb.append(" *\n");
751                            }
752    
753                            sb.append(docletTags);
754                    }
755    
756                    sb.append(indent);
757                    sb.append(" */\n");
758    
759                    if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
760                            Validator.isNull(docletTags)) {
761    
762                            return null;
763                    }
764    
765                    return sb.toString();
766            }
767    
768            private String _getMethodKey(Element methodElement) {
769                    StringBuilder sb = new StringBuilder();
770    
771                    sb.append(methodElement.elementText("name"));
772                    sb.append("(");
773    
774                    List<Element> paramElements = methodElement.elements("param");
775    
776                    for (Element paramElement : paramElements) {
777                            sb.append(paramElement.elementText("name"));
778                            sb.append("|");
779                            sb.append(paramElement.elementText("type"));
780                            sb.append(",");
781                    }
782    
783                    sb.append(")");
784    
785                    return sb.toString();
786            }
787    
788            private String _getMethodKey(JavaMethod javaMethod) {
789                    StringBuilder sb = new StringBuilder();
790    
791                    sb.append(javaMethod.getName());
792                    sb.append("(");
793    
794                    JavaParameter[] javaParameters = javaMethod.getParameters();
795    
796                    for (JavaParameter javaParameter : javaParameters) {
797                            sb.append(javaParameter.getName());
798                            sb.append("|");
799                            sb.append(javaParameter.getType().getValue());
800                            sb.append(",");
801                    }
802    
803                    sb.append(")");
804    
805                    return sb.toString();
806            }
807    
808            private String _getSpacesIndent(int length) {
809                    String indent = StringPool.BLANK;
810    
811                    for (int i = 0; i < length; i++) {
812                            indent += StringPool.SPACE;
813                    }
814    
815                    return indent;
816            }
817    
818            private boolean _hasAnnotation(
819                    AbstractBaseJavaEntity abstractBaseJavaEntity, String annotationName) {
820    
821                    Annotation[] annotations = abstractBaseJavaEntity.getAnnotations();
822    
823                    if (annotations == null) {
824                            return false;
825                    }
826    
827                    for (int i = 0; i < annotations.length; i++) {
828                            Type type = annotations[i].getType();
829    
830                            JavaClass javaClass = type.getJavaClass();
831    
832                            if (annotationName.equals(javaClass.getName())) {
833                                    return true;
834                            }
835                    }
836    
837                    return false;
838            }
839    
840            private boolean _isGenerated(String content) {
841                    if (content.contains("* @generated") || content.contains("$ANTLR")) {
842                            return true;
843                    }
844                    else {
845                            return false;
846                    }
847            }
848    
849            private boolean  _isOverrideMethod(
850                    JavaClass javaClass, JavaMethod javaMethod,
851                    Collection<JavaClass> ancestorJavaClasses) {
852    
853                    if (javaClass.isInterface() || javaMethod.isConstructor() ||
854                            javaMethod.isPrivate() || javaMethod.isStatic()) {
855    
856                            return false;
857                    }
858    
859                    String methodName = javaMethod.getName();
860    
861                    JavaParameter[] javaParameters = javaMethod.getParameters();
862    
863                    Type[] types = new Type[javaParameters.length];
864    
865                    for (int i = 0; i < javaParameters.length; i++) {
866                            types[i] = javaParameters[i].getType();
867                    }
868    
869                    // Check for matching method in each ancestor
870    
871                    for (JavaClass ancestorJavaClass : ancestorJavaClasses) {
872                            JavaMethod ancestorJavaMethod =
873                                    ancestorJavaClass.getMethodBySignature(methodName, types);
874    
875                            if (ancestorJavaMethod == null) {
876                                    continue;
877                            }
878    
879                            boolean samePackage = false;
880    
881                            JavaPackage ancestorJavaPackage = ancestorJavaClass.getPackage();
882    
883                            if (ancestorJavaPackage != null) {
884                                    samePackage = ancestorJavaPackage.equals(
885                                            javaClass.getPackage());
886                            }
887    
888                            // Check if the method is in scope
889    
890                            if (samePackage) {
891                                    return !ancestorJavaMethod.isPrivate();
892                            }
893                            else {
894                                    if (ancestorJavaMethod.isProtected() ||
895                                            ancestorJavaMethod.isPublic()) {
896    
897                                            return true;
898                                    }
899                                    else {
900                                            return false;
901                                    }
902                            }
903                    }
904    
905                    return false;
906            }
907    
908            private String _removeJavadocFromJava(
909                    JavaClass javaClass, String content) {
910    
911                    Set<Integer> lineNumbers = new HashSet<Integer>();
912    
913                    lineNumbers.add(_getJavaClassLineNumber(javaClass));
914    
915                    JavaMethod[] javaMethods = javaClass.getMethods();
916    
917                    for (JavaMethod javaMethod : javaMethods) {
918                            lineNumbers.add(javaMethod.getLineNumber());
919                    }
920    
921                    JavaField[] javaFields = javaClass.getFields();
922    
923                    for (JavaField javaField : javaFields) {
924                            lineNumbers.add(javaField.getLineNumber());
925                    }
926    
927                    String[] lines = StringUtil.splitLines(content);
928    
929                    for (int lineNumber : lineNumbers) {
930                            if (lineNumber == 0) {
931                                    continue;
932                            }
933    
934                            int pos = lineNumber - 2;
935    
936                            String line = lines[pos];
937    
938                            if (line == null) {
939                                    continue;
940                            }
941    
942                            line = line.trim();
943    
944                            if (line.endsWith("*/")) {
945                                    while (true) {
946                                            lines[pos] = null;
947    
948                                            if (line.startsWith("/**")) {
949                                                    break;
950                                            }
951    
952                                            line = lines[--pos].trim();
953                                    }
954                            }
955                    }
956    
957                    StringBuilder sb = new StringBuilder(content.length());
958    
959                    for (String line : lines) {
960                            if (line != null) {
961                                    sb.append(line);
962                                    sb.append("\n");
963                            }
964                    }
965    
966                    return sb.toString().trim();
967            }
968    
969            private void _format(String fileName) throws Exception {
970                    FileInputStream fis = new FileInputStream(
971                            new File(_basedir + fileName));
972    
973                    byte[] bytes = new byte[fis.available()];
974    
975                    fis.read(bytes);
976    
977                    fis.close();
978    
979                    String originalContent = new String(bytes);
980    
981                    if (fileName.endsWith("JavadocFormatter.java") ||
982                            fileName.endsWith("SourceFormatter.java") ||
983                            _isGenerated(originalContent)) {
984    
985                            return;
986                    }
987    
988                    JavaClass javaClass = _getJavaClass(
989                            fileName, new UnsyncStringReader(originalContent));
990    
991                    String javadocLessContent = _removeJavadocFromJava(
992                            javaClass, originalContent);
993    
994                    Document document = _getJavadocDocument(javaClass);
995    
996                    _updateJavaFromDocument(
997                            fileName, originalContent, javadocLessContent, document);
998            }
999    
1000            private String _formatInlines(String text) {
1001    
1002                    // Capitalize ID
1003    
1004                    text = text.replaceAll("(?i)\\bid(s)?\\b", "ID$1");
1005    
1006                    // Wrap special constants in code tags
1007    
1008                    text = text.replaceAll(
1009                            "(?i)(?<!<code>|\\w)(null|false|true)(?!\\w)", "<code>$1</code>");
1010    
1011                    return text;
1012            }
1013    
1014            private String _trimMultilineText(String text) {
1015                    String[] textArray = StringUtil.splitLines(text);
1016    
1017                    for (int i = 0; i < textArray.length; i++) {
1018                            textArray[i] = textArray[i].trim();
1019                    }
1020    
1021                    return StringUtil.merge(textArray, " ");
1022            }
1023    
1024            private void _updateJavaFromDocument(
1025                            String fileName, String originalContent, String javadocLessContent,
1026                            Document document)
1027                    throws Exception {
1028    
1029                    String[] lines = StringUtil.splitLines(javadocLessContent);
1030    
1031                    JavaClass javaClass = _getJavaClass(
1032                            fileName, new UnsyncStringReader(javadocLessContent));
1033    
1034                    List<JavaClass> ancestorJavaClasses = _getAncestorJavaClasses(
1035                            javaClass);
1036    
1037                    Element rootElement = document.getRootElement();
1038    
1039                    Map<Integer, String> commentsMap = new TreeMap<Integer, String>();
1040    
1041                    commentsMap.put(
1042                            _getJavaClassLineNumber(javaClass),
1043                            _getJavaClassComment(rootElement, javaClass));
1044    
1045                    Map<String, Element> methodElementsMap = new HashMap<String, Element>();
1046    
1047                    List<Element> methodElements = rootElement.elements("method");
1048    
1049                    for (Element methodElement : methodElements) {
1050                            String methodKey = _getMethodKey(methodElement);
1051    
1052                            methodElementsMap.put(methodKey, methodElement);
1053                    }
1054    
1055                    JavaMethod[] javaMethods = javaClass.getMethods();
1056    
1057                    for (JavaMethod javaMethod : javaMethods) {
1058                            if (commentsMap.containsKey(javaMethod.getLineNumber())) {
1059                                    continue;
1060                            }
1061    
1062                            String javaMethodComment = _getJavaMethodComment(
1063                                    lines, methodElementsMap, javaMethod);
1064    
1065                            // Handle override tag insertion
1066    
1067                            if (!_hasAnnotation(javaMethod, "Override")) {
1068                                    if (_isOverrideMethod(
1069                                                    javaClass, javaMethod, ancestorJavaClasses)) {
1070    
1071                                            String overrideLine =
1072                                                    _getIndent(lines, javaMethod) + "@Override\n";
1073    
1074                                            if (Validator.isNotNull(javaMethodComment)) {
1075                                                    javaMethodComment =     javaMethodComment + overrideLine;
1076                                            }
1077                                            else {
1078                                                    javaMethodComment = overrideLine;
1079                                            }
1080                                    }
1081                            }
1082    
1083                            commentsMap.put(javaMethod.getLineNumber(), javaMethodComment);
1084                    }
1085    
1086                    Map<String, Element> fieldElementsMap = new HashMap<String, Element>();
1087    
1088                    List<Element> fieldElements = rootElement.elements("field");
1089    
1090                    for (Element fieldElement : fieldElements) {
1091                            String fieldKey = _getFieldKey(fieldElement);
1092    
1093                            fieldElementsMap.put(fieldKey, fieldElement);
1094                    }
1095    
1096                    JavaField[] javaFields = javaClass.getFields();
1097    
1098                    for (JavaField javaField : javaFields) {
1099                            if (commentsMap.containsKey(javaField.getLineNumber())) {
1100                                    continue;
1101                            }
1102    
1103                            commentsMap.put(
1104                                    javaField.getLineNumber(),
1105                                    _getJavaFieldComment(lines, fieldElementsMap, javaField));
1106                    }
1107    
1108                    StringBuilder sb = new StringBuilder(javadocLessContent.length());
1109    
1110                    for (int lineNumber = 1; lineNumber <= lines.length; lineNumber++) {
1111                            String line = lines[lineNumber - 1];
1112    
1113                            String comments = commentsMap.get(lineNumber);
1114    
1115                            if (comments != null) {
1116                                    sb.append(comments);
1117                            }
1118    
1119                            sb.append(line);
1120                            sb.append("\n");
1121                    }
1122    
1123                    String formattedContent = sb.toString().trim();
1124    
1125                    if (!originalContent.equals(formattedContent)) {
1126                            File file = new File(_basedir + fileName);
1127    
1128                            _fileUtil.write(file, formattedContent.getBytes());
1129    
1130                            System.out.println("Writing " + file);
1131                    }
1132            }
1133    
1134            private String _wrapText(String text, String indent) {
1135                    int indentLength = _getIndentLength(indent);
1136    
1137                    // Do not wrap text inside <pre>
1138    
1139                    if (text.contains("<pre>")) {
1140                            Pattern pattern = Pattern.compile(
1141                                    "(?<=^|</pre>).+?(?=$|<pre>)", Pattern.DOTALL);
1142    
1143                            Matcher matcher = pattern.matcher(text);
1144    
1145                            StringBuffer sb = new StringBuffer();
1146    
1147                            while (matcher.find()) {
1148                                    String wrapped = _formatInlines(matcher.group());
1149    
1150                                    wrapped = StringUtil.wrap(
1151                                            wrapped, 80 - indentLength, "\n");
1152    
1153                                    matcher.appendReplacement(sb, wrapped);
1154                            }
1155    
1156                            matcher.appendTail(sb);
1157    
1158                            sb.append("\n");
1159    
1160                            text = sb.toString();
1161                    }
1162                    else {
1163                            text = _formatInlines(text);
1164    
1165                            text = StringUtil.wrap(text, 80 - indentLength, "\n");
1166                    }
1167    
1168                    text = text.replaceAll("(?m)^", indent);
1169                    text = text.replaceAll("(?m) +$", StringPool.BLANK);
1170    
1171                    return text;
1172            }
1173    
1174            private static FileImpl _fileUtil = FileImpl.getInstance();
1175            private static SAXReaderImpl _saxReaderUtil = SAXReaderImpl.getInstance();
1176    
1177            private String _basedir = "./";
1178            private boolean _initializeMissingJavadocs;
1179    
1180    }