@Override
  public List<Node> selectNodes(
      String xPathExpression, String comparisonXPathExpression, boolean removeDuplicates) {

    return SAXReaderImpl.toNewNodes(
        _node.selectNodes(xPathExpression, comparisonXPathExpression, removeDuplicates));
  }
  @Override
  public Object selectObject(String xPathExpression) {
    Object obj = _node.selectObject(xPathExpression);

    if (obj == null) {
      return null;
    } else if (obj instanceof List<?>) {
      return SAXReaderImpl.toNewNodes((List<org.dom4j.Node>) obj);
    } else {
      return obj;
    }
  }
/**
 * @author Brian Wing Shun Chan
 * @author Igor Spasic
 * @author Wesley Gong
 * @author Hugo Huijser
 */
public abstract class BaseSourceProcessor implements SourceProcessor {

  public BaseSourceProcessor() {
    portalSource = _isPortalSource();

    try {
      _properties = _getProperties();
    } catch (Exception e) {
      ReflectionUtil.throwException(e);
    }
  }

  @Override
  public void format(boolean useProperties, boolean printErrors, boolean autoFix) throws Exception {

    _init(useProperties, printErrors, autoFix);

    format();

    sourceFormatterHelper.close();
  }

  @Override
  public String format(String fileName, boolean useProperties, boolean printErrors, boolean autoFix)
      throws Exception {

    try {
      _init(useProperties, printErrors, autoFix);

      return format(fileName);
    } finally {
      sourceFormatterHelper.close();
    }
  }

  @Override
  public List<String> getErrorMessages() {
    List<String> errorMessages = new ArrayList<String>();

    for (Map.Entry<String, List<String>> entry : _errorMessagesMap.entrySet()) {

      errorMessages.addAll(entry.getValue());
    }

    return errorMessages;
  }

  @Override
  public SourceMismatchException getFirstSourceMismatchException() {
    return _firstSourceMismatchException;
  }

  protected static boolean isExcluded(List<String> exclusions, String absolutePath) {

    return isExcluded(exclusions, absolutePath, -1);
  }

  protected static boolean isExcluded(List<String> exclusions, String absolutePath, int lineCount) {

    return isExcluded(exclusions, absolutePath, lineCount, null);
  }

  protected static boolean isExcluded(
      List<String> exclusions, String absolutePath, int lineCount, String javaTermName) {

    if (ListUtil.isEmpty(exclusions)) {
      return false;
    }

    String absolutePathWithJavaTermName = null;

    if (Validator.isNotNull(javaTermName)) {
      absolutePathWithJavaTermName = absolutePath + StringPool.AT + javaTermName;
    }

    String absolutePathWithLineCount = null;

    if (lineCount > 0) {
      absolutePathWithLineCount = absolutePath + StringPool.AT + lineCount;
    }

    for (String exclusion : exclusions) {
      if (absolutePath.endsWith(exclusion)
          || ((absolutePathWithJavaTermName != null)
              && absolutePathWithJavaTermName.endsWith(exclusion))
          || ((absolutePathWithLineCount != null)
              && absolutePathWithLineCount.endsWith(exclusion))) {

        return true;
      }
    }

    return false;
  }

  protected static void processErrorMessage(String fileName, String message) {
    List<String> errorMessages = _errorMessagesMap.get(fileName);

    if (errorMessages == null) {
      errorMessages = new ArrayList<String>();
    }

    errorMessages.add(message);

    _errorMessagesMap.put(fileName, errorMessages);
  }

  protected void checkEmptyCollection(String line, String fileName, int lineCount) {

    // LPS-46028

    Matcher matcher = emptyCollectionPattern.matcher(line);

    if (matcher.find()) {
      String collectionType = TextFormatter.format(matcher.group(1), TextFormatter.J);

      processErrorMessage(
          fileName, "Use Collections.empty" + collectionType + "(): " + fileName + " " + lineCount);
    }
  }

  protected void checkIfClauseParentheses(String ifClause, String fileName, int lineCount) {

    int quoteCount = StringUtil.count(ifClause, StringPool.QUOTE);

    if ((quoteCount % 2) == 1) {
      return;
    }

    ifClause = stripQuotes(ifClause, CharPool.QUOTE);

    ifClause = stripQuotes(ifClause, CharPool.APOSTROPHE);

    if (ifClause.contains(StringPool.DOUBLE_SLASH)
        || ifClause.contains("/*")
        || ifClause.contains("*/")) {

      return;
    }

    if (hasRedundantParentheses(ifClause, "||", "&&")
        || hasRedundantParentheses(ifClause, "&&", "||")) {

      processErrorMessage(fileName, "redundant parentheses: " + fileName + " " + lineCount);
    }

    ifClause = stripRedundantParentheses(ifClause);

    int level = 0;
    int max = StringUtil.count(ifClause, StringPool.OPEN_PARENTHESIS);
    int previousParenthesisPos = -1;

    int[] levels = new int[max];

    for (int i = 0; i < ifClause.length(); i++) {
      char c = ifClause.charAt(i);

      if ((c == CharPool.OPEN_PARENTHESIS) || (c == CharPool.CLOSE_PARENTHESIS)) {

        if (previousParenthesisPos != -1) {
          String s = ifClause.substring(previousParenthesisPos + 1, i);

          if (hasMissingParentheses(s)) {
            processErrorMessage(fileName, "missing parentheses: " + fileName + " " + lineCount);
          }
        }

        previousParenthesisPos = i;

        if (c == CharPool.OPEN_PARENTHESIS) {
          levels[level] = i;

          level += 1;
        } else {
          int posOpenParenthesis = levels[level - 1];

          if (level > 1) {
            char nextChar = ifClause.charAt(i + 1);
            char previousChar = ifClause.charAt(posOpenParenthesis - 1);

            if (!Character.isLetterOrDigit(nextChar)
                && (nextChar != CharPool.PERIOD)
                && !Character.isLetterOrDigit(previousChar)) {

              String s = ifClause.substring(posOpenParenthesis + 1, i);

              if (hasRedundantParentheses(s)) {
                processErrorMessage(
                    fileName, "redundant parentheses: " + fileName + " " + lineCount);
              }
            }

            if ((previousChar == CharPool.OPEN_PARENTHESIS)
                && (nextChar == CharPool.CLOSE_PARENTHESIS)) {

              processErrorMessage(fileName, "redundant parentheses: " + fileName + " " + lineCount);
            }
          }

          level -= 1;
        }
      }
    }
  }

  protected void checkInefficientStringMethods(
      String line, String fileName, String absolutePath, int lineCount) {

    if (isRunsOutsidePortal(absolutePath)) {
      return;
    }

    String methodName = "toLowerCase";

    int pos = line.indexOf(".toLowerCase()");

    if (pos == -1) {
      methodName = "toUpperCase";

      pos = line.indexOf(".toUpperCase()");
    }

    if ((pos == -1) && !line.contains("StringUtil.equalsIgnoreCase(")) {
      methodName = "equalsIgnoreCase";

      pos = line.indexOf(".equalsIgnoreCase(");
    }

    if (pos != -1) {
      processErrorMessage(
          fileName, "Use StringUtil." + methodName + ": " + fileName + " " + lineCount);
    }
  }

  protected void checkLanguageKeys(String fileName, String content, Pattern pattern)
      throws IOException {

    String fileExtension = fileUtil.getExtension(fileName);

    if (!portalSource || fileExtension.equals("vm")) {
      return;
    }

    if (_portalLanguageProperties == null) {
      _portalLanguageProperties = new Properties();

      ClassLoader classLoader = BaseSourceProcessor.class.getClassLoader();

      InputStream inputStream = classLoader.getResourceAsStream("content/Language.properties");

      _portalLanguageProperties.load(inputStream);
    }

    Matcher matcher = pattern.matcher(content);

    while (matcher.find()) {
      String[] languageKeys = getLanguageKeys(matcher);

      for (String languageKey : languageKeys) {
        if (Validator.isNumber(languageKey)
            || languageKey.endsWith(StringPool.DASH)
            || languageKey.endsWith(StringPool.OPEN_BRACKET)
            || languageKey.endsWith(StringPool.PERIOD)
            || languageKey.endsWith(StringPool.UNDERLINE)
            || languageKey.startsWith(StringPool.DASH)
            || languageKey.startsWith(StringPool.OPEN_BRACKET)
            || languageKey.startsWith(StringPool.OPEN_CURLY_BRACE)
            || languageKey.startsWith(StringPool.PERIOD)
            || languageKey.startsWith(StringPool.UNDERLINE)
            || _portalLanguageProperties.containsKey(languageKey)) {

          continue;
        }

        Properties languageProperties = getLanguageProperties(fileName);

        if ((languageProperties == null) || !languageProperties.containsKey(languageKey)) {

          processErrorMessage(
              fileName, "missing language key: " + languageKey + StringPool.SPACE + fileName);
        }
      }
    }
  }

  protected void checkStringBundler(String line, String fileName, int lineCount) {

    if ((!line.startsWith("sb.append(") && !line.contains("SB.append(")) || !line.endsWith(");")) {

      return;
    }

    int pos = line.indexOf(".append(");

    line = line.substring(pos + 8, line.length() - 2);

    line = stripQuotes(line, CharPool.QUOTE);

    if (!line.contains(" + ")) {
      return;
    }

    String[] lineParts = StringUtil.split(line, " + ");

    for (String linePart : lineParts) {
      int closeParenthesesCount = StringUtil.count(linePart, StringPool.CLOSE_PARENTHESIS);
      int openParenthesesCount = StringUtil.count(linePart, StringPool.OPEN_PARENTHESIS);

      if (closeParenthesesCount != openParenthesesCount) {
        return;
      }

      if (Validator.isNumber(linePart)) {
        return;
      }
    }

    processErrorMessage(fileName, "plus: " + fileName + " " + lineCount);
  }

  protected abstract String doFormat(
      File file, String fileName, String absolutePath, String content) throws Exception;

  protected String fixCompatClassImports(String absolutePath, String content) throws IOException {

    if (portalSource
        || !_usePortalCompatImport
        || absolutePath.contains("/ext-")
        || absolutePath.contains("/portal-compat-shared/")) {

      return content;
    }

    Map<String, String> compatClassNamesMap = getCompatClassNamesMap();

    String newContent = content;

    for (Map.Entry<String, String> entry : compatClassNamesMap.entrySet()) {
      String compatClassName = entry.getKey();
      String extendedClassName = entry.getValue();

      Pattern pattern = Pattern.compile(extendedClassName + "\\W");

      while (true) {
        Matcher matcher = pattern.matcher(newContent);

        if (!matcher.find()) {
          break;
        }

        newContent =
            newContent.substring(0, matcher.start())
                + compatClassName
                + newContent.substring(matcher.end() - 1);
      }
    }

    return newContent;
  }

  protected String fixCopyright(String content, String absolutePath, String fileName)
      throws IOException {

    if (_copyright == null) {
      _copyright = getContent("copyright.txt", 4);
    }

    String copyright = _copyright;

    if (fileName.endsWith(".vm") || Validator.isNull(copyright)) {
      return content;
    }

    if (_oldCopyright == null) {
      _oldCopyright = getContent("old-copyright.txt", 4);
    }

    if (Validator.isNotNull(_oldCopyright) && content.contains(_oldCopyright)) {

      content = StringUtil.replace(content, _oldCopyright, copyright);

      processErrorMessage(fileName, "old (c): " + fileName);
    }

    if (!content.contains(copyright)) {
      String customCopyright = getCustomCopyright(absolutePath);

      if (Validator.isNotNull(customCopyright)) {
        copyright = customCopyright;
      }

      if (!content.contains(copyright)) {
        processErrorMessage(fileName, "(c): " + fileName);
      }
    }

    if (fileName.endsWith(".jsp") || fileName.endsWith(".jspf")) {
      content =
          StringUtil.replace(content, "<%\n" + copyright + "\n%>", "<%--\n" + copyright + "\n--%>");
    }

    int x = content.indexOf("* Copyright (c) 2000-");

    if (x == -1) {
      return content;
    }

    int y = content.indexOf("Liferay", x);

    String contentCopyrightYear = content.substring(x, y);

    x = copyright.indexOf("* Copyright (c) 2000-");

    if (x == -1) {
      return content;
    }

    y = copyright.indexOf("Liferay", x);

    String copyrightYear = copyright.substring(x, y);

    return StringUtil.replace(content, contentCopyrightYear, copyrightYear);
  }

  protected String fixIncorrectParameterTypeForLanguageUtil(
      String content, boolean autoFix, String fileName) {

    if (portalSource) {
      return content;
    }

    String expectedParameter = getProperty("languageutil.expected.parameter");
    String incorrectParameter = getProperty("languageutil.incorrect.parameter");

    if (!content.contains("LanguageUtil.format(" + incorrectParameter + ", ")
        && !content.contains("LanguageUtil.get(" + incorrectParameter + ", ")) {

      return content;
    }

    if (autoFix) {
      content =
          StringUtil.replace(
              content,
              new String[] {
                "LanguageUtil.format(" + incorrectParameter + ", ",
                "LanguageUtil.get(" + incorrectParameter + ", "
              },
              new String[] {
                "LanguageUtil.format(" + expectedParameter + ", ",
                "LanguageUtil.get(" + expectedParameter + ", "
              });
    } else {
      processErrorMessage(
          fileName,
          "(Unicode)LanguageUtil.format/get methods require "
              + expectedParameter
              + " parameter instead of "
              + incorrectParameter
              + " "
              + fileName);
    }

    return content;
  }

