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