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 formatJSP(String fileName, String absolutePath, String content)
      throws IOException {

    StringBundler sb = new StringBundler();

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

    int lineCount = 0;

    String line = null;

    String previousLine = StringPool.BLANK;

    String currentAttributeAndValue = null;
    String previousAttribute = null;
    String previousAttributeAndValue = null;

    boolean readAttributes = false;

    String currentException = null;
    String previousException = null;

    boolean hasUnsortedExceptions = false;

    boolean javaSource = false;

    while ((line = unsyncBufferedReader.readLine()) != null) {
      lineCount++;

      if (!fileName.contains("jsonw") || !fileName.endsWith("action.jsp")) {

        line = trimLine(line, false);
      }

      if (line.contains("<aui:button ") && line.contains("type=\"button\"")) {

        processErrorMessage(fileName, "aui:button " + fileName + " " + lineCount);
      }

      if (line.contains("debugger.")) {
        processErrorMessage(fileName, "debugger " + fileName + " " + lineCount);
      }

      String trimmedLine = StringUtil.trimLeading(line);
      String trimmedPreviousLine = StringUtil.trimLeading(previousLine);

      checkStringBundler(trimmedLine, fileName, lineCount);

      checkEmptyCollection(trimmedLine, fileName, lineCount);

      if (trimmedLine.equals("<%") || trimmedLine.equals("<%!")) {
        javaSource = true;
      } else if (trimmedLine.equals("%>")) {
        javaSource = false;
      }

      if (javaSource || trimmedLine.contains("<%= ")) {
        checkInefficientStringMethods(line, fileName, absolutePath, lineCount);
      }

      if (javaSource
          && portalSource
          && !isExcluded(_unusedVariablesExclusions, fileName, lineCount)
          && !_jspContents.isEmpty()
          && hasUnusedVariable(fileName, trimmedLine)) {

        processErrorMessage(fileName, "Unused variable: " + fileName + " " + lineCount);
      }

      if (!trimmedLine.equals("%>")
          && line.contains("%>")
          && !line.contains("--%>")
          && !line.contains(" %>")) {

        line = StringUtil.replace(line, "%>", " %>");
      }

      if (line.contains("<%=") && !line.contains("<%= ")) {
        line = StringUtil.replace(line, "<%=", "<%= ");
      }

      if (trimmedPreviousLine.equals("%>")
          && Validator.isNotNull(line)
          && !trimmedLine.equals("-->")) {

        sb.append("\n");
      } else if (Validator.isNotNull(previousLine)
          && !trimmedPreviousLine.equals("<!--")
          && trimmedLine.equals("<%")) {

        sb.append("\n");
      } else if (trimmedPreviousLine.equals("<%") && Validator.isNull(line)) {

        continue;
      } else if (trimmedPreviousLine.equals("<%") && trimmedLine.startsWith("//")) {

        sb.append("\n");
      } else if (Validator.isNull(previousLine) && trimmedLine.equals("%>") && (sb.index() > 2)) {

        String lineBeforePreviousLine = sb.stringAt(sb.index() - 3);

        if (!lineBeforePreviousLine.startsWith("//")) {
          sb.setIndex(sb.index() - 1);
        }
      }

      if ((trimmedLine.startsWith("if (")
              || trimmedLine.startsWith("else if (")
              || trimmedLine.startsWith("while ("))
          && trimmedLine.endsWith(") {")) {

        checkIfClauseParentheses(trimmedLine, fileName, lineCount);
      }

      if (readAttributes) {
        if (!trimmedLine.startsWith(StringPool.FORWARD_SLASH)
            && !trimmedLine.startsWith(StringPool.GREATER_THAN)) {

          int pos = trimmedLine.indexOf(StringPool.EQUAL);

          if (pos != -1) {
            String attribute = trimmedLine.substring(0, pos);

            if (!trimmedLine.endsWith(StringPool.APOSTROPHE)
                && !trimmedLine.endsWith(StringPool.GREATER_THAN)
                && !trimmedLine.endsWith(StringPool.QUOTE)) {

              processErrorMessage(fileName, "attribute: " + fileName + " " + lineCount);

              readAttributes = false;
            } else if (trimmedLine.endsWith(StringPool.APOSTROPHE)
                && !trimmedLine.contains(StringPool.QUOTE)) {

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

              readAttributes = false;
            } else if (Validator.isNotNull(previousAttribute)) {
              if (!isAttributName(attribute) && !attribute.startsWith(StringPool.LESS_THAN)) {

                processErrorMessage(fileName, "attribute: " + fileName + " " + lineCount);

                readAttributes = false;
              } else if (Validator.isNull(previousAttributeAndValue)
                  && (previousAttribute.compareTo(attribute) > 0)) {

                previousAttributeAndValue = previousLine;
                currentAttributeAndValue = line;
              }
            }

            if (!readAttributes) {
              previousAttribute = null;
              previousAttributeAndValue = null;
            } else {
              previousAttribute = attribute;
            }
          }
        } else {
          previousAttribute = null;

          readAttributes = false;
        }
      }

      if (!hasUnsortedExceptions) {
        int i = line.indexOf("<liferay-ui:error exception=\"<%=");

        if (i != -1) {
          currentException = line.substring(i + 33);

          if (Validator.isNotNull(previousException)
              && (previousException.compareTo(currentException) > 0)) {

            hasUnsortedExceptions = true;
          }
        }

        if (!hasUnsortedExceptions) {
          previousException = currentException;
          currentException = null;
        }
      }

      if (trimmedLine.startsWith(StringPool.LESS_THAN)
          && !trimmedLine.startsWith("<%")
          && !trimmedLine.startsWith("<!")) {

        if (!trimmedLine.contains(StringPool.GREATER_THAN)
            && !trimmedLine.contains(StringPool.SPACE)) {

          readAttributes = true;
        } else {
          line = sortAttributes(fileName, line, lineCount, true);
        }
      }

      if (!trimmedLine.contains(StringPool.DOUBLE_SLASH)
          && !trimmedLine.startsWith(StringPool.STAR)) {

        while (trimmedLine.contains(StringPool.TAB)) {
          line = StringUtil.replaceLast(line, StringPool.TAB, StringPool.SPACE);

          trimmedLine = StringUtil.replaceLast(trimmedLine, StringPool.TAB, StringPool.SPACE);
        }

        while (trimmedLine.contains(StringPool.DOUBLE_SPACE)
            && !trimmedLine.contains(StringPool.QUOTE + StringPool.DOUBLE_SPACE)
            && !fileName.endsWith(".vm")) {

          line = StringUtil.replaceLast(line, StringPool.DOUBLE_SPACE, StringPool.SPACE);

          trimmedLine =
              StringUtil.replaceLast(trimmedLine, StringPool.DOUBLE_SPACE, StringPool.SPACE);
        }
      }

      if (!fileName.endsWith("/touch.jsp")) {
        int x = line.indexOf("<%@ include file");

        if (x != -1) {
          x = line.indexOf(StringPool.QUOTE, x);

          int y = line.indexOf(StringPool.QUOTE, x + 1);

          if (y != -1) {
            String includeFileName = line.substring(x + 1, y);

            Matcher matcher = _jspIncludeFilePattern.matcher(includeFileName);

            if (!matcher.find()) {
              processErrorMessage(fileName, "include: " + fileName + " " + lineCount);
            }
          }
        }
      }

      line = replacePrimitiveWrapperInstantiation(fileName, line, lineCount);

      previousLine = line;

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

    unsyncBufferedReader.close();

    content = sb.toString();

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

    content = formatTaglibQuotes(fileName, content, StringPool.QUOTE);
    content = formatTaglibQuotes(fileName, content, StringPool.APOSTROPHE);

    if (Validator.isNotNull(previousAttributeAndValue)) {
      content =
          StringUtil.replaceFirst(
              content,
              previousAttributeAndValue + "\n" + currentAttributeAndValue,
              currentAttributeAndValue + "\n" + previousAttributeAndValue);
    }

    if (hasUnsortedExceptions) {
      if ((StringUtil.count(content, currentException) > 1)
          || (StringUtil.count(content, previousException) > 1)) {

        processErrorMessage(fileName, "unsorted exceptions: " + fileName);
      } else {
        content = StringUtil.replaceFirst(content, previousException, currentException);

        content = StringUtil.replaceLast(content, currentException, previousException);
      }
    }

    return content;
  }