  protected String fixSessionKey(String fileName, String content, Pattern pattern) {

    Matcher matcher = pattern.matcher(content);

    if (!matcher.find()) {
      return content;
    }

    String newContent = content;

    do {
      String match = matcher.group();

      String s = null;

      if (pattern.equals(sessionKeyPattern)) {
        s = StringPool.COMMA;
      } else if (pattern.equals(taglibSessionKeyPattern)) {
        s = "key=";
      }

      int x = match.indexOf(s);

      if (x == -1) {
        continue;
      }

      x = x + s.length();

      String substring = match.substring(x).trim();

      String quote = StringPool.BLANK;

      if (substring.startsWith(StringPool.APOSTROPHE)) {
        quote = StringPool.APOSTROPHE;
      } else if (substring.startsWith(StringPool.QUOTE)) {
        quote = StringPool.QUOTE;
      } else {
        continue;
      }

      int y = match.indexOf(quote, x);
      int z = match.indexOf(quote, y + 1);

      if ((y == -1) || (z == -1)) {
        continue;
      }

      String prefix = match.substring(0, y + 1);
      String suffix = match.substring(z);
      String oldKey = match.substring(y + 1, z);

      boolean alphaNumericKey = true;

      for (char c : oldKey.toCharArray()) {
        if (!Validator.isChar(c)
            && !Validator.isDigit(c)
            && (c != CharPool.DASH)
            && (c != CharPool.UNDERLINE)) {

          alphaNumericKey = false;
        }
      }

      if (!alphaNumericKey) {
        continue;
      }

      String newKey = TextFormatter.format(oldKey, TextFormatter.O);

      newKey = TextFormatter.format(newKey, TextFormatter.M);

      if (newKey.equals(oldKey)) {
        continue;
      }

      String oldSub = prefix.concat(oldKey).concat(suffix);
      String newSub = prefix.concat(newKey).concat(suffix);

      newContent = StringUtil.replaceFirst(newContent, oldSub, newSub);
    } while (matcher.find());

    return newContent;
  }

  protected abstract void format() throws Exception;

  protected String format(File file, String fileName, String absolutePath, String content)
      throws Exception {

    _errorMessagesMap.remove(fileName);

    String newContent = doFormat(file, fileName, absolutePath, content);

    newContent = StringUtil.replace(newContent, StringPool.RETURN, StringPool.BLANK);

    if (content.equals(newContent)) {
      return content;
    }

    return format(file, fileName, absolutePath, newContent);
  }

  protected String format(String fileName) throws Exception {
    File file = new File(BASEDIR + fileName);

    fileName = StringUtil.replace(fileName, StringPool.BACK_SLASH, StringPool.SLASH);

    String absolutePath = getAbsolutePath(file);

    String content = fileUtil.read(file);

    String newContent = format(file, fileName, absolutePath, content);

    processFormattedFile(file, fileName, content, newContent);

    return newContent;
  }

  protected String formatJavaTerms(
      String fileName,
      String absolutePath,
      String content,
      String javaClassContent,
      List<String> javaTermSortExclusions,
      List<String> testAnnotationsExclusions)
      throws Exception {

    JavaClass javaClass = new JavaClass(fileName, absolutePath, javaClassContent, StringPool.TAB);

    String newJavaClassContent =
        javaClass.formatJavaTerms(javaTermSortExclusions, testAnnotationsExclusions);

    if (!javaClassContent.equals(newJavaClassContent)) {
      return StringUtil.replaceFirst(content, javaClassContent, newJavaClassContent);
    }

    return content;
  }

  protected String getAbsolutePath(File file) {
    String absolutePath = fileUtil.getAbsolutePath(file);

    return StringUtil.replace(absolutePath, "/./", StringPool.SLASH);
  }

  protected Map<String, String> getCompatClassNamesMap() throws IOException {
    if (_compatClassNamesMap != null) {
      return _compatClassNamesMap;
    }

    Map<String, String> compatClassNamesMap = new HashMap<String, String>();

    String[] includes =
        new String[] {"**\\portal-compat-shared\\src\\com\\liferay\\compat\\**\\*.java"};

    String basedir = BASEDIR;

    List<String> fileNames = new ArrayList<String>();

    for (int i = 0; i < 3; i++) {
      fileNames = getFileNames(basedir, new String[0], includes);

      if (!fileNames.isEmpty()) {
        break;
      }

      basedir = "../" + basedir;
    }

    for (String fileName : fileNames) {
      File file = new File(basedir + fileName);

      String content = fileUtil.read(file);

      fileName = StringUtil.replace(fileName, StringPool.BACK_SLASH, StringPool.SLASH);

      fileName = StringUtil.replace(fileName, StringPool.SLASH, StringPool.PERIOD);

      int pos = fileName.indexOf("com.");

      String compatClassName = fileName.substring(pos);

      compatClassName = compatClassName.substring(0, compatClassName.length() - 5);

      String extendedClassName = StringUtil.replace(compatClassName, "compat.", StringPool.BLANK);

      if (content.contains("extends " + extendedClassName)) {
        compatClassNamesMap.put(compatClassName, extendedClassName);
      }
    }

    _compatClassNamesMap = compatClassNamesMap;

    return _compatClassNamesMap;
  }

  protected String getContent(String fileName, int level) throws IOException {
    File file = getFile(fileName, level);

    if (file != null) {
      String content = fileUtil.read(file);

      if (Validator.isNotNull(content)) {
        return content;
      }
    }

    return StringPool.BLANK;
  }

  protected String getCustomCopyright(String absolutePath) throws IOException {

    for (int x = absolutePath.length(); ; ) {
      x = absolutePath.lastIndexOf(StringPool.SLASH, x);

      if (x == -1) {
        break;
      }

      String copyright = fileUtil.read(absolutePath.substring(0, x + 1) + "copyright.txt");

      if (Validator.isNotNull(copyright)) {
        return copyright;
      }

      x = x - 1;
    }

    return null;
  }

  protected File getFile(String fileName, int level) {
    for (int i = 0; i < level; i++) {
      if (fileUtil.exists(fileName)) {
        return new File(fileName);
      }

      fileName = "../" + fileName;
    }

    return null;
  }

  protected List<String> getFileNames(String basedir, String[] excludes, String[] includes) {

    DirectoryScanner directoryScanner = new DirectoryScanner();

    directoryScanner.setBasedir(basedir);

    if (_excludes != null) {
      excludes = ArrayUtil.append(excludes, _excludes);
    }

    directoryScanner.setExcludes(excludes);

    directoryScanner.setIncludes(includes);

    return sourceFormatterHelper.scanForFiles(directoryScanner);
  }

  protected List<String> getFileNames(String[] excludes, String[] includes) {
    return getFileNames(BASEDIR, excludes, includes);
  }

  protected String[] getLanguageKeys(Matcher matcher) {
    int groupCount = matcher.groupCount();

    if (groupCount == 1) {
      String languageKey = matcher.group(1);

      if (Validator.isNotNull(languageKey)) {
        return new String[] {languageKey};
      }
    } else if (groupCount == 2) {
      String languageKey = matcher.group(2);

      languageKey = TextFormatter.format(languageKey, TextFormatter.P);

      return new String[] {languageKey};
    }

    StringBundler sb = new StringBundler();

    String match = matcher.group();

    int count = 0;

    for (int i = 0; i < match.length(); i++) {
      char c = match.charAt(i);

      switch (c) {
        case CharPool.CLOSE_PARENTHESIS:
          if (count <= 1) {
            return new String[0];
          }

          count--;

          break;

        case CharPool.OPEN_PARENTHESIS:
          count++;

          break;

        case CharPool.QUOTE:
          if (count > 1) {
            break;
          }

          while (i < match.length()) {
            i++;

            if (match.charAt(i) == CharPool.QUOTE) {
              String languageKey = sb.toString();

              if (match.startsWith("names")) {
                return StringUtil.split(languageKey);
              } else {
                return new String[] {languageKey};
              }
            }

            sb.append(match.charAt(i));
          }
      }
    }

    return new String[0];
  }

  protected Properties getLanguageProperties(String fileName) {
    StringBundler sb = new StringBundler(4);

    int pos = fileName.indexOf("/docroot/");

    sb.append(BASEDIR);

    if (pos != -1) {
      sb.append(fileName.substring(0, pos + 9));
      sb.append("WEB-INF/src/");
    } else {
      pos = fileName.indexOf("/src/");

      if (pos == -1) {
        return null;
      }

      sb.append(fileName.substring(0, pos + 5));
    }

    sb.append("content/Language.properties");

    try {
      Properties properties = new Properties();

      InputStream inputStream = new FileInputStream(sb.toString());

      properties.load(inputStream);

      return properties;
    } catch (Exception e) {
    }

    return null;
  }

  protected String getMainReleaseVersion() {
    if (_mainReleaseVersion != null) {
      return _mainReleaseVersion;
    }

    String releaseVersion = ReleaseInfo.getVersion();

    int pos = releaseVersion.lastIndexOf(StringPool.PERIOD);

    _mainReleaseVersion = releaseVersion.substring(0, pos) + ".0";

    return _mainReleaseVersion;
  }

  protected String getProperty(String key) {
    return _properties.getProperty(key);
  }

  protected List<String> getPropertyList(String key) {
    return ListUtil.fromString(GetterUtil.getString(getProperty(key)), StringPool.COMMA);
  }

  protected boolean hasMissingParentheses(String s) {
    if (Validator.isNull(s)) {
      return false;
    }

    boolean containsAndOperator = s.contains("&&");
    boolean containsOrOperator = s.contains("||");

    if (containsAndOperator && containsOrOperator) {
      return true;
    }

    boolean containsCompareOperator =
        (s.contains(" == ")
            || s.contains(" != ")
            || s.contains(" < ")
            || s.contains(" > ")
            || s.contains(" =< ")
            || s.contains(" => ")
            || s.contains(" <= ")
            || s.contains(" >= "));
    boolean containsMathOperator =
        (s.contains(" = ")
            || s.contains(" - ")
            || s.contains(" + ")
            || s.contains(" & ")
            || s.contains(" % ")
            || s.contains(" * ")
            || s.contains(" / "));

    if (containsCompareOperator
        && (containsAndOperator
            || containsOrOperator
            || (containsMathOperator && !s.contains(StringPool.OPEN_BRACKET)))) {

      return true;
    }

    return false;
  }

  protected boolean hasRedundantParentheses(String s) {
    if (!s.contains("&&") && !s.contains("||")) {
      for (int x = 0; ; ) {
        x = s.indexOf(StringPool.CLOSE_PARENTHESIS);

        if (x == -1) {
          break;
        }

        int y = s.substring(0, x).lastIndexOf(StringPool.OPEN_PARENTHESIS);

        if (y == -1) {
          break;
        }

        s = s.substring(0, y) + s.substring(x + 1);
      }
    }

    if (Validator.isNotNull(s) && !s.contains(StringPool.SPACE)) {
      return true;
    } else {
      return false;
    }
  }

  protected boolean hasRedundantParentheses(String s, String operator1, String operator2) {

    String[] parts = StringUtil.split(s, operator1);

    if (parts.length < 3) {
      return false;
    }

    for (int i = 1; i < (parts.length - 1); i++) {
      String part = parts[i];

      if (part.contains(operator2) || part.contains("!(")) {
        continue;
      }

      int closeParenthesesCount = StringUtil.count(part, StringPool.CLOSE_PARENTHESIS);
      int openParenthesesCount = StringUtil.count(part, StringPool.OPEN_PARENTHESIS);

      if (Math.abs(closeParenthesesCount - openParenthesesCount) == 1) {
        return true;
      }
    }

    return false;
  }

  protected boolean isAttributName(String attributeName) {
    if (Validator.isNull(attributeName)) {
      return false;
    }

    Matcher matcher = attributeNamePattern.matcher(attributeName);

    return matcher.matches();
  }

  protected boolean isRunsOutsidePortal(String absolutePath) {
    if (_runOutsidePortalExclusions == null) {
      _runOutsidePortalExclusions = getPropertyList("run.outside.portal.excludes");
    }

    for (String runOutsidePortalExclusions : _runOutsidePortalExclusions) {
      if (absolutePath.contains(runOutsidePortalExclusions)) {
        return true;
      }
    }

    return false;
  }

