001    /**
002     * Copyright (c) 2000-2013 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.UnsyncBufferedReader;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019    import com.liferay.portal.kernel.util.CharPool;
020    import com.liferay.portal.kernel.util.GetterUtil;
021    import com.liferay.portal.kernel.util.StringBundler;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Tuple;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.kernel.xml.Document;
027    import com.liferay.portal.kernel.xml.Element;
028    import com.liferay.portal.tools.javadocformatter.SinceJava;
029    import com.liferay.portal.tools.servicebuilder.ServiceBuilder;
030    import com.liferay.portal.util.FileImpl;
031    import com.liferay.portal.xml.SAXReaderImpl;
032    import com.liferay.util.xml.DocUtil;
033    
034    import com.thoughtworks.qdox.JavaDocBuilder;
035    import com.thoughtworks.qdox.model.AbstractBaseJavaEntity;
036    import com.thoughtworks.qdox.model.AbstractJavaEntity;
037    import com.thoughtworks.qdox.model.Annotation;
038    import com.thoughtworks.qdox.model.DocletTag;
039    import com.thoughtworks.qdox.model.JavaClass;
040    import com.thoughtworks.qdox.model.JavaField;
041    import com.thoughtworks.qdox.model.JavaMethod;
042    import com.thoughtworks.qdox.model.JavaPackage;
043    import com.thoughtworks.qdox.model.JavaParameter;
044    import com.thoughtworks.qdox.model.Type;
045    import com.thoughtworks.qdox.model.annotation.AnnotationValue;
046    
047    import java.io.File;
048    import java.io.FileInputStream;
049    import java.io.FileOutputStream;
050    import java.io.FileReader;
051    import java.io.IOException;
052    import java.io.InputStream;
053    import java.io.OutputStreamWriter;
054    import java.io.Reader;
055    import java.io.Writer;
056    
057    import java.util.ArrayList;
058    import java.util.Collection;
059    import java.util.HashMap;
060    import java.util.HashSet;
061    import java.util.List;
062    import java.util.Map;
063    import java.util.Properties;
064    import java.util.Set;
065    import java.util.TreeMap;
066    import java.util.regex.Matcher;
067    import java.util.regex.Pattern;
068    
069    import org.apache.tools.ant.DirectoryScanner;
070    
071    /**
072     * @author Brian Wing Shun Chan
073     * @author Connor McKay
074     * @author James Hinkey
075     * @author Hugo Huijser
076     */
077    public class JavadocFormatter {
078    
079            public static void main(String[] args) {
080                    try {
081                            new JavadocFormatter(args);
082                    }
083                    catch (Exception e) {
084                            e.printStackTrace();
085                    }
086            }
087    
088            public JavadocFormatter(String[] args) throws Exception {
089                    Map<String, String> arguments = ArgumentsUtil.parseArguments(args);
090    
091                    String init = arguments.get("javadoc.init");
092    
093                    if (Validator.isNotNull(init) && !init.startsWith("$")) {
094                            _initializeMissingJavadocs = GetterUtil.getBoolean(init);
095                    }
096    
097                    _inputDir = GetterUtil.getString(arguments.get("javadoc.input.dir"));
098    
099                    if (_inputDir.startsWith("$")) {
100                            _inputDir = "./";
101                    }
102    
103                    if (!_inputDir.endsWith("/")) {
104                            _inputDir += "/";
105                    }
106    
107                    System.out.println("Input directory is " + _inputDir);
108    
109                    String limit = arguments.get("javadoc.limit");
110    
111                    _outputFilePrefix = GetterUtil.getString(
112                            arguments.get("javadoc.output.file.prefix"));
113    
114                    if (_outputFilePrefix.startsWith("$")) {
115                            _outputFilePrefix = "javadocs";
116                    }
117    
118                    String update = arguments.get("javadoc.update");
119    
120                    if (Validator.isNotNull(update) && !update.startsWith("$")) {
121                            _updateJavadocs = GetterUtil.getBoolean(update);
122                    }
123    
124                    DirectoryScanner directoryScanner = new DirectoryScanner();
125    
126                    directoryScanner.setBasedir(_inputDir);
127                    directoryScanner.setExcludes(
128                            new String[] {"**\\classes\\**", "**\\portal-client\\**"});
129    
130                    List<String> includes = new ArrayList<String>();
131    
132                    if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
133                            System.out.println("Limit on " + limit);
134    
135                            String[] limitArray = StringUtil.split(limit, '/');
136    
137                            for (String curLimit : limitArray) {
138                                    includes.add(
139                                            "**\\" + StringUtil.replace(curLimit, ".", "\\") +
140                                                    "\\**\\*.java");
141                                    includes.add("**\\" + curLimit + ".java");
142                            }
143                    }
144                    else {
145                            includes.add("**\\*.java");
146                    }
147    
148                    directoryScanner.setIncludes(
149                            includes.toArray(new String[includes.size()]));
150    
151                    directoryScanner.scan();
152    
153                    String[] fileNames = directoryScanner.getIncludedFiles();
154    
155                    if ((fileNames.length == 0) && Validator.isNotNull(limit) &&
156                            !limit.startsWith("$")) {
157    
158                            StringBundler sb = new StringBundler("Limit file not found: ");
159    
160                            sb.append(limit);
161    
162                            if (limit.contains(".")) {
163                                    sb.append(" Specify limit filename without package path or ");
164                                    sb.append("file type suffix.");
165                            }
166    
167                            System.out.println(sb.toString());
168                    }
169    
170                    _languagePropertiesFile = new File("src/content/Language.properties");
171    
172                    if (_languagePropertiesFile.exists()) {
173                            _languageProperties = new Properties();
174    
175                            _languageProperties.load(
176                                    new FileInputStream(_languagePropertiesFile.getAbsolutePath()));
177                    }
178    
179                    for (String fileName : fileNames) {
180                            fileName = StringUtil.replace(fileName, "\\", "/");
181    
182                            _format(fileName);
183                    }
184    
185                    for (Map.Entry<String, Tuple> entry : _javadocxXmlTuples.entrySet()) {
186                            Tuple tuple = entry.getValue();
187    
188                            File javadocsXmlFile = (File)tuple.getObject(1);
189                            String oldJavadocsXmlContent = (String)tuple.getObject(2);
190                            Document javadocsXmlDocument = (Document)tuple.getObject(3);
191    
192                            Element javadocsXmlRootElement =
193                                    javadocsXmlDocument.getRootElement();
194    
195                            javadocsXmlRootElement.sortElementsByChildElement(
196                                    "javadoc", "type");
197    
198                            String newJavadocsXmlContent =
199                                    javadocsXmlDocument.formattedString();
200    
201                            if (!oldJavadocsXmlContent.equals(newJavadocsXmlContent)) {
202                                    _fileUtil.write(javadocsXmlFile, newJavadocsXmlContent);
203                            }
204    
205                            _detachUnnecessaryTypes(javadocsXmlRootElement);
206    
207                            File javadocsRuntimeXmlFile = new File(
208                                    StringUtil.replaceLast(
209                                            javadocsXmlFile.toString(), "-all.xml", "-rt.xml"));
210    
211                            String oldJavadocsRuntimeXmlContent = StringPool.BLANK;
212    
213                            if (javadocsRuntimeXmlFile.exists()) {
214                                    oldJavadocsRuntimeXmlContent = _fileUtil.read(
215                                            javadocsRuntimeXmlFile);
216                            }
217    
218                            String newJavadocsRuntimeXmlContent =
219                                    javadocsXmlDocument.compactString();
220    
221                            if (!oldJavadocsRuntimeXmlContent.equals(
222                                            newJavadocsRuntimeXmlContent)) {
223    
224                                    _fileUtil.write(
225                                            javadocsRuntimeXmlFile, newJavadocsRuntimeXmlContent);
226                            }
227                    }
228            }
229    
230            private List<Tuple> _addAncestorJavaClassTuples(
231                    JavaClass javaClass, List<Tuple> ancestorJavaClassTuples) {
232    
233                    JavaClass superJavaClass = javaClass.getSuperJavaClass();
234    
235                    if (superJavaClass != null) {
236                            ancestorJavaClassTuples.add(new Tuple(superJavaClass));
237    
238                            ancestorJavaClassTuples = _addAncestorJavaClassTuples(
239                                    superJavaClass, ancestorJavaClassTuples);
240                    }
241    
242                    Type[] implementz = javaClass.getImplements();
243    
244                    for (Type implement : implementz) {
245                            Type[] actualTypeArguments = implement.getActualTypeArguments();
246                            JavaClass implementedInterface = implement.getJavaClass();
247    
248                            if (actualTypeArguments == null) {
249                                    ancestorJavaClassTuples.add(new Tuple(implementedInterface));
250                            }
251                            else {
252                                    ancestorJavaClassTuples.add(
253                                            new Tuple(implementedInterface, actualTypeArguments));
254                            }
255    
256                            ancestorJavaClassTuples = _addAncestorJavaClassTuples(
257                                    implementedInterface, ancestorJavaClassTuples);
258                    }
259    
260                    return ancestorJavaClassTuples;
261            }
262    
263            private void _addClassCommentElement(
264                    Element rootElement, JavaClass javaClass) {
265    
266                    String comment = _getCDATA(javaClass);
267    
268                    if (comment.startsWith("Copyright (c)")) {
269                            comment = StringPool.BLANK;
270                    }
271    
272                    if (Validator.isNull(comment)) {
273                            return;
274                    }
275    
276                    Element commentElement = rootElement.addElement("comment");
277    
278                    commentElement.addCDATA(comment);
279            }
280    
281            private void _addDocletElements(
282                            Element parentElement, AbstractJavaEntity abstractJavaEntity,
283                            String name)
284                    throws Exception {
285    
286                    DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);
287    
288                    for (DocletTag docletTag : docletTags) {
289                            String value = docletTag.getValue();
290    
291                            value = _trimMultilineText(value);
292    
293                            value = StringUtil.replace(value, " </", "</");
294    
295                            Element element = parentElement.addElement(name);
296    
297                            element.addCDATA(value);
298                    }
299    
300                    if ((docletTags.length == 0) && name.equals("author")) {
301                            Element element = parentElement.addElement(name);
302    
303                            element.addCDATA(ServiceBuilder.AUTHOR);
304                    }
305            }
306    
307            private String _addDocletTags(
308                    Element parentElement, String[] tagNames, String indent,
309                    boolean publicAccess) {
310    
311                    List<String> allTagNames = new ArrayList<String>();
312                    List<String> customTagNames = new ArrayList<String>();
313                    List<String> requiredTagNames = new ArrayList<String>();
314    
315                    for (String tagName : tagNames) {
316                            List<Element> elements = parentElement.elements(tagName);
317    
318                            for (Element element : elements) {
319                                    Element commentElement = element.element("comment");
320    
321                                    String comment = null;
322    
323                                    // Get comment by comment element's text or the element's text
324    
325                                    if (commentElement != null) {
326                                            comment = commentElement.getText();
327                                    }
328                                    else {
329                                            comment = element.getText();
330                                    }
331    
332                                    if (tagName.equals("param") || tagName.equals("return") ||
333                                            tagName.equals("throws")) {
334    
335                                            if (Validator.isNotNull(comment)) {
336                                                    requiredTagNames.add(tagName);
337                                            }
338                                            else if (tagName.equals("param")) {
339                                                    if (GetterUtil.getBoolean(
340                                                                    element.elementText("required"))) {
341    
342                                                            requiredTagNames.add(tagName);
343                                                    }
344                                            }
345                                            else if (tagName.equals("throws")) {
346                                                    if (GetterUtil.getBoolean(
347                                                                    element.elementText("required"))) {
348    
349                                                            requiredTagNames.add(tagName);
350                                                    }
351                                            }
352                                    }
353                                    else {
354                                            customTagNames.add(tagName);
355                                    }
356    
357                                    allTagNames.add(tagName);
358                            }
359                    }
360    
361                    int maxTagNameLength = 0;
362    
363                    List<String> maxTagNameLengthTags = new ArrayList<String>();
364    
365                    if (_initializeMissingJavadocs) {
366                            maxTagNameLengthTags.addAll(allTagNames);
367                    }
368                    else if (_updateJavadocs) {
369                            if (!requiredTagNames.isEmpty()) {
370                                    maxTagNameLengthTags.addAll(allTagNames);
371                            }
372                            else {
373                                    maxTagNameLengthTags.addAll(customTagNames);
374                                    maxTagNameLengthTags.addAll(requiredTagNames);
375                            }
376                    }
377                    else {
378                            maxTagNameLengthTags.addAll(customTagNames);
379                            maxTagNameLengthTags.addAll(requiredTagNames);
380                    }
381    
382                    for (String name : maxTagNameLengthTags) {
383                            if (name.length() > maxTagNameLength) {
384                                    maxTagNameLength = name.length();
385                            }
386                    }
387    
388                    // There should be an @ sign before the tag and a space after it
389    
390                    maxTagNameLength += 2;
391    
392                    String tagNameIndent = _getSpacesIndent(maxTagNameLength);
393    
394                    StringBundler sb = new StringBundler();
395    
396                    for (String tagName : tagNames) {
397                            List<Element> elements = parentElement.elements(tagName);
398    
399                            for (Element element : elements) {
400                                    Element commentElement = element.element("comment");
401    
402                                    String comment = null;
403    
404                                    if (commentElement != null) {
405                                            comment = commentElement.getText();
406                                    }
407                                    else {
408                                            comment = element.getText();
409                                    }
410    
411                                    String elementName = element.elementText("name");
412    
413                                    if (Validator.isNotNull(comment)) {
414                                            comment = _assembleTagComment(
415                                                    tagName, elementName, comment, indent, tagNameIndent);
416    
417                                            sb.append(comment);
418                                    }
419                                    else {
420                                            if (_initializeMissingJavadocs && publicAccess) {
421    
422                                                    // Write out all tags
423    
424                                                    comment = _assembleTagComment(
425                                                            tagName, elementName, comment, indent,
426                                                            tagNameIndent);
427    
428                                                    sb.append(comment);
429                                            }
430                                            else if (_updateJavadocs && publicAccess) {
431                                                    if (!tagName.equals("param") &&
432                                                            !tagName.equals("return") &&
433                                                            !tagName.equals("throws")) {
434    
435                                                            // Write out custom tag
436    
437                                                            comment = _assembleTagComment(
438                                                                    tagName, elementName, comment, indent,
439                                                                    tagNameIndent);
440    
441                                                            sb.append(comment);
442                                                    }
443                                                    else if (!requiredTagNames.isEmpty()) {
444    
445                                                            // Write out all tags
446    
447                                                            comment = _assembleTagComment(
448                                                                    tagName, elementName, comment, indent,
449                                                                    tagNameIndent);
450    
451                                                            sb.append(comment);
452                                                    }
453                                                    else {
454    
455                                                            // Skip empty common tag
456    
457                                                    }
458                                            }
459                                            else {
460                                                    if (!tagName.equals("param") &&
461                                                            !tagName.equals("return") &&
462                                                            !tagName.equals("throws")) {
463    
464                                                            // Write out custom tag
465    
466                                                            comment = _assembleTagComment(
467                                                                    tagName, elementName, comment, indent,
468                                                                    tagNameIndent);
469    
470                                                            sb.append(comment);
471                                                    }
472                                                    else if (tagName.equals("param") ||
473                                                                     tagName.equals("return") ||
474                                                                     tagName.equals("throws")) {
475    
476                                                            if (GetterUtil.getBoolean(
477                                                                            element.elementText("required"))) {
478    
479                                                                    elementName = element.elementText("name");
480    
481                                                                    comment = _assembleTagComment(
482                                                                            tagName, elementName, comment, indent,
483                                                                            tagNameIndent);
484    
485                                                                    sb.append(comment);
486                                                            }
487                                                    }
488                                                    else {
489    
490                                                            // Skip empty common tag
491    
492                                                    }
493                                            }
494                                    }
495                            }
496                    }
497    
498                    return sb.toString();
499            }
500    
501            private void _addFieldElement(Element rootElement, JavaField javaField)
502                    throws Exception {
503    
504                    Element fieldElement = rootElement.addElement("field");
505    
506                    DocUtil.add(fieldElement, "name", javaField.getName());
507    
508                    String comment = _getCDATA(javaField);
509    
510                    if (Validator.isNotNull(comment)) {
511                            Element commentElement = fieldElement.addElement("comment");
512    
513                            commentElement.addCDATA(comment);
514                    }
515    
516                    _addDocletElements(fieldElement, javaField, "version");
517                    _addDocletElements(fieldElement, javaField, "see");
518                    _addDocletElements(fieldElement, javaField, "since");
519                    _addDocletElements(fieldElement, javaField, "deprecated");
520            }
521    
522            private void _addMethodElement(Element rootElement, JavaMethod javaMethod)
523                    throws Exception {
524    
525                    Element methodElement = rootElement.addElement("method");
526    
527                    DocUtil.add(methodElement, "name", javaMethod.getName());
528    
529                    String comment = _getCDATA(javaMethod);
530    
531                    if (Validator.isNotNull(comment)) {
532                            Element commentElement = methodElement.addElement("comment");
533    
534                            commentElement.addCDATA(_getCDATA(javaMethod));
535                    }
536    
537                    _addDocletElements(methodElement, javaMethod, "version");
538                    _addParamElements(methodElement, javaMethod);
539                    _addReturnElement(methodElement, javaMethod);
540                    _addThrowsElements(methodElement, javaMethod);
541                    _addDocletElements(methodElement, javaMethod, "see");
542                    _addDocletElements(methodElement, javaMethod, "since");
543                    _addDocletElements(methodElement, javaMethod, "deprecated");
544            }
545    
546            private void _addParamElement(
547                    Element methodElement, JavaParameter javaParameter,
548                    DocletTag[] paramDocletTags) {
549    
550                    String name = javaParameter.getName();
551    
552                    String value = null;
553    
554                    for (DocletTag paramDocletTag : paramDocletTags) {
555                            String curValue = paramDocletTag.getValue();
556    
557                            if (!curValue.startsWith(name)) {
558                                    continue;
559                            }
560                            else {
561                                    value = curValue;
562    
563                                    break;
564                            }
565                    }
566    
567                    Element paramElement = methodElement.addElement("param");
568    
569                    DocUtil.add(paramElement, "name", name);
570                    DocUtil.add(paramElement, "type", _getTypeValue(javaParameter));
571    
572                    if (value != null) {
573                            value = value.substring(name.length());
574    
575                            DocUtil.add(paramElement, "required", true);
576                    }
577    
578                    value = _trimMultilineText(value);
579    
580                    Element commentElement = paramElement.addElement("comment");
581    
582                    commentElement.addCDATA(value);
583            }
584    
585            private void _addParamElements(
586                    Element methodElement, JavaMethod javaMethod) {
587    
588                    JavaParameter[] javaParameters = javaMethod.getParameters();
589    
590                    DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");
591    
592                    for (JavaParameter javaParameter : javaParameters) {
593                            _addParamElement(methodElement, javaParameter, paramDocletTags);
594                    }
595            }
596    
597            private void _addReturnElement(Element methodElement, JavaMethod javaMethod)
598                    throws Exception {
599    
600                    Type returns = javaMethod.getReturns();
601    
602                    if (returns == null) {
603                            return;
604                    }
605    
606                    String returnsValue = returns.getValue();
607    
608                    if (returnsValue.equals("void")) {
609                            return;
610                    }
611    
612                    Element returnElement = methodElement.addElement("return");
613    
614                    DocletTag[] returnDocletTags = javaMethod.getTagsByName("return");
615    
616                    String comment = StringPool.BLANK;
617    
618                    if (returnDocletTags.length > 0) {
619                            DocletTag returnDocletTag = returnDocletTags[0];
620    
621                            comment = GetterUtil.getString(returnDocletTag.getValue());
622    
623                            DocUtil.add(returnElement, "required", true);
624                    }
625    
626                    comment = _trimMultilineText(comment);
627    
628                    Element commentElement = returnElement.addElement("comment");
629    
630                    commentElement.addCDATA(comment);
631            }
632    
633            private void _addThrowsElement(
634                    Element methodElement, Type exceptionType,
635                    DocletTag[] throwsDocletTags) {
636    
637                    JavaClass javaClass = exceptionType.getJavaClass();
638    
639                    String name = javaClass.getName();
640    
641                    String value = null;
642    
643                    for (DocletTag throwsDocletTag : throwsDocletTags) {
644                            String curValue = throwsDocletTag.getValue();
645    
646                            if (!curValue.startsWith(name)) {
647                                    continue;
648                            }
649                            else {
650                                    value = curValue;
651    
652                                    break;
653                            }
654                    }
655    
656                    Element throwsElement = methodElement.addElement("throws");
657    
658                    DocUtil.add(throwsElement, "name", name);
659                    DocUtil.add(throwsElement, "type", exceptionType.getValue());
660    
661                    if (value != null) {
662                            value = value.substring(name.length());
663    
664                            DocUtil.add(throwsElement, "required", true);
665                    }
666    
667                    value = _trimMultilineText(value);
668    
669                    Element commentElement = throwsElement.addElement("comment");
670    
671                    commentElement.addCDATA(_getCDATA(value));
672            }
673    
674            private void _addThrowsElements(
675                    Element methodElement, JavaMethod javaMethod) {
676    
677                    Type[] exceptionTypes = javaMethod.getExceptions();
678    
679                    DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");
680    
681                    for (Type exceptionType : exceptionTypes) {
682                            _addThrowsElement(methodElement, exceptionType, throwsDocletTags);
683                    }
684            }
685    
686            private String _assembleTagComment(
687                    String tagName, String elementName, String comment, String indent,
688                    String tagNameIndent) {
689    
690                    String indentAndTagName = indent + StringPool.AT + tagName;
691    
692                    if (Validator.isNotNull(elementName)) {
693                            if (Validator.isNotNull(comment)) {
694                                    comment = elementName + StringPool.SPACE + comment;
695                            }
696                            else {
697                                    comment = elementName;
698                            }
699    
700                            // <name indent> elementName [comment]
701    
702                            comment = _wrapText(comment, indent + tagNameIndent);
703    
704                            // * @name <name indent> elementName [comment]
705    
706                            comment =
707                                    indentAndTagName + comment.substring(indentAndTagName.length());
708                    }
709                    else {
710                            if (Validator.isNotNull(comment)) {
711    
712                                    // <name indent> comment
713    
714                                    comment = _wrapText(comment, indent + tagNameIndent);
715    
716                                    // * @name <name indent> comment
717    
718                                    comment =
719                                            indentAndTagName +
720                                                    comment.substring(indentAndTagName.length());
721                            }
722                            else {
723    
724                                    // * @name
725    
726                                    comment = indentAndTagName + "\n";
727                            }
728                    }
729    
730                    return comment;
731            }
732    
733            private void _detachUnnecessaryTypes(Element rootElement) {
734                    List<Element> elements = rootElement.elements();
735    
736                    for (Element element : elements) {
737                            String type = element.elementText("type");
738    
739                            if (!type.contains(".service.") || !type.endsWith("ServiceImpl")) {
740                                    element.detach();
741                            }
742                    }
743            }
744    
745            private void _format(String fileName) throws Exception {
746                    InputStream inputStream = new FileInputStream(_inputDir + fileName);
747    
748                    byte[] bytes = new byte[inputStream.available()];
749    
750                    inputStream.read(bytes);
751    
752                    inputStream.close();
753    
754                    String originalContent = new String(bytes, StringPool.UTF8);
755    
756                    if (fileName.endsWith("JavadocFormatter.java") ||
757                            fileName.endsWith("SourceFormatter.java") ||
758                            _hasGeneratedTag(originalContent)) {
759    
760                            return;
761                    }
762    
763                    JavaClass javaClass = _getJavaClass(
764                            fileName, new UnsyncStringReader(originalContent));
765    
766                    String javadocLessContent = _removeJavadocFromJava(
767                            javaClass, originalContent);
768    
769                    Document document = _getJavadocDocument(javaClass);
770    
771                    _updateJavadocsXmlFile(fileName, javaClass, document);
772    
773                    _updateJavaFromDocument(
774                            fileName, originalContent, javadocLessContent, document);
775            }
776    
777            private String _formatCDATA(String cdata, String exclude) {
778                    StringBundler sb = new StringBundler();
779    
780                    String startTag = "<" + exclude + ">";
781                    String endTag = "</" + exclude + ">";
782    
783                    String[] cdataParts = cdata.split(startTag);
784    
785                    for (String cdataPart : cdataParts) {
786                            if (!cdataPart.contains(endTag)) {
787                                    cdataPart = _getCDATA(cdataPart);
788                            }
789    
790                            if (cdataPart.contains("</" + exclude + ">")) {
791                                    sb.append(startTag);
792                            }
793    
794                            sb.append(cdataPart);
795                    }
796    
797                    return sb.toString();
798            }
799    
800            private String _formatInlines(String text) {
801    
802                    // Capitalize ID
803    
804                    text = text.replaceAll("[?@param id](?i)\\bid(s)?\\b", " ID$1");
805    
806                    // Wrap special constants in code tags
807    
808                    text = text.replaceAll(
809                            "(?i)(?<!<code>|\\w)(null|false|true)(?!\\w)", "<code>$1</code>");
810    
811                    return text;
812            }
813    
814            private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
815                    return _getCDATA(abstractJavaEntity.getComment());
816            }
817    
818            private String _getCDATA(String cdata) {
819                    if (cdata == null) {
820                            return StringPool.BLANK;
821                    }
822                    else if (cdata.contains("<pre>")) {
823                            cdata = _formatCDATA(cdata, "pre");
824                    }
825                    else if (cdata.contains("<table>")) {
826                            cdata = _formatCDATA(cdata, "table");
827                    }
828                    else {
829                            cdata = cdata.replaceAll(
830                                    "(?s)\\s*<(p|[ou]l)>\\s*(.*?)\\s*</\\1>\\s*",
831                                    "\n\n<$1>\n$2\n</$1>\n\n");
832                            cdata = cdata.replaceAll(
833                                    "(?s)\\s*<li>\\s*(.*?)\\s*</li>\\s*", "\n<li>\n$1\n</li>\n");
834                            cdata = StringUtil.replace(cdata, "</li>\n\n<li>", "</li>\n<li>");
835                            cdata = cdata.replaceAll("\n\\s+\n", "\n\n");
836                            cdata.replaceAll(" +", " ");
837    
838                            // Trim whitespace inside paragraph tags or in the first paragraph
839    
840                            Pattern pattern = Pattern.compile(
841                                    "(^.*?(?=\n\n|$)+|(?<=<p>\n).*?(?=\n</p>))", Pattern.DOTALL);
842    
843                            Matcher matcher = pattern.matcher(cdata);
844    
845                            StringBuffer sb = new StringBuffer();
846    
847                            while (matcher.find()) {
848                                    String trimmed = _trimMultilineText(matcher.group());
849    
850                                    // Escape dollar signs
851    
852                                    trimmed = trimmed.replaceAll("\\$", "\\\\\\$");
853    
854                                    matcher.appendReplacement(sb, trimmed);
855                            }
856    
857                            matcher.appendTail(sb);
858    
859                            cdata = sb.toString();
860                    }
861    
862                    return cdata.trim();
863            }
864    
865            private String _getClassName(String fileName) {
866                    int pos = fileName.indexOf("src/");
867    
868                    if (pos == -1) {
869                            pos = fileName.indexOf("test/integration/");
870    
871                            if (pos != -1) {
872                                    pos = fileName.indexOf("integration/", pos);
873                            }
874                    }
875    
876                    if (pos == -1) {
877                            pos = fileName.indexOf("test/unit/");
878    
879                            if (pos != -1) {
880                                    pos = fileName.indexOf("unit/", pos);
881                            }
882                    }
883    
884                    if (pos == -1) {
885                            pos = fileName.indexOf("test/");
886                    }
887    
888                    if (pos == -1) {
889                            pos = fileName.indexOf("service/");
890                    }
891    
892                    if (pos == -1) {
893                            throw new RuntimeException(fileName);
894                    }
895    
896                    pos = fileName.indexOf("/", pos);
897    
898                    String srcFile = fileName.substring(pos + 1, fileName.length());
899    
900                    return StringUtil.replace(
901                            srcFile.substring(0, srcFile.length() - 5), "/", ".");
902            }
903    
904            private String _getFieldKey(Element fieldElement) {
905                    return fieldElement.elementText("name");
906            }
907    
908            private String _getFieldKey(JavaField javaField) {
909                    return javaField.getName();
910            }
911    
912            private String _getIndent(
913                    String[] lines, AbstractBaseJavaEntity abstractBaseJavaEntity) {
914    
915                    String line = lines[abstractBaseJavaEntity.getLineNumber() - 1];
916    
917                    String indent = StringPool.BLANK;
918    
919                    for (char c : line.toCharArray()) {
920                            if (Character.isWhitespace(c)) {
921                                    indent += c;
922                            }
923                            else {
924                                    break;
925                            }
926                    }
927    
928                    return indent;
929            }
930    
931            private int _getIndentLength(String indent) {
932                    int indentLength = 0;
933    
934                    for (char c : indent.toCharArray()) {
935                            if (c == '\t') {
936                                    indentLength = indentLength + 4;
937                            }
938                            else {
939                                    indentLength++;
940                            }
941                    }
942    
943                    return indentLength;
944            }
945    
946            private JavaClass _getJavaClass(String fileName, Reader reader)
947                    throws Exception {
948    
949                    String className = _getClassName(fileName);
950    
951                    JavaDocBuilder javadocBuilder = new JavaDocBuilder();
952    
953                    if (reader == null) {
954                            File file = new File(fileName);
955    
956                            if (!file.exists()) {
957                                    return null;
958                            }
959    
960                            javadocBuilder.addSource(file);
961                    }
962                    else {
963                            javadocBuilder.addSource(reader);
964                    }
965    
966                    return javadocBuilder.getClassByName(className);
967            }
968    
969            private String _getJavaClassComment(
970                    Element rootElement, JavaClass javaClass) {
971    
972                    StringBundler sb = new StringBundler();
973    
974                    String indent = StringPool.BLANK;
975    
976                    sb.append("/**\n");
977    
978                    String comment = rootElement.elementText("comment");
979    
980                    if (Validator.isNotNull(comment)) {
981                            sb.append(_wrapText(comment, indent + " * "));
982                    }
983    
984                    String docletTags = _addDocletTags(
985                            rootElement,
986                            new String[] {
987                                    "author", "version", "see", "since", "serial", "deprecated"
988                            },
989                            indent + " * ", _hasPublicModifier(javaClass));
990    
991                    if (Validator.isNotNull(docletTags)) {
992                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
993                                    sb.append(" *\n");
994                            }
995    
996                            sb.append(docletTags);
997                    }
998    
999                    sb.append(" */\n");
1000    
1001                    return sb.toString();
1002            }
1003    
1004            private int _getJavaClassLineNumber(JavaClass javaClass) {
1005                    int lineNumber = javaClass.getLineNumber();
1006    
1007                    Annotation[] annotations = javaClass.getAnnotations();
1008    
1009                    if (annotations.length == 0) {
1010                            return lineNumber;
1011                    }
1012    
1013                    for (Annotation annotation : annotations) {
1014                            int annotationLineNumber = annotation.getLineNumber();
1015    
1016                            Map<String, String> propertyMap = annotation.getPropertyMap();
1017    
1018                            if (propertyMap.isEmpty()) {
1019                                    annotationLineNumber--;
1020                            }
1021    
1022                            if (annotationLineNumber < lineNumber) {
1023                                    lineNumber = annotationLineNumber;
1024                            }
1025                    }
1026    
1027                    return lineNumber;
1028            }
1029    
1030            private Document _getJavadocDocument(JavaClass javaClass) throws Exception {
1031                    Element rootElement = _saxReaderUtil.createElement("javadoc");
1032    
1033                    Document document = _saxReaderUtil.createDocument(rootElement);
1034    
1035                    DocUtil.add(rootElement, "name", javaClass.getName());
1036                    DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());
1037    
1038                    _addClassCommentElement(rootElement, javaClass);
1039                    _addDocletElements(rootElement, javaClass, "author");
1040                    _addDocletElements(rootElement, javaClass, "version");
1041                    _addDocletElements(rootElement, javaClass, "see");
1042                    _addDocletElements(rootElement, javaClass, "since");
1043                    _addDocletElements(rootElement, javaClass, "serial");
1044                    _addDocletElements(rootElement, javaClass, "deprecated");
1045    
1046                    JavaMethod[] javaMethods = javaClass.getMethods();
1047    
1048                    for (JavaMethod javaMethod : javaMethods) {
1049                            _addMethodElement(rootElement, javaMethod);
1050                    }
1051    
1052                    JavaField[] javaFields = javaClass.getFields();
1053    
1054                    for (JavaField javaField : javaFields) {
1055                            _addFieldElement(rootElement, javaField);
1056                    }
1057    
1058                    return document;
1059            }
1060    
1061            private Tuple _getJavadocsXmlTuple(String fileName) throws Exception {
1062                    File file = new File(_inputDir + fileName);
1063    
1064                    String absolutePath = file.getAbsolutePath();
1065    
1066                    absolutePath = StringUtil.replace(absolutePath, "\\", "/");
1067                    absolutePath = StringUtil.replace(absolutePath, "/./", "/");
1068    
1069                    int pos = absolutePath.indexOf("/portal-impl/src/");
1070    
1071                    String srcDirName = null;
1072    
1073                    if (pos != -1) {
1074                            srcDirName = absolutePath.substring(0, pos + 17);
1075                    }
1076    
1077                    if (srcDirName == null) {
1078                            pos = absolutePath.indexOf("/portal-kernel/src/");
1079    
1080                            if (pos == -1) {
1081                                    pos = absolutePath.indexOf("/portal-service/src/");
1082                            }
1083    
1084                            if (pos == -1) {
1085                                    pos = absolutePath.indexOf("/util-bridges/src/");
1086                            }
1087    
1088                            if (pos == -1) {
1089                                    pos = absolutePath.indexOf("/util-java/src/");
1090                            }
1091    
1092                            if (pos == -1) {
1093                                    pos = absolutePath.indexOf("/util-taglib/src/");
1094                            }
1095    
1096                            if (pos != -1) {
1097                                    srcDirName =
1098                                            absolutePath.substring(0, pos) + "/portal-impl/src/";
1099                            }
1100                    }
1101    
1102                    if (srcDirName == null) {
1103                            pos = absolutePath.indexOf("/WEB-INF/src/");
1104    
1105                            if (pos != -1) {
1106                                    srcDirName = absolutePath.substring(0, pos + 13);
1107                            }
1108                    }
1109    
1110                    if (srcDirName == null) {
1111                            return null;
1112                    }
1113    
1114                    Tuple tuple = _javadocxXmlTuples.get(srcDirName);
1115    
1116                    if (tuple != null) {
1117                            return tuple;
1118                    }
1119    
1120                    File javadocsXmlFile = new File(
1121                            srcDirName, "META-INF/" + _outputFilePrefix + "-all.xml");
1122    
1123                    if (!javadocsXmlFile.exists()) {
1124                            _fileUtil.write(
1125                                    javadocsXmlFile,
1126                                    "<?xml version=\"1.0\"?>\n\n<javadocs>\n</javadocs>");
1127                    }
1128    
1129                    String javadocsXmlContent = _fileUtil.read(javadocsXmlFile);
1130    
1131                    Document javadocsXmlDocument = _saxReaderUtil.read(javadocsXmlContent);
1132    
1133                    tuple = new Tuple(
1134                            srcDirName, javadocsXmlFile, javadocsXmlContent,
1135                            javadocsXmlDocument);
1136    
1137                    _javadocxXmlTuples.put(srcDirName, tuple);
1138    
1139                    return tuple;
1140            }
1141    
1142            private String _getJavaFieldComment(
1143                    String[] lines, Map<String, Element> fieldElementsMap,
1144                    JavaField javaField) {
1145    
1146                    String fieldKey = _getFieldKey(javaField);
1147    
1148                    Element fieldElement = fieldElementsMap.get(fieldKey);
1149    
1150                    if (fieldElement == null) {
1151                            return null;
1152                    }
1153    
1154                    String indent = _getIndent(lines, javaField);
1155    
1156                    StringBundler sb = new StringBundler();
1157    
1158                    sb.append(indent);
1159                    sb.append("/**\n");
1160    
1161                    String comment = fieldElement.elementText("comment");
1162    
1163                    if (Validator.isNotNull(comment)) {
1164                            sb.append(_wrapText(comment, indent + " * "));
1165                    }
1166    
1167                    String docletTags = _addDocletTags(
1168                            fieldElement,
1169                            new String[] {"version", "see", "since", "deprecated"},
1170                            indent + " * ", _hasPublicModifier(javaField));
1171    
1172                    if (Validator.isNotNull(docletTags)) {
1173                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
1174                                    sb.append(indent);
1175                                    sb.append(" *\n");
1176                            }
1177    
1178                            sb.append(docletTags);
1179                    }
1180    
1181                    sb.append(indent);
1182                    sb.append(" */\n");
1183    
1184                    if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
1185                            Validator.isNull(docletTags)) {
1186    
1187                            return null;
1188                    }
1189    
1190                    if (!_hasPublicModifier(javaField) && Validator.isNull(comment) &&
1191                            Validator.isNull(docletTags)) {
1192    
1193                            return null;
1194                    }
1195    
1196                    return sb.toString();
1197            }
1198    
1199            private String _getJavaMethodComment(
1200                    String[] lines, Map<String, Element> methodElementsMap,
1201                    JavaMethod javaMethod) {
1202    
1203                    String methodKey = _getMethodKey(javaMethod);
1204    
1205                    Element methodElement = methodElementsMap.get(methodKey);
1206    
1207                    if (methodElement == null) {
1208                            return null;
1209                    }
1210    
1211                    String indent = _getIndent(lines, javaMethod);
1212    
1213                    StringBundler sb = new StringBundler();
1214    
1215                    sb.append(indent);
1216                    sb.append("/**\n");
1217    
1218                    String comment = methodElement.elementText("comment");
1219    
1220                    if (Validator.isNotNull(comment)) {
1221                            sb.append(_wrapText(comment, indent + " * "));
1222                    }
1223    
1224                    String docletTags = _addDocletTags(
1225                            methodElement,
1226                            new String[] {
1227                                    "version", "param", "return", "throws", "see", "since",
1228                                    "deprecated"
1229                            },
1230                            indent + " * ", _hasPublicModifier(javaMethod));
1231    
1232                    if (Validator.isNotNull(docletTags)) {
1233                            if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
1234                                    sb.append(indent);
1235                                    sb.append(" *\n");
1236                            }
1237    
1238                            sb.append(docletTags);
1239                    }
1240    
1241                    sb.append(indent);
1242                    sb.append(" */\n");
1243    
1244                    if (!_initializeMissingJavadocs && Validator.isNull(comment) &&
1245                            Validator.isNull(docletTags)) {
1246    
1247                            return null;
1248                    }
1249    
1250                    if (!_hasPublicModifier(javaMethod) && Validator.isNull(comment) &&
1251                            Validator.isNull(docletTags)) {
1252    
1253                            return null;
1254                    }
1255    
1256                    return sb.toString();
1257            }
1258    
1259            private String _getMethodKey(Element methodElement) {
1260                    StringBundler sb = new StringBundler();
1261    
1262                    sb.append(methodElement.elementText("name"));
1263                    sb.append(StringPool.OPEN_PARENTHESIS);
1264    
1265                    List<Element> paramElements = methodElement.elements("param");
1266    
1267                    for (Element paramElement : paramElements) {
1268                            sb.append(paramElement.elementText("name"));
1269                            sb.append("|");
1270                            sb.append(paramElement.elementText("type"));
1271                            sb.append(",");
1272                    }
1273    
1274                    sb.append(StringPool.CLOSE_PARENTHESIS);
1275    
1276                    return sb.toString();
1277            }
1278    
1279            private String _getMethodKey(JavaMethod javaMethod) {
1280                    StringBundler sb = new StringBundler();
1281    
1282                    sb.append(javaMethod.getName());
1283                    sb.append(StringPool.OPEN_PARENTHESIS);
1284    
1285                    JavaParameter[] javaParameters = javaMethod.getParameters();
1286    
1287                    for (JavaParameter javaParameter : javaParameters) {
1288                            sb.append(javaParameter.getName());
1289                            sb.append("|");
1290                            sb.append(_getTypeValue(javaParameter));
1291                            sb.append(",");
1292                    }
1293    
1294                    sb.append(StringPool.CLOSE_PARENTHESIS);
1295    
1296                    return sb.toString();
1297            }
1298    
1299            private String _getSpacesIndent(int length) {
1300                    String indent = StringPool.BLANK;
1301    
1302                    for (int i = 0; i < length; i++) {
1303                            indent += StringPool.SPACE;
1304                    }
1305    
1306                    return indent;
1307            }
1308    
1309            private String _getTypeValue(JavaParameter javaParameter) {
1310                    Type type = javaParameter.getType();
1311    
1312                    String typeValue = type.getValue();
1313    
1314                    if (type.isArray()) {
1315                            typeValue += "[]";
1316                    }
1317    
1318                    return typeValue;
1319            }
1320    
1321            private boolean _hasAnnotation(
1322                    AbstractBaseJavaEntity abstractBaseJavaEntity, String annotationName) {
1323    
1324                    Annotation[] annotations = abstractBaseJavaEntity.getAnnotations();
1325    
1326                    if (annotations == null) {
1327                            return false;
1328                    }
1329    
1330                    for (int i = 0; i < annotations.length; i++) {
1331                            Type type = annotations[i].getType();
1332    
1333                            JavaClass javaClass = type.getJavaClass();
1334    
1335                            if (annotationName.equals(javaClass.getName())) {
1336                                    return true;
1337                            }
1338                    }
1339    
1340                    return false;
1341            }
1342    
1343            private boolean _hasGeneratedTag(String content) {
1344                    if (content.contains("* @generated") || content.contains("$ANTLR")) {
1345                            return true;
1346                    }
1347                    else {
1348                            return false;
1349                    }
1350            }
1351    
1352            private boolean _hasPublicModifier(AbstractJavaEntity abstractJavaEntity) {
1353                    String[] modifiers = abstractJavaEntity.getModifiers();
1354    
1355                    if (modifiers == null) {
1356                            return false;
1357                    }
1358    
1359                    for (String modifier : modifiers) {
1360                            if (modifier.equals("public")) {
1361                                    return true;
1362                            }
1363                    }
1364    
1365                    return false;
1366            }
1367    
1368            private boolean _isOverrideMethod(
1369                    JavaClass javaClass, JavaMethod javaMethod,
1370                    Collection<Tuple> ancestorJavaClassTuples) {
1371    
1372                    if (javaMethod.isConstructor() || javaMethod.isPrivate() ||
1373                            javaMethod.isStatic() ||
1374                            _overridesHigherJavaAPIVersion(javaMethod)) {
1375    
1376                            return false;
1377                    }
1378    
1379                    String methodName = javaMethod.getName();
1380    
1381                    JavaParameter[] javaParameters = javaMethod.getParameters();
1382    
1383                    Type[] types = new Type[javaParameters.length];
1384    
1385                    for (int i = 0; i < javaParameters.length; i++) {
1386                            types[i] = javaParameters[i].getType();
1387                    }
1388    
1389                    // Check for matching method in each ancestor
1390    
1391                    for (Tuple ancestorJavaClassTuple : ancestorJavaClassTuples) {
1392                            JavaClass ancestorJavaClass =
1393                                    (JavaClass)ancestorJavaClassTuple.getObject(0);
1394    
1395                            JavaMethod ancestorJavaMethod = null;
1396    
1397                            if (ancestorJavaClassTuple.getSize() > 1) {
1398    
1399                                    // LPS-35613
1400    
1401                                    Type[] ancestorActualTypeArguments =
1402                                            (Type[])ancestorJavaClassTuple.getObject(1);
1403    
1404                                    Type[] genericTypes = new Type[types.length];
1405    
1406                                    for (int i = 0; i < types.length; i++) {
1407                                            Type type = types[i];
1408    
1409                                            String typeValue = type.getValue();
1410    
1411                                            boolean useGenericType = false;
1412    
1413                                            for (int j = 0; j < ancestorActualTypeArguments.length;
1414                                                            j++) {
1415    
1416                                                    if (typeValue.equals(
1417                                                                    ancestorActualTypeArguments[j].getValue())) {
1418    
1419                                                            useGenericType = true;
1420    
1421                                                            break;
1422                                                    }
1423                                            }
1424    
1425                                            if (useGenericType) {
1426                                                    genericTypes[i] = new Type("java.lang.Object");
1427                                            }
1428                                            else {
1429                                                    genericTypes[i] = type;
1430                                            }
1431                                    }
1432    
1433                                    ancestorJavaMethod = ancestorJavaClass.getMethodBySignature(
1434                                            methodName, genericTypes);
1435                            }
1436                            else {
1437                                    ancestorJavaMethod = ancestorJavaClass.getMethodBySignature(
1438                                            methodName, types);
1439                            }
1440    
1441                            if (ancestorJavaMethod == null) {
1442                                    continue;
1443                            }
1444    
1445                            boolean samePackage = false;
1446    
1447                            JavaPackage ancestorJavaPackage = ancestorJavaClass.getPackage();
1448    
1449                            if (ancestorJavaPackage != null) {
1450                                    samePackage = ancestorJavaPackage.equals(
1451                                            javaClass.getPackage());
1452                            }
1453    
1454                            // Check if the method is in scope
1455    
1456                            if (samePackage) {
1457                                    return !ancestorJavaMethod.isPrivate();
1458                            }
1459                            else {
1460                                    if (ancestorJavaMethod.isProtected() ||
1461                                            ancestorJavaMethod.isPublic()) {
1462    
1463                                            return true;
1464                                    }
1465                                    else {
1466                                            return false;
1467                                    }
1468                            }
1469                    }
1470    
1471                    return false;
1472            }
1473    
1474            private boolean _overridesHigherJavaAPIVersion(JavaMethod javaMethod) {
1475                    Annotation[] annotations = javaMethod.getAnnotations();
1476    
1477                    if (annotations == null) {
1478                            return false;
1479                    }
1480    
1481                    for (Annotation annotation : annotations) {
1482                            Type type = annotation.getType();
1483    
1484                            JavaClass javaClass = type.getJavaClass();
1485    
1486                            String javaClassName = javaClass.getFullyQualifiedName();
1487    
1488                            if (javaClassName.equals(SinceJava.class.getName())) {
1489                                    AnnotationValue value = annotation.getProperty("value");
1490    
1491                                    double sinceJava = GetterUtil.getDouble(
1492                                            value.getParameterValue());
1493    
1494                                    if (sinceJava > _LOWEST_SUPPORTED_JAVA_VERSION) {
1495                                            return true;
1496                                    }
1497                            }
1498                    }
1499    
1500                    return false;
1501            }
1502    
1503            private String _removeJavadocFromJava(JavaClass javaClass, String content) {
1504                    Set<Integer> lineNumbers = new HashSet<Integer>();
1505    
1506                    lineNumbers.add(_getJavaClassLineNumber(javaClass));
1507    
1508                    JavaMethod[] javaMethods = javaClass.getMethods();
1509    
1510                    for (JavaMethod javaMethod : javaMethods) {
1511                            lineNumbers.add(javaMethod.getLineNumber());
1512                    }
1513    
1514                    JavaField[] javaFields = javaClass.getFields();
1515    
1516                    for (JavaField javaField : javaFields) {
1517                            lineNumbers.add(javaField.getLineNumber());
1518                    }
1519    
1520                    String[] lines = StringUtil.splitLines(content);
1521    
1522                    for (int lineNumber : lineNumbers) {
1523                            if (lineNumber == 0) {
1524                                    continue;
1525                            }
1526    
1527                            int pos = lineNumber - 2;
1528    
1529                            String line = lines[pos];
1530    
1531                            if (line == null) {
1532                                    continue;
1533                            }
1534    
1535                            line = line.trim();
1536    
1537                            if (line.endsWith("*/")) {
1538                                    while (true) {
1539                                            lines[pos] = null;
1540    
1541                                            if (line.startsWith("/**") || line.startsWith("/*")) {
1542                                                    break;
1543                                            }
1544    
1545                                            line = lines[--pos].trim();
1546                                    }
1547                            }
1548                    }
1549    
1550                    StringBundler sb = new StringBundler(content.length());
1551    
1552                    for (String line : lines) {
1553                            if (line != null) {
1554                                    sb.append(line);
1555                                    sb.append("\n");
1556                            }
1557                    }
1558    
1559                    return sb.toString().trim();
1560            }
1561    
1562            private String _trimMultilineText(String text) {
1563                    String[] textArray = StringUtil.splitLines(text);
1564    
1565                    for (int i = 0; i < textArray.length; i++) {
1566                            textArray[i] = textArray[i].trim();
1567                    }
1568    
1569                    return StringUtil.merge(textArray, " ");
1570            }
1571    
1572            private void _updateJavadocsXmlFile(
1573                            String fileName, JavaClass javaClass, Document javaClassDocument)
1574                    throws Exception {
1575    
1576                    String javaClassFullyQualifiedName = javaClass.getFullyQualifiedName();
1577    
1578                    /*if (!javaClassFullyQualifiedName.contains(".service.") ||
1579                            !javaClassFullyQualifiedName.endsWith("ServiceImpl")) {
1580    
1581                            return;
1582                    }*/
1583    
1584                    Tuple javadocsXmlTuple = _getJavadocsXmlTuple(fileName);
1585    
1586                    if (javadocsXmlTuple == null) {
1587                            return;
1588                    }
1589    
1590                    Document javadocsXmlDocument = (Document)javadocsXmlTuple.getObject(3);
1591    
1592                    Element javadocsXmlRootElement = javadocsXmlDocument.getRootElement();
1593    
1594                    List<Element> javadocElements = javadocsXmlRootElement.elements(
1595                            "javadoc");
1596    
1597                    for (Element javadocElement : javadocElements) {
1598                            String type = javadocElement.elementText("type");
1599    
1600                            if (type.equals(javaClassFullyQualifiedName)) {
1601                                    Element javaClassRootElement =
1602                                            javaClassDocument.getRootElement();
1603    
1604                                    if (Validator.equals(
1605                                                    javadocElement.formattedString(),
1606                                                    javaClassRootElement.formattedString())) {
1607    
1608                                            return;
1609                                    }
1610    
1611                                    javadocElement.detach();
1612    
1613                                    break;
1614                            }
1615                    }
1616    
1617                    javadocsXmlRootElement.add(javaClassDocument.getRootElement());
1618            }
1619    
1620            private void _updateJavaFromDocument(
1621                            String fileName, String originalContent, String javadocLessContent,
1622                            Document document)
1623                    throws Exception {
1624    
1625                    String[] lines = StringUtil.splitLines(javadocLessContent);
1626    
1627                    JavaClass javaClass = _getJavaClass(
1628                            fileName, new UnsyncStringReader(javadocLessContent));
1629    
1630                    _updateLanguageProperties(document, javaClass.getName());
1631    
1632                    List<Tuple> ancestorJavaClassTuples = new ArrayList<Tuple>();
1633    
1634                    ancestorJavaClassTuples = _addAncestorJavaClassTuples(
1635                            javaClass, ancestorJavaClassTuples);
1636    
1637                    Element rootElement = document.getRootElement();
1638    
1639                    Map<Integer, String> commentsMap = new TreeMap<Integer, String>();
1640    
1641                    commentsMap.put(
1642                            _getJavaClassLineNumber(javaClass),
1643                            _getJavaClassComment(rootElement, javaClass));
1644    
1645                    Map<String, Element> methodElementsMap = new HashMap<String, Element>();
1646    
1647                    List<Element> methodElements = rootElement.elements("method");
1648    
1649                    for (Element methodElement : methodElements) {
1650                            String methodKey = _getMethodKey(methodElement);
1651    
1652                            methodElementsMap.put(methodKey, methodElement);
1653                    }
1654    
1655                    JavaMethod[] javaMethods = javaClass.getMethods();
1656    
1657                    for (JavaMethod javaMethod : javaMethods) {
1658                            if (commentsMap.containsKey(javaMethod.getLineNumber())) {
1659                                    continue;
1660                            }
1661    
1662                            String javaMethodComment = _getJavaMethodComment(
1663                                    lines, methodElementsMap, javaMethod);
1664    
1665                            // Handle override tag insertion
1666    
1667                            if (!_hasAnnotation(javaMethod, "Override")) {
1668                                    if (_isOverrideMethod(
1669                                                    javaClass, javaMethod, ancestorJavaClassTuples)) {
1670    
1671                                            String overrideLine =
1672                                                    _getIndent(lines, javaMethod) + "@Override\n";
1673    
1674                                            if (Validator.isNotNull(javaMethodComment)) {
1675                                                    javaMethodComment = javaMethodComment + overrideLine;
1676                                            }
1677                                            else {
1678                                                    javaMethodComment = overrideLine;
1679                                            }
1680                                    }
1681                            }
1682    
1683                            commentsMap.put(javaMethod.getLineNumber(), javaMethodComment);
1684                    }
1685    
1686                    Map<String, Element> fieldElementsMap = new HashMap<String, Element>();
1687    
1688                    List<Element> fieldElements = rootElement.elements("field");
1689    
1690                    for (Element fieldElement : fieldElements) {
1691                            String fieldKey = _getFieldKey(fieldElement);
1692    
1693                            fieldElementsMap.put(fieldKey, fieldElement);
1694                    }
1695    
1696                    JavaField[] javaFields = javaClass.getFields();
1697    
1698                    for (JavaField javaField : javaFields) {
1699                            if (commentsMap.containsKey(javaField.getLineNumber())) {
1700                                    continue;
1701                            }
1702    
1703                            commentsMap.put(
1704                                    javaField.getLineNumber(),
1705                                    _getJavaFieldComment(lines, fieldElementsMap, javaField));
1706                    }
1707    
1708                    StringBundler sb = new StringBundler(javadocLessContent.length());
1709    
1710                    for (int lineNumber = 1; lineNumber <= lines.length; lineNumber++) {
1711                            String line = lines[lineNumber - 1];
1712    
1713                            String comments = commentsMap.get(lineNumber);
1714    
1715                            if (comments != null) {
1716                                    sb.append(comments);
1717                            }
1718    
1719                            sb.append(line);
1720                            sb.append("\n");
1721                    }
1722    
1723                    String formattedContent = sb.toString().trim();
1724    
1725                    if (!originalContent.equals(formattedContent)) {
1726                            File file = new File(_inputDir + fileName);
1727    
1728                            _fileUtil.write(file, formattedContent.getBytes(StringPool.UTF8));
1729    
1730                            System.out.println("Writing " + file);
1731                    }
1732            }
1733    
1734            private void _updateLanguageProperties(Document document, String className)
1735                    throws IOException {
1736    
1737                    if (_languageProperties == null) {
1738                            return;
1739                    }
1740    
1741                    int index = className.indexOf("ServiceImpl");
1742    
1743                    if (index <= 0) {
1744                            return;
1745                    }
1746    
1747                    StringBundler sb = new StringBundler();
1748    
1749                    sb.append(Character.toLowerCase(className.charAt(0)));
1750    
1751                    for (int i = 1; i < index; i++) {
1752                            char c = className.charAt(i);
1753    
1754                            if (Character.isUpperCase(c)) {
1755                                    sb.append(CharPool.DASH);
1756                                    sb.append(Character.toLowerCase(c));
1757                            }
1758                            else {
1759                                    sb.append(c);
1760                            }
1761                    }
1762    
1763                    sb.append("-service-help");
1764    
1765                    String key = sb.toString();
1766    
1767                    String value = _languageProperties.getProperty(key);
1768    
1769                    if (value == null) {
1770                            return;
1771                    }
1772    
1773                    Element rootElement = document.getRootElement();
1774    
1775                    String comment = rootElement.elementText("comment");
1776    
1777                    if ((comment == null) || value.equals(comment)) {
1778                            return;
1779                    }
1780    
1781                    index = comment.indexOf("\n\n");
1782    
1783                    if (index != -1) {
1784                            value = comment.substring(0, index);
1785                    }
1786                    else {
1787                            value = comment;
1788                    }
1789    
1790                    _updateLanguageProperties(key, value);
1791            }
1792    
1793            private void _updateLanguageProperties(String key, String value)
1794                    throws IOException {
1795    
1796                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
1797                            new FileReader(_languagePropertiesFile));
1798    
1799                    StringBundler sb = new StringBundler();
1800    
1801                    boolean begin = false;
1802                    boolean firstLine = true;
1803                    String linePrefix = key + "=";
1804    
1805                    String line = null;
1806    
1807                    while ((line = unsyncBufferedReader.readLine()) != null) {
1808                            if (line.equals(StringPool.BLANK)) {
1809                                    begin = !begin;
1810                            }
1811    
1812                            if (firstLine) {
1813                                    firstLine = false;
1814                            }
1815                            else {
1816                                    sb.append(StringPool.NEW_LINE);
1817                            }
1818    
1819                            if (line.startsWith(linePrefix)) {
1820                                    sb.append(linePrefix + value);
1821                            }
1822                            else {
1823                                    sb.append(line);
1824                            }
1825                    }
1826    
1827                    unsyncBufferedReader.close();
1828    
1829                    Writer writer = new OutputStreamWriter(
1830                            new FileOutputStream(_languagePropertiesFile, false),
1831                            StringPool.UTF8);
1832    
1833                    writer.write(sb.toString());
1834    
1835                    writer.close();
1836    
1837                    System.out.println(
1838                            "Updating " + _languagePropertiesFile + " key " + key);
1839            }
1840    
1841            private String _wrapText(String text, int indentLength, String exclude) {
1842                    StringBuffer sb = new StringBuffer();
1843    
1844                    StringBundler regexSB = new StringBundler("(?<=^|</");
1845    
1846                    regexSB.append(exclude);
1847                    regexSB.append(">).+?(?=$|<");
1848                    regexSB.append(exclude);
1849                    regexSB.append(">)");
1850    
1851                    Pattern pattern = Pattern.compile(regexSB.toString(), Pattern.DOTALL);
1852    
1853                    Matcher matcher = pattern.matcher(text);
1854    
1855                    while (matcher.find()) {
1856                            String wrapped = _formatInlines(matcher.group());
1857    
1858                            wrapped = StringUtil.wrap(wrapped, 80 - indentLength, "\n");
1859    
1860                            matcher.appendReplacement(sb, wrapped);
1861                    }
1862    
1863                    matcher.appendTail(sb);
1864    
1865                    return sb.toString();
1866            }
1867    
1868            private String _wrapText(String text, String indent) {
1869                    int indentLength = _getIndentLength(indent);
1870    
1871                    if (text.contains("<pre>")) {
1872                            text = _wrapText(text, indentLength, "pre");
1873                    }
1874                    else if (text.contains("<table>")) {
1875                            text = _wrapText(text, indentLength, "table");
1876                    }
1877                    else {
1878                            text = _formatInlines(text);
1879                            text = StringUtil.wrap(text, 80 - indentLength, "\n");
1880                    }
1881    
1882                    text = text.replaceAll("(?m)^", indent);
1883                    text = text.replaceAll("(?m) +$", StringPool.BLANK);
1884    
1885                    return text;
1886            }
1887    
1888            private static final double _LOWEST_SUPPORTED_JAVA_VERSION = 1.6;
1889    
1890            private static FileImpl _fileUtil = FileImpl.getInstance();
1891            private static SAXReaderImpl _saxReaderUtil = SAXReaderImpl.getInstance();
1892    
1893            private boolean _initializeMissingJavadocs;
1894            private String _inputDir;
1895            private Map<String, Tuple> _javadocxXmlTuples =
1896                    new HashMap<String, Tuple>();
1897            private Properties _languageProperties;
1898            private File _languagePropertiesFile;
1899            private String _outputFilePrefix;
1900            private boolean _updateJavadocs;
1901    
1902    }