  protected void processFormattedFile(File file, String fileName, String content, String newContent)
      throws IOException {

    if (_printErrors) {
      List<String> errorMessages = _errorMessagesMap.get(fileName);

      if (errorMessages != null) {
        for (String errorMessage : errorMessages) {
          sourceFormatterHelper.printError(fileName, errorMessage);
        }
      }
    }

    if (content.equals(newContent)) {
      return;
    }

    if (_autoFix) {
      fileUtil.write(file, newContent);
    } else if (_firstSourceMismatchException == null) {
      _firstSourceMismatchException = new SourceMismatchException(fileName, content, newContent);
    }

    if (_printErrors) {
      sourceFormatterHelper.printError(fileName, file);
    }
  }

  protected String replacePrimitiveWrapperInstantiation(
      String fileName, String line, int lineCount) {

    if (true) {
      return line;
    }

    String newLine =
        StringUtil.replace(
            line,
            new String[] {
              "new Boolean(",
              "new Byte(",
              "new Character(",
              "new Integer(",
              "new Long(",
              "new Short("
            },
            new String[] {
              "Boolean.valueOf(", "Byte.valueOf(", "Character.valueOf(",
              "Integer.valueOf(", "Long.valueOf(", "Short.valueOf("
            });

    if (!line.equals(newLine)) {
      processErrorMessage(fileName, "> new Primitive(: " + fileName + " " + lineCount);
    }

    return newLine;
  }

  protected String sortAttributes(
      String fileName, String line, int lineCount, boolean allowApostropheDelimeter) {

    String s = line;

    int x = s.indexOf(StringPool.SPACE);

    if (x == -1) {
      return line;
    }

    s = s.substring(x + 1);

    String previousAttribute = null;
    String previousAttributeAndValue = null;

    boolean wrongOrder = false;

    for (x = 0; ; ) {
      x = s.indexOf(StringPool.EQUAL);

      if ((x == -1) || (s.length() <= (x + 1))) {
        return line;
      }

      String attribute = s.substring(0, x);

      if (!isAttributName(attribute)) {
        return line;
      }

      if (Validator.isNotNull(previousAttribute) && (previousAttribute.compareTo(attribute) > 0)) {

        wrongOrder = true;
      }

      s = s.substring(x + 1);

      char delimeter = s.charAt(0);

      if ((delimeter != CharPool.APOSTROPHE) && (delimeter != CharPool.QUOTE)) {

        if (delimeter != CharPool.AMPERSAND) {
          processErrorMessage(fileName, "delimeter: " + fileName + " " + lineCount);
        }

        return line;
      }

      s = s.substring(1);

      String value = null;

      int y = -1;

      while (true) {
        y = s.indexOf(delimeter, y + 1);

        if ((y == -1) || (s.length() <= (y + 1))) {
          return line;
        }

        value = s.substring(0, y);

        if (value.startsWith("<%")) {
          int endJavaCodeSignCount = StringUtil.count(value, "%>");
          int startJavaCodeSignCount = StringUtil.count(value, "<%");

          if (endJavaCodeSignCount == startJavaCodeSignCount) {
            break;
          }
        } else {
          int greaterThanCount = StringUtil.count(value, StringPool.GREATER_THAN);
          int lessThanCount = StringUtil.count(value, StringPool.LESS_THAN);

          if (greaterThanCount == lessThanCount) {
            break;
          }
        }
      }

      if (delimeter == CharPool.APOSTROPHE) {
        if (!value.contains(StringPool.QUOTE)) {
          line =
              StringUtil.replace(
                  line,
                  StringPool.APOSTROPHE + value + StringPool.APOSTROPHE,
                  StringPool.QUOTE + value + StringPool.QUOTE);

          return sortAttributes(fileName, line, lineCount, allowApostropheDelimeter);
        } else if (!allowApostropheDelimeter) {
          String newValue = StringUtil.replace(value, StringPool.QUOTE, "&quot;");

          line =
              StringUtil.replace(
                  line,
                  StringPool.APOSTROPHE + value + StringPool.APOSTROPHE,
                  StringPool.QUOTE + newValue + StringPool.QUOTE);

          return sortAttributes(fileName, line, lineCount, allowApostropheDelimeter);
        }
      }

      StringBundler sb = new StringBundler(5);

      sb.append(attribute);
      sb.append(StringPool.EQUAL);
      sb.append(delimeter);
      sb.append(value);
      sb.append(delimeter);

      String currentAttributeAndValue = sb.toString();

      if (wrongOrder) {
        if ((StringUtil.count(line, currentAttributeAndValue) == 1)
            && (StringUtil.count(line, previousAttributeAndValue) == 1)) {

          line = StringUtil.replaceFirst(line, previousAttributeAndValue, currentAttributeAndValue);

          line = StringUtil.replaceLast(line, currentAttributeAndValue, previousAttributeAndValue);

          return sortAttributes(fileName, line, lineCount, allowApostropheDelimeter);
        }

        return line;
      }

      s = s.substring(y + 1);

      if (s.startsWith(StringPool.GREATER_THAN)) {
        x = s.indexOf(StringPool.SPACE);

        if (x == -1) {
          return line;
        }

        s = s.substring(x + 1);

        previousAttribute = null;
        previousAttributeAndValue = null;
      } else {
        s = StringUtil.trimLeading(s);

        previousAttribute = attribute;
        previousAttributeAndValue = currentAttributeAndValue;
      }
    }
  }

  protected String stripLine(String s, char startDelimeter, char endDelimeter) {

    boolean insideDelimeters = false;
    int level = 0;

    StringBundler sb = new StringBundler();

    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);

      if (insideDelimeters) {
        if (c == endDelimeter) {
          if (level > 0) {
            level -= 1;
          } else {
            if ((c > 1)
                && (s.charAt(i - 1) == CharPool.BACK_SLASH)
                && (s.charAt(i - 2) != CharPool.BACK_SLASH)) {

              continue;
            }

            insideDelimeters = false;
          }
        } else if (c == startDelimeter) {
          level += 1;
        }
      } else if (c == startDelimeter) {
        insideDelimeters = true;
      } else {
        sb.append(c);
      }
    }

    return sb.toString();
  }

  protected String stripQuotes(String s, char delimeter) {
    return stripLine(s, delimeter, delimeter);
  }

  protected String stripRedundantParentheses(String s) {
    for (int x = 0; ; ) {
      x = s.indexOf(StringPool.OPEN_PARENTHESIS, x + 1);
      int y = s.indexOf(StringPool.CLOSE_PARENTHESIS, x);

      if ((x == -1) || (y == -1)) {
        return s;
      }

      String linePart = s.substring(x + 1, y);

      linePart = StringUtil.replace(linePart, StringPool.COMMA, StringPool.BLANK);

      if (Validator.isAlphanumericName(linePart) || Validator.isNull(linePart)) {

        s = s.substring(0, x) + s.substring(y + 1);
      }
    }
  }

  protected String trimContent(String content, boolean allowLeadingSpaces) throws IOException {

    StringBundler sb = new StringBundler();

    UnsyncBufferedReader unsyncBufferedReader =
        new UnsyncBufferedReader(new UnsyncStringReader(content));

    String line = null;

    while ((line = unsyncBufferedReader.readLine()) != null) {
      sb.append(trimLine(line, allowLeadingSpaces));
      sb.append("\n");
    }

    unsyncBufferedReader.close();

    content = sb.toString();

    if (content.endsWith("\n")) {
      content = content.substring(0, content.length() - 1);
    }

    return content;
  }

  protected String trimLine(String line, boolean allowLeadingSpaces) {
    if (line.trim().length() == 0) {
      return StringPool.BLANK;
    }

    line = StringUtil.trimTrailing(line);

    if (allowLeadingSpaces || !line.startsWith(StringPool.SPACE) || line.startsWith(" *")) {

      return line;
    }

    if (!line.startsWith(StringPool.FOUR_SPACES)) {
      while (line.startsWith(StringPool.SPACE)) {
        line = StringUtil.replaceFirst(line, StringPool.SPACE, StringPool.BLANK);
      }
    } else {
      int pos = 0;

      String temp = line;

      while (temp.startsWith(StringPool.FOUR_SPACES)) {
        line = StringUtil.replaceFirst(line, StringPool.FOUR_SPACES, StringPool.TAB);

        pos++;

        temp = line.substring(pos);
      }
    }

    return line;
  }

  protected static final String BASEDIR = "./";

  protected static Pattern attributeNamePattern = Pattern.compile("[a-z]+[-_a-zA-Z0-9]*");
  protected static Pattern emptyCollectionPattern =
      Pattern.compile("Collections\\.EMPTY_(LIST|MAP|SET)");
  protected static FileImpl fileUtil = FileImpl.getInstance();
  protected static Pattern languageKeyPattern =
      Pattern.compile("LanguageUtil.(?:get|format)\\([^;%]+|Liferay.Language.get\\('([^']+)");
  protected static boolean portalSource;
  protected static SAXReaderImpl saxReaderUtil = SAXReaderImpl.getInstance();
  protected static Pattern sessionKeyPattern =
      Pattern.compile(
          "SessionErrors.(?:add|contains|get)\\([^;%&|!]+|"
              .concat("SessionMessages.(?:add|contains|get)\\([^;%&|!]+"),
          Pattern.MULTILINE);
  protected static SourceFormatterHelper sourceFormatterHelper;
  protected static Pattern taglibSessionKeyPattern =
      Pattern.compile("<liferay-ui:error [^>]+>|<liferay-ui:success [^>]+>", Pattern.MULTILINE);

  private String[] _getExcludes() {
    List<String> excludesList =
        ListUtil.fromString(GetterUtil.getString(System.getProperty("source.formatter.excludes")));

    excludesList.addAll(getPropertyList("source.formatter.excludes"));

    String[] includes = new String[] {"**\\source_formatter.ignore"};

    List<String> ignoreFileNames = getFileNames(new String[0], includes);

    for (String ignoreFileName : ignoreFileNames) {
      excludesList.add(ignoreFileName.substring(0, ignoreFileName.length() - 23) + "**");
    }

    return excludesList.toArray(new String[excludesList.size()]);
  }

  private Properties _getProperties() throws Exception {
    String fileName = "source-formatter.properties";

    Properties properties = new Properties();

    if (portalSource) {
      ClassLoader classLoader = BaseSourceProcessor.class.getClassLoader();

      properties.load(
          classLoader.getResourceAsStream("com/liferay/portal/tools/dependencies/" + fileName));

      return properties;
    }

    List<Properties> propertiesList = new ArrayList<Properties>();

    for (int i = 0; i <= 2; i++) {
      try {
        InputStream inputStream = new FileInputStream(fileName);

        Properties props = new Properties();

        props.load(inputStream);

        propertiesList.add(props);
      } catch (FileNotFoundException fnfe) {
      }

      fileName = "../" + fileName;
    }

    if (propertiesList.isEmpty()) {
      return properties;
    }

    properties = propertiesList.get(0);

    if (propertiesList.size() == 1) {
      return properties;
    }

    for (int i = 1; i < propertiesList.size(); i++) {
      Properties props = propertiesList.get(i);

      Enumeration<String> enu = (Enumeration<String>) props.propertyNames();

      while (enu.hasMoreElements()) {
        String key = enu.nextElement();

        String value = props.getProperty(key);

        if (Validator.isNull(value)) {
          continue;
        }

        if (properties.containsKey(key)) {
          String existingValue = properties.getProperty(key);

          if (Validator.isNotNull(existingValue)) {
            value = existingValue + StringPool.COMMA + value;
          }
        }

        properties.put(key, value);
      }
    }

    return properties;
  }

  private void _init(boolean useProperties, boolean printErrors, boolean autoFix) throws Exception {

    _errorMessagesMap = new HashMap<String, List<String>>();

    sourceFormatterHelper = new SourceFormatterHelper(useProperties);

    sourceFormatterHelper.init();

    _autoFix = autoFix;

    _excludes = _getExcludes();

    _printErrors = printErrors;

    _usePortalCompatImport = GetterUtil.getBoolean(getProperty("use.portal.compat.import"));
  }

  private boolean _isPortalSource() {
    if (getFile("portal-impl", 4) != null) {
      return true;
    } else {
      return false;
    }
  }

  private static Map<String, List<String>> _errorMessagesMap = new HashMap<String, List<String>>();
  private static boolean _printErrors;

  private boolean _autoFix;
  private Map<String, String> _compatClassNamesMap;
  private String _copyright;
  private String[] _excludes;
  private SourceMismatchException _firstSourceMismatchException;
  private String _mainReleaseVersion;
  private String _oldCopyright;
  private Properties _portalLanguageProperties;
  private Properties _properties;
  private List<String> _runOutsidePortalExclusions;
  private boolean _usePortalCompatImport;
}
  @Override
  public List<Node> selectNodes(String xPathExpression, String comparisonXPathExpression) {

    return SAXReaderImpl.toNewNodes(_node.selectNodes(xPathExpression, comparisonXPathExpression));
  }
Example #5
0
  public void setProcessingInstructions(List<ProcessingInstruction> processingInstructions) {

    _branch.setProcessingInstructions(
        SAXReaderImpl.toOldProcessingInstructions(processingInstructions));
  }
Example #6
0
 public List<Node> content() {
   return SAXReaderImpl.toNewNodes(_branch.content());
 }
Example #7
0
 public void setContent(List<Node> content) {
   _branch.setContent(SAXReaderImpl.toOldNodes(content));
 }
Example #8
0
 public List<ProcessingInstruction> processingInstructions(String target) {
   return SAXReaderImpl.toNewProcessingInstructions(_branch.processingInstructions(target));
 }
Example #9
0
 public List<ProcessingInstruction> processingInstructions() {
   return SAXReaderImpl.toNewProcessingInstructions(_branch.processingInstructions());
 }
/**
 * @author Brian Wing Shun Chan
 * @author Connor McKay
 * @author James Hinkey
 * @author Hugo Huijser
 */
public class JavadocFormatter {

  public static void main(String[] args) {
    try {
      new JavadocFormatter(args);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public JavadocFormatter(String[] args) throws Exception {
    Map<String, String> arguments = ArgumentsUtil.parseArguments(args);

    String init = arguments.get("javadoc.init");

    if (Validator.isNotNull(init) && !init.startsWith("$")) {
      _initializeMissingJavadocs = GetterUtil.getBoolean(init);
    }

    _inputDir = GetterUtil.getString(arguments.get("javadoc.input.dir"));

    if (_inputDir.startsWith("$")) {
      _inputDir = "./";
    }

    if (!_inputDir.endsWith("/")) {
      _inputDir += "/";
    }

    System.out.println("Input directory is " + _inputDir);

    String limit = arguments.get("javadoc.limit");

    _outputFilePrefix = GetterUtil.getString(arguments.get("javadoc.output.file.prefix"));

    if (_outputFilePrefix.startsWith("$")) {
      _outputFilePrefix = "javadocs";
    }

    String update = arguments.get("javadoc.update");

    if (Validator.isNotNull(update) && !update.startsWith("$")) {
      _updateJavadocs = GetterUtil.getBoolean(update);
    }

    DirectoryScanner directoryScanner = new DirectoryScanner();

    directoryScanner.setBasedir(_inputDir);
    directoryScanner.setExcludes(new String[] {"**\\classes\\**", "**\\portal-client\\**"});

    List<String> includes = new ArrayList<String>();

    if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
      System.out.println("Limit on " + limit);

      String[] limitArray = StringUtil.split(limit, '/');

      for (String curLimit : limitArray) {
        includes.add("**\\" + StringUtil.replace(curLimit, ".", "\\") + "\\**\\*.java");
        includes.add("**\\" + curLimit + ".java");
      }
    } else {
      includes.add("**\\*.java");
    }

    directoryScanner.setIncludes(includes.toArray(new String[includes.size()]));

    directoryScanner.scan();

    String[] fileNames = directoryScanner.getIncludedFiles();

    if ((fileNames.length == 0) && Validator.isNotNull(limit) && !limit.startsWith("$")) {

      StringBundler sb = new StringBundler("Limit file not found: ");

      sb.append(limit);

      if (limit.contains(".")) {
        sb.append(" Specify limit filename without package path or ");
        sb.append("file type suffix.");
      }

      System.out.println(sb.toString());
    }

    _languagePropertiesFile = new File("src/content/Language.properties");

    if (_languagePropertiesFile.exists()) {
      _languageProperties = new Properties();

      _languageProperties.load(new FileInputStream(_languagePropertiesFile.getAbsolutePath()));
    }

    for (String fileName : fileNames) {
      fileName = StringUtil.replace(fileName, "\\", "/");

      _format(fileName);
    }

    for (Map.Entry<String, Tuple> entry : _javadocxXmlTuples.entrySet()) {
      Tuple tuple = entry.getValue();

      File javadocsXmlFile = (File) tuple.getObject(1);
      String oldJavadocsXmlContent = (String) tuple.getObject(2);
      Document javadocsXmlDocument = (Document) tuple.getObject(3);

      Element javadocsXmlRootElement = javadocsXmlDocument.getRootElement();

      javadocsXmlRootElement.sortElementsByChildElement("javadoc", "type");

      String newJavadocsXmlContent = javadocsXmlDocument.formattedString();

      if (!oldJavadocsXmlContent.equals(newJavadocsXmlContent)) {
        _fileUtil.write(javadocsXmlFile, newJavadocsXmlContent);
      }

      _detachUnnecessaryTypes(javadocsXmlRootElement);

      File javadocsRuntimeXmlFile =
          new File(StringUtil.replaceLast(javadocsXmlFile.toString(), "-all.xml", "-rt.xml"));

      String oldJavadocsRuntimeXmlContent = StringPool.BLANK;

      if (javadocsRuntimeXmlFile.exists()) {
        oldJavadocsRuntimeXmlContent = _fileUtil.read(javadocsRuntimeXmlFile);
      }

      String newJavadocsRuntimeXmlContent = javadocsXmlDocument.compactString();

      if (!oldJavadocsRuntimeXmlContent.equals(newJavadocsRuntimeXmlContent)) {

        _fileUtil.write(javadocsRuntimeXmlFile, newJavadocsRuntimeXmlContent);
      }
    }
  }

  private List<Tuple> _addAncestorJavaClassTuples(
      JavaClass javaClass, List<Tuple> ancestorJavaClassTuples) {

    JavaClass superJavaClass = javaClass.getSuperJavaClass();

    if (superJavaClass != null) {
      ancestorJavaClassTuples.add(new Tuple(superJavaClass));

      ancestorJavaClassTuples =
          _addAncestorJavaClassTuples(superJavaClass, ancestorJavaClassTuples);
    }

    Type[] implementz = javaClass.getImplements();

    for (Type implement : implementz) {
      Type[] actualTypeArguments = implement.getActualTypeArguments();
      JavaClass implementedInterface = implement.getJavaClass();

      if (actualTypeArguments == null) {
        ancestorJavaClassTuples.add(new Tuple(implementedInterface));
      } else {
        ancestorJavaClassTuples.add(new Tuple(implementedInterface, actualTypeArguments));
      }

      ancestorJavaClassTuples =
          _addAncestorJavaClassTuples(implementedInterface, ancestorJavaClassTuples);
    }

    return ancestorJavaClassTuples;
  }

  private void _addClassCommentElement(Element rootElement, JavaClass javaClass) {

    String comment = _getCDATA(javaClass);

    if (comment.startsWith("Copyright (c)")) {
      comment = StringPool.BLANK;
    }

    if (Validator.isNull(comment)) {
      return;
    }

    Element commentElement = rootElement.addElement("comment");

    commentElement.addCDATA(comment);
  }

  private void _addDocletElements(
      Element parentElement, AbstractJavaEntity abstractJavaEntity, String name) throws Exception {

    DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);

    for (DocletTag docletTag : docletTags) {
      String value = docletTag.getValue();

      value = _trimMultilineText(value);

      value = StringUtil.replace(value, " </", "</");

      Element element = parentElement.addElement(name);

      element.addCDATA(value);
    }

    if ((docletTags.length == 0) && name.equals("author")) {
      Element element = parentElement.addElement(name);

      element.addCDATA(ServiceBuilder.AUTHOR);
    }
  }

  private String _addDocletTags(
      Element parentElement, String[] tagNames, String indent, boolean publicAccess) {

    List<String> allTagNames = new ArrayList<String>();
    List<String> customTagNames = new ArrayList<String>();
    List<String> requiredTagNames = new ArrayList<String>();

    for (String tagName : tagNames) {
      List<Element> elements = parentElement.elements(tagName);

      for (Element element : elements) {
        Element commentElement = element.element("comment");

        String comment = null;

        // Get comment by comment element's text or the element's text

        if (commentElement != null) {
          comment = commentElement.getText();
        } else {
          comment = element.getText();
        }

        if (tagName.equals("param") || tagName.equals("return") || tagName.equals("throws")) {

          if (Validator.isNotNull(comment)) {
            requiredTagNames.add(tagName);
          } else if (tagName.equals("param")) {
            if (GetterUtil.getBoolean(element.elementText("required"))) {

              requiredTagNames.add(tagName);
            }
          } else if (tagName.equals("throws")) {
            if (GetterUtil.getBoolean(element.elementText("required"))) {

              requiredTagNames.add(tagName);
            }
          }
        } else {
          customTagNames.add(tagName);
        }

        allTagNames.add(tagName);
      }
    }

    int maxTagNameLength = 0;

    List<String> maxTagNameLengthTags = new ArrayList<String>();

    if (_initializeMissingJavadocs) {
      maxTagNameLengthTags.addAll(allTagNames);
    } else if (_updateJavadocs) {
      if (!requiredTagNames.isEmpty()) {
        maxTagNameLengthTags.addAll(allTagNames);
      } else {
        maxTagNameLengthTags.addAll(customTagNames);
        maxTagNameLengthTags.addAll(requiredTagNames);
      }
    } else {
      maxTagNameLengthTags.addAll(customTagNames);
      maxTagNameLengthTags.addAll(requiredTagNames);
    }

    for (String name : maxTagNameLengthTags) {
      if (name.length() > maxTagNameLength) {
        maxTagNameLength = name.length();
      }
    }

    // There should be an @ sign before the tag and a space after it

    maxTagNameLength += 2;

    String tagNameIndent = _getSpacesIndent(maxTagNameLength);

    StringBundler sb = new StringBundler();

    for (String tagName : tagNames) {
      List<Element> elements = parentElement.elements(tagName);

      for (Element element : elements) {
        Element commentElement = element.element("comment");

        String comment = null;

        if (commentElement != null) {
          comment = commentElement.getText();
        } else {
          comment = element.getText();
        }

        String elementName = element.elementText("name");

        if (Validator.isNotNull(comment)) {
          comment = _assembleTagComment(tagName, elementName, comment, indent, tagNameIndent);

          sb.append(comment);
        } else {
          if (_initializeMissingJavadocs && publicAccess) {

            // Write out all tags

            comment = _assembleTagComment(tagName, elementName, comment, indent, tagNameIndent);

            sb.append(comment);
          } else if (_updateJavadocs && publicAccess) {
            if (!tagName.equals("param")
                && !tagName.equals("return")
                && !tagName.equals("throws")) {

              // Write out custom tag

              comment = _assembleTagComment(tagName, elementName, comment, indent, tagNameIndent);

              sb.append(comment);
            } else if (!requiredTagNames.isEmpty()) {

              // Write out all tags

              comment = _assembleTagComment(tagName, elementName, comment, indent, tagNameIndent);

              sb.append(comment);
            } else {

              // Skip empty common tag

            }
          } else {
            if (!tagName.equals("param")
                && !tagName.equals("return")
                && !tagName.equals("throws")) {

              // Write out custom tag

              comment = _assembleTagComment(tagName, elementName, comment, indent, tagNameIndent);

              sb.append(comment);
            } else if (tagName.equals("param")
                || tagName.equals("return")
                || tagName.equals("throws")) {

              if (GetterUtil.getBoolean(element.elementText("required"))) {

                elementName = element.elementText("name");

                comment = _assembleTagComment(tagName, elementName, comment, indent, tagNameIndent);

                sb.append(comment);
              }
            } else {

              // Skip empty common tag

            }
          }
        }
      }
    }

    return sb.toString();
  }

  private void _addFieldElement(Element rootElement, JavaField javaField) throws Exception {

    Element fieldElement = rootElement.addElement("field");

    DocUtil.add(fieldElement, "name", javaField.getName());

    String comment = _getCDATA(javaField);

    if (Validator.isNotNull(comment)) {
      Element commentElement = fieldElement.addElement("comment");

      commentElement.addCDATA(comment);
    }

    _addDocletElements(fieldElement, javaField, "version");
    _addDocletElements(fieldElement, javaField, "see");
    _addDocletElements(fieldElement, javaField, "since");
    _addDocletElements(fieldElement, javaField, "deprecated");
  }

  private void _addMethodElement(Element rootElement, JavaMethod javaMethod) throws Exception {

    Element methodElement = rootElement.addElement("method");

    DocUtil.add(methodElement, "name", javaMethod.getName());

    String comment = _getCDATA(javaMethod);

    if (Validator.isNotNull(comment)) {
      Element commentElement = methodElement.addElement("comment");

      commentElement.addCDATA(_getCDATA(javaMethod));
    }

    _addDocletElements(methodElement, javaMethod, "version");
    _addParamElements(methodElement, javaMethod);
    _addReturnElement(methodElement, javaMethod);
    _addThrowsElements(methodElement, javaMethod);
    _addDocletElements(methodElement, javaMethod, "see");
    _addDocletElements(methodElement, javaMethod, "since");
    _addDocletElements(methodElement, javaMethod, "deprecated");
  }

  private void _addParamElement(
      Element methodElement, JavaParameter javaParameter, DocletTag[] paramDocletTags) {

    String name = javaParameter.getName();

    String value = null;

    for (DocletTag paramDocletTag : paramDocletTags) {
      String curValue = paramDocletTag.getValue();

      if (!curValue.startsWith(name)) {
        continue;
      } else {
        value = curValue;

        break;
      }
    }

    Element paramElement = methodElement.addElement("param");

    DocUtil.add(paramElement, "name", name);
    DocUtil.add(paramElement, "type", _getTypeValue(javaParameter));

    if (value != null) {
      value = value.substring(name.length());

      DocUtil.add(paramElement, "required", true);
    }

    value = _trimMultilineText(value);

    Element commentElement = paramElement.addElement("comment");

    commentElement.addCDATA(value);
  }

  private void _addParamElements(Element methodElement, JavaMethod javaMethod) {

    JavaParameter[] javaParameters = javaMethod.getParameters();

    DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");

    for (JavaParameter javaParameter : javaParameters) {
      _addParamElement(methodElement, javaParameter, paramDocletTags);
    }
  }

  private void _addReturnElement(Element methodElement, JavaMethod javaMethod) throws Exception {

    Type returns = javaMethod.getReturns();

    if (returns == null) {
      return;
    }

    String returnsValue = returns.getValue();

    if (returnsValue.equals("void")) {
      return;
    }

    Element returnElement = methodElement.addElement("return");

    DocletTag[] returnDocletTags = javaMethod.getTagsByName("return");

    String comment = StringPool.BLANK;

    if (returnDocletTags.length > 0) {
      DocletTag returnDocletTag = returnDocletTags[0];

      comment = GetterUtil.getString(returnDocletTag.getValue());

      DocUtil.add(returnElement, "required", true);
    }

    comment = _trimMultilineText(comment);

    Element commentElement = returnElement.addElement("comment");

    commentElement.addCDATA(comment);
  }

  private void _addThrowsElement(
      Element methodElement, Type exceptionType, DocletTag[] throwsDocletTags) {

    JavaClass javaClass = exceptionType.getJavaClass();

    String name = javaClass.getName();

    String value = null;

    for (DocletTag throwsDocletTag : throwsDocletTags) {
      String curValue = throwsDocletTag.getValue();

      if (!curValue.startsWith(name)) {
        continue;
      } else {
        value = curValue;

        break;
      }
    }

    Element throwsElement = methodElement.addElement("throws");

    DocUtil.add(throwsElement, "name", name);
    DocUtil.add(throwsElement, "type", exceptionType.getValue());

    if (value != null) {
      value = value.substring(name.length());

      DocUtil.add(throwsElement, "required", true);
    }

    value = _trimMultilineText(value);

    Element commentElement = throwsElement.addElement("comment");

    commentElement.addCDATA(_getCDATA(value));
  }

  private void _addThrowsElements(Element methodElement, JavaMethod javaMethod) {

    Type[] exceptionTypes = javaMethod.getExceptions();

    DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");

    for (Type exceptionType : exceptionTypes) {
      _addThrowsElement(methodElement, exceptionType, throwsDocletTags);
    }
  }

  private String _assembleTagComment(
      String tagName, String elementName, String comment, String indent, String tagNameIndent) {

    String indentAndTagName = indent + StringPool.AT + tagName;

    if (Validator.isNotNull(elementName)) {
      if (Validator.isNotNull(comment)) {
        comment = elementName + StringPool.SPACE + comment;
      } else {
        comment = elementName;
      }

      // <name indent> elementName [comment]

      comment = _wrapText(comment, indent + tagNameIndent);

      // * @name <name indent> elementName [comment]

      comment = indentAndTagName + comment.substring(indentAndTagName.length());
    } else {
      if (Validator.isNotNull(comment)) {

        // <name indent> comment

        comment = _wrapText(comment, indent + tagNameIndent);

        // * @name <name indent> comment

        comment = indentAndTagName + comment.substring(indentAndTagName.length());
      } else {

        // * @name

        comment = indentAndTagName + "\n";
      }
    }

    return comment;
  }

  private void _detachUnnecessaryTypes(Element rootElement) {
    List<Element> elements = rootElement.elements();

    for (Element element : elements) {
      String type = element.elementText("type");

      if (!type.contains(".service.") || !type.endsWith("ServiceImpl")) {
        element.detach();
      }
    }
  }

  private void _format(String fileName) throws Exception {
    InputStream inputStream = new FileInputStream(_inputDir + fileName);

    byte[] bytes = new byte[inputStream.available()];

    inputStream.read(bytes);

    inputStream.close();

    String originalContent = new String(bytes, StringPool.UTF8);

    if (fileName.endsWith("JavadocFormatter.java")
        || fileName.endsWith("SourceFormatter.java")
        || _hasGeneratedTag(originalContent)) {

      return;
    }

    JavaClass javaClass = _getJavaClass(fileName, new UnsyncStringReader(originalContent));

    String javadocLessContent = _removeJavadocFromJava(javaClass, originalContent);

    Document document = _getJavadocDocument(javaClass);

    _updateJavadocsXmlFile(fileName, javaClass, document);

    _updateJavaFromDocument(fileName, originalContent, javadocLessContent, document);
  }

  private String _formatCDATA(String cdata, String exclude) {
    StringBundler sb = new StringBundler();

    String startTag = "<" + exclude + ">";
    String endTag = "</" + exclude + ">";

    String[] cdataParts = cdata.split(startTag);

    for (String cdataPart : cdataParts) {
      if (!cdataPart.contains(endTag)) {
        cdataPart = _getCDATA(cdataPart);
      }

      if (cdataPart.contains("</" + exclude + ">")) {
        sb.append(startTag);
      }

      sb.append(cdataPart);
    }

    return sb.toString();
  }

  private String _formatInlines(String text) {

    // Capitalize ID

    text = text.replaceAll("[?@param id](?i)\\bid(s)?\\b", " ID$1");

    // Wrap special constants in code tags

    text = text.replaceAll("(?i)(?<!<code>|\\w)(null|false|true)(?!\\w)", "<code>$1</code>");

    return text;
  }

  private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
    return _getCDATA(abstractJavaEntity.getComment());
  }

  private String _getCDATA(String cdata) {
    if (cdata == null) {
      return StringPool.BLANK;
    } else if (cdata.contains("<pre>")) {
      cdata = _formatCDATA(cdata, "pre");
    } else if (cdata.contains("<table>")) {
      cdata = _formatCDATA(cdata, "table");
    } else {
      cdata =
          cdata.replaceAll("(?s)\\s*<(p|[ou]l)>\\s*(.*?)\\s*</\\1>\\s*", "\n\n<$1>\n$2\n</$1>\n\n");
      cdata = cdata.replaceAll("(?s)\\s*<li>\\s*(.*?)\\s*</li>\\s*", "\n<li>\n$1\n</li>\n");
      cdata = StringUtil.replace(cdata, "</li>\n\n<li>", "</li>\n<li>");
      cdata = cdata.replaceAll("\n\\s+\n", "\n\n");
      cdata.replaceAll(" +", " ");

      // Trim whitespace inside paragraph tags or in the first paragraph

      Pattern pattern =
          Pattern.compile("(^.*?(?=\n\n|$)+|(?<=<p>\n).*?(?=\n</p>))", Pattern.DOTALL);

      Matcher matcher = pattern.matcher(cdata);

      StringBuffer sb = new StringBuffer();

      while (matcher.find()) {
        String trimmed = _trimMultilineText(matcher.group());

        // Escape dollar signs

        trimmed = trimmed.replaceAll("\\$", "\\\\\\$");

        matcher.appendReplacement(sb, trimmed);
      }

      matcher.appendTail(sb);

      cdata = sb.toString();
    }

    return cdata.trim();
  }

  private String _getClassName(String fileName) {
    int pos = fileName.indexOf("src/");

    if (pos == -1) {
      pos = fileName.indexOf("test/integration/");

      if (pos != -1) {
        pos = fileName.indexOf("integration/", pos);
      }
    }

    if (pos == -1) {
      pos = fileName.indexOf("test/unit/");

      if (pos != -1) {
        pos = fileName.indexOf("unit/", pos);
      }
    }

    if (pos == -1) {
      pos = fileName.indexOf("test/");
    }

    if (pos == -1) {
      pos = fileName.indexOf("service/");
    }

    if (pos == -1) {
      throw new RuntimeException(fileName);
    }

    pos = fileName.indexOf("/", pos);

    String srcFile = fileName.substring(pos + 1, fileName.length());

    return StringUtil.replace(srcFile.substring(0, srcFile.length() - 5), "/", ".");
  }

  private String _getFieldKey(Element fieldElement) {
    return fieldElement.elementText("name");
  }

  private String _getFieldKey(JavaField javaField) {
    return javaField.getName();
  }

  private String _getIndent(String[] lines, AbstractBaseJavaEntity abstractBaseJavaEntity) {

    String line = lines[abstractBaseJavaEntity.getLineNumber() - 1];

    String indent = StringPool.BLANK;

    for (char c : line.toCharArray()) {
      if (Character.isWhitespace(c)) {
        indent += c;
      } else {
        break;
      }
    }

    return indent;
  }

  private int _getIndentLength(String indent) {
    int indentLength = 0;

    for (char c : indent.toCharArray()) {
      if (c == '\t') {
        indentLength = indentLength + 4;
      } else {
        indentLength++;
      }
    }

    return indentLength;
  }

  private JavaClass _getJavaClass(String fileName, Reader reader) throws Exception {

    String className = _getClassName(fileName);

    JavaDocBuilder javadocBuilder = new JavaDocBuilder();

    if (reader == null) {
      File file = new File(fileName);

      if (!file.exists()) {
        return null;
      }

      javadocBuilder.addSource(file);
    } else {
      javadocBuilder.addSource(reader);
    }

    return javadocBuilder.getClassByName(className);
  }

  private String _getJavaClassComment(Element rootElement, JavaClass javaClass) {

    StringBundler sb = new StringBundler();

    String indent = StringPool.BLANK;

    sb.append("/**\n");

    String comment = rootElement.elementText("comment");

    if (Validator.isNotNull(comment)) {
      sb.append(_wrapText(comment, indent + " * "));
    }

    String docletTags =
        _addDocletTags(
            rootElement,
            new String[] {"author", "version", "see", "since", "serial", "deprecated"},
            indent + " * ",
            _hasPublicModifier(javaClass));

    if (Validator.isNotNull(docletTags)) {
      if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
        sb.append(" *\n");
      }

      sb.append(docletTags);
    }

    sb.append(" */\n");

    return sb.toString();
  }

  private int _getJavaClassLineNumber(JavaClass javaClass) {
    int lineNumber = javaClass.getLineNumber();

    Annotation[] annotations = javaClass.getAnnotations();

    if (annotations.length == 0) {
      return lineNumber;
    }

    for (Annotation annotation : annotations) {
      int annotationLineNumber = annotation.getLineNumber();

      Map<String, String> propertyMap = annotation.getPropertyMap();

      if (propertyMap.isEmpty()) {
        annotationLineNumber--;
      }

      if (annotationLineNumber < lineNumber) {
        lineNumber = annotationLineNumber;
      }
    }

    return lineNumber;
  }

  private Document _getJavadocDocument(JavaClass javaClass) throws Exception {
    Element rootElement = _saxReaderUtil.createElement("javadoc");

    Document document = _saxReaderUtil.createDocument(rootElement);

    DocUtil.add(rootElement, "name", javaClass.getName());
    DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());

    _addClassCommentElement(rootElement, javaClass);
    _addDocletElements(rootElement, javaClass, "author");
    _addDocletElements(rootElement, javaClass, "version");
    _addDocletElements(rootElement, javaClass, "see");
    _addDocletElements(rootElement, javaClass, "since");
    _addDocletElements(rootElement, javaClass, "serial");
    _addDocletElements(rootElement, javaClass, "deprecated");

    JavaMethod[] javaMethods = javaClass.getMethods();

    for (JavaMethod javaMethod : javaMethods) {
      _addMethodElement(rootElement, javaMethod);
    }

    JavaField[] javaFields = javaClass.getFields();

    for (JavaField javaField : javaFields) {
      _addFieldElement(rootElement, javaField);
    }

    return document;
  }

  private Tuple _getJavadocsXmlTuple(String fileName) throws Exception {
    File file = new File(_inputDir + fileName);

    String absolutePath = file.getAbsolutePath();

    absolutePath = StringUtil.replace(absolutePath, "\\", "/");
    absolutePath = StringUtil.replace(absolutePath, "/./", "/");

    int pos = absolutePath.indexOf("/portal-impl/src/");

    String srcDirName = null;

    if (pos != -1) {
      srcDirName = absolutePath.substring(0, pos + 17);
    }

    if (srcDirName == null) {
      pos = absolutePath.indexOf("/portal-kernel/src/");

      if (pos == -1) {
        pos = absolutePath.indexOf("/portal-service/src/");
      }

      if (pos == -1) {
        pos = absolutePath.indexOf("/util-bridges/src/");
      }

      if (pos == -1) {
        pos = absolutePath.indexOf("/util-java/src/");
      }

      if (pos == -1) {
        pos = absolutePath.indexOf("/util-taglib/src/");
      }

      if (pos != -1) {
        srcDirName = absolutePath.substring(0, pos) + "/portal-impl/src/";
      }
    }

    if (srcDirName == null) {
      pos = absolutePath.indexOf("/WEB-INF/src/");

      if (pos != -1) {
        srcDirName = absolutePath.substring(0, pos + 13);
      }
    }

    if (srcDirName == null) {
      return null;
    }

    Tuple tuple = _javadocxXmlTuples.get(srcDirName);

    if (tuple != null) {
      return tuple;
    }

    File javadocsXmlFile = new File(srcDirName, "META-INF/" + _outputFilePrefix + "-all.xml");

    if (!javadocsXmlFile.exists()) {
      _fileUtil.write(javadocsXmlFile, "<?xml version=\"1.0\"?>\n\n<javadocs>\n</javadocs>");
    }

    String javadocsXmlContent = _fileUtil.read(javadocsXmlFile);

    Document javadocsXmlDocument = _saxReaderUtil.read(javadocsXmlContent);

    tuple = new Tuple(srcDirName, javadocsXmlFile, javadocsXmlContent, javadocsXmlDocument);

    _javadocxXmlTuples.put(srcDirName, tuple);

    return tuple;
  }

  private String _getJavaFieldComment(
      String[] lines, Map<String, Element> fieldElementsMap, JavaField javaField) {

    String fieldKey = _getFieldKey(javaField);

    Element fieldElement = fieldElementsMap.get(fieldKey);

    if (fieldElement == null) {
      return null;
    }

    String indent = _getIndent(lines, javaField);

    StringBundler sb = new StringBundler();

    sb.append(indent);
    sb.append("/**\n");

    String comment = fieldElement.elementText("comment");

    if (Validator.isNotNull(comment)) {
      sb.append(_wrapText(comment, indent + " * "));
    }

    String docletTags =
        _addDocletTags(
            fieldElement,
            new String[] {"version", "see", "since", "deprecated"},
            indent + " * ",
            _hasPublicModifier(javaField));

    if (Validator.isNotNull(docletTags)) {
      if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
        sb.append(indent);
        sb.append(" *\n");
      }

      sb.append(docletTags);
    }

    sb.append(indent);
    sb.append(" */\n");

    if (!_initializeMissingJavadocs && Validator.isNull(comment) && Validator.isNull(docletTags)) {

      return null;
    }

    if (!_hasPublicModifier(javaField)
        && Validator.isNull(comment)
        && Validator.isNull(docletTags)) {

      return null;
    }

    return sb.toString();
  }

  private String _getJavaMethodComment(
      String[] lines, Map<String, Element> methodElementsMap, JavaMethod javaMethod) {

    String methodKey = _getMethodKey(javaMethod);

    Element methodElement = methodElementsMap.get(methodKey);

    if (methodElement == null) {
      return null;
    }

    String indent = _getIndent(lines, javaMethod);

    StringBundler sb = new StringBundler();

    sb.append(indent);
    sb.append("/**\n");

    String comment = methodElement.elementText("comment");

    if (Validator.isNotNull(comment)) {
      sb.append(_wrapText(comment, indent + " * "));
    }

    String docletTags =
        _addDocletTags(
            methodElement,
            new String[] {"version", "param", "return", "throws", "see", "since", "deprecated"},
            indent + " * ",
            _hasPublicModifier(javaMethod));

    if (Validator.isNotNull(docletTags)) {
      if (_initializeMissingJavadocs || Validator.isNotNull(comment)) {
        sb.append(indent);
        sb.append(" *\n");
      }

      sb.append(docletTags);
    }

    sb.append(indent);
    sb.append(" */\n");

    if (!_initializeMissingJavadocs && Validator.isNull(comment) && Validator.isNull(docletTags)) {

      return null;
    }

    if (!_hasPublicModifier(javaMethod)
        && Validator.isNull(comment)
        && Validator.isNull(docletTags)) {

      return null;
    }

    return sb.toString();
  }

  private String _getMethodKey(Element methodElement) {
    StringBundler sb = new StringBundler();

    sb.append(methodElement.elementText("name"));
    sb.append(StringPool.OPEN_PARENTHESIS);

    List<Element> paramElements = methodElement.elements("param");

    for (Element paramElement : paramElements) {
      sb.append(paramElement.elementText("name"));
      sb.append("|");
      sb.append(paramElement.elementText("type"));
      sb.append(",");
    }

    sb.append(StringPool.CLOSE_PARENTHESIS);

    return sb.toString();
  }

  private String _getMethodKey(JavaMethod javaMethod) {
    StringBundler sb = new StringBundler();

    sb.append(javaMethod.getName());
    sb.append(StringPool.OPEN_PARENTHESIS);

    JavaParameter[] javaParameters = javaMethod.getParameters();

    for (JavaParameter javaParameter : javaParameters) {
      sb.append(javaParameter.getName());
      sb.append("|");
      sb.append(_getTypeValue(javaParameter));
      sb.append(",");
    }

    sb.append(StringPool.CLOSE_PARENTHESIS);

    return sb.toString();
  }

  private String _getSpacesIndent(int length) {
    String indent = StringPool.BLANK;

    for (int i = 0; i < length; i++) {
      indent += StringPool.SPACE;
    }

    return indent;
  }

  private String _getTypeValue(JavaParameter javaParameter) {
    Type type = javaParameter.getType();

    String typeValue = type.getValue();

    if (type.isArray()) {
      typeValue += "[]";
    }

    return typeValue;
  }

  private boolean _hasAnnotation(
      AbstractBaseJavaEntity abstractBaseJavaEntity, String annotationName) {

    Annotation[] annotations = abstractBaseJavaEntity.getAnnotations();

    if (annotations == null) {
      return false;
    }

    for (int i = 0; i < annotations.length; i++) {
      Type type = annotations[i].getType();

      JavaClass javaClass = type.getJavaClass();

      if (annotationName.equals(javaClass.getName())) {
        return true;
      }
    }

    return false;
  }

  private boolean _hasGeneratedTag(String content) {
    if (content.contains("* @generated") || content.contains("$ANTLR")) {
      return true;
    } else {
      return false;
    }
  }

  private boolean _hasPublicModifier(AbstractJavaEntity abstractJavaEntity) {
    String[] modifiers = abstractJavaEntity.getModifiers();

    if (modifiers == null) {
      return false;
    }

    for (String modifier : modifiers) {
      if (modifier.equals("public")) {
        return true;
      }
    }

    return false;
  }

  private boolean _isOverrideMethod(
      JavaClass javaClass, JavaMethod javaMethod, Collection<Tuple> ancestorJavaClassTuples) {

    if (javaMethod.isConstructor() || javaMethod.isPrivate() || javaMethod.isStatic()) {

      return false;
    }

    String methodName = javaMethod.getName();

    JavaParameter[] javaParameters = javaMethod.getParameters();

    Type[] types = new Type[javaParameters.length];

    for (int i = 0; i < javaParameters.length; i++) {
      types[i] = javaParameters[i].getType();
    }

    // Check for matching method in each ancestor

    for (Tuple ancestorJavaClassTuple : ancestorJavaClassTuples) {
      JavaClass ancestorJavaClass = (JavaClass) ancestorJavaClassTuple.getObject(0);

      JavaMethod ancestorJavaMethod = null;

      if (ancestorJavaClassTuple.getSize() > 1) {

        // LPS-35613

        Type[] ancestorActualTypeArguments = (Type[]) ancestorJavaClassTuple.getObject(1);

        Type[] genericTypes = new Type[types.length];

        for (int i = 0; i < types.length; i++) {
          Type type = types[i];

          String typeValue = type.getValue();

          boolean useGenericType = false;

          for (int j = 0; j < ancestorActualTypeArguments.length; j++) {

            if (typeValue.equals(ancestorActualTypeArguments[j].getValue())) {

              useGenericType = true;

              break;
            }
          }

          if (useGenericType) {
            genericTypes[i] = new Type("java.lang.Object");
          } else {
            genericTypes[i] = type;
          }
        }

        ancestorJavaMethod = ancestorJavaClass.getMethodBySignature(methodName, genericTypes);
      } else {
        ancestorJavaMethod = ancestorJavaClass.getMethodBySignature(methodName, types);
      }

      if (ancestorJavaMethod == null) {
        continue;
      }

      boolean samePackage = false;

      JavaPackage ancestorJavaPackage = ancestorJavaClass.getPackage();

      if (ancestorJavaPackage != null) {
        samePackage = ancestorJavaPackage.equals(javaClass.getPackage());
      }

      // Check if the method is in scope

      if (samePackage) {
        return !ancestorJavaMethod.isPrivate();
      } else {
        if (ancestorJavaMethod.isProtected() || ancestorJavaMethod.isPublic()) {

          return true;
        } else {
          return false;
        }
      }
    }

    return false;
  }

  private String _removeJavadocFromJava(JavaClass javaClass, String content) {
    Set<Integer> lineNumbers = new HashSet<Integer>();

    lineNumbers.add(_getJavaClassLineNumber(javaClass));

    JavaMethod[] javaMethods = javaClass.getMethods();

    for (JavaMethod javaMethod : javaMethods) {
      lineNumbers.add(javaMethod.getLineNumber());
    }

    JavaField[] javaFields = javaClass.getFields();

    for (JavaField javaField : javaFields) {
      lineNumbers.add(javaField.getLineNumber());
    }

    String[] lines = StringUtil.splitLines(content);

    for (int lineNumber : lineNumbers) {
      if (lineNumber == 0) {
        continue;
      }

      int pos = lineNumber - 2;

      String line = lines[pos];

      if (line == null) {
        continue;
      }

      line = line.trim();

      if (line.endsWith("*/")) {
        while (true) {
          lines[pos] = null;

          if (line.startsWith("/**") || line.startsWith("/*")) {
            break;
          }

          line = lines[--pos].trim();
        }
      }
    }

    StringBundler sb = new StringBundler(content.length());

    for (String line : lines) {
      if (line != null) {
        sb.append(line);
        sb.append("\n");
      }
    }

    return sb.toString().trim();
  }

  private String _trimMultilineText(String text) {
    String[] textArray = StringUtil.splitLines(text);

    for (int i = 0; i < textArray.length; i++) {
      textArray[i] = textArray[i].trim();
    }

    return StringUtil.merge(textArray, " ");
  }

  private void _updateJavadocsXmlFile(
      String fileName, JavaClass javaClass, Document javaClassDocument) throws Exception {

    String javaClassFullyQualifiedName = javaClass.getFullyQualifiedName();

    /*if (!javaClassFullyQualifiedName.contains(".service.") ||
    	!javaClassFullyQualifiedName.endsWith("ServiceImpl")) {

    	return;
    }*/

    Tuple javadocsXmlTuple = _getJavadocsXmlTuple(fileName);

    if (javadocsXmlTuple == null) {
      return;
    }

    Document javadocsXmlDocument = (Document) javadocsXmlTuple.getObject(3);

    Element javadocsXmlRootElement = javadocsXmlDocument.getRootElement();

    List<Element> javadocElements = javadocsXmlRootElement.elements("javadoc");

    for (Element javadocElement : javadocElements) {
      String type = javadocElement.elementText("type");

      if (type.equals(javaClassFullyQualifiedName)) {
        Element javaClassRootElement = javaClassDocument.getRootElement();

        if (Validator.equals(
            javadocElement.formattedString(), javaClassRootElement.formattedString())) {

          return;
        }

        javadocElement.detach();

        break;
      }
    }

    javadocsXmlRootElement.add(javaClassDocument.getRootElement());
  }

  private void _updateJavaFromDocument(
      String fileName, String originalContent, String javadocLessContent, Document document)
      throws Exception {

    String[] lines = StringUtil.splitLines(javadocLessContent);

    JavaClass javaClass = _getJavaClass(fileName, new UnsyncStringReader(javadocLessContent));

    _updateLanguageProperties(document, javaClass.getName());

    List<Tuple> ancestorJavaClassTuples = new ArrayList<Tuple>();

    ancestorJavaClassTuples = _addAncestorJavaClassTuples(javaClass, ancestorJavaClassTuples);

    Element rootElement = document.getRootElement();

    Map<Integer, String> commentsMap = new TreeMap<Integer, String>();

    commentsMap.put(
        _getJavaClassLineNumber(javaClass), _getJavaClassComment(rootElement, javaClass));

    Map<String, Element> methodElementsMap = new HashMap<String, Element>();

    List<Element> methodElements = rootElement.elements("method");

    for (Element methodElement : methodElements) {
      String methodKey = _getMethodKey(methodElement);

      methodElementsMap.put(methodKey, methodElement);
    }

    JavaMethod[] javaMethods = javaClass.getMethods();

    for (JavaMethod javaMethod : javaMethods) {
      if (commentsMap.containsKey(javaMethod.getLineNumber())) {
        continue;
      }

      String javaMethodComment = _getJavaMethodComment(lines, methodElementsMap, javaMethod);

      // Handle override tag insertion

      if (!_hasAnnotation(javaMethod, "Override")) {
        if (_isOverrideMethod(javaClass, javaMethod, ancestorJavaClassTuples)) {

          String overrideLine = _getIndent(lines, javaMethod) + "@Override\n";

          if (Validator.isNotNull(javaMethodComment)) {
            javaMethodComment = javaMethodComment + overrideLine;
          } else {
            javaMethodComment = overrideLine;
          }
        }
      }

      commentsMap.put(javaMethod.getLineNumber(), javaMethodComment);
    }

    Map<String, Element> fieldElementsMap = new HashMap<String, Element>();

    List<Element> fieldElements = rootElement.elements("field");

    for (Element fieldElement : fieldElements) {
      String fieldKey = _getFieldKey(fieldElement);

      fieldElementsMap.put(fieldKey, fieldElement);
    }

    JavaField[] javaFields = javaClass.getFields();

    for (JavaField javaField : javaFields) {
      if (commentsMap.containsKey(javaField.getLineNumber())) {
        continue;
      }

      commentsMap.put(
          javaField.getLineNumber(), _getJavaFieldComment(lines, fieldElementsMap, javaField));
    }

    StringBundler sb = new StringBundler(javadocLessContent.length());

    for (int lineNumber = 1; lineNumber <= lines.length; lineNumber++) {
      String line = lines[lineNumber - 1];

      String comments = commentsMap.get(lineNumber);

      if (comments != null) {
        sb.append(comments);
      }

      sb.append(line);
      sb.append("\n");
    }

    String formattedContent = sb.toString().trim();

    if (!originalContent.equals(formattedContent)) {
      File file = new File(_inputDir + fileName);

      _fileUtil.write(file, formattedContent.getBytes(StringPool.UTF8));

      System.out.println("Writing " + file);
    }
  }

  private void _updateLanguageProperties(Document document, String className) throws IOException {

    if (_languageProperties == null) {
      return;
    }

    int index = className.indexOf("ServiceImpl");

    if (index <= 0) {
      return;
    }

    StringBundler sb = new StringBundler();

    sb.append(Character.toLowerCase(className.charAt(0)));

    for (int i = 1; i < index; i++) {
      char c = className.charAt(i);

      if (Character.isUpperCase(c)) {
        if (((i + 1) < index) && Character.isLowerCase(className.charAt(i + 1))) {

          sb.append(CharPool.DASH);
        }

        sb.append(Character.toLowerCase(c));
      } else {
        sb.append(c);
      }
    }

    sb.append("-service-help");

    String key = sb.toString();

    String value = _languageProperties.getProperty(key);

    if (value == null) {
      return;
    }

    Element rootElement = document.getRootElement();

    String comment = rootElement.elementText("comment");

    if ((comment == null) || value.equals(comment)) {
      return;
    }

    index = comment.indexOf("\n\n");

    if (index != -1) {
      value = comment.substring(0, index);
    } else {
      value = comment;
    }

    _updateLanguageProperties(key, value);
  }

  private void _updateLanguageProperties(String key, String value) throws IOException {

    UnsyncBufferedReader unsyncBufferedReader =
        new UnsyncBufferedReader(new FileReader(_languagePropertiesFile));

    StringBundler sb = new StringBundler();

    boolean begin = false;
    boolean firstLine = true;
    String linePrefix = key + "=";

    String line = null;

    while ((line = unsyncBufferedReader.readLine()) != null) {
      if (line.equals(StringPool.BLANK)) {
        begin = !begin;
      }

      if (firstLine) {
        firstLine = false;
      } else {
        sb.append(StringPool.NEW_LINE);
      }

      if (line.startsWith(linePrefix)) {
        sb.append(linePrefix + value);
      } else {
        sb.append(line);
      }
    }

    unsyncBufferedReader.close();

    Writer writer =
        new OutputStreamWriter(
            new FileOutputStream(_languagePropertiesFile, false), StringPool.UTF8);

    writer.write(sb.toString());

    writer.close();

    System.out.println("Updating " + _languagePropertiesFile + " key " + key);
  }

  private String _wrapText(String text, int indentLength, String exclude) {
    StringBuffer sb = new StringBuffer();

    StringBundler regexSB = new StringBundler("(?<=^|</");

    regexSB.append(exclude);
    regexSB.append(">).+?(?=$|<");
    regexSB.append(exclude);
    regexSB.append(">)");

    Pattern pattern = Pattern.compile(regexSB.toString(), Pattern.DOTALL);

    Matcher matcher = pattern.matcher(text);

    while (matcher.find()) {
      String wrapped = _formatInlines(matcher.group());

      wrapped = StringUtil.wrap(wrapped, 80 - indentLength, "\n");

      matcher.appendReplacement(sb, wrapped);
    }

    matcher.appendTail(sb);

    return sb.toString();
  }

  private String _wrapText(String text, String indent) {
    int indentLength = _getIndentLength(indent);

    if (text.contains("<pre>")) {
      text = _wrapText(text, indentLength, "pre");
    } else if (text.contains("<table>")) {
      text = _wrapText(text, indentLength, "table");
    } else {
      text = _formatInlines(text);
      text = StringUtil.wrap(text, 80 - indentLength, "\n");
    }

    text = text.replaceAll("(?m)^", indent);
    text = text.replaceAll("(?m) +$", StringPool.BLANK);

    return text;
  }

  private static FileImpl _fileUtil = FileImpl.getInstance();
  private static SAXReaderImpl _saxReaderUtil = SAXReaderImpl.getInstance();

  private boolean _initializeMissingJavadocs;
  private String _inputDir;
  private Map<String, Tuple> _javadocxXmlTuples = new HashMap<String, Tuple>();
  private Properties _languageProperties;
  private File _languagePropertiesFile;
  private String _outputFilePrefix;
  private boolean _updateJavadocs;
}
/** @author Brian Wing Shun Chan */
public class JavadocBuilder {

  public static void main(String[] args) {
    try {
      new JavadocBuilder(args);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public JavadocBuilder(String[] args) throws Exception {
    CmdLineParser cmdLineParser = new CmdLineParser();

    CmdLineParser.Option commandOption = cmdLineParser.addStringOption("command");
    CmdLineParser.Option limitOption = cmdLineParser.addStringOption("limit");
    CmdLineParser.Option ignoreAutogeneratedOption =
        cmdLineParser.addBooleanOption("ignoreAutogenerated");

    cmdLineParser.parse(args);

    String command = (String) cmdLineParser.getOptionValue(commandOption);
    String limit = (String) cmdLineParser.getOptionValue(limitOption);
    Boolean ignoreAutogenerated = (Boolean) cmdLineParser.getOptionValue(ignoreAutogeneratedOption);

    _process(command, limit, ignoreAutogenerated);
  }

  private void _addClassCommentElement(Element rootElement, JavaClass javaClass) {

    Element commentElement = rootElement.addElement("comment");

    String comment = _getCDATA(javaClass);

    if (comment.startsWith("Copyright (c) 2000-2010 Liferay, Inc.")) {
      comment = StringPool.BLANK;
    }

    if (comment.startsWith("<a href=\"" + javaClass.getName() + ".java.html\">")) {

      int pos = comment.indexOf("</a>");

      comment = comment.substring(pos + 4).trim();
    }

    commentElement.addCDATA(comment);
  }

  private void _addDocletElements(
      Element parentElement, AbstractJavaEntity abstractJavaEntity, String name) {

    DocletTag[] docletTags = abstractJavaEntity.getTagsByName(name);

    for (DocletTag docletTag : docletTags) {
      String value = docletTag.getValue();

      if (name.equals("author")
          || name.equals("see")
          || name.equals("since")
          || name.equals("version")) {

        /*if (value.startsWith("Raymond Aug")) {
        	value = "Raymond Aug\u00c3\u00a9";
        }*/

        DocUtil.add(parentElement, name, value);
      } else {
        Element element = parentElement.addElement(name);

        element.addCDATA(value);
      }
    }
  }

  private void _addDocletTags(Element parentElement, String name, String indent, StringBuilder sb) {

    List<Element> elements = parentElement.elements(name);

    for (Element element : elements) {
      sb.append(indent);
      sb.append(" * @");
      sb.append(name);
      sb.append(" ");

      Element commentElement = element.element("comment");

      if (commentElement != null) {
        sb.append(element.elementText("name"));
        sb.append(" ");
        sb.append(_getCDATA(element.elementText("comment")));
      } else {
        sb.append(_getCDATA(element.getText()));
      }

      sb.append("\n");
    }
  }

  private void _addFieldElement(Element rootElement, JavaField javaField) {
    Element fieldElement = rootElement.addElement("field");

    DocUtil.add(fieldElement, "name", javaField.getName());

    Element commentElement = fieldElement.addElement("comment");

    commentElement.addCDATA(_getCDATA(javaField));

    _addDocletElements(fieldElement, javaField, "deprecated");
    _addDocletElements(fieldElement, javaField, "see");
    _addDocletElements(fieldElement, javaField, "since");
    _addDocletElements(fieldElement, javaField, "version");
  }

  private void _addMethodElement(Element rootElement, JavaMethod javaMethod) {
    Element methodElement = rootElement.addElement("method");

    DocUtil.add(methodElement, "name", javaMethod.getName());

    Element commentElement = methodElement.addElement("comment");

    commentElement.addCDATA(_getCDATA(javaMethod));

    _addDocletElements(methodElement, javaMethod, "deprecated");
    _addParamElements(methodElement, javaMethod);
    _addReturnElement(methodElement, javaMethod);
    _addDocletElements(methodElement, javaMethod, "see");
    _addDocletElements(methodElement, javaMethod, "since");
    _addThrowsElements(methodElement, javaMethod);
    _addDocletElements(methodElement, javaMethod, "version");
  }

  private void _addParamElement(
      Element methodElement, JavaParameter javaParameter, DocletTag[] paramDocletTags) {

    String name = javaParameter.getName();
    String type = javaParameter.getType().getValue();
    String value = null;

    for (DocletTag paramDocletTag : paramDocletTags) {
      String curValue = paramDocletTag.getValue();

      if (!curValue.startsWith(name)) {
        continue;
      } else {
        curValue = value;

        break;
      }
    }

    Element paramElement = methodElement.addElement("param");

    DocUtil.add(paramElement, "name", name);
    DocUtil.add(paramElement, "type", type);

    if (value != null) {
      value = value.substring(name.length());
    }

    Element commentElement = paramElement.addElement("comment");

    commentElement.addCDATA(_getCDATA(value));
  }

  private void _addParamElements(Element methodElement, JavaMethod javaMethod) {

    JavaParameter[] javaParameters = javaMethod.getParameters();

    DocletTag[] paramDocletTags = javaMethod.getTagsByName("param");

    for (JavaParameter javaParameter : javaParameters) {
      _addParamElement(methodElement, javaParameter, paramDocletTags);
    }
  }

  private void _addReturnElement(Element methodElement, JavaMethod javaMethod) {

    Type returns = javaMethod.getReturns();

    if ((returns == null) || returns.getValue().equals("void")) {
      return;
    }

    _addDocletElements(methodElement, javaMethod, "return");
  }

  private void _addThrowsElement(
      Element methodElement, Type exception, DocletTag[] throwsDocletTags) {

    String name = exception.getJavaClass().getName();
    String value = null;

    for (DocletTag throwsDocletTag : throwsDocletTags) {
      String curValue = throwsDocletTag.getValue();

      if (!curValue.startsWith(name)) {
        continue;
      } else {
        curValue = value;

        break;
      }
    }

    Element throwsElement = methodElement.addElement("throws");

    DocUtil.add(throwsElement, "name", name);
    DocUtil.add(throwsElement, "type", exception.getValue());

    if (value != null) {
      value = value.substring(name.length());
    }

    Element commentElement = throwsElement.addElement("comment");

    commentElement.addCDATA(_getCDATA(value));
  }

  private void _addThrowsElements(Element methodElement, JavaMethod javaMethod) {

    Type[] exceptions = javaMethod.getExceptions();

    DocletTag[] throwsDocletTags = javaMethod.getTagsByName("throws");

    for (Type exception : exceptions) {
      _addThrowsElement(methodElement, exception, throwsDocletTags);
    }
  }

  private String _getCDATA(AbstractJavaEntity abstractJavaEntity) {
    return _getCDATA(abstractJavaEntity.getComment());
  }

  private String _getCDATA(String cdata) {
    if (cdata == null) {
      return StringPool.BLANK;
    }

    cdata =
        StringUtil.replace(
            cdata, new String[] {"\n", "<p>", "</p>"}, new String[] {" ", " <p> ", " </p> "});

    while (cdata.contains("  ")) {
      cdata = StringUtil.replace(cdata, "  ", " ");
    }

    return cdata.trim();
  }

  private String _getFieldKey(Element fieldElement) {
    return fieldElement.elementText("name");
  }

  private String _getFieldKey(JavaField javaField) {
    return javaField.getName();
  }

  private JavaClass _getJavaClass(String fileName) throws Exception {
    return _getJavaClass(fileName, null);
  }

  private JavaClass _getJavaClass(String fileName, Reader reader) throws Exception {

    int pos = fileName.indexOf("src/");

    if (pos == -1) {
      pos = fileName.indexOf("test/");
    }

    if (pos == -1) {
      throw new RuntimeException(fileName);
    }

    pos = fileName.indexOf("/", pos);

    String srcFile = fileName.substring(pos + 1, fileName.length());
    String className = StringUtil.replace(srcFile.substring(0, srcFile.length() - 5), "/", ".");

    JavaDocBuilder builder = new JavaDocBuilder();

    if (reader == null) {
      File file = new File(fileName);

      if (!file.exists()) {
        return null;
      }

      builder.addSource(file);
    } else {
      builder.addSource(reader);
    }

    return builder.getClassByName(className);
  }

  private String _getJavaClassComment(Element rootElement, JavaClass javaClass) {

    StringBuilder sb = new StringBuilder();

    sb.append("/**\n");
    sb.append(" * ");
    sb.append(_getCDATA(rootElement.elementText("comment")));
    sb.append("\n");
    sb.append(" *\n");

    String indent = StringPool.BLANK;

    _addDocletTags(rootElement, "author", indent, sb);
    _addDocletTags(rootElement, "deprecated", indent, sb);
    _addDocletTags(rootElement, "see", indent, sb);
    _addDocletTags(rootElement, "serial", indent, sb);
    _addDocletTags(rootElement, "since", indent, sb);
    _addDocletTags(rootElement, "version", indent, sb);

    sb.append(" */\n");

    return sb.toString();
  }

  private String _getJavadocXml(JavaClass javaClass) throws Exception {
    Element rootElement = _saxReaderUtil.createElement("javadoc");

    Document document = _saxReaderUtil.createDocument(rootElement);

    DocUtil.add(rootElement, "name", javaClass.getName());
    DocUtil.add(rootElement, "type", javaClass.getFullyQualifiedName());

    _addClassCommentElement(rootElement, javaClass);
    _addDocletElements(rootElement, javaClass, "author");
    _addDocletElements(rootElement, javaClass, "deprecated");
    _addDocletElements(rootElement, javaClass, "see");
    _addDocletElements(rootElement, javaClass, "serial");
    _addDocletElements(rootElement, javaClass, "since");
    _addDocletElements(rootElement, javaClass, "version");

    JavaMethod[] javaMethods = javaClass.getMethods();

    for (JavaMethod javaMethod : javaMethods) {
      _addMethodElement(rootElement, javaMethod);
    }

    JavaField[] javaFields = javaClass.getFields();

    for (JavaField javaField : javaFields) {
      _addFieldElement(rootElement, javaField);
    }

    return document.formattedString();
  }

  private String _getJavaFieldComment(
      String[] lines, Map<String, Element> fieldElementsMap, JavaField javaField) {

    String fieldKey = _getFieldKey(javaField);

    Element fieldElement = fieldElementsMap.get(fieldKey);

    if (fieldElement == null) {
      return null;
    }

    String line = lines[javaField.getLineNumber() - 1];

    String indent = StringPool.BLANK;

    for (char c : line.toCharArray()) {
      if (Character.isWhitespace(c)) {
        indent += c;
      } else {
        break;
      }
    }

    StringBuilder sb = new StringBuilder();

    sb.append(indent);
    sb.append("/**\n");
    sb.append(indent);
    sb.append(" * ");
    sb.append(fieldElement.elementText("comment"));
    sb.append("\n");
    sb.append(indent);
    sb.append(" *\n");

    _addDocletTags(fieldElement, "deprecated", indent, sb);
    _addDocletTags(fieldElement, "see", indent, sb);
    _addDocletTags(fieldElement, "since", indent, sb);
    _addDocletTags(fieldElement, "version", indent, sb);

    sb.append(indent);
    sb.append(" */\n");

    return sb.toString();
  }

  private String _getJavaMethodComment(
      String[] lines, Map<String, Element> methodElementsMap, JavaMethod javaMethod) {

    String methodKey = _getMethodKey(javaMethod);

    Element methodElement = methodElementsMap.get(methodKey);

    if (methodElement == null) {
      return null;
    }

    String line = lines[javaMethod.getLineNumber() - 1];

    String indent = StringPool.BLANK;

    for (char c : line.toCharArray()) {
      if (Character.isWhitespace(c)) {
        indent += c;
      } else {
        break;
      }
    }

    StringBuilder sb = new StringBuilder();

    sb.append(indent);
    sb.append("/**\n");
    sb.append(indent);
    sb.append(" * ");
    sb.append(methodElement.elementText("comment"));
    sb.append("\n");
    sb.append(indent);
    sb.append(" *\n");

    _addDocletTags(methodElement, "deprecated", indent, sb);
    _addDocletTags(methodElement, "param", indent, sb);
    _addDocletTags(methodElement, "return", indent, sb);
    _addDocletTags(methodElement, "see", indent, sb);
    _addDocletTags(methodElement, "since", indent, sb);
    _addDocletTags(methodElement, "throws", indent, sb);
    _addDocletTags(methodElement, "version", indent, sb);

    sb.append(indent);
    sb.append(" */\n");

    return sb.toString();
  }

  private String _getMethodKey(Element methodElement) {
    StringBuilder sb = new StringBuilder();

    sb.append(methodElement.elementText("name"));
    sb.append("(");

    List<Element> paramElements = methodElement.elements("param");

    for (Element paramElement : paramElements) {
      sb.append(paramElement.elementText("name"));
      sb.append("|");
      sb.append(paramElement.elementText("type"));
      sb.append(",");
    }

    sb.append(")");

    return sb.toString();
  }

  private String _getMethodKey(JavaMethod javaMethod) {
    StringBuilder sb = new StringBuilder();

    sb.append(javaMethod.getName());
    sb.append("(");

    JavaParameter[] javaParameters = javaMethod.getParameters();

    for (JavaParameter javaParameter : javaParameters) {
      sb.append(javaParameter.getName());
      sb.append("|");
      sb.append(javaParameter.getType().getValue());
      sb.append(",");
    }

    sb.append(")");

    return sb.toString();
  }

  private boolean _isGenerated(String content) {
    if (content.contains("<javadoc autogenerated=\"true\">")) {
      return true;
    } else {
      return false;
    }
  }

  private void _process(String command, String limit, Boolean ignoreAutogenerated)
      throws Exception {

    DirectoryScanner ds = new DirectoryScanner();

    ds.setBasedir(_basedir);
    ds.setExcludes(new String[] {"**\\classes\\**", "**\\portal-client\\**", "**\\portal-web\\**"});

    List<String> includes = new ArrayList<String>();

    if (Validator.isNotNull(limit) && !limit.startsWith("$")) {
      String[] limitArray = StringUtil.split(limit, '/');

      for (String curLimit : limitArray) {
        includes.add("**\\" + StringUtil.replace(curLimit, ".", "\\") + "\\**\\*.java");
        includes.add("**\\" + curLimit + ".java");
      }
    } else {
      includes.add("**\\*.java");
    }

    ds.setIncludes(includes.toArray(new String[includes.size()]));

    ds.scan();

    String[] fileNames = ds.getIncludedFiles();

    for (String fileName : fileNames) {
      fileName = StringUtil.replace(fileName, "\\", "/");

      /*if (!fileName.endsWith("Isolation.java")) {
      	continue;
      }*/

      if ((ignoreAutogenerated != null) && (ignoreAutogenerated.booleanValue())) {

        File file = new File(_basedir + fileName);

        if (file.exists()) {
          String oldContent = _fileUtil.read(_basedir + fileName + "doc");

          if (_isGenerated(oldContent)) {
            continue;
          }
        }
      }

      if (command.equals("cleanup")) {
        _processGet(fileName);
        _processSave(fileName);
        _processDelete(fileName);
      } else if (command.equals("commit")) {
        _processSave(fileName);
        _processDelete(fileName);
      } else if (command.equals("delete")) {
        _processDelete(fileName);
      } else if (command.equals("get")) {
        _processGet(fileName);
      } else if (command.equals("save")) {
        _processSave(fileName);
      }
    }
  }

  private void _processDelete(String fileName) throws Exception {
    _removeJavadocFromJava(fileName, true);
  }

  private void _processGet(String fileName) throws Exception {
    File javadocFile = new File(_basedir + fileName + "doc");

    if (!javadocFile.exists()) {
      _updateJavadocFromJava(fileName);
    }

    String javaWithoutJavadoc = _removeJavadocFromJava(fileName, false);

    _updateJavaFromJavadoc(fileName, javaWithoutJavadoc);
  }

  private void _processSave(String fileName) throws Exception {
    _updateJavadocFromJava(fileName);
  }

  private String _removeJavadocFromJava(String fileName, boolean log) throws Exception {

    File file = new File(_basedir + fileName);

    String oldContent = _fileUtil.read(file);

    String[] lines = StringUtil.splitLines(oldContent);

    JavaClass javaClass = _getJavaClass(fileName, new UnsyncStringReader(oldContent));

    Set<Integer> lineNumbers = new HashSet<Integer>();

    lineNumbers.add(javaClass.getLineNumber());

    JavaMethod[] javaMethods = javaClass.getMethods();

    for (JavaMethod javaMethod : javaMethods) {
      lineNumbers.add(javaMethod.getLineNumber());
    }

    JavaField[] javaFields = javaClass.getFields();

    for (JavaField javaField : javaFields) {
      lineNumbers.add(javaField.getLineNumber());
    }

    for (int lineNumber : lineNumbers) {
      int pos = lineNumber - 2;

      String line = lines[pos].trim();

      if (line.endsWith("*/")) {
        while (true) {
          lines[pos] = null;

          if (line.startsWith("/**")) {
            break;
          }

          line = lines[--pos].trim();
        }
      }
    }

    StringBuilder sb = new StringBuilder(oldContent.length());

    for (String line : lines) {
      if (line != null) {
        sb.append(line);
        sb.append("\n");
      }
    }

    String newContent = sb.toString().trim();

    if ((oldContent == null) || !oldContent.equals(newContent)) {
      _fileUtil.write(file, newContent);

      if (log) {
        System.out.println("Writing " + file);
      }
    }

    return newContent;
  }

  private void _updateJavadocFromJava(String fileName) throws Exception {
    File file = new File(_basedir + fileName + "doc");

    String oldContent = null;

    if (file.exists()) {
      oldContent = _fileUtil.read(file);

      if (_isGenerated(oldContent)) {
        return;
      }
    }

    JavaClass javaClass = _getJavaClass(fileName);

    String newContent = _getJavadocXml(javaClass);

    if ((oldContent == null) || !oldContent.equals(newContent)) {
      _fileUtil.write(file, newContent.getBytes());

      System.out.println("Writing " + file);
    }
  }

  private void _updateJavaFromJavadoc(String fileName, String oldContent) throws Exception {

    File javadocFile = new File(_basedir + fileName + "doc");

    if (!javadocFile.exists()) {
      return;
    }

    File file = new File(_basedir + fileName);

    if (oldContent == null) {
      oldContent = _fileUtil.read(file);
    }

    String[] lines = StringUtil.splitLines(oldContent);

    JavaClass javaClass = _getJavaClass(fileName, new UnsyncStringReader(oldContent));

    Document document = _saxReaderUtil.read(javadocFile);

    Element rootElement = document.getRootElement();

    Map<Integer, String> commentsMap = new TreeMap<Integer, String>();

    commentsMap.put(javaClass.getLineNumber(), _getJavaClassComment(rootElement, javaClass));

    Map<String, Element> methodElementsMap = new HashMap<String, Element>();

    List<Element> methodElements = rootElement.elements("method");

    for (Element methodElement : methodElements) {
      String methodKey = _getMethodKey(methodElement);

      methodElementsMap.put(methodKey, methodElement);
    }

    JavaMethod[] javaMethods = javaClass.getMethods();

    for (JavaMethod javaMethod : javaMethods) {
      if (commentsMap.containsKey(javaMethod.getLineNumber())) {
        continue;
      }

      commentsMap.put(
          javaMethod.getLineNumber(), _getJavaMethodComment(lines, methodElementsMap, javaMethod));
    }

    Map<String, Element> fieldElementsMap = new HashMap<String, Element>();

    List<Element> fieldElements = rootElement.elements("field");

    for (Element fieldElement : fieldElements) {
      String fieldKey = _getFieldKey(fieldElement);

      fieldElementsMap.put(fieldKey, fieldElement);
    }

    JavaField[] javaFields = javaClass.getFields();

    for (JavaField javaField : javaFields) {
      if (commentsMap.containsKey(javaField.getLineNumber())) {
        continue;
      }

      commentsMap.put(
          javaField.getLineNumber(), _getJavaFieldComment(lines, fieldElementsMap, javaField));
    }

    StringBuilder sb = new StringBuilder(oldContent.length());

    for (int lineNumber = 1; lineNumber <= lines.length; lineNumber++) {
      String line = lines[lineNumber - 1];

      String comments = commentsMap.get(lineNumber);

      if (comments != null) {
        sb.append(comments);
      }

      sb.append(line);
      sb.append("\n");
    }

    String newContent = sb.toString().trim();

    if ((oldContent == null) || !oldContent.equals(newContent)) {
      _fileUtil.write(file, newContent);

      System.out.println("Writing " + file);
    }
  }

  private static FileImpl _fileUtil = FileImpl.getInstance();
  private static SAXReaderImpl _saxReaderUtil = SAXReaderImpl.getInstance();

  private String _basedir = "./";
}