Package org.eclipse.php.formatter.core

Source Code of org.eclipse.php.formatter.core.CodeFormatterVisitor

/*******************************************************************************
* Copyright (c) 2013 Zend Techologies Ltd.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Zend Technologies Ltd. - initial API and implementation
*******************************************************************************/
package org.eclipse.php.formatter.core;

import java.io.Reader;
import java.io.StringReader;
import java.util.*;

import java_cup.runtime.Symbol;

import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.TypeReference;
import org.eclipse.jface.text.*;
import org.eclipse.php.internal.core.PHPVersion;
import org.eclipse.php.internal.core.ast.nodes.*;
import org.eclipse.php.internal.core.ast.scanner.AstLexer;
import org.eclipse.php.internal.core.ast.visitor.AbstractVisitor;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPDocBlock;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPDocTag;
import org.eclipse.php.internal.core.compiler.ast.nodes.VarComment;
import org.eclipse.php.internal.core.compiler.ast.parser.php5.CompilerAstLexer;
import org.eclipse.php.internal.core.documentModel.parser.PHPRegionContext;
import org.eclipse.php.internal.core.documentModel.partitioner.PHPPartitionTypes;
import org.eclipse.php.internal.core.format.ICodeFormattingProcessor;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.wst.sse.core.internal.provisional.text.*;

/**
* Description: This class formats a given {@link StructuredDocument}
*
* @author moshe, 2007
*/
public class CodeFormatterVisitor extends AbstractVisitor implements
    ICodeFormattingProcessor {

  public static final int NO_LINE_WRAP = 0;
  public static final int FIRST_WRAP_WHEN_NECESSARY = 1;
  public static final int WRAP_FIRST_ELEMENT = 2;
  public static final int WRAP_ALL_ELEMENTS = 3;
  public static final int WRAP_ALL_ELEMENTS_NO_INDENT_FIRST = 4;
  public static final int WRAP_ALL_ELEMENTS_EXCEPT_FIRST = 5;
  public static final int ALWAYS_WRAP_ELEMENT = 6;
  public static final int WRAP_WHEN_NECESSARY = 7;

  private static final int NO_LINE_WRAP_INDENT = -1;
  private static final int DEFAULT_INDENTATION = 0;
  private static final int INDENT_ON_COLUMN = 1;
  private static final int INDENT_ONE = 2;

  private static final String EMPTY_STRING = ""; //$NON-NLS-1$
  private static final String FUNCTION_NAME_PRINT = "print"; //$NON-NLS-1$

  private static final byte PHP_OPEN_TAG = 0;
  private static final byte PHP_OPEN_SHORT_TAG = 1;
  private static final char PHP_OPEN_SHORT_TAG_WITH_EQUAL = 2;
  private static final char PHP_OPEN_ASP_TAG = 3;

  private static final char EQUAL = '=';
  private static final String KEY_VALUE_OPERATOR = "=>"; //$NON-NLS-1$
  private static final char OPEN_PARN = '(';
  private static final char CLOSE_PARN = ')';
  private static final char OPEN_CURLY = '{';
  // private static final char CLOSE_CURLY = '}';
  private static final char OPEN_BRACKET = '[';
  private static final char CLOSE_BRACKET = ']';
  private static final char COLON = ':';
  private static final char SEMICOLON = ';';
  private static final char SPACE = ' ';
  private static final char COMMA = ',';
  private static final char QUESTION_MARK = '?';
  private static final char PHPDOC_CLASS_SEPARATOR = '|';
  private String lineSeparator;

  private CodeFormatterPreferences preferences;
  private final IDocument document;
  private PHPVersion phpVersion;
  private boolean useShortTags;

  private int indentationLevel;
  private boolean indentationLevelDesending = false;
  private AstLexer astLexer;
  private boolean isPhpEqualTag = false;
  private int startRegionPosition = -1;
  private int endRegionPosition = Integer.MAX_VALUE;
  private boolean isPrevSpace = false;
  private boolean isHeredocSemicolon = false;
  private int lineWidth = 0;
  private int binaryExpressionLineWrapPolicy = -1;// use this as local since
  // it changes its state
  private int binaryExpressionIndentGap = 0;// use this as local since it
  // changes its state
  private boolean wasBinaryExpressionWrapped = false;
  private String binaryExpressionSavedBuffer = null;
  private InfixExpression binaryExpressionSavedNode = null;
  private int binaryExpressionSavedChangesIndex = -1;
  private int binaryExpressionRevertPolicy = -1;
  private boolean isBinaryExpressionExtraIndentation = false;

  // append chars to buffer through insertSpace or appendToBuffer
  private StringBuffer replaceBuffer = new StringBuffer();
  private List<Symbol> tokens = new ArrayList<Symbol>();

  /**
   * list of <ReplaceEdit>
   */
  private List<ReplaceEdit> changes = new LinkedList<ReplaceEdit>();
  private int stInScriptin = -1;
  private boolean isInsideFun;

  private Stack<Integer> chainStack = new Stack<Integer>();
  private Integer peek;
  private Set<IfStatement> processedIfStatements = new HashSet<IfStatement>();
  private boolean newLineOfComment;
  private List<String> commentWords;
  /** disabling */
  boolean editsEnabled;
  boolean useTags;
  int tagsKind;
  private String disablingTag, enablingTag;
  // this is for never indent at first line
  private boolean doNotIndent = false;
  boolean inComment = false;
  private int indentLengthForComment;
  private String indentStringForComment;
  private boolean blockEnd;
  private boolean recordCommentIndentVariables = false;
  // for block comment,multiline comment at the end of break statement of case
  // statement
  private List<Integer> indentationLevelList = new ArrayList<Integer>();
  Stack<CommentIndentationObject> commentIndetationStack = new Stack<CodeFormatterVisitor.CommentIndentationObject>();

  private boolean ignoreEmptyLineSetting = false;

  public CodeFormatterVisitor(IDocument document,
      CodeFormatterPreferences codeFormatterPreferences,
      String lineSeparator, PHPVersion phpVersion, boolean useShortTags,
      IRegion region) throws Exception {
    this(document, codeFormatterPreferences, lineSeparator, phpVersion,
        useShortTags, region, 0);
  }

  public CodeFormatterVisitor(IDocument document,
      CodeFormatterPreferences codeFormatterPreferences,
      String lineSeparator, PHPVersion phpVersion, boolean useShortTags,
      IRegion region, int indentationLevel) throws Exception {
    this.phpVersion = phpVersion;
    this.useShortTags = useShortTags;
    this.document = document;
    this.lineSeparator = lineSeparator;
    this.indentationLevel = indentationLevel;
    this.preferences = codeFormatterPreferences;
    this.startRegionPosition = region.getOffset();
    this.endRegionPosition = startRegionPosition + region.getLength();

    ignoreEmptyLineSetting = !preferences.indent_empty_lines;

    Program program = null;
    try {
      final Reader reader = new StringReader(document.get());
      program = ASTParser.newParser(reader, phpVersion, true).createAST(
          new NullProgressMonitor());
    } catch (Exception e) {
      Logger.log(Logger.INFO,
          "Parsing error, file could not be formatted.");//$NON-NLS-1$
    }

    this.useTags = preferences.use_tags;
    this.tagsKind = 0;
    if (this.useTags) {
      if (preferences.disabling_tag != null
          && preferences.disabling_tag.length > 0) {
        this.disablingTag = new String(preferences.disabling_tag);
      }
      if (preferences.enabling_tag != null
          && preferences.enabling_tag.length > 0) {
        this.enablingTag = new String(preferences.enabling_tag);
      }
    }
    this.editsEnabled = true;
    if (program != null) {
      program.accept(this);
    }

  }

  public CodeFormatterVisitor(IDocument document, String lineSeparator,
      PHPVersion phpVersion, boolean useShortTags, IRegion region)
      throws Exception {
    this(document, CodeFormatterPreferences.getDefaultPreferences(),
        lineSeparator, phpVersion, useShortTags, region, 0);
  }

  // insert chars to the buffer
  private void appendToBuffer(Object obj) {
    isPrevSpace = false;
    if (obj == null)
      return;
    replaceBuffer.append(obj);
    if (!lineSeparator.equals(obj)) {
      lineWidth += obj.toString().length();
    }
  }

  private int checkFirstTokenLength(int start, int end) {
    int length = 0;
    try {
      scan(start, end);
      Symbol token = (Symbol) tokens.get(0);
      length = token.right - token.left;
    } catch (Exception e) {
      Logger.logException(e);
    }
    return length;
  }

  private int countStrInBuffer(String str) {
    int count = 0;
    int index = replaceBuffer.indexOf(str);
    while (index >= 0 && index < replaceBuffer.length()) {
      index = replaceBuffer.indexOf(str, index + 1);
      count++;
    }
    return count;
  }

  private char getBufferFirstChar(int position) throws BadLocationException {
    for (int offset = position; offset < replaceBuffer.length(); offset++) {
      char currChar = replaceBuffer.charAt(offset);
      if (currChar != ' ' && currChar != '\t' && currChar != '\r'
          && currChar != '\n') {
        // not empty line
        return currChar;
      }
    }
    return '\0';
  }

  public List<ReplaceEdit> getChanges() {
    IRegion[] partitions = new IRegion[0];
    try {
      partitions = getAllSingleLine(TextUtilities.computePartitioning(
          document,
          IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, 0,
          document.getLength(), false));
    } catch (BadLocationException e) {
    }
    List<ReplaceEdit> allChanges = Collections.unmodifiableList(changes);
    List<ReplaceEdit> result = new ArrayList<ReplaceEdit>();
    for (ReplaceEdit edit : allChanges) {
      if (isInSingleLine(edit, partitions, 0)) {
        continue;
      }
      result.add(edit);
    }
    return Collections.unmodifiableList(result);
  }

  private int getCharPosition(int start, int end, char c) {
    try {
      for (int index = 0; start + index < end; index++) {
        if (document.getChar(start + index) == c) {
          return start + index;
        }
      }
    } catch (BadLocationException e) {
      Logger.logException(e);
    }
    return -1;
  }

  private String getDocumentString(int start, int end) {
    char[] result = new char[end - start];
    try {
      for (int index = 0; start + index < end; index++) {
        result[index] = document.getChar(start + index);
      }
    } catch (BadLocationException e) {
      Logger.logException(e);
    }
    return new String(result);
  }

  private AstLexer getLexer(Reader reader) throws Exception {
    AstLexer result = null;
    if (PHPVersion.PHP5.equals(phpVersion)) {
      result = new CompilerAstLexer(reader);
      ((CompilerAstLexer) result).setAST(new AST(reader, PHPVersion.PHP5,
          false, useShortTags));
      stInScriptin = CompilerAstLexer.ST_IN_SCRIPTING; // save the initial
      // state for reset
      // operation
    } else if (PHPVersion.PHP4.equals(phpVersion)) {
      result = new org.eclipse.php.internal.core.compiler.ast.parser.php4.CompilerAstLexer(
          reader);
      ((org.eclipse.php.internal.core.compiler.ast.parser.php4.CompilerAstLexer) result)
          .setAST(new AST(reader, PHPVersion.PHP4, false,
              useShortTags));
      stInScriptin = org.eclipse.php.internal.core.compiler.ast.parser.php4.CompilerAstLexer.ST_IN_SCRIPTING; // save
      // the
      // initial
      // state
      // for
      // reset
      // operation
    } else if (PHPVersion.PHP5_3.equals(phpVersion)) {
      result = new org.eclipse.php.internal.core.compiler.ast.parser.php53.CompilerAstLexer(
          reader);
      ((org.eclipse.php.internal.core.compiler.ast.parser.php53.CompilerAstLexer) result)
          .setAST(new AST(reader, PHPVersion.PHP5_3, false,
              useShortTags));
      stInScriptin = org.eclipse.php.internal.core.compiler.ast.parser.php53.CompilerAstLexer.ST_IN_SCRIPTING; // save
      // the
      // initial
      // state
      // for
      // reset
      // operation
    } else if (PHPVersion.PHP5_4.equals(phpVersion)) {
      result = new org.eclipse.php.internal.core.compiler.ast.parser.php54.CompilerAstLexer(
          reader);
      ((org.eclipse.php.internal.core.compiler.ast.parser.php54.CompilerAstLexer) result)
          .setAST(new AST(reader, PHPVersion.PHP5_4, false,
              useShortTags));
      stInScriptin = org.eclipse.php.internal.core.compiler.ast.parser.php54.CompilerAstLexer.ST_IN_SCRIPTING; // save
      // the
      // initial
      // state
      // for
      // reset
      // operation
    } else if (PHPVersion.PHP5_5.equals(phpVersion)) {
      result = new org.eclipse.php.internal.core.compiler.ast.parser.php55.CompilerAstLexer(
          reader);
      ((org.eclipse.php.internal.core.compiler.ast.parser.php55.CompilerAstLexer) result)
          .setAST(new AST(reader, PHPVersion.PHP5_5, false,
              useShortTags));
      stInScriptin = org.eclipse.php.internal.core.compiler.ast.parser.php55.CompilerAstLexer.ST_IN_SCRIPTING; // save
      // the
      // initial
      // state
      // for
      // reset
      // operation
    } else {
      throw new IllegalArgumentException("unrecognized version " //$NON-NLS-1$
          + phpVersion);
    }
    return result;
  }

  private byte getPhpStartTag(int offset) {
    try {
      if (document.getChar(offset) == '<') {
        if (document.getChar(offset + 1) == '%') {
          return PHP_OPEN_ASP_TAG;
        } else if (document.getChar(offset + 2) == '=') {
          return PHP_OPEN_SHORT_TAG_WITH_EQUAL;
        } else if (document.getChar(offset + 2) != 'p'
            && document.getChar(offset + 2) != 'P') {
          return PHP_OPEN_SHORT_TAG;
        } else if (document.getChar(offset + 1) == '?') {
          return PHP_OPEN_TAG;
        }
      }
    } catch (Exception e) {
      Logger.logException(e);
    }
    return -1;
  }

  private int getPhpTagIndentationLevel(int offset) {
    try {
      final int line = document.getLineOfOffset(offset);
      final int startLineOffset = document.getLineOffset(line);

      int diff = 0;
      for (int i = startLineOffset; i < offset; i++) {
        if (document.getChar(i) == '\t' || document.getChar(i) == ' ') {
          diff++;
        } else {
          break;
        }
      }

      if (preferences.indentationChar == CodeFormatterPreferences.TAB_CHAR) {
        return diff;
      } else {
        if (preferences.indentationSize <= 0) {
          return -1;
        }
        return diff / preferences.indentationSize;
      }
    } catch (Exception e) {
      Logger.logException(e);
    }
    return -1;
  }

  /**
   * handle the action of if, while, do while, for statements.
   */
  private void handleAction(int lastPosition, Statement action,
      boolean addNewlineBeforeAction) {
    boolean isIndentationAdded = false;
    if (action.getType() == ASTNode.BLOCK) {
      isIndentationAdded = handleBlockOpenBrace(
          this.preferences.brace_position_for_block,
          this.preferences.insert_space_before_opening_brace_in_block);
    } else if (action.getType() == ASTNode.EMPTY_STATEMENT) {
      // This is an empty statement
      if (this.preferences.new_line_for_empty_statement) {
        insertNewLine();
        indentationLevel++;
        indent();
        isIndentationAdded = true;
      }
    } else {
      if (addNewlineBeforeAction) {
        // single statement should indent
        indentationLevel++;
        insertNewLine();
        indent();
        isIndentationAdded = true;
      } else {
        insertSpace();
      }
    }
    handleChars(lastPosition, action.getStart());

    action.accept(this);

    if (isIndentationAdded) {
      indentationLevel--;
      indentationLevelDesending = true;
    }
  }

  /**
   * handle the '{' of a block
   *
   * @param bracePosition
   *            one of Sameline
   * @param placeSpaceBeforeOpenCurly
   * @return
   */
  private boolean handleBlockOpenBrace(byte bracePosition,
      boolean placeSpaceBeforeOpenCurly) {
    boolean isIndentationAdded = false;
    switch (bracePosition) {
    case CodeFormatterPreferences.NEXT_LINE_INDENT:
      indentationLevel++;
      isIndentationAdded = true;
    case CodeFormatterPreferences.NEXT_LINE:
      insertNewLine();
      indent();
      break;
    default:
    case CodeFormatterPreferences.SAME_LINE:
      if (placeSpaceBeforeOpenCurly) {
        insertSpace();
      }
      break;
    }
    return isIndentationAdded;
  }

  @SuppressWarnings("unchecked")
  private void handleChars(int offset, int end) {
    try {
      // check if the changed region is in the formatting requested region
      if (startRegionPosition < end && endRegionPosition >= end) {
        boolean hasComments = hasComments(offset, end);

        if (hasComments) {
          // handle the comments
          handleComments(offset, end, astLexer.getCommentList(),
              false, 0);
        } else {
          handleCharsWithoutComments(offset, end);
        }
      }

      // clear the buffer
      replaceBuffer.setLength(0);
    } catch (Exception e) {
      Logger.logException(e);
    }
  }

  @SuppressWarnings("unchecked")
  private void handleChars1(int offset, int end, boolean isIndented,
      int indentGap) {
    try {
      // check if the changed region is in the formatting requested region
      if (startRegionPosition < end && endRegionPosition >= end) {
        boolean hasComments = hasComments(offset, end);

        if (hasComments) {
          // handle the comments
          handleComments(offset, end, astLexer.getCommentList(),
              isIndented, indentGap);
        } else {
          handleCharsWithoutComments(offset, end);
        }
      }

      // clear the buffer
      replaceBuffer.setLength(0);
    } catch (Exception e) {
      Logger.logException(e);
    }
  }

  private boolean hasComments(int offset, int end) throws Exception {
    scan(offset, end);

    assert astLexer != null;
    boolean hasComments = astLexer.getCommentList().size() > 0;
    return hasComments;
  }

  private void handleCharsWithoutComments(int offset, int end)
      throws BadLocationException {
    handleCharsWithoutComments(offset, end, false);
  }

  private void handleCharsWithoutComments(int offset, int end,
      boolean isComment) throws BadLocationException {
    String content = document.get(offset, end - offset).toLowerCase();
    int phpTagOpenIndex = -1;
    if (!isComment
        && ((phpTagOpenIndex = content.indexOf("<?")) != -1 || (phpTagOpenIndex = content //$NON-NLS-1$
            .indexOf("<%")) != -1)) { //$NON-NLS-1$
      handleSplittedPhpBlock(offset + phpTagOpenIndex, end);
    }

    else {
      // reset the isPrevSpace while replacing the chars
      isPrevSpace = false;
      int startLine = document.getLineOfOffset(offset);
      int endLine = document.getLineOfOffset(end);
      int emptyLines = 0;

      if (!ignoreEmptyLineSetting) {
        // count empty lines
        for (int line = startLine; line < endLine; line++) {
          if (isEmptyLine(line)) {
            emptyLines++;
          }
        }

        // set the preserve empty lines
        if (emptyLines > preferences.blank_line_preserve_empty_lines) {
          emptyLines = preferences.blank_line_preserve_empty_lines;
        }

        int newLinesInBuffer = countStrInBuffer(lineSeparator);

        // add empty lines
        if (emptyLines > 0 && newLinesInBuffer < emptyLines + 1) {
          for (int line = newLinesInBuffer; line < emptyLines + 1; line++) {
            insertNewLine();
          }
          if (inComment) {
            if (!doNotIndent) {
              indentForComment(indentationLevelDesending);
            }
          } else {
            indent();
          }

        }

      }
      ignoreEmptyLineSetting = false;

      // check if the replacement and the origin string are the same
      boolean needToReplace = true;
      if (end - offset == replaceBuffer.length()) {
        // in case the buffer is empty and the doc length is 0
        // no need to replace
        if (end - offset == 0 && replaceBuffer.length() == 0) {
          needToReplace = false;
        } else {
          // the buffer and document segment length are the same
          // in case of 2 different chars we need to replace the
          // document segment
          needToReplace = false;
          for (int index = 0; offset + index < end; index++) {
            char docChar = document.getChar(offset + index);
            char bufferChar = replaceBuffer.charAt(index);
            if (docChar != bufferChar) {
              needToReplace = true;
              break;
            }
          }
        }
      }
      if (needToReplace && editsEnabled) {
        insertString(offset, end, replaceBuffer.toString());
      }
      if (recordCommentIndentVariables) {
        recordCommentIndentVariables = false;
        indentLengthForComment = lineWidth;
        String afterNewLine = EMPTY_STRING;
        int position = replaceBuffer.lastIndexOf(lineSeparator);
        if (position >= 0) {
          if (getBufferFirstChar(position + lineSeparator.length()) == '\0') {
            afterNewLine = replaceBuffer.substring(position
                + lineSeparator.length(),
                replaceBuffer.length());
          }
        } else {
          if (getBufferFirstChar(0) == '\0') {
            afterNewLine = replaceBuffer.toString();
          }
        }
        indentStringForComment = afterNewLine;
      }
      indentationLevelDesending = false;
      // clear the buffer
      replaceBuffer.setLength(0);
    }
  }

  class CommentIndentationObject {
    boolean indented;

  }

  /**
   * handle comma list (e.g. 1,2,3)
   *
   * @param array
   *            ASTNode array
   * @param lastPosition
   *            the position of the last ASTNode
   * @param insertSpaceBeforeComma
   * @param insertSpaceAfterComma
   * @param b
   * @param k
   * @param j
   * @return the last element end position
   */
  private int handleCommaList(ASTNode[] array, int lastPosition,
      boolean insertSpaceBeforeComma, boolean insertSpaceAfterComma,
      int lineWrapPolicy, int indentGap, boolean forceSplit) {
    int oldIndentationLevel = indentationLevel;
    boolean wasBinaryExpressionWrapped = this.wasBinaryExpressionWrapped;
    if (array.length == 0) {
      return lastPosition;
    }

    // save the changes index position
    String savedBuffer = replaceBuffer.toString();
    int changesIndex = changes.size() - 1;
    int savedLastPosition = lastPosition;
    boolean isExtraIndentation = false;

    // Map<Integer, CommentIndentationObject> commentIndetationMap = new
    // HashMap<Integer, CommentIndentationObject>();
    CommentIndentationObject cio = new CommentIndentationObject();
    commentIndetationStack.add(cio);
    // commentIndetationMap.put(array., cio);
    boolean isFirst = true;
    for (int i = 0; i < array.length; i++) {
      if (!isFirst) {
        if (insertSpaceBeforeComma) {
          insertSpace();
        }
        appendToBuffer(COMMA);
        if (insertSpaceAfterComma) {
          insertSpace();
        }
      }

      // after the first element and wrap policy is except first element
      if (i == 1 && lineWrapPolicy == WRAP_ALL_ELEMENTS_EXCEPT_FIRST) {
        savedBuffer = replaceBuffer.toString();
        changesIndex = changes.size() - 1;
        savedLastPosition = lastPosition;
      }

      switch (lineWrapPolicy) {
      case NO_LINE_WRAP:
        break;
      case FIRST_WRAP_WHEN_NECESSARY:
        if (lineWidth + array[i].getLength() > this.preferences.line_wrap_line_split) {
          lineWrapPolicy = WRAP_WHEN_NECESSARY;
          insertNewLine();
          if (!cio.indented) {
            indentationLevel += indentGap;
          }

          indent();
        }
        break;
      case WRAP_WHEN_NECESSARY:
        if (lineWidth + array[i].getLength() > this.preferences.line_wrap_line_split) {
          insertNewLine();
          indent();
        }
        break;
      case WRAP_FIRST_ELEMENT:
        if (forceSplit
            || lineWidth + array[i].getLength() > this.preferences.line_wrap_line_split) {
          revert(savedBuffer, changesIndex);
          lastPosition = savedLastPosition;
          i = 0;
          lineWrapPolicy = WRAP_WHEN_NECESSARY;
          insertNewLine();
          if (!cio.indented) {
            indentationLevel += indentGap;
          }
          indent();
        }
        break;
      case WRAP_ALL_ELEMENTS:
        if (forceSplit
            || lineWidth + array[i].getLength() > this.preferences.line_wrap_line_split) {
          revert(savedBuffer, changesIndex);
          lastPosition = savedLastPosition;
          i = 0;
          lineWrapPolicy = ALWAYS_WRAP_ELEMENT;
          insertNewLine();
          if (!cio.indented) {
            indentationLevel += indentGap;
          }
          indent();
        }
        break;
      case WRAP_ALL_ELEMENTS_NO_INDENT_FIRST:
        if (forceSplit
            || lineWidth + array[i].getLength() > this.preferences.line_wrap_line_split) {
          // revert the buffer
          revert(savedBuffer, changesIndex);
          lastPosition = savedLastPosition;
          i = 0;
          lineWrapPolicy = ALWAYS_WRAP_ELEMENT;
          insertNewLine();
          if (!cio.indented) {
            indentationLevel += indentGap;
          }
          indent();

          // increase the indentation level after the first element
          indentationLevel++;
          isExtraIndentation = true;
        }
        break;
      case WRAP_ALL_ELEMENTS_EXCEPT_FIRST:
        if (forceSplit
            || lineWidth + array[i].getLength() > this.preferences.line_wrap_line_split) {
          // revert
          revert(savedBuffer, changesIndex);
          lastPosition = savedLastPosition;
          i = (i > 0) ? 1 : 0;
          lineWrapPolicy = ALWAYS_WRAP_ELEMENT;
          insertNewLine();
          if (!cio.indented) {
            indentationLevel += indentGap;
          }
          indent();
        }
        break;
      case ALWAYS_WRAP_ELEMENT:
        insertNewLine();
        indent();
        break;
      }

      // workaround; remove this after fixing of
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=326384
      int start = array[i].getStart();
      try {
        // a NamespaceName object can be wrapped in
        // a FormalParameter object
        Object obj = array[i] instanceof FormalParameter ? ((FormalParameter) array[i])
            .getParameterType() : array[i];

        // obj may be null
        if (obj instanceof NamespaceName
            && ((NamespaceName) obj).isGlobal()) {
          if (Character.isWhitespace(document.getChar(start - 1))
              || document.getChar(start - 1) == '\\') {
            start -= 1;
          }
        } else if (i == 0 && array[i] instanceof UseStatementPart
            && ((UseStatementPart) array[i]).getName() != null
            && ((UseStatementPart) array[i]).getName().isGlobal()) {
          if (Character.isWhitespace(document.getChar(start - 1))
              || document.getChar(start - 1) == '\\') {
            start -= 1;
          }
        }
      } catch (BadLocationException e) {
        // should not be here
      }
      // workaround end
      handleChars1(lastPosition, start,
          oldIndentationLevel != indentationLevel, indentGap);
      array[i].accept(this);
      if (array[i] instanceof FunctionInvocation) {
        FunctionInvocation functionInvocation = (FunctionInvocation) array[i];
        if (functionInvocation.getArrayDereferenceList() != null
            && !functionInvocation.getArrayDereferenceList()
                .getDereferences().isEmpty()) {
          lastPosition = functionInvocation
              .getArrayDereferenceList()
              .getDereferences()
              .get(functionInvocation.getArrayDereferenceList()
                  .getDereferences().size() - 1).getEnd();
        } else {
          lastPosition = array[i].getEnd();
        }
      } else {
        lastPosition = array[i].getEnd();
      }

      isFirst = false;
    }
    commentIndetationStack.pop();
    if (isExtraIndentation) {
      indentationLevel--;
    }

    if (oldIndentationLevel != indentationLevel) {
      indentationLevel = oldIndentationLevel;
    }

    if (wasBinaryExpressionWrapped != this.wasBinaryExpressionWrapped) {
      this.wasBinaryExpressionWrapped = wasBinaryExpressionWrapped;
    }
    return lastPosition;
  }

  // TODO: Do correct comment placement
  // https://bugs.eclipse.org/bugs/show_bug.cgi?id=440209
  // https://bugs.eclipse.org/bugs/show_bug.cgi?id=440820
  private void handleComments(
      int offset,
      int end,
      List<org.eclipse.php.internal.core.compiler.ast.nodes.Comment> commentList,
      boolean isIndented, int indentGap) throws Exception {
    boolean oldIgnoreEmptyLineSetting = ignoreEmptyLineSetting;
    ignoreEmptyLineSetting = false;

    int startLine = document.getLineOfOffset(offset);
    int start = offset;
    boolean needIndentNewLine = false;
    boolean indentationLevelDesending = this.indentationLevelDesending;
    inComment = true;
    boolean previousCommentIsSingleLine = false;

    comments: for (Iterator<org.eclipse.php.internal.core.compiler.ast.nodes.Comment> iter = commentList
        .iterator(); iter.hasNext();) {
      org.eclipse.php.internal.core.compiler.ast.nodes.Comment comment = iter
          .next();
      int commentStartLine = document.getLineOfOffset(comment
          .sourceStart() + offset);
      int position = replaceBuffer.lastIndexOf(lineSeparator);
      boolean startAtFirstColumn = (document
          .getLineOffset(commentStartLine) == comment.sourceStart()
          + offset);
      boolean endWithNewLineIndent = endWithNewLineIndent(replaceBuffer
          .toString());
      String afterNewLine = EMPTY_STRING;
      boolean indentOnFirstColumn;
      String commentContent;
      switch (comment.getCommentType()) {
      case org.eclipse.php.internal.core.compiler.ast.nodes.Comment.TYPE_SINGLE_LINE:
        indentOnFirstColumn = !startAtFirstColumn
            || !this.preferences.never_indent_line_comments_on_first_column;
        if (startLine == commentStartLine) {
          indentOnFirstColumn = false;
          IRegion startLinereg = document
              .getLineInformation(startLine);
          // TODO: Do line width calculation based on the
          // formatted content instead of the original content
          lineWidth = comment.sourceStart() + offset
              - startLinereg.getOffset();
          if (position >= 0) {
            if (getBufferFirstChar(position
                + lineSeparator.length()) == '\0') {
              afterNewLine = replaceBuffer.substring(position
                  + lineSeparator.length(),
                  replaceBuffer.length());
              replaceBuffer.replace(position,
                  replaceBuffer.length(), ""); //$NON-NLS-1$
              insertSpaces(1);
            } else {
              insertSpace();
            }
          } else {
            if (getBufferFirstChar(0) == '\0') {
              replaceBuffer.setLength(0);
              insertSpaces(1);
            } else {
              insertSpace();
            }
          }
        } else {
          if (indentationLevelDesending) {
            IRegion reg = document
                .getLineInformation(commentStartLine - 1);
            char previousChar = document.getChar(reg.getOffset()
                + reg.getLength() - 1);
            int indentationSize = preferences.indentationSize;

            // add empty lines
            if (previousChar != '{') {
              for (int line = 0; line < preferences.blank_line_preserve_empty_lines; line++) {
                insertNewLine();
              }
              if (isInsideFun) {
                indentationSize++;
              }
            }
            // End fixing.

            // add single indentationChar * indentationSize
            // Because the comment is the previous indentation level
            for (int i = 0; i < indentationSize; i++) {
              appendToBuffer(preferences.indentationChar);
              lineWidth += (preferences.indentationChar == CodeFormatterPreferences.SPACE_CHAR) ? 0
                  : 3;
            }
          }

          if (getBufferFirstChar(0) == '\0') {
            if (position >= 0) {
              replaceBuffer.setLength(0);
              lineWidth = 0;
              insertNewLine();
            } else {
              replaceBuffer.setLength(0);
              lineWidth = 0;
            }
          } else {
            if (position >= 0
                && getBufferFirstChar(position
                    + lineSeparator.length()) == '\0') {
              replaceBuffer.replace(position,
                  replaceBuffer.length(), ""); //$NON-NLS-1$
            }
            insertNewLine();
            if (!isIndented && !commentIndetationStack.isEmpty()) {
              CommentIndentationObject cio = commentIndetationStack
                  .peek();
              if (!cio.indented) {
                cio.indented = true;
                indentationLevel += indentGap;
              }
            }
            // TODO should add indent level
          }
          if (indentationLevelDesending || blockEnd) {
            for (int i = 0; i < preferences.indentationSize; i++) {
              appendToBuffer(preferences.indentationChar);
              lineWidth += (preferences.indentationChar == CodeFormatterPreferences.SPACE_CHAR) ? 0
                  : 3;
            }
          }
          needIndentNewLine = true;
        }

        doNotIndent = true;
        boolean resetCommentIndentVariables = true;
        if (indentOnFirstColumn) {
          if (previousCommentIsSingleLine
              && indentStringForComment != null) {
            appendToBuffer(indentStringForComment);
            // adjust lineWidth,because indentLengthForComment may
            // contain '\t'
            lineWidth = indentLengthForComment;
            resetCommentIndentVariables = false;
          } else {
            indent();
          }
          if (lineWidth > 0) {
            startAtFirstColumn = false;
          }
          doNotIndent = false;
        }
        previousCommentIsSingleLine = true;
        handleCharsWithoutComments(start, comment.sourceStart()
            + offset);

        doNotIndent = false;
        resetEnableStatus(document.get(comment.sourceStart() + offset,
            comment.sourceEnd() - comment.sourceStart()));
        if (this.editsEnabled
            && this.preferences.comment_format_line_comment
            && (startAtFirstColumn
                && this.preferences.comment_format_line_comment_starting_on_first_column || !startAtFirstColumn)) {
          if (resetCommentIndentVariables) {
            resetCommentIndentVariables();
          }

          if (startLine == commentStartLine) {
            initCommentIndentVariables(offset, startLine, comment,
                endWithNewLineIndent);
            // adjust lineWidth,because indentLengthForComment may
            // contain '\t'
            lineWidth = indentLengthForComment;
          }
          if (startAtFirstColumn
              && this.preferences.never_indent_line_comments_on_first_column) {
            indentLengthForComment = 0;
            indentStringForComment = ""; //$NON-NLS-1$
          }

          commentContent = document.get(comment.sourceStart()
              + offset,
              comment.sourceEnd() - comment.sourceStart());
          boolean needInsertNewLine = commentContent
              .endsWith(lineSeparator);
          if (!needInsertNewLine) {
            String[] delimiters = document.getLegalLineDelimiters();
            for (int i = 0; i < delimiters.length; i++) {
              needInsertNewLine = commentContent
                  .endsWith(delimiters[i]);
              if (needInsertNewLine) {
                break;
              }
            }
          }
          int commentTokLen = commentContent.startsWith("#") ? 1 : 2;//$NON-NLS-1$
          commentWords = Arrays.asList(commentContent
              .substring(commentTokLen).trim().split("[ \t]")); //$NON-NLS-1$
          commentWords = removeEmptyString(commentWords);
          commentContent = join(commentWords, " "); //$NON-NLS-1$
          commentContent = commentContent.trim();

          boolean newLineStart = true;
          appendToBuffer("//"); //$NON-NLS-1$

          for (String word : commentWords) {
            if (this.preferences.comment_line_length != 9999
                && !newLineStart
                && (lineWidth + 1 + word.length() > this.preferences.comment_line_length)) {
              insertNewLine();
              // start at first column, and more than
              // comment_line_length
              if (!startAtFirstColumn
                  || (startAtFirstColumn && indentOnFirstColumn)) {
                if (indentLengthForComment >= 0) {
                  appendToBuffer(indentStringForComment);

                } else {
                  indent();
                }
              }
              appendToBuffer("//"); //$NON-NLS-1$
              insertSpaces(1);
              appendToBuffer(word);
            } else {
              insertSpaces(1);
              appendToBuffer(word);
              newLineStart = false;
            }
          }
          handleCharsWithoutComments(comment.sourceStart() + offset,
              comment.sourceEnd() + offset, true);
          if (needInsertNewLine) {
            insertNewLine();
            needInsertNewLine = false;
          } else {
            insertSpaces(1);
            afterNewLine = EMPTY_STRING;
          }
        } else {
          commentContent = document.get(comment.sourceStart()
              + offset,
              comment.sourceEnd() - comment.sourceStart());
          boolean needInsertNewLine = commentContent
              .endsWith(lineSeparator);
          if (!needInsertNewLine) {
            String[] delimiters = document.getLegalLineDelimiters();
            for (int i = 0; i < delimiters.length; i++) {
              needInsertNewLine = commentContent
                  .endsWith(delimiters[i]);
              if (needInsertNewLine) {
                break;
              }
            }
          }
          if (needInsertNewLine) {
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=441825
            lineWidth = 0;
            needInsertNewLine = false;
          } else {
            afterNewLine = EMPTY_STRING;
          }
        }

        start = comment.sourceEnd() + offset;
        break;
      case org.eclipse.php.internal.core.compiler.ast.nodes.Comment.TYPE_PHPDOC:
        previousCommentIsSingleLine = false;
        inComment = false;
        handleCharsWithoutComments(start, comment.sourceStart()
            + offset);
        inComment = true;
        resetEnableStatus(document.get(comment.sourceStart() + offset,
            comment.sourceEnd() - comment.sourceStart()));
        String codeBeforeComment = document.get(0,
            comment.sourceStart() + offset).trim();
        boolean isHeaderComment = codeBeforeComment.equals("<?") //$NON-NLS-1$
            || codeBeforeComment.equals("<?php"); //$NON-NLS-1$
        if ((!isHeaderComment || this.preferences.comment_format_header)
            && this.editsEnabled
            && this.preferences.comment_format_javadoc_comment) {
          PHPDocBlock block = (PHPDocBlock) comment;

          newLineOfComment = false;
          appendToBuffer("/**"); //$NON-NLS-1$

          commentWords = new ArrayList<String>();
          org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[] texts = block
              .getTexts()
              .toArray(
                  new org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[block
                      .getTexts().size()]);
          PHPDocTag[] tags = block.getTags();
          if ((tags == null || tags.length == 0)) {
            texts = getNonblankScalars(texts);
          }
          boolean lastLineIsBlank = false;
          boolean isFirst = true;

          // description is blank
          if (getNonblankScalars(texts).length == 0) {
            texts = new org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[0];
          }
          if (this.preferences.comment_new_lines_at_javadoc_boundaries) {
            insertNewLineForPHPDoc();
            // description is blank
            if (texts.length == 0) {
              lastLineIsBlank = true;
            }
          }
          int textsLength = texts.length;
          for (int j = 0; j < textsLength; j++) {
            org.eclipse.php.internal.core.compiler.ast.nodes.Scalar scalar = texts[j];
            String word = scalar.getValue();
            if (word.trim().length() > 0) {
              commentWords.add(word);
              if (this.preferences.join_lines_in_comments) {

                if (!isFirst) {
                  insertNewLineForPHPDoc();
                }
                isFirst = false;
                initCommentWords();
                formatPHPDocText(commentWords, null, false,
                    false);
                commentWords = new ArrayList<String>();
                lastLineIsBlank = false;
              }
            } else if (!this.preferences.comment_clear_blank_lines_in_javadoc_comment) {
              // don't duplicate first blank line
              if (isFirst
                  && this.preferences.comment_new_lines_at_javadoc_boundaries
                  && commentWords.isEmpty()) {
                isFirst = false;
                lastLineIsBlank = true;
                continue;
              }
              isFirst = false;
              initCommentWords();
              formatPHPDocText(commentWords, null, false, false);
              insertNewLineForPHPDoc();
              commentWords = new ArrayList<String>();
              lastLineIsBlank = true;
            }
          }
          if (!commentWords.isEmpty()) {
            initCommentWords();
            formatPHPDocText(commentWords, null, false, false);
            lastLineIsBlank = false;
          }

          if (tags != null && tags.length > 0) {
            if (this.preferences.comment_insert_empty_line_before_root_tags
                && !lastLineIsBlank) {
              insertNewLineForPHPDoc();
              appendToBuffer(" "); //$NON-NLS-1$
            }
            for (int i = 0; i < tags.length; i++) {
              PHPDocTag phpDocTag = tags[i];
              boolean insertTag = true;
              String[] words = phpDocTag.getDescTexts();

              if ((i == tags.length - 1)
                  && !this.preferences.comment_new_lines_at_javadoc_boundaries) {
                words = getNonblankWords(words);
              }
              commentWords = new ArrayList<String>();

              if (getNonblankWords(words).length == 0) {
                boolean hasRefs = phpDocTag
                    .getReferencesWithOrigOrder().length != 0;
                int nbLines = words.length;
                // https://bugs.eclipse.org/bugs/show_bug.cgi?id=433938
                if (!hasRefs && nbLines > 1) {
                  nbLines--;
                }
                // insert several lines
                formatCommentWords(phpDocTag, insertTag, false);
                for (int j = 0; j < nbLines; j++) {
                  insertNewLineForPHPDoc();
                }
              } else {
                for (int j = 0; j < words.length; j++) {
                  String word = words[j];
                  if (word.trim().length() > 0) {
                    commentWords.add(word);
                    if (this.preferences.join_lines_in_comments) {

                      formatCommentWords(phpDocTag,
                          insertTag, true);
                      insertTag = false;
                    }
                  } else if (!this.preferences.comment_clear_blank_lines_in_javadoc_comment
                      && !insertTag) {

                    formatCommentWords(phpDocTag,
                        insertTag, true);
                    insertTag = false;
                  }

                }
                if (!commentWords.isEmpty() || insertTag) {
                  formatCommentWords(phpDocTag, insertTag,
                      !commentWords.isEmpty());
                }
              }
            }
            lastLineIsBlank = false;
          }
          if (this.preferences.comment_new_lines_at_javadoc_boundaries
              && !lastLineIsBlank) {
            insertNewLineForPHPDoc();
            appendToBuffer("/"); //$NON-NLS-1$
          } else if (lastLineIsBlank) {
            appendToBuffer("/"); //$NON-NLS-1$
          } else {
            indertWordToComment("*/"); //$NON-NLS-1$
          }
          handleCharsWithoutComments(comment.sourceStart() + offset,
              comment.sourceEnd() + offset, true);
        } else {
          commentContent = document.get(comment.sourceStart()
              + offset,
              comment.sourceEnd() - comment.sourceStart());
          List<String> lines = Arrays.asList(commentContent.split(
              "\r\n?|\n", -1)); //$NON-NLS-1$
          appendToBuffer(lines.get(0));
          // indent all lines, even empty lines
          for (int i = 1; i < lines.size(); i++) {
            insertNewLineForPHPDoc(false);
            appendToBuffer(lines.get(i).replaceFirst("^[ \t]+", "")); //$NON-NLS-1$
          }
          handleCharsWithoutComments(comment.sourceStart() + offset,
              comment.sourceEnd() + offset, true);
        }
        start = comment.sourceEnd() + offset;
        insertNewLine();
        indent();
        break;
      case org.eclipse.php.internal.core.compiler.ast.nodes.Comment.TYPE_MULTILINE:
        previousCommentIsSingleLine = false;
        // ignore multi line comments in the middle of code
        // example while /* kuku */ ( /* kuku */$a > 0 )
        if (getBufferFirstChar(0) != '\0') {
          replaceBuffer.setLength(0);
          IRegion reg = document.getLineInformationOfOffset(end);
          // TODO: Do line width calculation based on the
          // formatted content instead of the original content
          lineWidth = end - reg.getOffset();
          resetEnableStatus(document.get(comment.sourceStart()
              + offset,
              comment.sourceEnd() - comment.sourceStart()));
          for (; iter.hasNext();) {
            org.eclipse.php.internal.core.compiler.ast.nodes.Comment nextComment = iter
                .next();
            resetEnableStatus(document.get(
                nextComment.sourceStart() + offset,
                nextComment.sourceEnd()
                    - nextComment.sourceStart()));
          }
          start = end;
          break comments;
        }

        // buffer contains only whitespace chars
        indentOnFirstColumn = !startAtFirstColumn
            || !this.preferences.never_indent_block_comments_on_first_column;
        if (startLine == commentStartLine) {
          indentOnFirstColumn = false;
          IRegion startLinereg = document
              .getLineInformation(startLine);
          // TODO: Do line width calculation based on the
          // formatted content instead of the original content
          lineWidth = comment.sourceStart() + offset
              - startLinereg.getOffset();
          if (position >= 0) {
            // if (getBufferFirstChar(position
            // + lineSeparator.length()) == '\0') {
            afterNewLine = replaceBuffer.substring(position
                + lineSeparator.length(),
                replaceBuffer.length());
            replaceBuffer.replace(position, replaceBuffer.length(),
                ""); //$NON-NLS-1$
            insertSpaces(1);
            // } else {
            // insertSpace();
            // }
          } else {
            // if (getBufferFirstChar(0) == '\0') {
            replaceBuffer.setLength(0);
            insertSpaces(1);
            // } else {
            // insertSpace();
            // }
          }
        } else {
          if (position >= 0) {
            // if (getBufferFirstChar(position
            // + lineSeparator.length()) == '\0') {
            replaceBuffer.replace(
                position + lineSeparator.length(),
                replaceBuffer.length(), ""); //$NON-NLS-1$
            lineWidth = 0;
            // } else {
            // insertNewLine();
            // }
          } else {
            // if (getBufferFirstChar(0) == '\0') {
            replaceBuffer.setLength(0);
            lineWidth = 0;
            // } else {
            // insertNewLine();
            // }
          }
          if (indentationLevelDesending || blockEnd) {
            // add single indentationChar * indentationSize
            // Because the comment is the previous
            // indentation
            // level
            for (int i = 0; i < preferences.indentationSize; i++) {
              appendToBuffer(preferences.indentationChar);
              lineWidth += (preferences.indentationChar == CodeFormatterPreferences.SPACE_CHAR) ? 0
                  : 3;
            }
          }
          needIndentNewLine = true;
        }
        resetCommentIndentVariables();
        if (startLine != commentStartLine && blockEnd) {
          recordCommentIndentVariables = true;
        }
        doNotIndent = true;
        if (indentOnFirstColumn) {
          indent();
          doNotIndent = false;
          if (lineWidth > 0) {
            startAtFirstColumn = false;
          }
        }

        handleCharsWithoutComments(start, comment.sourceStart()
            + offset);
        doNotIndent = false;
        start = comment.sourceEnd() + offset;
        resetEnableStatus(document.get(comment.sourceStart() + offset,
            comment.sourceEnd() - comment.sourceStart()));

        if (startLine == commentStartLine) {
          initCommentIndentVariables(offset, startLine, comment,
              endWithNewLineIndent);
          lineWidth = indentLengthForComment;
        }
        if (startAtFirstColumn
            && this.preferences.never_indent_block_comments_on_first_column) {
          indentLengthForComment = 0;
          indentStringForComment = ""; //$NON-NLS-1$
        }
        if (this.editsEnabled
            && this.preferences.comment_format_block_comment
            && !(comment instanceof VarComment)) {
          appendToBuffer("/*"); //$NON-NLS-1$
          commentContent = document.get(comment.sourceStart()
              + offset,
              comment.sourceEnd() - comment.sourceStart());

          // boolean needInsertNewLine = commentContent
          // .endsWith(lineSeparator);
          // if (!needInsertNewLine) {
          // String[] delimiters = document.getLegalLineDelimiters();
          // for (int i = 0; i < delimiters.length; i++) {
          // needInsertNewLine = commentContent
          // .endsWith(delimiters[i]);
          // if (needInsertNewLine) {
          // break;
          // }
          // }
          // }
          commentContent = commentContent.trim();
          commentContent = commentContent.substring(2,
              commentContent.length() - 2);
          List<String> lines = Arrays.asList(commentContent.split(
              "\r\n?|\n", -1)); //$NON-NLS-1$
          commentWords = new ArrayList<String>();
          if (lines.size() == 1) {
            String word = lines.get(0).trim();
            commentWords.add(word);
            initCommentWords();
            StringBuffer sb = new StringBuffer();
            for (String w : commentWords) {
              if (w.trim().length() == 0) {
                continue;
              }
              sb.append(w).append(" "); //$NON-NLS-1$
            }
            // +1 means ' ' after "/*",+2 means "*/"
            if (this.preferences.comment_line_length == 9999
                || lineWidth + 1 + sb.length() + 2 <= this.preferences.comment_line_length) {
              appendToBuffer(" "); //$NON-NLS-1$
              appendToBuffer(sb.toString());
              appendToBuffer("*/"); //$NON-NLS-1$
              commentWords = new ArrayList<String>();
              handleCharsWithoutComments(comment.sourceStart()
                  + offset, comment.sourceEnd() + offset,
                  true);
              // if (needInsertNewLine) {
              insertNewLine();
              // needInsertNewLine = false;
              // } else {
              // IRegion reg = document
              // .getLineInformation(commentEndLine);
              // int lengthAfterCommentEnd = reg.getOffset()
              // + reg.getLength()
              // - (comment.sourceEnd() + offset);
              // if (lengthAfterCommentEnd <= 0) {
              // insertNewLine();
              // } else {
              // String stringAfterCommentEnd = document
              // .get(comment.sourceEnd() + offset,
              // lengthAfterCommentEnd);
              // if (stringAfterCommentEnd.trim().length() == 0) {
              // insertNewLine();
              // } else {
              // insertSpaces(1);
              // afterNewLine = EMPTY_STRING;
              // }
              // }
              // }
              break;
            }
            commentWords = new ArrayList<String>();
          }
          newLineOfComment = false;
          if (this.preferences.comment_new_lines_at_block_boundaries) {
            insertNewLineForPHPBlockComment(indentLengthForComment,
                indentStringForComment);
            newLineOfComment = true;
          }
          boolean isFirst = true;
          for (int j = 0; j < lines.size(); j++) {
            String word = lines.get(j).trim();
            if (word.startsWith("*")) { //$NON-NLS-1$
              word = word.substring(1);
            }
            if (word.length() > 0) {
              commentWords.add(word);
              if (this.preferences.join_lines_in_comments) {
                if (!isFirst) {
                  insertNewLineForPHPBlockComment(
                      indentLengthForComment,
                      indentStringForComment);
                  newLineOfComment = true;
                }
                isFirst = false;
                formatCommentBlockWords(indentLengthForComment,
                    indentStringForComment);
              }
            } else if (!this.preferences.comment_clear_blank_lines_in_block_comment) {
              if (j != 0 && j != lines.size() - 1) {
                formatCommentBlockWords(indentLengthForComment,
                    indentStringForComment);
                // don't duplicate first blank line
                if (isFirst
                    && this.preferences.comment_new_lines_at_block_boundaries) {
                  newLineOfComment = true;
                  isFirst = false;
                  continue;
                }
                insertNewLineForPHPBlockComment(
                    indentLengthForComment,
                    indentStringForComment);
                newLineOfComment = true;
                isFirst = false;
              }
            }
          }
          if (!commentWords.isEmpty()) {
            formatCommentBlockWords(indentLengthForComment,
                indentStringForComment);
            isFirst = false;
          }
          if (isFirst
              && this.preferences.comment_new_lines_at_block_boundaries) {
            appendToBuffer("/"); //$NON-NLS-1$
          } else if (newLineOfComment
              || this.preferences.comment_new_lines_at_block_boundaries) {
            insertNewLine();
            if (indentLengthForComment >= 0) {
              appendToBuffer(indentStringForComment);
            } else {
              indent();
            }
            appendToBuffer(" */"); //$NON-NLS-1$
          } else {
            indertWordToComment("*/"); //$NON-NLS-1$
          }
          newLineOfComment = false;
          handleCharsWithoutComments(comment.sourceStart() + offset,
              comment.sourceEnd() + offset, true);
        } else {
          commentContent = document.get(comment.sourceStart()
              + offset,
              comment.sourceEnd() - comment.sourceStart());
          List<String> lines = Arrays.asList(commentContent.split(
              "\r\n?|\n", -1)); //$NON-NLS-1$
          appendToBuffer(lines.get(0));
          // indent all lines, even empty lines
          for (int i = 1; i < lines.size(); i++) {
            insertNewLineForPHPBlockComment(indentLengthForComment,
                indentStringForComment, false);
            appendToBuffer(lines.get(i).replaceFirst("^[ \t]+", "")); //$NON-NLS-1$
          }
          handleCharsWithoutComments(comment.sourceStart() + offset,
              comment.sourceEnd() + offset, true);
        }
        insertNewLine();
        break;
      }
      if (needIndentNewLine) {
        indent();
        needIndentNewLine = false;
        afterNewLine = EMPTY_STRING;
      }
      appendToBuffer(afterNewLine);
    }
    inComment = false;
    ignoreEmptyLineSetting = oldIgnoreEmptyLineSetting;
    handleCharsWithoutComments(start, end);
  }

  private void indentBaseOnPrevLine(int commentStartLine)
      throws BadLocationException {
    IRegion prevLine = document.getLineInformation(commentStartLine);
    loop: for (int i = 0; i < prevLine.getLength(); i++) {
      switch (document.getChar(i + prevLine.getOffset())) {
      case ' ':
      case '\t':
      case '\r':
      case '\n':
        appendToBuffer(document.getChar(i + prevLine.getOffset()));
        break;
      default:
        break loop;
      }

    }
  }

  private boolean isComment(IRegion iRegion) {
    for (int i = 0; i < iRegion.getLength() - 1; i++) {
      try {
        switch (document.getChar(iRegion.getOffset() + i)) {
        case '/':
          if (document.getChar(iRegion.getOffset() + i + 1) == '/')
            return true;
          else if (document.getChar(iRegion.getOffset() + i + 1) == '*')
            return true;
        case '*':
          return true;
        case ' ':
        case '\t':
          break;
        default:
          return false;
        }
      } catch (BadLocationException e) {
        Logger.logException(e);
      }
    }
    return false;
  }

  private boolean endWithNewLineIndent(String string) {
    String indent = getIndent();
    return string.endsWith(lineSeparator + indent);
  }

  private String getIndent() {
    StringBuffer sb = new StringBuffer();

    for (int i = 0; i < indentationLevel * preferences.indentationSize; i++) {
      sb.append(preferences.indentationChar);
    }
    return sb.toString();
  }

  private void resetCommentIndentVariables() {
    indentLengthForComment = -1;
    indentStringForComment = null;
  }

  private void indentForComment(boolean indentationLevelDesending) {

    indent();
    if (indentationLevelDesending || blockEnd) {
      for (int i = 0; i < preferences.indentationSize; i++) {
        appendToBuffer(preferences.indentationChar);
        lineWidth += (preferences.indentationChar == CodeFormatterPreferences.SPACE_CHAR) ? 0
            : 3;
      }
    }
  }

  private void initCommentIndentVariables(int offset, int startLine,
      org.eclipse.php.internal.core.compiler.ast.nodes.Comment comment,
      boolean endWithNewLineIndent) throws BadLocationException {
    // TODO the value should be calculated from ReplaceEdit changes
    indentLengthForComment = 0;
    indentStringForComment = ""; //$NON-NLS-1$
    IRegion startRegion = document.getLineInformation(startLine);
    String startLineContent = document.get(startRegion.getOffset(),
        comment.sourceStart() + offset - startRegion.getOffset())
        .trim();
    // indentStringForComment = FormatterUtils.getLineBlanks(document,
    // startRegion);

    StringBuffer sb = new StringBuffer();
    int lastIndentationLevel = indentationLevel;
    if (endWithNewLineIndent) {
      if (indentationLevelList.size() >= 2) {
        lastIndentationLevel = indentationLevelList
            .get(indentationLevelList.size() - 2);
      } else {
        lastIndentationLevel = indentationLevelList
            .get(indentationLevelList.size() - 1);
      }
    } else {
      lastIndentationLevel = indentationLevelList
          .get(indentationLevelList.size() - 1);
    }
    for (int i = 0; i < lastIndentationLevel * preferences.indentationSize; i++) {
      sb.append(preferences.indentationChar);
    }
    for (int i = 0; i < startLineContent.length(); i++) {
      sb.append(" "); //$NON-NLS-1$
    }
    if (startLineContent.length() > 0) {
      sb.append(" "); //$NON-NLS-1$
    }
    indentStringForComment = sb.toString();
    char[] blankArray = indentStringForComment.toCharArray();
    for (int i = 0; i < blankArray.length; i++) {
      if (blankArray[i] == '\t') {
        indentLengthForComment += 3;
      } else {
        indentLengthForComment++;
      }
    }
  }

  public static List<String> removeEmptyString(List<String> commentWords) {
    List<String> result = new ArrayList<String>();
    for (int i = 0; i < commentWords.size(); i++) {
      String word = commentWords.get(i);
      if (word.trim().length() != 0) {
        result.add(word);
      }

    }
    return result;
  }

  private org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[] getNonblankScalars(
      org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[] texts) {
    int end = texts.length;
    for (int i = texts.length - 1; i >= 0; i--) {
      if (texts[i].getValue().trim().length() == 0) {
        if (end > 0) {
          end--;
        }
      } else {
        break;
      }
    }
    if (end == 0) {
      return new org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[0];
    }
    int start = 0;
    for (int i = 0; i < texts.length; i++) {
      if (texts[i].getValue().trim().length() == 0) {
        if (start < texts.length - 1) {
          start++;
        }
      } else {
        break;
      }
    }
    org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[] result = new org.eclipse.php.internal.core.compiler.ast.nodes.Scalar[end
        - start];
    System.arraycopy(texts, start, result, 0, end - start);
    return result;
  }

  private String[] getNonblankWords(String[] words) {
    int length = words.length;
    for (int i = words.length - 1; i >= 0; i--) {
      if (words[i].trim().length() == 0) {
        length--;
      } else {
        break;
      }
    }
    String[] result = new String[length];
    System.arraycopy(words, 0, result, 0, length);
    return result;
  }

  private void resetEnableStatus(String content) {
    int enablingTagIndex = -1;
    int disablingTagIndex = -1;
    if (this.useTags) {
      if (this.disablingTag != null) {
        disablingTagIndex = content.lastIndexOf(disablingTag);
      }
      if (this.enablingTag != null) {
        enablingTagIndex = content.lastIndexOf(enablingTag);
      }
      if (enablingTagIndex < disablingTagIndex) {
        this.editsEnabled = false;
      } else if (enablingTagIndex > disablingTagIndex) {
        this.editsEnabled = true;
      }
    }
  }

  private void formatCommentBlockWords(int indentLength, String blanks) {
    initCommentWords();
    for (String word : commentWords) {
      if (word.trim().length() == 0) {
        continue;
      }
      indertWordToCommentBlock(word, indentLength, blanks);
    }
    commentWords = new ArrayList<String>();

  }

  private void formatCommentWords(PHPDocTag phpDocTag, boolean insertTag,
      boolean hasDesc) {
    initCommentWords();
    insertNewLineForPHPDoc();
    formatPHPDocText(commentWords, phpDocTag, insertTag, hasDesc);
    commentWords = new ArrayList<String>();

  }

  private void initCommentWords() {
    String commentContent = join(commentWords, " "); //$NON-NLS-1$
    commentContent = commentContent.trim();
    commentWords = Arrays.asList(commentContent.split("[ \t\r\n]")); //$NON-NLS-1$
    commentWords = removeEmptyString(commentWords);
  }

  private void insertNewLineForPHPBlockComment(int indentLength, String blanks) {
    insertNewLineForPHPBlockComment(indentLength, blanks, true);
  }

  private void insertNewLineForPHPBlockComment(int indentLength,
      String blanks, boolean addCommentSymbol) {
    insertNewLine();
    if (indentLength >= 0) {
      appendToBuffer(blanks);
      lineWidth = lineWidth + (indentLength - blanks.length());
    } else {
      indent();
    }
    if (addCommentSymbol) {
      appendToBuffer(" *"); //$NON-NLS-1$
    } else {
      appendToBuffer(" "); //$NON-NLS-1$
    }
  }

  private void insertNewLineForPHPDoc() {
    insertNewLineForPHPDoc(true);
  }

  private void insertNewLineForPHPDoc(boolean addCommentSymbol) {
    insertNewLine();
    indent();
    if (addCommentSymbol) {
      appendToBuffer(" *"); //$NON-NLS-1$
    } else {
      appendToBuffer(" "); //$NON-NLS-1$
    }
  }

  private void formatPHPDocText(List<String> words, PHPDocTag phpDocTag,
      boolean insertTag, boolean hasDesc) {
    boolean insertSpace = true;
    String tag = ""; //$NON-NLS-1$
    // int indentLength = 0;
    if (phpDocTag != null) {
      tag = "@" + PHPDocTag.getTagKind(phpDocTag.getTagKind()); //$NON-NLS-1$
      // if (indentationLevelDesending) {
      // for (int i = 0; i < preferences.indentationSize; i++) {
      // indentLength += (preferences.indentationChar ==
      // CodeFormatterPreferences.SPACE_CHAR) ? 1
      // : 4;
      // }
      // }
    }
    int tagLength = tag.length() + 1;
    newLineOfComment = true;
    if (phpDocTag != null) {
      if (insertTag) {
        insertSpaces(1);
        String reference = getTagReference(phpDocTag);
        appendToBuffer(tag);
        appendToBuffer(reference);
      }
      newLineOfComment = false;
      if (this.preferences.comment_insert_new_line_for_parameter
          && phpDocTag.getTagKind() == PHPDocTag.PARAM) {
        if (insertTag && hasDesc) {
          insertNewLineForPHPDoc();
        }
        if (this.preferences.comment_indent_root_tags) {
          insertSpaces(tagLength);
        }

        insertSpaces(1);
        insertSpace = false;

        newLineOfComment = true;
        if (this.preferences.comment_indent_root_tags
            && this.preferences.comment_indent_parameter_description) {
          for (int i = 0; i < preferences.indentationSize; i++) {
            appendToBuffer(preferences.indentationChar);
            lineWidth += (preferences.indentationChar == CodeFormatterPreferences.SPACE_CHAR) ? 0
                : 3;
          }

        }
      } else if (!insertTag && this.preferences.comment_indent_root_tags) {
        insertSpaces(tagLength);
      }
    }

    for (String word : words) {
      if (word.trim().length() == 0) {
        continue;
      }
      indertWordToComment(phpDocTag, tagLength, word, insertSpace);
      insertSpace = true;
    }
  }

  private String getTagReference(PHPDocTag phpDocTag) {
    SimpleReference[] reference = phpDocTag.getReferencesWithOrigOrder();
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < reference.length; i++) {
      if (i > 0 && reference[i - 1] instanceof TypeReference
          && reference[i] instanceof TypeReference) {
        sb.append(PHPDOC_CLASS_SEPARATOR)
            .append(reference[i].getName()); //$NON-NLS-1$
      } else {
        sb.append(" ").append(reference[i].getName()); //$NON-NLS-1$
      }
    }
    return sb.toString();
  }

  private void indertWordToComment(String word) {
    indertWordToComment(null, 0, word, true);
  }

  private void indertWordToCommentBlock(String word, int indentLength,
      String blanks) {
    if (this.preferences.comment_line_length != 9999
        && !newLineOfComment
        && (lineWidth + 1 + word.length() > this.preferences.comment_line_length)) {
      insertNewLine();
      if (indentLength >= 0) {
        appendToBuffer(blanks);
        lineWidth = lineWidth + (indentLength - blanks.length());
      } else {
        indent();
      }

      appendToBuffer(" * "); //$NON-NLS-1$

      appendToBuffer(word);
    } else {
      insertSpaces(1);
      appendToBuffer(word);
      newLineOfComment = false;
    }
  }

  private void indertWordToComment(PHPDocTag phpDocTag, int tagLength,
      String word, boolean insertSpace) {
    word = word.trim();
    if (this.preferences.comment_line_length != 9999
        && !newLineOfComment
        && (lineWidth + 1 + word.length() > this.preferences.comment_line_length)) {
      insertNewLineForPHPDoc();
      appendToBuffer(" "); //$NON-NLS-1$

      if (phpDocTag != null) {
        if (this.preferences.comment_indent_root_tags) {
          insertSpaces(tagLength);
        }

        if (this.preferences.comment_indent_root_tags
            && this.preferences.comment_indent_parameter_description
            && phpDocTag.getTagKind() == PHPDocTag.PARAM) {
          for (int i = 0; i < preferences.indentationSize; i++) {
            appendToBuffer(preferences.indentationChar);
            lineWidth += (preferences.indentationChar == CodeFormatterPreferences.SPACE_CHAR) ? 0
                : 3;
          }
        }
      }
      appendToBuffer(word);
    } else {
      if (insertSpace) {
        insertSpaces(1);
      }
      appendToBuffer(word);
      newLineOfComment = false;
    }
  }

  private void insertSpaces(int size) {
    for (int i = 0; i < size; i++) {
      replaceBuffer.append(SPACE);
      lineWidth++;
      isPrevSpace = true;
    }
  }

  private void handleForSemicolon(Expression[] beforExpressions,
      Expression[] afterExpressions) {
    if (this.preferences.insert_space_before_semicolon_in_for
        && beforExpressions.length > 0) {
      insertSpace();
    }
    appendToBuffer(SEMICOLON);
    if (this.preferences.insert_space_after_semicolon_in_for
        && afterExpressions.length > 0) {
      insertSpace();
    }
  }

  /**
   * handle the PHP end tag
   *
   * @param start
   *            the end position of the ASTNode before the semicolon
   * @param end
   *            the position of the semicolon -1
   */
  private void handlePhpEndTag(int start, int end, String endTagStr) {
    appendToBuffer(endTagStr);

    try {
      boolean foundTag = false;
      for (int index = 0; start + index < end; index++) {
        char currentChar = document.getChar(start + index);
        if (foundTag) {
          appendToBuffer(currentChar);
        } else if (currentChar == '>') {
          foundTag = true;
        }
      }
    } catch (BadLocationException e) {
      Logger.logException(e);
    }
  }

  /**
   * handle the last semicolon of statement
   *
   * @param start
   *            the end position of the ASTNode before the semicolon
   * @param end
   *            the position of the semicolon -1
   */
  private void handleSemicolon(int start, int end) {
    if (this.preferences.insert_space_before_semicolon
        && !isHeredocSemicolon) {
      insertSpace();
    }
    // check if the statement end with ; or ?>
    if (isContainChar(start, end, SEMICOLON)) {
      appendToBuffer(SEMICOLON);
      if (isHeredocSemicolon && isPhpEqualTag) {
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=411322
        // always insert a new line after the closing HEREDOC tag
        isPhpEqualTag = false;
        insertNewLine();
        isPhpEqualTag = true;
      }
      isHeredocSemicolon = false;
    } else if (isContainChar(start, end, QUESTION_MARK)) {
      handlePhpEndTag(start, end, "?>"); //$NON-NLS-1$
    } else {
      handlePhpEndTag(start, end, "%>"); //$NON-NLS-1$
    }
    // till the semicolon
    handleChars(start, end);
    binaryExpressionLineWrapPolicy = -1;// reset the policy for binary
    // expression states
    if (wasBinaryExpressionWrapped) {
      indentationLevel -= binaryExpressionIndentGap;
      wasBinaryExpressionWrapped = false;
    }
    binaryExpressionIndentGap = 0;
    binaryExpressionSavedBuffer = null;
    if (isBinaryExpressionExtraIndentation) {
      indentationLevel--;
      isBinaryExpressionExtraIndentation = false;
    }
  }

  /**
   * add the indentation text in the
   *
   */
  private void indent() {
    if (!isPhpEqualTag) {
      indentationLevelList.add(indentationLevel);
      for (int i = 0; i < indentationLevel * preferences.indentationSize; i++) {
        appendToBuffer(preferences.indentationChar);
        lineWidth += (preferences.indentationChar == CodeFormatterPreferences.SPACE_CHAR) ? 0
            : 3;
      }
    }
  }

  // this method calculates the delta of lines width for AST
  // nodes such as
  // scalars, html in-lines etc... since these types can have multiple lines
  // we still need to check tabs length
  private void updateLinesWidth(ASTNode node) {
    try {
      int lineForStart = document.getLineOfOffset(node.getStart());
      int lineForEnd = document.getLineOfOffset(node.getEnd());

      if (lineForStart == lineForEnd) {
        lineWidth += node.getLength();
      } else {
        lineWidth = document.getLineLength(lineForEnd);
      }
    } catch (BadLocationException e) {
      Logger.logException(e);
    }
  }

  private void insertNewLine() {
    if (!isPhpEqualTag) {
      appendToBuffer(lineSeparator);
      lineWidth = 0;
    }
  }

  private void insertNewLines(Statement statement) {

    int numberOfLines = getNumbreOfLines(statement);

    for (int i = 0; i < numberOfLines; i++) {
      insertNewLine();
    }
  }

  private int getNumbreOfLines(Statement statement) {
    int numberOfLines = 1;
    switch (statement.getType()) {
    case ASTNode.NAMESPACE:
      numberOfLines = this.preferences.blank_lines_before_namespace + 1;
      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;
      break;
    case ASTNode.FUNCTION_DECLARATION:
    case ASTNode.METHOD_DECLARATION:
      numberOfLines = this.preferences.blank_line_before_method_declaration + 1;
      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;
      break;
    case ASTNode.FIELD_DECLARATION:
      numberOfLines = this.preferences.blank_line_before_field_declaration + 1;
      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;
      break;
    case ASTNode.CLASS_DECLARATION:
    case ASTNode.INTERFACE_DECLARATION:
      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;
      numberOfLines = this.preferences.blank_line_before_class_declaration + 1;
      break;
    case ASTNode.CONSTANT_DECLARATION:
      numberOfLines = this.preferences.blank_line_before_constant_declaration + 1;
      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;
      break;
    default:
      // no empty lines
      numberOfLines = 1;
      break;
    }
    return numberOfLines;
  }

  private void handleSplittedPhpBlock(int offset, int end)
      throws BadLocationException {
    IRegion lineRegion = document.getLineInformationOfOffset(offset);
    switch (getPhpStartTag(offset)) {
    case PHP_OPEN_ASP_TAG:
    case PHP_OPEN_SHORT_TAG:
      if (document
          .get(offset + 2,
              lineRegion.getOffset() + lineRegion.getLength()
                  - (offset + 2)).trim().length() != 0) {
        insertNewLine();
      }
      handleCharsWithoutComments(offset + 2, end);
      break;
    case PHP_OPEN_SHORT_TAG_WITH_EQUAL:
      handleCharsWithoutComments(offset + 3, end);
      break;
    case PHP_OPEN_TAG:
      if (document
          .get(offset + 5,
              lineRegion.getOffset() + lineRegion.getLength()
                  - (offset + 5)).trim().length() != 0) {
        insertNewLine();
      }
      handleCharsWithoutComments(offset + 5, end);
      break;
    }
  }

  private void insertSpace() {
    if (!isPrevSpace) {
      replaceBuffer.append(SPACE);
      lineWidth++;
      isPrevSpace = true;
    }
  }

  private void insertString(int offset, int end, String content) {
    assert end >= offset;
    ReplaceEdit replaceEdit = new ReplaceEdit(offset, end - offset, content);
    changes.add(replaceEdit);
  }

  private boolean isContainChar(int start, int end, char c) {
    try {
      for (int index = 0; start + index < end; index++) {
        if (document.getChar(start + index) == c) {
          return true;
        }
      }
    } catch (BadLocationException e) {
      Logger.logException(e);
    }
    return false;
  }

  private boolean isEmptyLine(int line) throws BadLocationException {
    int lineStart = document.getLineOffset(line);
    int lineEnd = lineStart + document.getLineLength(line);

    for (int offset = lineStart; offset < lineEnd; offset++) {
      char currChar = document.getChar(offset);
      if (currChar != ' ' && currChar != '\t' && currChar != '\r'
          && currChar != '\n') {
        // not empty line
        return false;
      }
    }
    return true;
  }

  // this operation "reverts" the visitor into the last "saved" state of the
  // changes
  // since when we need to go back to first element within comma-separated
  // list
  // after we already added the formatting changes into the buffer.
  private void revert(String savedBuffer, int changesIndex) {
    replaceBuffer.setLength(0);
    replaceBuffer.append(savedBuffer);
    for (int index = changes.size() - 1; index > changesIndex; index--) {
      changes.remove(index);
    }
  }

  private void scan(final int offset, final int end) throws Exception {
    final Reader reader = new DocumentReader(document, offset, end - offset);

    if (astLexer == null) {
      // create the lexer for the first time
      astLexer = getLexer(reader);
    } else {
      // reset the lexer
      astLexer.yyreset(reader);
      astLexer.resetCommentList();
    }
    // ST_IN_SCRIPTING
    assert stInScriptin != -1;
    astLexer.yybegin(stInScriptin);

    tokens.clear();
    try {
      Symbol symbol = null;
      do {
        symbol = astLexer.next_token();
        tokens.add(symbol);
      } while (symbol != null && symbol.sym != 0);
    } catch (Exception e) {
      // do nothing
    }
  }

  private int setSpaceAfterBlock(int offset) {
    if (this.preferences.insert_space_after_closing_brace_in_block) {
      insertSpace();
      // handleChars(offset, offset + 1);
      // return offset + 1;
      handleChars(offset, offset);
    }
    return offset;
  }

  public boolean visit(ArrayAccess arrayAccess) {
    Expression variableName = arrayAccess.getName();
    variableName.accept(this);
    if (this.preferences.insert_space_before_opening_bracket_in_array) {
      insertSpace();
    }
    if (arrayAccess.getArrayType() == ArrayAccess.VARIABLE_ARRAY) {
      appendToBuffer(OPEN_BRACKET);
    } else {
      appendToBuffer(OPEN_CURLY);
    }
    int lastPosition = variableName.getEnd();
    if (arrayAccess.getIndex() == null) {
      if (this.preferences.insert_space_between_empty_brackets) {
        insertSpace();
      }
    } else {
      if (this.preferences.insert_space_after_opening_bracket_in_array) {
        insertSpace();
      }
      handleChars(lastPosition, arrayAccess.getIndex().getStart());

      arrayAccess.getIndex().accept(this);
      if (this.preferences.insert_space_before_closing_bracket_in_array) {
        insertSpace();
      }
      lastPosition = arrayAccess.getIndex().getEnd();
    }
    handleChars(lastPosition, arrayAccess.getEnd() - 1);
    lineWidth++;// we need to add the closing bracket/curly
    return false;
  }

  public boolean visit(ArrayCreation arrayCreation) {
    if (this.preferences.insert_space_before_opening_paren_in_array) {
      insertSpace();
    }
    int lastPosition; // array
    if (arrayCreation.isHasArrayKey()) {
      appendToBuffer(OPEN_PARN);
      lastPosition = arrayCreation.getStart() + 5;
    } else {
      // appendToBuffer(OPEN_BRACKET);
      lastPosition = arrayCreation.getStart() + 1;
    }

    List<ArrayElement> eleList = arrayCreation.elements();
    ArrayElement[] elements = new ArrayElement[eleList.size()];
    elements = eleList.toArray(elements);

    if (elements.length > 0) {
      if (this.preferences.insert_space_after_opening_paren_in_array) {
        insertSpace();
      }

      if (this.preferences.new_line_after_open_array_parenthesis) {
        insertNewLine();
        indentationLevel++;
        indent();
        indentationLevel--;
      }

      lineWidth += 5;
      int indentationGap = calculateIndentGap(
          this.preferences.line_wrap_expressions_in_array_init_indent_policy,
          this.preferences.line_wrap_array_init_indentation);

      // work around for close bracket.
      lineWidth++;

      lastPosition = handleCommaList(
          elements,
          lastPosition,
          this.preferences.insert_space_before_list_comma_in_array,
          this.preferences.insert_space_after_list_comma_in_array,
          this.preferences.line_wrap_expressions_in_array_init_line_wrap_policy,
          indentationGap,
          this.preferences.line_wrap_expressions_in_array_init_force_split);

      // work around for close bracket.
      lineWidth--;

      if (this.preferences.insert_space_before_closing_paren_in_array) {
        insertSpace();
      }

      if (this.preferences.new_line_before_close_array_parenthesis_array) {
        insertNewLine();
        indent();
      }

    }

    if (arrayCreation.isHasArrayKey()) {
      appendToBuffer(CLOSE_PARN);
    } else {
      appendToBuffer(CLOSE_BRACKET);
    }

    if (arrayCreation.getArrayDereferenceList() != null
        && !arrayCreation.getArrayDereferenceList().getDereferences()
            .isEmpty()) {
      lastPosition = formatDereference(lastPosition,
          arrayCreation.getArrayDereferenceList());
    } else {
      handleChars(lastPosition, arrayCreation.getEnd());
    }

    return false;
  }

  private int calculateIndentGap(int indentationPolicy, int defaultIndentation) {
    switch (indentationPolicy) {
    case DEFAULT_INDENTATION:
      return defaultIndentation;
    case INDENT_ON_COLUMN:
      // gap is the number of indentation for all the line
      // we increase the indentationLevel to get the gap
      int lineIndentation;
      if (this.preferences.indentationChar == '\t') {
        lineIndentation = (int) Math.ceil(lineWidth / 4);
      } else {
        lineIndentation = (int) Math.ceil(lineWidth
            / this.preferences.indentationSize);
      }
      return lineIndentation - indentationLevel;
    case INDENT_ONE:
      return 1;
    default:
      return NO_LINE_WRAP_INDENT;
    }
  }

  public boolean visit(ArrayElement arrayElement) {
    if (arrayElement.getKey() != null) {
      arrayElement.getKey().accept(this);
      if (this.preferences.insert_space_before_arrow_in_array) {
        insertSpace();
      }
      appendToBuffer(KEY_VALUE_OPERATOR);
      if (this.preferences.insert_space_after_arrow_in_array) {
        insertSpace();
      }
      handleChars(arrayElement.getKey().getEnd(), arrayElement.getValue()
          .getStart());
    }
    arrayElement.getValue().accept(this);
    return false;
  }

  public boolean visit(Assignment assignment) {
    VariableBase leftSide = assignment.getLeftHandSide();
    leftSide.accept(this);
    if (this.preferences.insert_space_before_assignment) {
      insertSpace();
    }
    appendToBuffer(Assignment.getOperator(assignment.getOperator()));
    if (this.preferences.insert_space_after_assignment) {
      insertSpace();
    }

    // handle the chars between the variable to the value
    Expression rightSideValue = assignment.getRightHandSide();

    handleChars(leftSide.getEnd(), rightSideValue.getStart());

    rightSideValue.accept(this);
    return false;
  }

  public boolean visit(ASTError astError) {
    updateLinesWidth(astError);
    return false;
  }

  public boolean visit(BackTickExpression backTickExpression) {
    updateLinesWidth(backTickExpression);
    return false;
  }

  public boolean visit(Block block) {
    boolean blockIndentation = false;
    boolean isPhpMode = true;
    boolean isEmptyBlockNewLine = true;
    boolean isUnbracketedNamespace = false;
    boolean isNamespace = false;

    boolean isClassDeclaration = false;
    boolean isFunctionDeclaration = false;

    switch (block.getParent().getType()) {
    case ASTNode.NAMESPACE:
      isNamespace = true;
      if (!block.isCurly()) {
        isEmptyBlockNewLine = false;
        isUnbracketedNamespace = true;
        if (block.statements().size() > 0) {
          Statement statement = block.statements().get(0);
          // need check how many new lines will the next statement
          // insert
          int numberOfLines = getNumbreOfLines(statement) - 1;
          numberOfLines = this.preferences.blank_lines_after_namespace
              - numberOfLines;
          if (numberOfLines > 0) {
            for (int j = 0; j < numberOfLines; j++) {
              insertNewLine();
            }
          }
        } else {
          for (int i = 0; i < this.preferences.blank_lines_after_namespace; i++) {
            insertNewLine();
          }
        }
        // ignoreEmptyLineSetting = true;
        ignoreEmptyLineSetting = !preferences.indent_empty_lines;

        break;
      }
      if (block.statements().size() > 0) {
        Statement statement = block.statements().get(0);
        // need check how many new lines will the next statement insert
        int numberOfLines = getNumbreOfLines(statement) - 1;
        numberOfLines = this.preferences.blank_lines_after_namespace
            - numberOfLines;
        if (numberOfLines > 0) {
          for (int j = 0; j < numberOfLines; j++) {
            insertNewLine();
          }
        }
      } else {
        for (int i = 0; i < this.preferences.blank_lines_after_namespace; i++) {
          insertNewLine();
        }
      }
      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;

    case ASTNode.CLASS_DECLARATION:
    case ASTNode.INTERFACE_DECLARATION:
      isEmptyBlockNewLine = preferences.new_line_in_empty_class_body;
      blockIndentation = this.preferences.indent_statements_within_type_declaration;
      isClassDeclaration = true;
      break;
    case ASTNode.SWITCH_STATEMENT:
      blockIndentation = this.preferences.indent_statements_within_switch;
      break;
    case ASTNode.FUNCTION_DECLARATION:
      isEmptyBlockNewLine = preferences.new_line_in_empty_method_body;
      blockIndentation = this.preferences.indent_statements_within_function;
      for (int i = 0; i < this.preferences.blank_line_at_begin_of_method; i++) {
        insertNewLine();
      }
      isFunctionDeclaration = true;

      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;

      break;
    default:
      isEmptyBlockNewLine = preferences.new_line_in_empty_block;
      blockIndentation = this.preferences.indent_statements_within_block;
      break;
    }

    if (blockIndentation) {
      indentationLevel++;
    }

    int lastStatementEndOffset;
    if (isUnbracketedNamespace) {
      lastStatementEndOffset = block.getStart() - 1;
    } else {
      lastStatementEndOffset = block.getStart() + 1;
    }

    List<Statement> statementsList = block.statements();
    Statement[] statements = new Statement[statementsList.size()];
    statements = block.statements().toArray(statements);
    for (int i = 0; i < statements.length; i++) {
      boolean isHtmlStatement = statements[i].getType() == ASTNode.IN_LINE_HTML;
      boolean isASTError = statements[i].getType() == ASTNode.AST_ERROR;
      if (isASTError && i + 1 < statements.length) {
        lastStatementEndOffset = statements[i + 1].getStart();
      } else {
        if (isPhpMode && !isHtmlStatement) {
          // PHP -> PHP
          if (getPhpStartTag(lastStatementEndOffset) != -1) {
            insertNewLine();
          }
          if (isThrowOrReturnFormatCase(statements)) {
            // do nothing... This is a Throw/Return case
          } else {
            insertNewLines(statements[i]);
            indent();
          }
          handleChars(lastStatementEndOffset,
              statements[i].getStart());
        } else if (isPhpMode && isHtmlStatement) {
          // PHP -> HTML
          isPhpMode = false;
        } else if (!isPhpMode && !isHtmlStatement) {
          // HTML -> PHP
          isPhpEqualTag = getPhpStartTag(lastStatementEndOffset) == PHP_OPEN_SHORT_TAG_WITH_EQUAL;
          insertNewLines(statements[i]);
          indent();
          if (lastStatementEndOffset <= statements[i].getStart()) {
            handleChars(lastStatementEndOffset,
                statements[i].getStart());
          }
          isPhpMode = true;
        } else {
          // HTML -> HTML
          assert false;
        }
        statements[i].accept(this);
        lastStatementEndOffset = statements[i].getEnd();
        if (isNamespace && i + 1 < statements.length
            && statements[i].getType() == ASTNode.USE_STATEMENT) {
          if (statements[i + 1].getType() == ASTNode.USE_STATEMENT) {
            // for (int j = 0; j <
            // this.preferences.blank_lines_between_use_statements;
            // j++) {
            // insertNewLine();
            // }

            // ignoreEmptyLineSetting = true;
            ignoreEmptyLineSetting = !preferences.indent_empty_lines;

          } else {
            // need check how many new lines will the next statement
            // insert
            int numberOfLines = getNumbreOfLines(statements[i + 1]) - 1;
            numberOfLines = this.preferences.blank_lines_after_use_statements
                - numberOfLines;
            if (numberOfLines > 0) {
              for (int j = 0; j < numberOfLines; j++) {
                insertNewLine();
              }
            }

            // ignoreEmptyLineSetting = true;
            ignoreEmptyLineSetting = !preferences.indent_empty_lines;

          }
        }
      }
    }
    // in case of the last statement is html statement
    if (!isPhpMode) {
      isPhpEqualTag = false;
    }
    // set the block end
    if (blockIndentation) {
      indentationLevel--;
      indentationLevelDesending = true;
    }
    int endPosition = block.getEnd() - 1;
    boolean hasComments = false;
    if (startRegionPosition < endPosition
        && endRegionPosition >= endPosition) {
      try {
        hasComments = hasComments(lastStatementEndOffset, endPosition);
      } catch (Exception e) {
      }
    }

    if (statements.length > 0 || isEmptyBlockNewLine || hasComments) {
      if (isUnbracketedNamespace || isThrowOrReturnFormatCase(statements)) {
        // do not add new line... Throw/Return Statements within an If
        // Statement block
      } else {
        // if ((statements.length > 0 || hasComments)
        // && (isClassDeclaration || isFunctionDeclaration)) {
        // if (isClassDeclaration) {
        // for (int j = 0; j < preferences.blank_line_at_end_of_class;
        // j++) {
        // insertNewLine();
        // }
        // if (preferences.blank_line_at_end_of_class > 0) {
        // indent();
        // }
        // } else {
        // for (int j = 0; j < preferences.blank_line_at_end_of_method;
        // j++) {
        // insertNewLine();
        // }
        // if (preferences.blank_line_at_end_of_method > 0) {
        // indent();
        // }
        // }
        // } else {
        insertNewLine();
        indent();
        // }

      }
    }

    if (block.getEnd() > block.getStart()) {
      int end = block.getEnd() - 1;
      if (!block.isCurly()) {
        switch (block.getParent().getType()) {
        case ASTNode.SWITCH_STATEMENT:
          end = block.getEnd() - "endswitch".length();//$NON-NLS-1$
          break;
        case ASTNode.WHILE_STATEMENT:
          end = block.getEnd() - "endwhile".length();//$NON-NLS-1$
          break;
        case ASTNode.FOR_STATEMENT:
          end = block.getEnd() - "endfor".length();//$NON-NLS-1$
          break;
        case ASTNode.FOR_EACH_STATEMENT:
          end = block.getEnd() - "endforeach".length();//$NON-NLS-1$
          break;
        case ASTNode.DECLARE_STATEMENT:
          end = block.getEnd() - "enddeclare".length();//$NON-NLS-1$
          break;
        case ASTNode.IF_STATEMENT:
          end = block.getEnd();
          break;
        }
      }
      if (/*
         * (statements.length > 0 || hasComments) &&
         */(isClassDeclaration || isFunctionDeclaration)) {
        if (isClassDeclaration) {
          for (int j = 0; j < preferences.blank_line_at_end_of_class; j++) {
            insertNewLine();
          }
          if (preferences.blank_line_at_end_of_class > 0) {
            indent();
          }
        } else {
          for (int j = 0; j < preferences.blank_line_at_end_of_method; j++) {
            insertNewLine();
          }
          if (preferences.blank_line_at_end_of_method > 0) {
            indent();
          }
        }
      }

      // ignoreEmptyLineSetting = true;
      ignoreEmptyLineSetting = !preferences.indent_empty_lines;

      blockEnd = true;
      handleChars(lastStatementEndOffset, end);
      blockEnd = false;
      lineWidth++;// closing curly
    }
    return false;
  }

  // this checks whether it is an IF block (with curly) with one line and the
  // line
  // is either return OR throw expression AND the FORMAT flag is ON
  private boolean isThrowOrReturnFormatCase(Statement[] statements) {
    return preferences.control_statement_keep_guardian_on_one_line
        && (statements.length == 1)
        && (statements[0].getParent().getParent() instanceof IfStatement)
        && (((IfStatement) statements[0].getParent().getParent())
            .getFalseStatement() == null)
        && (statements[0].getType() == ASTNode.RETURN_STATEMENT
            || statements[0].getType() == ASTNode.YIELD_STATEMENT || statements[0]
            .getType() == ASTNode.THROW_STATEMENT);
  }

  public boolean visit(BreakStatement breakStatement) {
    int lastPosition = breakStatement.getStart() + 5;
    lineWidth += 5;
    Expression expression = breakStatement.getExpression();
    if (expression != null) {
      insertSpace();
      handleChars(lastPosition, expression.getStart());

      expression.accept(this);
      lastPosition = expression.getEnd();
    }

    handleSemicolon(lastPosition, breakStatement.getEnd());
    return false;
  }

  public boolean visit(CastExpression castExpression) {
    // (type) expression
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_before_cast_type) {
      insertSpace();
    }

    // get the exact cast type
    String type = EMPTY_STRING;
    int start = castExpression.getStart();
    int end = castExpression.getExpression().getStart();
    switch (castExpression.getCastingType()) {
    case CastExpression.TYPE_INT:
      if (isContainChar(start, end, 'e')) {
        type = "integer"; //$NON-NLS-1$
      } else {
        type = "int"; //$NON-NLS-1$
      }
      break;
    case CastExpression.TYPE_REAL:
      if (isContainChar(start, end, 'f')) {
        type = "float"; //$NON-NLS-1$
      } else if (isContainChar(start, end, 'r')) {
        type = "real"; //$NON-NLS-1$
      } else {
        type = "double"; //$NON-NLS-1$
      }
      break;
    case CastExpression.TYPE_STRING:
      type = "string"; //$NON-NLS-1$
      break;
    case CastExpression.TYPE_ARRAY:
      type = "array"; //$NON-NLS-1$
      break;
    case CastExpression.TYPE_OBJECT:
      type = "object"; //$NON-NLS-1$
      break;
    case CastExpression.TYPE_BOOL:
      if (isContainChar(start, end, 'e')) {
        type = "boolean"; //$NON-NLS-1$
      } else {
        type = "bool"; //$NON-NLS-1$
      }
      break;
    case CastExpression.TYPE_UNSET:
      type = "unset"; //$NON-NLS-1$
      break;
    }

    appendToBuffer(type);
    if (this.preferences.insert_space_after_cast_type) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);
    if (this.preferences.insert_space_after_cast_expression) {
      insertSpace();
    }
    // till the expression
    handleChars(start, end);

    castExpression.getExpression().accept(this);
    return false;
  }

  public boolean visit(CatchClause catchClause) {
    // handle the chars between the 'catch' and the identifier start
    // position
    if (this.preferences.insert_space_before_opening_paren_in_catch) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_opening_paren_in_catch) {
      insertSpace();
    }
    lineWidth += 5;
    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=326384
    int start = catchClause.getClassName().getStart();
    if (catchClause.getClassName() instanceof NamespaceName) {
      NamespaceName namespaceName = (NamespaceName) catchClause
          .getClassName();
      try {
        if (namespaceName.isGlobal()
            && (Character.isWhitespace(document.getChar(start - 1)) || document
                .getChar(start - 1) == '\\')) {
          start -= 1;
        }
      } catch (BadLocationException e) {
        // should not be here
      }
    }
    // end
    handleChars(catchClause.getStart() + 5, start);

    // handle the catch identifier
    catchClause.getClassName().accept(this);
    insertSpace();
    handleChars(catchClause.getClassName().getEnd(), catchClause
        .getVariable().getStart());
    catchClause.getVariable().accept(this);

    // set the catch closing parn spaces
    if (this.preferences.insert_space_before_closing_paren_in_catch) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    boolean isIndentationAdded = handleBlockOpenBrace(
        this.preferences.brace_position_for_block,
        this.preferences.insert_space_before_opening_brace_in_block);
    handleChars(catchClause.getVariable().getEnd(), catchClause.getBody()
        .getStart());
    catchClause.getBody().accept(this);
    if (isIndentationAdded) {
      indentationLevel--;
      indentationLevelDesending = true;
    }
    return false;
  }

  public boolean visit(ConstantDeclaration classConstantDeclaration) {
    boolean isFirst = true;
    insertSpace();
    int lastPosition = classConstantDeclaration.getStart() + 5;
    lineWidth += 5;
    List<Identifier> names = classConstantDeclaration.names();

    Identifier[] variableNames = new Identifier[names.size()];
    variableNames = names.toArray(variableNames);

    for (int i = 0; i < variableNames.length; i++) {
      // handle comma between variables
      if (!isFirst) {
        if (this.preferences.insert_space_before_comma_in_class_constant) {
          insertSpace();
        }
        appendToBuffer(COMMA);
        if (this.preferences.insert_space_after_comma_in_class_constant) {
          insertSpace();
        }
      }
      handleChars(lastPosition, variableNames[i].getStart());
      variableNames[i].accept(this);
      lastPosition = variableNames[i].getEnd();

      // handle initial assignments
      if (this.preferences.insert_space_before_assignment) {
        insertSpace();
      }
      appendToBuffer(EQUAL);
      if (this.preferences.insert_space_after_assignment) {
        insertSpace();
      }

      List<Expression> initializers = classConstantDeclaration
          .initializers();

      Expression[] constantValues = new Expression[initializers.size()];

      constantValues = initializers.toArray(constantValues);

      handleChars(lastPosition, constantValues[i].getStart());
      constantValues[i].accept(this);
      lastPosition = constantValues[i].getEnd();
      isFirst = false;
    }
    handleSemicolon(lastPosition, classConstantDeclaration.getEnd());
    return false;
  }

  public boolean visit(ClassDeclaration classDeclaration) {
    // handle spaces between modifier, 'class' and class name
    String modifier = ClassDeclaration.getModifier(classDeclaration
        .getModifier());
    if (!modifier.equals(EMPTY_STRING)) {
      appendToBuffer(modifier);
      insertSpace();
    }
    if (classDeclaration instanceof TraitDeclaration) {
      appendToBuffer("trait"); //$NON-NLS-1$
    } else {
      appendToBuffer("class"); //$NON-NLS-1$
    }
    insertSpace();
    handleChars(classDeclaration.getStart(), classDeclaration.getName()
        .getStart());

    classDeclaration.getName().accept(this);

    int lastPosition = classDeclaration.getName().getEnd();
    Expression superClass = classDeclaration.getSuperClass();
    // handle super class
    if (superClass != null) {
      appendToBuffer(" extends "); //$NON-NLS-1$
      int start = superClass.getStart();
      // workaround
      // remove this after fixing of
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=326384
      try {
        if (superClass instanceof NamespaceName
            && ((NamespaceName) superClass).isGlobal()) {
          if (Character.isWhitespace(document.getChar(start - 1))
              || document.getChar(start - 1) == '\\') {
            start -= 1;
          }
        }
      } catch (BadLocationException e) {
        // should not be here
      }
      // end

      handleChars(lastPosition, start);
      classDeclaration.getSuperClass().accept(this);
      lastPosition = classDeclaration.getSuperClass().getEnd();
    }

    List<Identifier> interfacesList = classDeclaration.interfaces();
    Identifier[] interfaces = new Identifier[interfacesList.size()];
    interfaces = interfacesList.toArray(interfaces);
    // handle class implements
    if (interfaces != null && interfaces.length > 0) {
      appendToBuffer(" implements "); //$NON-NLS-1$

      int indentationGap = calculateIndentGap(
          this.preferences.line_wrap_superinterfaces_in_type_declaration_indent_policy,
          this.preferences.line_wrap_wrapped_lines_indentation);
      lastPosition = handleCommaList(
          interfaces,
          lastPosition,
          this.preferences.insert_space_before_comma_in_implements,
          this.preferences.insert_space_after_comma_in_implements,
          this.preferences.line_wrap_superinterfaces_in_type_declaration_line_wrap_policy,
          indentationGap,
          this.preferences.line_wrap_superinterfaces_in_type_declaration_force_split);
    }

    // handle class body
    boolean isIndentationAdded = handleBlockOpenBrace(
        this.preferences.brace_position_for_class,
        this.preferences.insert_space_before_opening_brace_in_class);
    handleChars(lastPosition, classDeclaration.getBody().getStart());

    classDeclaration.getBody().accept(this);

    if (isIndentationAdded) {
      indentationLevel--;
      indentationLevelDesending = true;
    }
    return false;
  }

  public boolean visit(ClassInstanceCreation classInstanceCreation) {
    if (classInstanceCreation.getChainingInstanceCall() != null
        && !classInstanceCreation.getChainingInstanceCall()
            .getChainingMethodOrProperty().isEmpty()) {
      appendToBuffer(OPEN_PARN);
    }
    // insertSpace();
    appendToBuffer("new "); //$NON-NLS-1$
    // lineWidth += 3; // the 'new' word
    handleChars(classInstanceCreation.getStart(), classInstanceCreation
        .getClassName().getStart());
    classInstanceCreation.getClassName().accept(this);
    if (this.preferences.insert_space_before_opening_paren_in_function) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    List<Expression> ctorParams = classInstanceCreation.ctorParams();
    int numberOfCtorParameters = ctorParams.size();

    if (numberOfCtorParameters == 0
        && this.preferences.insert_space_between_empty_paren_in_function) {
      insertSpace();
    }

    if (numberOfCtorParameters > 0
        && this.preferences.insert_space_after_opening_paren_in_function) {
      insertSpace();
    }

    Expression[] arrayOfParameters = (Expression[]) ctorParams
        .toArray(new Expression[ctorParams.size()]);
    int indentationGap = calculateIndentGap(
        this.preferences.line_wrap_arguments_in_allocation_expression_indent_policy,
        this.preferences.line_wrap_wrapped_lines_indentation);
    int lastPosition = handleCommaList(
        arrayOfParameters,
        classInstanceCreation.getClassName().getEnd(),
        this.preferences.insert_space_before_comma_in_function,
        this.preferences.insert_space_after_comma_in_function,
        this.preferences.line_wrap_arguments_in_allocation_expression_line_wrap_policy,
        indentationGap,
        this.preferences.line_wrap_arguments_in_allocation_expression_force_split);

    if (numberOfCtorParameters > 0
        && this.preferences.insert_space_before_closing_paren_in_function) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);
    if (classInstanceCreation.getChainingInstanceCall() != null
        && !classInstanceCreation.getChainingInstanceCall()
            .getChainingMethodOrProperty().isEmpty()) {
      appendToBuffer(CLOSE_PARN);
    }
    handleChars(lastPosition, classInstanceCreation.getEnd());
    return false;
  }

  public boolean visit(ClassName className) {
    className.getName().accept(this);
    return false;
  }

  public boolean visit(CloneExpression cloneExpression) {
    insertSpace();
    lineWidth += 5;// the 'clone'
    // till the expression
    Expression expression = cloneExpression.getExpression();
    handleChars(cloneExpression.getStart() + 5, expression.getStart());

    expression.accept(this);
    return false;
  }

  public boolean visit(Comment comment) {
    // do nothing
    return false;
  }

  public boolean visit(ConditionalExpression conditionalExpression) {

    // start
    // condition ? true : false
    conditionalExpression.getCondition().accept(this);
    // condition -> if true
    if (this.preferences.insert_space_before_conditional_question_mark) {
      insertSpace();
    }
    appendToBuffer(QUESTION_MARK);
    if (this.preferences.insert_space_after_conditional_question_mark) {
      insertSpace();
    }
    Expression ifTrue = conditionalExpression.getIfTrue();
    Expression ifFalse = conditionalExpression.getIfFalse();
    int offset = conditionalExpression.getCondition().getStart();

    int colonOffset = 0;
    if (ifTrue != null) {
      handleChars(conditionalExpression.getCondition().getEnd(),
          ifTrue.getStart());
      ifTrue.accept(this);
    } else {
      int length = offset;
      if (ifFalse != null) {
        length = ifFalse.getStart();
      }
      colonOffset = getCharPosition(conditionalExpression.getCondition()
          .getEnd(), length, ':');
      handleChars(conditionalExpression.getCondition().getEnd(),
          colonOffset);
    }

    // iftrue -> iffalse
    if (this.preferences.insert_space_before_conditional_colon) {
      insertSpace();
    }
    appendToBuffer(COLON);

    if (this.preferences.insert_space_after_conditional_colon) {
      insertSpace();
    }

    if (ifTrue != null && ifFalse != null) {
      handleChars(ifTrue.getEnd(), conditionalExpression.getIfFalse()
          .getStart());
    } else if (ifTrue == null && ifFalse != null) {
      handleChars(colonOffset, conditionalExpression.getIfFalse()
          .getStart());
    } else if (ifTrue != null && ifFalse == null) {
      handleChars(ifTrue.getEnd(), colonOffset);
    }

    if (ifFalse != null) {
      ifFalse.accept(this);
    }
    // end

    return false;
  }

  public boolean visit(ContinueStatement continueStatement) {
    int lastPosition = continueStatement.getStart() + 8;
    lineWidth += 8;
    Expression expression = continueStatement.getExpression();
    if (expression != null) {
      insertSpace();
      handleChars(lastPosition, expression.getStart());

      expression.accept(this);
      lastPosition = expression.getEnd();
    }

    handleSemicolon(lastPosition, continueStatement.getEnd());
    return false;
  }

  public boolean visit(DeclareStatement declareStatement) {
    boolean isFirst = true;
    if (this.preferences.insert_space_before_opening_paren_in_declare) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_opening_paren_in_declare) {
      insertSpace();
    }
    int lastPosition = declareStatement.getStart() + 7;
    lineWidth += 7;
    List<Identifier> direciveNameList = declareStatement.directiveNames();
    Identifier[] directiveNames = new Identifier[direciveNameList.size()];
    directiveNames = direciveNameList.toArray(directiveNames);

    for (int i = 0; i < directiveNames.length; i++) {
      // handle comma between variables
      if (!isFirst) {
        if (this.preferences.insert_space_before_comma_in_class_variable) {
          insertSpace();
        }
        appendToBuffer(COMMA);
        if (this.preferences.insert_space_after_comma_in_class_variable) {
          insertSpace();
        }
      }
      handleChars(lastPosition, directiveNames[i].getStart());
      directiveNames[i].accept(this);
      lastPosition = directiveNames[i].getEnd();

      // handle initial assignments
      if (this.preferences.insert_space_before_assignment) {
        insertSpace();
      }
      appendToBuffer(EQUAL);
      if (this.preferences.insert_space_after_assignment) {
        insertSpace();
      }

      List<Expression> directiveValuesList = declareStatement
          .directiveValues();
      Expression[] directiveValues = new Expression[directiveValuesList
          .size()];
      directiveValues = directiveValuesList.toArray(directiveValues);

      handleChars(lastPosition, directiveValues[i].getStart());
      directiveValues[i].accept(this);
      lastPosition = directiveValues[i].getEnd();
      isFirst = false;
    }
    if (this.preferences.insert_space_before_closing_paren_in_declare) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    handleAction(lastPosition, declareStatement.getBody(), true);
    return false;
  }

  public boolean visit(DoStatement doStatement) {
    // do-while body
    lineWidth += 2;
    Statement body = doStatement.getBody();
    handleAction(doStatement.getStart() + 2, body, true);
    int lastPosition = body.getEnd();// this variable
    // will be changed
    int doActionEnd = body.getEnd();
    if (preferences.control_statement_insert_newline_before_while_in_do) {
      insertNewLine();
      indent();
    } else {
      lastPosition = setSpaceAfterBlock(doActionEnd);
    }

    String textBetween = ""; //$NON-NLS-1$
    int indexOfWhile = -1;
    try {
      textBetween = document.get(doActionEnd,
          doStatement.getCondition().getStart() - doActionEnd)
          .toLowerCase();
    } catch (BadLocationException e) {
      Logger.logException(e);
      return false;
    }
    indexOfWhile = textBetween.indexOf("while"); //$NON-NLS-1$
    if (indexOfWhile > 0) {
      indexOfWhile += doActionEnd;
      handleChars(lastPosition, indexOfWhile);
      appendToBuffer("while"); //$NON-NLS-1$
      handleChars(indexOfWhile, indexOfWhile);
      lastPosition = indexOfWhile;
    } else {
      appendToBuffer("while"); //$NON-NLS-1$
    }

    // handle the chars between the 'while' and the condition start position
    if (this.preferences.insert_space_before_opening_paren_in_while) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_opening_paren_in_while) {
      insertSpace();
    }
    handleChars(lastPosition, doStatement.getCondition().getStart());

    // handle the while condition
    doStatement.getCondition().accept(this);

    // set the while closing paren spaces
    if (this.preferences.insert_space_before_closing_paren_in_while) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    handleSemicolon(doStatement.getCondition().getEnd(),
        doStatement.getEnd());
    return false;
  }

  public boolean visit(EchoStatement echoStatement) {
    int lastPosition = echoStatement.getStart() + 4;
    lineWidth += 4;
    insertSpace();

    List<Expression> expressionList = echoStatement.expressions();
    Expression[] expressions = new Expression[expressionList.size()];
    expressions = expressionList.toArray(expressions);

    lastPosition = handleCommaList(expressions, lastPosition,
        this.preferences.insert_space_before_comma_in_echo,
        this.preferences.insert_space_after_comma_in_echo,
        NO_LINE_WRAP, NO_LINE_WRAP_INDENT, false);

    handleSemicolon(lastPosition, echoStatement.getEnd());
    return false;
  }

  public boolean visit(EmptyStatement emptyStatement) {
    int start = emptyStatement.getStart();
    int end = emptyStatement.getEnd();
    if (isContainChar(start, end, '?')) {
      handlePhpEndTag(start, end, "?>"); //$NON-NLS-1$
    } else if (isContainChar(start, end, '%')) {
      handlePhpEndTag(start, end, "%>"); //$NON-NLS-1$
    } else {
      appendToBuffer(SEMICOLON);
    }
    handleChars(start, end);
    return false;
  }

  public boolean visit(ExpressionStatement expressionStatement) {
    Expression expression = expressionStatement.getExpression();
    expression.accept(this);
    handleSemicolon(expression.getEnd(), expressionStatement.getEnd());
    return false;
  }

  public boolean visit(FieldAccess fieldAccess) {
    fieldAccess.getDispatcher().accept(this);
    if (this.preferences.insert_space_before_arrow_in_field_access) {
      insertSpace();
    }
    appendToBuffer("->"); //$NON-NLS-1$
    if (this.preferences.insert_space_after_arrow_in_field_access) {
      insertSpace();
    }

    // handle the chars between the dispatcher to the property
    handleChars(fieldAccess.getDispatcher().getEnd(), fieldAccess
        .getField().getStart());

    fieldAccess.getField().accept(this);
    return false;
  }

  public boolean visit(FieldsDeclaration fieldsDeclaration) {
    boolean isFirst = true;
    Variable[] variableNames = fieldsDeclaration.getVariableNames();
    Expression[] initialValues = fieldsDeclaration.getInitialValues();
    int lastPosition = variableNames[0].getStart();

    // handle field modifiers
    String modifier = fieldsDeclaration.getModifierString();
    char firstChar = ' ';
    try {
      firstChar = document.getChar(fieldsDeclaration.getStart());
    } catch (BadLocationException e) {
      Logger.logException(e);
    }
    if (modifier.equalsIgnoreCase("public") //$NON-NLS-1$
        && (firstChar == 'v' || firstChar == 'V')) {
      modifier = "var"; //$NON-NLS-1$
    }

    appendToBuffer(modifier);
    insertSpace();

    handleChars(fieldsDeclaration.getStart(), lastPosition);

    for (int i = 0; i < variableNames.length; i++) {
      // handle comma between variables
      if (!isFirst) {
        if (this.preferences.insert_space_before_comma_in_class_variable) {
          insertSpace();
        }
        appendToBuffer(COMMA);
        if (this.preferences.insert_space_after_comma_in_class_variable) {
          insertSpace();
        }
      }
      handleChars(lastPosition, variableNames[i].getStart());
      variableNames[i].accept(this);
      lastPosition = variableNames[i].getEnd();

      if (initialValues[i] != null) {
        // handle initial assignments
        if (this.preferences.insert_space_before_assignment) {
          insertSpace();
        }
        appendToBuffer(EQUAL);
        if (this.preferences.insert_space_after_assignment) {
          insertSpace();
        }

        handleChars(lastPosition, initialValues[i].getStart());
        initialValues[i].accept(this);
        lastPosition = initialValues[i].getEnd();
      }
      isFirst = false;
    }
    handleSemicolon(lastPosition, fieldsDeclaration.getEnd());
    return false;
  }

  public boolean visit(ForEachStatement forEachStatement) {
    if (this.preferences.insert_space_before_open_paren_in_foreach) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_open_paren_in_foreach) {
      insertSpace();
    }
    lineWidth += 7;
    handleChars(forEachStatement.getStart() + 7, forEachStatement
        .getExpression().getStart());
    // handle [as key => value] or just [as value]
    forEachStatement.getExpression().accept(this);
    appendToBuffer(" as "); //$NON-NLS-1$
    int lastPosition = forEachStatement.getExpression().getEnd();
    if (forEachStatement.getKey() != null) {
      handleChars(forEachStatement.getExpression().getEnd(),
          forEachStatement.getKey().getStart());
      forEachStatement.getKey().accept(this);
      if (this.preferences.insert_space_before_arrow_in_foreach) {
        insertSpace();
      }
      appendToBuffer(KEY_VALUE_OPERATOR);
      if (this.preferences.insert_space_after_arrow_in_foreach) {
        insertSpace();
      }
      lastPosition = forEachStatement.getKey().getEnd();
    }
    handleChars(lastPosition, forEachStatement.getValue().getStart());
    forEachStatement.getValue().accept(this);

    if (this.preferences.insert_space_before_close_paren_in_foreach) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    handleAction(forEachStatement.getValue().getEnd(),
        forEachStatement.getStatement(), true);
    return false;
  }

  public boolean visit(FormalParameter formalParameter) {
    // handle const in PHP4
    int lastPosition = formalParameter.getStart();
    if (formalParameter.isMandatory()) {
      // the word 'const'
      lastPosition += 5;
      lineWidth += 5;
    }

    // handle type
    if (formalParameter.getParameterType() != null) {
      formalParameter.getParameterType().accept(this);
      lastPosition = formalParameter.getParameterType().getEnd();
      insertSpace();
    }
    handleChars(lastPosition, formalParameter.getParameterName().getStart());

    formalParameter.getParameterName().accept(this);
    if (formalParameter.hasDefaultValue()) {
      if (this.preferences.insert_space_before_assignment) {
        insertSpace();
      }
      appendToBuffer(EQUAL);
      if (this.preferences.insert_space_after_assignment) {
        insertSpace();
      }

      // handle the chars between the variable to the value
      handleChars(formalParameter.getParameterName().getEnd(),
          formalParameter.getDefaultValue().getStart());
    }
    return false;
  }

  public boolean visit(ForStatement forStatement) {
    int lastPosition = forStatement.getStart() + 3;
    lineWidth += 3;
    if (this.preferences.insert_space_before_open_paren_in_for) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);

    List<Expression> forExpressions = null;

    // handle initializers
    forExpressions = forStatement.initializers();
    Expression[] initializations = new Expression[forExpressions.size()];
    forExpressions.toArray(initializations);

    // handle conditions
    forExpressions = forStatement.conditions();
    Expression[] conditions = new Expression[forExpressions.size()];
    forExpressions.toArray(conditions);

    // handle updaters/increasements
    forExpressions = forStatement.updaters();
    Expression[] increasements = new Expression[forExpressions.size()];
    forExpressions.toArray(increasements);

    if (this.preferences.insert_space_after_open_paren_in_for
        && initializations.length > 0) {
      insertSpace();
    }

    lastPosition = handleCommaList(initializations, lastPosition,
        this.preferences.insert_space_before_comma_in_for,
        this.preferences.insert_space_after_comma_in_for, NO_LINE_WRAP,
        NO_LINE_WRAP_INDENT, false);
    handleForSemicolon(initializations, conditions);
    lastPosition = handleCommaList(conditions, lastPosition,
        this.preferences.insert_space_before_comma_in_for,
        this.preferences.insert_space_after_comma_in_for, NO_LINE_WRAP,
        NO_LINE_WRAP_INDENT, false);
    handleForSemicolon(conditions, increasements);
    lastPosition = handleCommaList(increasements, lastPosition,
        this.preferences.insert_space_before_comma_in_for,
        this.preferences.insert_space_after_comma_in_for, NO_LINE_WRAP,
        NO_LINE_WRAP_INDENT, false);

    if (this.preferences.insert_space_before_close_paren_in_for
        && increasements.length > 0) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    // for body
    handleAction(lastPosition, forStatement.getBody(), true);
    return false;
  }

  public boolean visit(FunctionDeclaration functionDeclaration) {
    isInsideFun = true;
    StringBuffer buffer = new StringBuffer();
    buffer.append(getDocumentString(functionDeclaration.getStart(),
        functionDeclaration.getStart() + 8));// append 'function'

    // handle referenced function with '&'
    if (functionDeclaration.isReference()) {
      buffer.append(" &"); //$NON-NLS-1$
    } else {
      buffer.append(' ');
    }

    buffer.append(functionDeclaration.getFunctionName().getName());

    appendToBuffer(buffer.toString());
    handleChars(functionDeclaration.getStart(), functionDeclaration
        .getFunctionName().getEnd());

    if (this.preferences.insert_space_before_opening_paren_in_function_declaration) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    int lastPosition = functionDeclaration.getFunctionName().getEnd();
    if (functionDeclaration.formalParameters().size() > 0) {
      if (this.preferences.insert_space_after_opening_paren_in_function_declaration) {
        insertSpace();
      }
      int indentationGap = calculateIndentGap(
          this.preferences.line_wrap_parameters_in_method_declaration_indent_policy,
          this.preferences.line_wrap_wrapped_lines_indentation);

      List<FormalParameter> parameterList = functionDeclaration
          .formalParameters();
      FormalParameter[] parameters = new FormalParameter[parameterList
          .size()];
      parameters = parameterList.toArray(parameters);

      lastPosition = handleCommaList(
          parameters,
          lastPosition,
          this.preferences.insert_space_before_comma_in_function_declaration,
          this.preferences.insert_space_after_comma_in_function_declaration,
          this.preferences.line_wrap_parameters_in_method_declaration_line_wrap_policy,
          indentationGap,
          this.preferences.line_wrap_parameters_in_method_declaration_force_split);

      if (this.preferences.insert_space_before_closing_paren_in_function_declaration) {
        insertSpace();
      }
    } else {
      if (this.preferences.insert_space_between_empty_paren_in_function_declaration) {
        insertSpace();
      }
    }
    appendToBuffer(CLOSE_PARN);

    // handle function body
    if (functionDeclaration.getBody() != null) {
      boolean isIndentationAdded = handleBlockOpenBrace(
          this.preferences.brace_position_for_function,
          this.preferences.insert_space_before_opening_brace_in_function);
      handleChars(lastPosition, functionDeclaration.getBody().getStart());

      functionDeclaration.getBody().accept(this);
      if (isIndentationAdded) {
        indentationLevel--;
        indentationLevelDesending = true;
      }
    } else {
      handleSemicolon(lastPosition, functionDeclaration.getEnd());
    }

    isInsideFun = false;
    return false;
  }

  public boolean visit(FunctionInvocation functionInvocation) {

    // in case of function print there no need for parenthesis
    Expression functionName = functionInvocation.getFunctionName()
        .getName();
    if (functionName.getType() == ASTNode.IDENTIFIER) {
      final String name = ((Identifier) functionName).getName();
      if (FUNCTION_NAME_PRINT.equalsIgnoreCase(name)) {
        handlePrintCall(functionInvocation);
        return false;
      }
    }

    innerVisit(functionInvocation);
    return false;
  }

  private void innerVisit(FunctionInvocation functionInvocation) {
    innerVisit(functionInvocation, true);
  }

  private void innerVisit(FunctionInvocation functionInvocation,
      boolean addParen) {
    Expression functionName = functionInvocation.getFunctionName()
        .getName();
    functionName.accept(this);

    if (this.preferences.insert_space_before_opening_paren_in_function) {
      insertSpace();
    }

    if (addParen) {
      appendToBuffer(OPEN_PARN);
    }

    int lastPosition = functionName.getEnd();
    if (functionInvocation.parameters().size() > 0) {
      if (this.preferences.insert_space_after_opening_paren_in_function) {
        insertSpace();
      }
      int indentationGap = calculateIndentGap(
          this.preferences.line_wrap_arguments_in_method_invocation_indent_policy,
          this.preferences.line_wrap_wrapped_lines_indentation);

      List<Expression> parametersList = functionInvocation.parameters();
      Expression[] parameters = new Expression[parametersList.size()];
      parameters = parametersList.toArray(parameters);

      // work around. count close bracket now.
      if (addParen) {
        lineWidth++;
      }

      // work around. count space now.
      if (this.preferences.insert_space_before_closing_paren_in_function) {
        lineWidth++;
      }

      lastPosition = handleCommaList(
          parameters,
          lastPosition,
          this.preferences.insert_space_before_comma_in_function,
          this.preferences.insert_space_after_comma_in_function,
          this.preferences.line_wrap_arguments_in_method_invocation_line_wrap_policy,
          indentationGap,
          this.preferences.line_wrap_arguments_in_method_invocation_force_split);

      if (this.preferences.insert_space_before_closing_paren_in_function) {
        // work around. count space now.
        lineWidth--;
        insertSpace();
      }

      // work around. count close bracket now.
      if (addParen) {
        lineWidth--;
      }

    } else {
      if (this.preferences.insert_space_between_empty_paren_in_function) {
        insertSpace();
      }
    }

    if (addParen) {
      appendToBuffer(CLOSE_PARN);
    }
    if (functionInvocation.getArrayDereferenceList() != null
        && !functionInvocation.getArrayDereferenceList()
            .getDereferences().isEmpty()) {
      lastPosition = formatDereference(lastPosition,
          functionInvocation.getArrayDereferenceList());
    } else {
      handleChars(lastPosition, functionInvocation.getEnd());
    }

  }

  private int formatDereference(int lastPosition, PHPArrayDereferenceList list) {
    handleChars(lastPosition, list.getDereferences().get(0).getStart());
    lastPosition = list.getDereferences().get(0).getStart();
    for (DereferenceNode dereferenceNode : list.getDereferences()) {

      if (dereferenceNode.getName() instanceof Scalar) {
        appendToBuffer(OPEN_BRACKET);
        // handleChars(lastPosition, dereferenceNode.getStart());
        Scalar scalar = (Scalar) dereferenceNode.getName();
        appendToBuffer(scalar.getStringValue());
        appendToBuffer(CLOSE_BRACKET);
        handleChars(dereferenceNode.getStart(),
            dereferenceNode.getEnd());
      } else {
        appendToBuffer(OPEN_BRACKET);
        handleChars(lastPosition, dereferenceNode.getName().getStart());
        dereferenceNode.getName().accept(this);
        appendToBuffer(CLOSE_BRACKET);
        handleChars(dereferenceNode.getName().getEnd(),
            dereferenceNode.getEnd());
      }

      // handleChars(dereferenceNode.getEnd(),
      // dereferenceNode.getEnd());
      lastPosition = dereferenceNode.getEnd();
    }

    return lastPosition;
  }

  private void handlePrintCall(FunctionInvocation functionInvocation) {

    List<Expression> parametersList = functionInvocation.parameters();
    Expression[] parameters = new Expression[parametersList.size()];
    parameters = parametersList.toArray(parameters);

    boolean hasParenthsis = parameters[0].getType() == ASTNode.PARENTHESIS_EXPRESSION; // print
    // always
    // have
    // one
    // parameter.

    if (hasParenthsis) {
      innerVisit(functionInvocation, false);
      return;
    }

    insertSpace();

    Expression functionName = functionInvocation.getFunctionName()
        .getName();

    int lastPosition = functionName.getEnd();

    int indentationGap = calculateIndentGap(
        this.preferences.line_wrap_arguments_in_method_invocation_indent_policy,
        this.preferences.line_wrap_wrapped_lines_indentation);

    lastPosition = handleCommaList(
        parameters,
        lastPosition,
        this.preferences.insert_space_before_comma_in_function,
        this.preferences.insert_space_after_comma_in_function,
        this.preferences.line_wrap_arguments_in_method_invocation_line_wrap_policy,
        indentationGap,
        this.preferences.line_wrap_arguments_in_method_invocation_force_split);

    handleChars(lastPosition, functionInvocation.getEnd());
  }

  public boolean visit(FunctionName functionName) {
    return true;
  }

  public boolean visit(GlobalStatement globalStatement) {
    int lastPosition = globalStatement.getStart() + 6;
    lineWidth += 6;// the word 'global'
    insertSpace();

    List<Variable> varList = globalStatement.variables();
    Expression[] variables = new Expression[varList.size()];
    variables = varList.toArray(variables);

    lastPosition = handleCommaList(variables, lastPosition,
        this.preferences.insert_space_before_comma_in_global,
        this.preferences.insert_space_after_comma_in_global,
        NO_LINE_WRAP, NO_LINE_WRAP_INDENT, false);

    handleSemicolon(lastPosition, globalStatement.getEnd());
    return false;
  }

  public boolean visit(Identifier identifier) {
    lineWidth += identifier.getLength();
    return false;
  }

  public boolean visit(IfStatement ifStatement) {
    // check if the token is 'if' or 'elseif'
    int len = checkFirstTokenLength(ifStatement.getStart(), ifStatement
        .getCondition().getStart());

    // handle the chars between the 'while' and the condition start position
    if (this.preferences.insert_space_before_opening_paren_in_if) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_opening_paren_in_if) {
      insertSpace();
    }
    lineWidth += len; // add the word 'if' OR 'elseif'
    handleChars(ifStatement.getStart() + len, ifStatement.getCondition()
        .getStart());

    // handle the if condition
    ifStatement.getCondition().accept(this);

    if (wasBinaryExpressionWrapped) {
      indentationLevel -= binaryExpressionIndentGap;
      wasBinaryExpressionWrapped = false;
    }
    // handle the chars between the condition end position and action start
    // position

    // set the while closing paren spaces
    if (this.preferences.insert_space_before_closing_paren_in_if) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    // action
    int lastPosition = ifStatement.getCondition().getEnd();
    boolean addNewlineBeforeAction = true;
    if (ifStatement.getTrueStatement().getType() != ASTNode.BLOCK) {
      if (len == 2) {// if
        addNewlineBeforeAction = !(preferences.control_statement_keep_then_on_same_line || (preferences.control_statement_keep_simple_if_on_one_line && ifStatement
            .getFalseStatement() == null));
      } else if (len == 6) {// elseif
        addNewlineBeforeAction = !preferences.control_statement_keep_then_on_same_line;
      }
    }
    handleAction(lastPosition, ifStatement.getTrueStatement(),
        addNewlineBeforeAction);
    lastPosition = ifStatement.getTrueStatement().getEnd();
    if (ifStatement.getFalseStatement() == null
        || ifStatement.getFalseStatement().getType() == ASTNode.AST_ERROR) {
      return false;
    }

    if (ifStatement.getFalseStatement().getType() == ASTNode.IF_STATEMENT) {
      try {
        handleElseIfCases(ifStatement);
      } catch (BadLocationException ble) {
        Logger.logException(ble);
        return false;
      }
    } else { // the false statement is not 'elseif' or 'else if'
      if (ifStatement.getTrueStatement().getType() == ASTNode.BLOCK) {
        if (preferences.control_statement_insert_newline_before_else_and_elseif_in_if) {
          insertNewLine();
          indent();
        } else {
          lastPosition = setSpaceAfterBlock(ifStatement
              .getTrueStatement().getEnd());
        }

        try {
          lastPosition = internalHandleElse(ifStatement, lastPosition);
        } catch (BadLocationException ble) {
          Logger.logException(ble);
          return false;
        }
        handleAction(lastPosition, ifStatement.getFalseStatement(),
            true);
        boolean processed = isProcessed(ifStatement);
        if (!((Block) ifStatement.getTrueStatement()).isCurly()
            && !processed) {
          handleChars(ifStatement.getFalseStatement().getEnd(),
              ifStatement.getEnd());
          appendToBuffer("endif;"); //$NON-NLS-1$
          handleChars(ifStatement.getEnd(), ifStatement.getEnd());
        }
      } else { // if the true statement is not a block then we should add
        // new line
        insertNewLine();
        indent();

        try {
          lastPosition = internalHandleElse(ifStatement, lastPosition);
        } catch (BadLocationException ble) {
          Logger.logException(ble);
          return false;
        }

        boolean elseActionInSameLine = preferences.control_statement_keep_else_on_same_line;
        handleAction(lastPosition, ifStatement.getFalseStatement(),
            !elseActionInSameLine);
      }
    }
    return false;
  }

  private boolean isProcessed(IfStatement ifStatement) {
    boolean precessed = true;
    if (!processedIfStatements.contains(ifStatement)) {
      addAllIfStatements(ifStatement);
      precessed = false;
    }
    return precessed;
  }

  private void handleElseIfCases(IfStatement ifStatement)
      throws BadLocationException {
    int lastPosition = ifStatement.getTrueStatement().getEnd();
    int len;
    IfStatement falseIfStatement = (IfStatement) ifStatement
        .getFalseStatement();
    len = checkFirstTokenLength(falseIfStatement.getStart(),
        falseIfStatement.getCondition().getStart());
    boolean elseIndentationLevelChanged = false;

    // information needed to handleChars between if statement end to the
    // else if...
    String textBetween = ""; //$NON-NLS-1$
    int trueStatementEnd = ifStatement.getTrueStatement().getEnd();
    int indexOfElse = -1;
    textBetween = document.get(trueStatementEnd,
        ifStatement.getFalseStatement().getStart() - trueStatementEnd)
        .toLowerCase();
    indexOfElse = textBetween.lastIndexOf("else"); //$NON-NLS-1$

    if (ifStatement.getTrueStatement().getType() == ASTNode.BLOCK) {
      if (preferences.control_statement_insert_newline_before_else_and_elseif_in_if) {
        insertNewLine();
        indent();
      } else {
        if (len == 2) {
          lastPosition = setSpaceAfterBlock(ifStatement
              .getTrueStatement().getEnd());
        }
      }

      if (len != 2) {// elseif case
        if (indexOfElse > 0) {
          handleChars(lastPosition, ifStatement.getFalseStatement()
              .getStart());
        } else {
          // fix for setSpaceAfterBlock when no space is required
          // before 'elseif'
          if (!preferences.control_statement_insert_newline_before_else_and_elseif_in_if
              && preferences.insert_space_after_closing_brace_in_block) {
            insertSpace();
          }
          handleChars(ifStatement.getTrueStatement().getEnd(),
              ifStatement.getFalseStatement().getStart());
        }
      } else {
        if (indexOfElse > 0) {
          indexOfElse += trueStatementEnd;
          handleChars(lastPosition, indexOfElse);
          appendToBuffer("else "); //$NON-NLS-1$
          if (!preferences.control_statement_keep_else_if_on_same_line) {
            insertNewLine();
            indentationLevel++;
            elseIndentationLevelChanged = true;
            indent();
          }
          handleChars(indexOfElse, ifStatement.getFalseStatement()
              .getStart());
          lastPosition = indexOfElse;
        } else {
          appendToBuffer("else "); //$NON-NLS-1$
          if (!preferences.control_statement_keep_else_if_on_same_line) {
            insertNewLine();
            indentationLevel++;
            elseIndentationLevelChanged = true;
            indent();
          }
          // the following line handles the case : '}else' when
          // setSpaceAfterBlock() is called and offset is set to +1
          indexOfElse = (trueStatementEnd < lastPosition) ? lastPosition
              : indexOfElse + trueStatementEnd;

          handleChars(indexOfElse, ifStatement.getFalseStatement()
              .getStart());
        }
      }
    } else { // if the true statement is not a block then we should add new
      // line
      insertNewLine();
      indent();
      if (indexOfElse > 0) {
        indexOfElse += trueStatementEnd;
        handleChars(lastPosition, indexOfElse);
        appendToBuffer("else "); //$NON-NLS-1$
        if (!preferences.control_statement_keep_else_if_on_same_line) {
          insertNewLine();
          indentationLevel++;
          elseIndentationLevelChanged = true;
          indent();
        }
        handleChars(indexOfElse, ifStatement.getFalseStatement()
            .getStart());
        lastPosition = indexOfElse;
      } else {
        appendToBuffer(len == 2 ? "else " : EMPTY_STRING); //$NON-NLS-1$
        if ((len == 2)
            && !preferences.control_statement_keep_else_if_on_same_line) {
          insertNewLine();
          indentationLevel++;
          elseIndentationLevelChanged = true;
          indent();
        }
        if (indexOfElse == -1) {// in case of: STATEMENT;elseif ...
          indexOfElse = 0;
        }

        handleChars(indexOfElse + trueStatementEnd, ifStatement
            .getFalseStatement().getStart());
      }
    }
    boolean processed = isProcessed(ifStatement);
    ifStatement.getFalseStatement().accept(this);
    if (elseIndentationLevelChanged) {
      indentationLevel--;
    }
    if (ifStatement.getTrueStatement().getType() == ASTNode.BLOCK
        && !((Block) ifStatement.getTrueStatement()).isCurly()
        && !processed) {
      handleChars(ifStatement.getFalseStatement().getEnd(),
          ifStatement.getEnd());
      appendToBuffer("endif;"); //$NON-NLS-1$
      handleChars(ifStatement.getEnd(), ifStatement.getEnd());
    }
  }

  private void addAllIfStatements(IfStatement ifStatement) {
    processedIfStatements.add(ifStatement);
    Statement falseIfStatement;
    while ((falseIfStatement = ifStatement.getFalseStatement()) instanceof IfStatement) {
      ifStatement = (IfStatement) falseIfStatement;
      processedIfStatements.add(ifStatement);
    }

  }

  // this will perform handleChars() between the statement's end AND the
  // 'else'
  private int internalHandleElse(IfStatement ifStatement, int lastPosition)
      throws BadLocationException {
    String textBetween = ""; //$NON-NLS-1$
    int trueStatementEnd = ifStatement.getTrueStatement().getEnd();
    int indexOfElse = -1;
    textBetween = document.get(trueStatementEnd,
        ifStatement.getFalseStatement().getStart() - trueStatementEnd)
        .toLowerCase();
    indexOfElse = textBetween.lastIndexOf("else"); //$NON-NLS-1$
    if (indexOfElse > 0) {
      indexOfElse += trueStatementEnd;
      handleChars(lastPosition, indexOfElse);
      appendToBuffer("else"); //$NON-NLS-1$
      handleChars(indexOfElse, indexOfElse);
      lastPosition = indexOfElse;
    } else {
      appendToBuffer("else"); //$NON-NLS-1$
    }
    return lastPosition;
  }

  public boolean visit(IgnoreError ignoreError) {
    lineWidth++;// the '@' sign
    ignoreError.getExpression().accept(this);
    return false;
  }

  public boolean visit(Include include) {
    int lastPosition = include.getStart();
    int len = (include.getIncludeType() == Include.IT_INCLUDE || include
        .getIncludeType() == Include.IT_REQUIRE) ? 7 : 12;
    lastPosition += len;
    lineWidth += len;// add 'include' 'require' 'require_once'

    insertSpace();
    handleChars(lastPosition, include.getExpression().getStart());

    include.getExpression().accept(this);
    return false;
  }

  public boolean visit(InfixExpression infixExpression) {
    boolean forceSplit = this.preferences.line_wrap_binary_expression_force_split;
    if (binaryExpressionLineWrapPolicy == -1) {// not initialized
      binaryExpressionLineWrapPolicy = this.preferences.line_wrap_binary_expression_line_wrap_policy;
      binaryExpressionIndentGap = calculateIndentGap(
          this.preferences.line_wrap_binary_expression_indent_policy,
          this.preferences.line_wrap_wrapped_lines_indentation);

    }

    if (binaryExpressionSavedBuffer == null) {
      binaryExpressionSavedBuffer = replaceBuffer.toString();
      binaryExpressionSavedNode = infixExpression;
      binaryExpressionSavedChangesIndex = changes.size() - 1;
      binaryExpressionRevertPolicy = -1;
    }

    infixExpression.getLeft().accept(this);
    int operator = infixExpression.getOperator();
    boolean isStringOperator = ((operator == InfixExpression.OP_STRING_AND)
        || (operator == InfixExpression.OP_STRING_OR) || (operator == InfixExpression.OP_STRING_XOR));

    if (isStringOperator
        || this.preferences.insert_space_before_binary_operation) {
      insertSpace();
    }
    appendToBuffer(InfixExpression.getOperator(operator));

    // Need consider the right expression at first,because the right
    // expression might be
    // long enough to expend the line width.
    // This should cause the line wrap.
    int lineW = calcLinesWidth(infixExpression.getRight());

    switch (binaryExpressionLineWrapPolicy) {
    case NO_LINE_WRAP:
      // no_wrap
      break;
    case FIRST_WRAP_WHEN_NECESSARY:
      if (lineW > this.preferences.line_wrap_line_split) {
        wasBinaryExpressionWrapped = indentationLevel == 1;
        binaryExpressionLineWrapPolicy = WRAP_WHEN_NECESSARY;
        insertNewLine();
        indentationLevel += binaryExpressionIndentGap;
        wasBinaryExpressionWrapped = true;
        indent();
      }
      break;
    case WRAP_WHEN_NECESSARY:
      if (lineW > this.preferences.line_wrap_line_split) {
        wasBinaryExpressionWrapped = true;
        insertNewLine();
        indent();
      }
      break;
    case WRAP_FIRST_ELEMENT:
      if (forceSplit || lineW > this.preferences.line_wrap_line_split) {
        if (binaryExpressionRevertPolicy != -1) {
          binaryExpressionRevertPolicy = -1;
          binaryExpressionLineWrapPolicy = WRAP_WHEN_NECESSARY;
          insertNewLine();
          indentationLevel += binaryExpressionIndentGap;
          indent();
        } else {
          binaryExpressionRevertPolicy = WRAP_FIRST_ELEMENT;
          binaryExpressionLineWrapPolicy = NO_LINE_WRAP;
        }
        wasBinaryExpressionWrapped = true;
      }
      break;
    case WRAP_ALL_ELEMENTS:
      if (forceSplit || lineW > this.preferences.line_wrap_line_split) {
        if (binaryExpressionRevertPolicy != -1) {
          binaryExpressionRevertPolicy = -1;
          binaryExpressionLineWrapPolicy = ALWAYS_WRAP_ELEMENT;
          insertNewLine();
          indentationLevel += binaryExpressionIndentGap;
          indent();
        } else {
          binaryExpressionRevertPolicy = WRAP_ALL_ELEMENTS;
          binaryExpressionLineWrapPolicy = NO_LINE_WRAP;
        }
        wasBinaryExpressionWrapped = true;
      }
      break;
    case WRAP_ALL_ELEMENTS_NO_INDENT_FIRST:
      if (forceSplit || lineW > this.preferences.line_wrap_line_split) {
        if (binaryExpressionRevertPolicy != -1) {
          binaryExpressionRevertPolicy = -1;
          binaryExpressionLineWrapPolicy = ALWAYS_WRAP_ELEMENT;
          insertNewLine();
          indentationLevel += binaryExpressionIndentGap;
          indent();

          // increase the indentation level after the first element
          indentationLevel++;
          isBinaryExpressionExtraIndentation = true;
        } else {
          binaryExpressionRevertPolicy = WRAP_ALL_ELEMENTS_NO_INDENT_FIRST;
          binaryExpressionLineWrapPolicy = NO_LINE_WRAP;
        }
        wasBinaryExpressionWrapped = true;
      }
      break;
    case WRAP_ALL_ELEMENTS_EXCEPT_FIRST:
      if (forceSplit || lineW > this.preferences.line_wrap_line_split) {
        if (binaryExpressionRevertPolicy != -1) {
          binaryExpressionLineWrapPolicy = WRAP_ALL_ELEMENTS;
        } else {
          binaryExpressionRevertPolicy = WRAP_ALL_ELEMENTS_EXCEPT_FIRST;
          binaryExpressionLineWrapPolicy = NO_LINE_WRAP;
        }
        wasBinaryExpressionWrapped = true;
      }
      break;
    case ALWAYS_WRAP_ELEMENT:
      insertNewLine();
      indent();
      break;
    }

    if (isStringOperator
        || this.preferences.insert_space_after_binary_operation) {
      insertSpace();
    }

    // handle the chars between the variable to the value
    handleChars(infixExpression.getLeft().getEnd(), infixExpression
        .getRight().getStart());

    if (binaryExpressionRevertPolicy != -1
        && infixExpression == binaryExpressionSavedNode) {
      if (binaryExpressionLineWrapPolicy == WRAP_ALL_ELEMENTS
          && binaryExpressionRevertPolicy == WRAP_ALL_ELEMENTS_EXCEPT_FIRST) {
        infixExpression.getRight().accept(this);
      } else {
        revert(binaryExpressionSavedBuffer,
            binaryExpressionSavedChangesIndex);
        binaryExpressionLineWrapPolicy = binaryExpressionRevertPolicy;
        infixExpression.accept(this);
      }
    } else {
      infixExpression.getRight().accept(this);
    }
    return false;
  }

  private int calcLinesWidth(ASTNode node) {
    int lineW = lineWidth;
    try {
      int lineForStart = document.getLineOfOffset(node.getStart());
      int lineForEnd = document.getLineOfOffset(node.getEnd());

      if (lineForStart == lineForEnd) {
        lineW += node.getLength();
      } else {
        lineW = document.getLineLength(lineForEnd);
      }
    } catch (BadLocationException e) {
      Logger.logException(e);
    }
    return lineW;
  }

  public boolean visit(InLineHtml inLineHtml) {
    updateLinesWidth(inLineHtml);
    return false;
  }

  public boolean visit(InstanceOfExpression instanceOfExpression) {
    instanceOfExpression.getExpression().accept(this);
    if (this.preferences.insert_space_before_binary_operation) {
      insertSpace();
    }
    appendToBuffer("instanceof"); //$NON-NLS-1$
    if (this.preferences.insert_space_after_binary_operation) {
      insertSpace();
    }

    // handle the chars between the variable to the value
    handleChars(instanceOfExpression.getExpression().getEnd(),
        instanceOfExpression.getClassName().getStart());

    instanceOfExpression.getClassName().accept(this);

    return false;
  }

  public boolean visit(InterfaceDeclaration interfaceDeclaration) {
    insertSpace();
    lineWidth += 9;// interface
    handleChars(interfaceDeclaration.getStart() + 9, interfaceDeclaration
        .getName().getStart());
    interfaceDeclaration.getName().accept(this);

    int lastPosition = interfaceDeclaration.getName().getEnd();

    List<Identifier> interfaceList = interfaceDeclaration.interfaces();
    Identifier[] interfaces = new Identifier[interfaceList.size()];
    interfaces = interfaceList.toArray(interfaces);

    if (interfaces.length > 0) {
      appendToBuffer(" extends "); //$NON-NLS-1$
      int indentationGap = calculateIndentGap(
          this.preferences.line_wrap_superinterfaces_in_type_declaration_indent_policy,
          this.preferences.line_wrap_wrapped_lines_indentation);
      lastPosition = handleCommaList(
          interfaces,
          lastPosition,
          this.preferences.insert_space_before_comma_in_implements,
          this.preferences.insert_space_after_comma_in_implements,
          this.preferences.line_wrap_superinterfaces_in_type_declaration_line_wrap_policy,
          indentationGap,
          this.preferences.line_wrap_superinterfaces_in_type_declaration_force_split);
    }

    boolean isIndentationAdded = handleBlockOpenBrace(
        this.preferences.brace_position_for_class,
        this.preferences.insert_space_before_opening_brace_in_class);
    handleChars(lastPosition, interfaceDeclaration.getBody().getStart());

    interfaceDeclaration.getBody().accept(this);

    if (isIndentationAdded) {
      indentationLevel--;
      indentationLevelDesending = true;
    }
    return false;
  }

  public boolean visit(ListVariable listVariable) {
    if (this.preferences.insert_space_before_opening_paren_in_list) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_opening_paren_in_list) {
      insertSpace();
    }

    int lastPosition = listVariable.getStart() + 4;
    lineWidth += 4;
    List<VariableBase> variables = listVariable.variables();
    VariableBase[] variablesArray = variables
        .toArray(new VariableBase[variables.size()]);
    lastPosition = handleCommaList(variablesArray, lastPosition,
        this.preferences.insert_space_before_comma_in_list,
        this.preferences.insert_space_after_comma_in_list,
        NO_LINE_WRAP, NO_LINE_WRAP_INDENT, false);

    if (this.preferences.insert_space_before_closing_paren_in_list) {
      if (variablesArray[variablesArray.length - 1].getLength() == 0
          && this.preferences.insert_space_after_comma_in_list) {
        // in the following case list($a,) we don't want the second
        // empty parameter to have two spaces. (like this: list( $a, ))
      } else {
        insertSpace();
      }
    }
    appendToBuffer(CLOSE_PARN);
    handleChars(lastPosition, listVariable.getEnd());

    return false;
  }

  public boolean visit(MethodDeclaration classMethodDeclaration) {
    // handle method modifiers
    String originalModifier = getDocumentString(
        classMethodDeclaration.getStart(),
        classMethodDeclaration.getFunction().getStart()).trim();
    StringTokenizer tokenizer = new StringTokenizer(originalModifier);
    StringBuffer strBuffer = new StringBuffer();
    while (tokenizer.hasMoreTokens()) {
      strBuffer.append(tokenizer.nextToken() + " "); //$NON-NLS-1$
    }
    int len;
    String formattedModifier = ""; //$NON-NLS-1$
    if ((len = strBuffer.length()) > 0) { /* trim trailing space */
      formattedModifier = strBuffer.toString().substring(0, len - 1);
    }

    appendToBuffer(formattedModifier);
    if (formattedModifier.length() > 0) {
      insertSpace();
    }
    handleChars(classMethodDeclaration.getStart(), classMethodDeclaration
        .getFunction().getStart());
    classMethodDeclaration.getFunction().accept(this);
    return false;
  }

  public boolean visit(MethodInvocation methodInvocation) {
    VariableBase dispatch = methodInvocation.getDispatcher();

    // 0029458: [Ticket 188445][Roman][Feature] Method chaining in Code
    // Formatter
    // Track the chain level here.
    if (this.preferences.new_line_in_second_invoke > 0) {
      if (chainStack.isEmpty() || chainStack.peek() == -1) {
        chainStack.push(1);
      } else {
        int value = chainStack.pop();
        chainStack.push(++value);
      }

      if (!(dispatch instanceof MethodInvocation)) {
        chainStack.push(-1);
      }
    }

    dispatch.accept(this);

    if (this.preferences.insert_space_before_arrow_in_method_invocation) {
      insertSpace();
    }

    if (this.preferences.new_line_in_second_invoke > 0) {
      // 0029458: [Ticket 188445][Roman][Feature] Method chaining in Code
      // Formatter
      // calculate the peek chain level.
      if (peek == null) {
        peek = -1;
      }
      if (chainStack.peek() == -1) {
        chainStack.pop();
      }

      peek = peek > chainStack.peek() ? peek : chainStack.peek();

      // if peek chain level bigger than the preference, wrap them
      if (peek > this.preferences.new_line_in_second_invoke - 1) {
        if (dispatch instanceof MethodInvocation) {

          insertNewLine();

          indentationLevel++;
          indent();
          indentationLevel--;
        }
      }
    }

    appendToBuffer("->"); //$NON-NLS-1$
    if (this.preferences.insert_space_after_arrow_in_method_invocation) {
      insertSpace();
    }

    if (this.preferences.insert_space_after_arrow_in_method_invocation) {
      insertSpace();
    }

    // handle the chars between the dispatcher to the property
    handleChars(methodInvocation.getDispatcher().getEnd(), methodInvocation
        .getMethod().getStart());

    methodInvocation.getMethod().accept(this);

    // 0029458: [Ticket 188445][Roman][Feature] Method chaining in Code
    // Formatter
    // Track the chain level.
    if (this.preferences.new_line_in_second_invoke > 0) {
      if (!chainStack.isEmpty()) {
        int value = chainStack.pop();
        value -= 1;
        if (value > 0) {
          chainStack.push(value);
        } else {
          if (!chainStack.isEmpty()) {
            peek = chainStack.peek();
          } else {
            peek = -1;
          }
        }
      }
    }
    return false;
  }

  public boolean visit(ParenthesisExpression parenthesisExpression) {
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_open_paren_in_parenthesis_expression) {
      insertSpace();
    }
    int lastPosition = parenthesisExpression.getStart();
    Expression expression = parenthesisExpression.getExpression();
    if (expression != null) {
      // till the expression
      handleChars(lastPosition, expression.getStart());
      expression.accept(this);
      lastPosition = expression.getEnd();
    }
    if (this.preferences.insert_space_before_close_paren_in_parenthesis_expression) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);
    handleChars(lastPosition, parenthesisExpression.getEnd());
    return false;
  }

  public boolean visit(PostfixExpression postfixExpressions) {
    postfixExpressions.getVariable().accept(this);
    if (this.preferences.insert_space_before_postfix_expression) {
      insertSpace();
    }
    appendToBuffer(PostfixExpression.getOperator(postfixExpressions
        .getOperator()));
    if (this.preferences.insert_space_before_prefix_expression) {
      insertSpace();
    }
    // handle the chars between the variable to the value
    handleChars(postfixExpressions.getVariable().getEnd(),
        postfixExpressions.getEnd());

    return false;
  }

  public boolean visit(PrefixExpression prefixExpression) {
    if (this.preferences.insert_space_before_prefix_expression) {
      insertSpace();
    }
    appendToBuffer(PostfixExpression.getOperator(prefixExpression
        .getOperator()));
    if (this.preferences.insert_space_after_prefix_expression) {
      insertSpace();
    }
    // handle the chars between the variable to the value
    handleChars(prefixExpression.getStart(), prefixExpression.getVariable()
        .getStart());
    prefixExpression.getVariable().accept(this);
    return false;
  }

  public boolean visit(Program program) {
    int lastStatementEndOffset = 0;
    boolean isPhpMode = false;
    List<Statement> statementList = program.statements();
    Statement[] statements = new Statement[statementList.size()];
    statements = statementList.toArray(statements);
    // FIXME if the php file only contains comments,the comments will not be
    // formatted
    // if (statements.length == 0 && !program.comments().isEmpty()) {
    // try {
    // Comment comment = program.comments().get(
    // program.comments().size() - 1);
    // boolean hasComments = hasComments(program.getStart(),
    // comment.getEnd());
    // if (hasComments) {
    // // handle the comments
    // handleComments(program.getStart(), program.getEnd(),
    // astLexer.getCommentList());
    // } else {
    // }
    // } catch (Exception e) {
    // }
    //
    // }

    for (int i = 0; i < statements.length; i++) {
      boolean isHtmlStatement = statements[i].getType() == ASTNode.IN_LINE_HTML;
      boolean isASTError = statements[i].getType() == ASTNode.AST_ERROR;
      // fixed bug 0015682
      // in case of previous statement is an error there is no need for
      // new lines
      // because the lastStatementEndOffset position move to the current
      // statement start position
      boolean isStatementAfterError = i > 0 ? statements[i - 1].getType() == ASTNode.AST_ERROR
          : false;
      if (isASTError && i + 1 < statements.length) {
        // move the lastStatementEndOffset position to the start of the
        // next statement start position
        lastStatementEndOffset = statements[i + 1].getStart();
      } else {
        if (isPhpMode && !isHtmlStatement) {

          // PHP -> PHP
          if (lastStatementEndOffset > 0) {
            if (!isStatementAfterError
                && getPhpStartTag(lastStatementEndOffset) != -1) {
              insertNewLine();
            }
            insertNewLines(statements[i]);
            indent();
            if (lastStatementEndOffset <= statements[i].getStart()) {
              handleChars(lastStatementEndOffset,
                  statements[i].getStart());
            }
          }
        } else if (isPhpMode && isHtmlStatement) {
          // PHP -> HTML
          if (lastStatementEndOffset > 0) {
            if (lastStatementEndOffset <= statements[i].getStart()) {
              handleChars(lastStatementEndOffset,
                  statements[i].getStart());
            }
          }
          isPhpMode = false;
        } else if (!isPhpMode && !isHtmlStatement) {
          // HTML -> PHP
          if (!isStatementAfterError) {
            isPhpEqualTag = getPhpStartTag(lastStatementEndOffset) == PHP_OPEN_SHORT_TAG_WITH_EQUAL;
            indentationLevel = getPhpTagIndentationLevel(lastStatementEndOffset);
            insertNewLines(statements[i]);
          }
          indent();
          if (lastStatementEndOffset <= statements[i].getStart()) {
            handleChars(lastStatementEndOffset,
                statements[i].getStart());
          }
          isPhpMode = true;
        } else {
          // first HTML
          isPhpMode = false;
        }
        statements[i].accept(this);
        lastStatementEndOffset = statements[i].getEnd();
        // need check how many new lines will the next statement
        // insert
        if (i + 1 < statements.length
            && statements[i].getType() == ASTNode.NAMESPACE
            && statements[i + 1].getType() == ASTNode.NAMESPACE) {
          int numberOfLines = getNumbreOfLines(statements[i + 1]) - 1;
          numberOfLines = this.preferences.blank_lines_between_namespaces
              - numberOfLines;
          if (numberOfLines > 0) {
            for (int j = 0; j < numberOfLines; j++) {
              insertNewLine();
            }
          }
          // ignoreEmptyLineSetting = true;
          ignoreEmptyLineSetting = !preferences.indent_empty_lines;
        }
      }
    }
    return false;
  }

  public boolean visit(Quote quote) {
    updateLinesWidth(quote);
    if (quote.getQuoteType() == Quote.QT_HEREDOC) {
      int i = quote.getEnd();
      if (isContainChar(i, i + 1, SEMICOLON)) {
        isHeredocSemicolon = true;
      } else {
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=411322
        // always insert a new line after the closing HEREDOC tag
        boolean isPhpEqualTagOld = isPhpEqualTag;
        isPhpEqualTag = false;
        insertNewLine();
        isPhpEqualTag = isPhpEqualTagOld;
      }
    }
    return false;
  }

  public boolean visit(Reference reference) {
    lineWidth++;// &$a
    reference.getExpression().accept(this);
    return false;
  }

  public boolean visit(ReflectionVariable reflectionVariable) {
    lineWidth++;// $$a
    reflectionVariable.getName().accept(this);
    return false;
  }

  public boolean visit(ReturnStatement returnStatement) {
    int lastPosition = returnStatement.getStart() + 6;
    lineWidth += 6;

    Expression expression = returnStatement.getExpression();
    if (expression != null) {
      insertSpace();
      handleChars(lastPosition, expression.getStart());
      expression.accept(this);
      lastPosition = expression.getEnd();
    }

    handleSemicolon(lastPosition, returnStatement.getEnd());
    return false;
  }

  public boolean visit(YieldExpression yieldExpression) {
    lineWidth += 5;
    // handle [key => expr] or just [expr]
    int lastPosition = yieldExpression.getStart() + 5;
    insertSpace();
    if (yieldExpression.getKey() != null) {
      handleChars(lastPosition, yieldExpression.getKey().getStart());
      yieldExpression.getKey().accept(this);
      if (this.preferences.insert_space_before_arrow_in_yield) {
        insertSpace();
      }
      appendToBuffer(KEY_VALUE_OPERATOR);
      if (this.preferences.insert_space_after_arrow_in_yield) {
        insertSpace();
      }
      lastPosition = yieldExpression.getKey().getEnd();
    }
    handleChars(lastPosition, yieldExpression.getExpression().getStart());
    yieldExpression.getExpression().accept(this);
    lastPosition = yieldExpression.getExpression().getEnd();

    return false;
  }

  public boolean visit(Scalar scalar) {
    updateLinesWidth(scalar);
    return false;
  }

  public boolean visit(StaticConstantAccess staticConstantAccess) {
    staticConstantAccess.getClassName().accept(this);
    if (this.preferences.insert_space_before_coloncolon_in_field_access) {
      insertSpace();
    }
    appendToBuffer(COLON);
    appendToBuffer(COLON);
    if (this.preferences.insert_space_after_coloncolon_in_field_access) {
      insertSpace();
    }
    handleChars(staticConstantAccess.getClassName().getEnd(),
        staticConstantAccess.getConstant().getStart());
    staticConstantAccess.getConstant().accept(this);
    return false;
  }

  public boolean visit(StaticFieldAccess staticFieldAccess) {
    staticFieldAccess.getClassName().accept(this);
    if (this.preferences.insert_space_before_coloncolon_in_field_access) {
      insertSpace();
    }
    appendToBuffer(COLON);
    appendToBuffer(COLON);
    if (this.preferences.insert_space_after_coloncolon_in_field_access) {
      insertSpace();
    }
    handleChars(staticFieldAccess.getClassName().getEnd(),
        staticFieldAccess.getField().getStart());
    staticFieldAccess.getField().accept(this);
    return false;
  }

  public boolean visit(StaticMethodInvocation staticMethodInvocation) {
    staticMethodInvocation.getClassName().accept(this);
    if (this.preferences.insert_space_before_coloncolon_in_method_invocation) {
      insertSpace();
    }
    appendToBuffer(COLON);
    appendToBuffer(COLON);
    if (this.preferences.insert_space_after_coloncolon_in_method_invocation) {
      insertSpace();
    }
    handleChars(staticMethodInvocation.getClassName().getEnd(),
        staticMethodInvocation.getMethod().getStart());
    staticMethodInvocation.getMethod().accept(this);
    return false;
  }

  public boolean visit(StaticStatement staticStatement) {
    int lastPosition = staticStatement.getStart() + 6;
    lineWidth += 6;
    insertSpace();

    List<Expression> expList = staticStatement.expressions();
    Expression[] expressions = new Expression[expList.size()];
    expressions = expList.toArray(expressions);

    lastPosition = handleCommaList(expressions, lastPosition,
        this.preferences.insert_space_before_comma_in_static,
        this.preferences.insert_space_after_comma_in_static,
        NO_LINE_WRAP, NO_LINE_WRAP_INDENT, false);

    handleSemicolon(lastPosition, staticStatement.getEnd());
    return false;
  }

  public boolean visit(SwitchCase switchCase) {
    // handle the chars between the 'case'/'default' and the condition start
    // position/ first statement
    int lastStatementEndOffset = 0;
    if (switchCase.isDefault()) {
      if (this.preferences.insert_space_after_switch_default) {
        insertSpace();
      }
      lastStatementEndOffset = switchCase.getStart() + 7;
      lineWidth += 7;// the word 'default'
    } else {
      insertSpace();
      lineWidth += 4;// the word 'case'
      handleChars(switchCase.getStart() + 4, switchCase.getValue()
          .getStart());
      switchCase.getValue().accept(this);
      if (this.preferences.insert_space_after_switch_case_value) {
        insertSpace();
      }
      lastStatementEndOffset = switchCase.getValue().getEnd();

    }
    appendToBuffer(COLON);

    int regularStatementIndentation = 0;
    int breakStatementIndentation = 0;
    if (this.preferences.indent_statements_within_case) {
      regularStatementIndentation++;
    }
    if (this.preferences.indent_break_statements_within_case) {
      breakStatementIndentation++;
    }
    Statement[] actions = new Statement[switchCase.actions().size()];
    switchCase.actions().toArray(actions);
    if (actions.length == 0) {
      handleChars(lastStatementEndOffset, switchCase.getEnd());
    } else {
      for (int i = 0; i < actions.length; i++) {
        if (actions[i].getType() == ASTNode.IN_LINE_HTML) {
          handleChars(lastStatementEndOffset, lastStatementEndOffset);
          lastStatementEndOffset = actions[i].getEnd();
          continue;
        }
        boolean isBreakStatement = actions[i].getType() == ASTNode.BREAK_STATEMENT;
        this.indentationLevel += isBreakStatement ? breakStatementIndentation
            : regularStatementIndentation;
        insertNewLine();
        indent();
        handleChars(lastStatementEndOffset, actions[i].getStart());
        actions[i].accept(this);
        lastStatementEndOffset = actions[i].getEnd();
        this.indentationLevel -= isBreakStatement ? breakStatementIndentation
            : regularStatementIndentation;
      }
    }
    return false;
  }

  public boolean visit(SwitchStatement switchStatement) {
    // handle the chars between the 'switch' and the expr start position
    if (this.preferences.insert_space_before_opening_paren_in_switch) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_opening_paren_in_switch) {
      insertSpace();
    }
    lineWidth += 6;
    Expression expression = switchStatement.getExpression();
    handleChars(switchStatement.getStart() + 6, expression.getStart());

    // handle the switch expr
    expression.accept(this);

    // handle the chars between the expression end position and action start
    // position

    // set the switch closing parn spaces
    if (this.preferences.insert_space_before_closing_paren_in_switch) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    // switch body
    boolean isIndentationAdded = false;
    isIndentationAdded = handleBlockOpenBrace(
        this.preferences.brace_position_for_switch,
        this.preferences.insert_space_before_opening_brace_in_switch);
    Block body = switchStatement.getBody();
    handleChars(expression.getEnd(), body.getStart());

    body.accept(this);

    if (isIndentationAdded) {
      indentationLevel--;
      indentationLevelDesending = true;
    }
    return false;
  }

  public boolean visit(ThrowStatement throwStatement) {
    insertSpace();
    lineWidth += 5;
    Expression expr = throwStatement.getExpression();
    handleChars(throwStatement.getStart() + 5, expr.getStart());

    expr.accept(this);

    handleSemicolon(expr.getEnd(), throwStatement.getEnd());

    return false;
  }

  public boolean visit(TryStatement tryStatement) {
    boolean isIndentationAdded = handleBlockOpenBrace(
        this.preferences.brace_position_for_block,
        this.preferences.insert_space_before_opening_brace_in_block);
    lineWidth += 3;
    Block body = tryStatement.getBody();
    handleChars(tryStatement.getStart() + 3, body.getStart());
    body.accept(this);
    if (isIndentationAdded) {
      indentationLevel--;
      indentationLevelDesending = true;
    }

    int lastStatementEndOffset = body.getEnd();
    List<CatchClause> clausesList = tryStatement.catchClauses();
    CatchClause[] catchClauses = new CatchClause[clausesList.size()];
    catchClauses = clausesList.toArray(catchClauses);

    for (int i = 0; i < catchClauses.length; i++) {
      if (preferences.control_statement_insert_newline_before_catch_in_try) {
        insertNewLine();
        indent();
      } else {
        if (this.preferences.insert_space_after_closing_brace_in_block) {
          insertSpace();
        }
      }
      handleChars(lastStatementEndOffset, catchClauses[i].getStart());
      catchClauses[i].accept(this);
      lastStatementEndOffset = catchClauses[i].getEnd();
    }
    if (tryStatement.finallyClause() != null) {
      if (preferences.control_statement_insert_newline_before_finally_in_try) {
        insertNewLine();
        indent();
      } else {
        if (this.preferences.insert_space_after_closing_brace_in_block) {
          insertSpace();
        }
      }
      handleChars(lastStatementEndOffset, tryStatement.finallyClause()
          .getStart());
      tryStatement.finallyClause().accept(this);
      lastStatementEndOffset = tryStatement.finallyClause().getEnd();
    }
    return false;
  }

  public boolean visit(UnaryOperation unaryOperation) {
    if (this.preferences.insert_space_before_unary_expression) {
      insertSpace();
    }
    appendToBuffer(UnaryOperation.getOperator(unaryOperation.getOperator()));
    if (this.preferences.insert_space_after_unary_expression) {
      insertSpace();
    }
    // handle the chars between the variable to the value
    Expression expr = unaryOperation.getExpression();
    handleChars(unaryOperation.getStart(), expr.getStart());
    expr.accept(this);
    return false;
  }

  public boolean visit(Variable variable) {
    if (variable.isDollared()) {
      lineWidth++;
    }
    variable.getName().accept(this);
    return false;
  }

  public boolean visit(WhileStatement whileStatement) {
    // handle the chars between the 'while' and the condition start position
    if (this.preferences.insert_space_before_opening_paren_in_while) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    if (this.preferences.insert_space_after_opening_paren_in_while) {
      insertSpace();
    }
    lineWidth += 5;
    handleChars(whileStatement.getStart() + 5, whileStatement
        .getCondition().getStart());

    // handle the while condition
    whileStatement.getCondition().accept(this);

    // handle the chars between the condition end position and action start
    // position

    // set the while closing paren spaces
    if (this.preferences.insert_space_before_closing_paren_in_while) {
      insertSpace();
    }
    appendToBuffer(CLOSE_PARN);

    // while action
    final int lastPosition = whileStatement.getCondition().getEnd();
    handleAction(lastPosition, whileStatement.getBody(), true);
    return false;
  }

  // PHP 5.3 new nodes:

  public boolean visit(NamespaceDeclaration namespaceDeclaration) {
    appendToBuffer("namespace"); //$NON-NLS-1$
    insertSpace();
    int lastPosition = namespaceDeclaration.getStart();
    if (namespaceDeclaration.getName() != null) {
      handleChars(namespaceDeclaration.getStart(), namespaceDeclaration
          .getName().getStart());
      namespaceDeclaration.getName().accept(this);

      lastPosition = namespaceDeclaration.getName().getEnd();
    }
    if (namespaceDeclaration.isBracketed()) {
      // handle class body
      boolean isIndentationAdded = handleBlockOpenBrace(
          this.preferences.brace_position_for_class,
          this.preferences.insert_space_before_opening_brace_in_class);
      handleChars(lastPosition, namespaceDeclaration.getBody().getStart());

      namespaceDeclaration.getBody().accept(this);

      if (isIndentationAdded) {
        indentationLevel--;
        indentationLevelDesending = true;
      }
    } else {
      handleSemicolon(lastPosition, namespaceDeclaration.getBody()
          .getStart() - 1);
      namespaceDeclaration.getBody().accept(this);
    }
    return false;
  }

  public boolean visit(NamespaceName namespaceName) {
    if (namespaceName.isGlobal()) {
      appendToBuffer("\\"); //$NON-NLS-1$
    }

    if (namespaceName.isCurrent()) {
      appendToBuffer("namespace\\"); //$NON-NLS-1$
    }
    List<Identifier> segments = namespaceName.segments();
    if (segments.size() > 0) {
      // workaround
      // remove this after fixing of
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=326384
      int start = namespaceName.getStart();
      try {
        if (namespaceName.isGlobal()
            && (Character.isWhitespace(document.getChar(start - 1)) || document
                .getChar(start - 1) == '\\')) {
          start -= 1;
        }
      } catch (BadLocationException e) {
        // should not be here
      }
      // end

      handleChars(start, segments.get(0).getStart());
      Iterator<Identifier> it = segments.iterator();
      Identifier prev = null;
      while (it.hasNext()) {
        Identifier identifier = it.next();
        if (prev != null) {
          handleChars(prev.getEnd(), identifier.getStart());
        }
        identifier.accept(this);
        if (it.hasNext()) {
          appendToBuffer("\\"); //$NON-NLS-1$
          prev = identifier;
        }
      }
    } else {
      handleChars(namespaceName.getStart(), namespaceName.getEnd());
    }
    return false;
  }

  public boolean visit(UseStatement useStatement) {
    int lastPosition = useStatement.getStart() + 3;
    lineWidth += 3;// the word 'use'
    insertSpace();

    List<UseStatementPart> parts = useStatement.parts();
    lastPosition = handleCommaList(
        parts.toArray(new ASTNode[parts.size()]), lastPosition,
        this.preferences.insert_space_before_comma_in_global,
        this.preferences.insert_space_after_comma_in_global,
        NO_LINE_WRAP, NO_LINE_WRAP_INDENT, false);

    handleSemicolon(lastPosition, useStatement.getEnd());
    return false;
  }

  public boolean visit(UseStatementPart useStatementPart) {
    useStatementPart.getName().accept(this);
    Identifier alias = useStatementPart.getAlias();
    if (alias != null) {
      insertSpace();
      appendToBuffer("as"); //$NON-NLS-1$
      insertSpace();
      handleChars(useStatementPart.getName().getEnd(), alias.getStart());
      alias.accept(this);
    }
    return false;
  }

  public boolean visit(LambdaFunctionDeclaration lambdaFunctionDeclaration) {
    StringBuffer buffer = new StringBuffer();
    if (lambdaFunctionDeclaration.isStatic()) {
      buffer.append("static "); //$NON-NLS-1$
    }
    buffer.append(getDocumentString(lambdaFunctionDeclaration.getStart(),
        lambdaFunctionDeclaration.getStart() + 8));// append 'function'

    // handle referenced function with '&'
    if (lambdaFunctionDeclaration.isReference()) {
      buffer.append(" &"); //$NON-NLS-1$
    } else {
      buffer.append(' ');
    }

    appendToBuffer(buffer.toString());
    handleChars(lambdaFunctionDeclaration.getStart(),
        lambdaFunctionDeclaration.getStart() + 8);

    if (this.preferences.insert_space_before_opening_paren_in_function_declaration) {
      insertSpace();
    }
    appendToBuffer(OPEN_PARN);
    List<FormalParameter> formalParameters = lambdaFunctionDeclaration
        .formalParameters();
    ASTNode[] params = (FormalParameter[]) formalParameters
        .toArray(new FormalParameter[formalParameters.size()]);
    int lastPosition = lambdaFunctionDeclaration.getStart() + 8;
    if (params.length > 0) {
      if (this.preferences.insert_space_after_opening_paren_in_function_declaration) {
        insertSpace();
      }
      int indentationGap = calculateIndentGap(
          this.preferences.line_wrap_parameters_in_method_declaration_indent_policy,
          this.preferences.line_wrap_wrapped_lines_indentation);
      lastPosition = handleCommaList(
          params,
          lastPosition,
          this.preferences.insert_space_before_comma_in_function_declaration,
          this.preferences.insert_space_after_comma_in_function_declaration,
          this.preferences.line_wrap_parameters_in_method_declaration_line_wrap_policy,
          indentationGap,
          this.preferences.line_wrap_parameters_in_method_declaration_force_split);

      if (this.preferences.insert_space_before_closing_paren_in_function_declaration) {
        insertSpace();
      }
    } else {
      if (this.preferences.insert_space_between_empty_paren_in_function_declaration) {
        insertSpace();
      }
    }
    appendToBuffer(CLOSE_PARN);

    List<Expression> variables = lambdaFunctionDeclaration
        .lexicalVariables();

    if (variables.size() > 0) {
      // TODO: Added a new preference?
      insertSpace();
      appendToBuffer("use"); //$NON-NLS-1$

      appendToBuffer(OPEN_PARN);
      if (this.preferences.insert_space_before_opening_paren_in_function_declaration) {
        insertSpace();
      }
      ASTNode[] vars = (Expression[]) variables
          .toArray(new Expression[variables.size()]);

      lastPosition = handleCommaList(
          vars,
          lastPosition,
          this.preferences.insert_space_before_comma_in_function_declaration,
          this.preferences.insert_space_after_comma_in_function_declaration,
          this.preferences.line_wrap_parameters_in_method_declaration_line_wrap_policy,
          0,
          this.preferences.line_wrap_parameters_in_method_declaration_force_split);

      if (this.preferences.insert_space_before_closing_paren_in_function_declaration) {
        insertSpace();
      }

      appendToBuffer(CLOSE_PARN);
    }

    // handle function body
    if (lambdaFunctionDeclaration.getBody() != null) {
      boolean isIndentationAdded = handleBlockOpenBrace(
          this.preferences.brace_position_for_function,
          this.preferences.insert_space_before_opening_brace_in_function);
      handleChars(lastPosition, lambdaFunctionDeclaration.getBody()
          .getStart());

      lambdaFunctionDeclaration.getBody().accept(this);
      if (isIndentationAdded) {
        indentationLevel--;
        indentationLevelDesending = true;
      }
    } else {
      handleSemicolon(lastPosition, lambdaFunctionDeclaration.getEnd());
    }

    return false;
  }

  public boolean visit(TraitUseStatement node) {
    if (node.getTraitList().size() > 0) {
      // int lastPosition = node.getStart() + 3;
      lineWidth += 3;// the word 'use'
      insertSpace();
      handleChars(node.getStart() + 3, node.getTraitList().get(0)
          .getStart());
    }
    return true;
  }

  /*
   * (non-Javadoc)
   *
   * @seeorg.eclipse.php.internal.core.format.ICodeFormattingProcessor#
   * createIndentationString(int)
   */
  public String createIndentationString(int indentationUnits) {
    if (indentationUnits < 0) {
      throw new IllegalArgumentException();
    }
    int tabs = 0;
    tabs = indentationUnits;
    if (tabs == 0) {
      return ""; //$NON-NLS-1$
    }
    StringBuffer buffer = new StringBuffer(tabs);
    for (int i = 0; i < tabs; i++) {
      buffer.append(preferences.indentationChar);
    }
    return buffer.toString();
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.php.internal.core.format.ICodeFormattingProcessor#getTextEdits
   * ()
   */
  public MultiTextEdit getTextEdits() {
    List<ReplaceEdit> allChanges = getChanges();
    MultiTextEdit rootEdit = new MultiTextEdit();
    for (ReplaceEdit edit : allChanges) {
      TextEdit textEdit = new org.eclipse.text.edits.ReplaceEdit(
          edit.offset, edit.length, edit.content);
      rootEdit.addChild(textEdit);
    }
    return rootEdit;
  }

  private boolean isInSingleLine(ReplaceEdit edit, IRegion[] partitions,
      int removedLength) {
    removedLength = 0;
    for (int i = 0; i < partitions.length; i++) {
      IRegion iTypedRegion = partitions[i];
      if (edit.offset >= iTypedRegion.getOffset() + removedLength
          && edit.offset + edit.length <= iTypedRegion.getOffset()
              + iTypedRegion.getLength() + removedLength) {
        return true;
      }
    }
    return false;
  }

  private IRegion[] getAllSingleLine(ITypedRegion[] partitions) {
    List<IRegion> result = new ArrayList<IRegion>();
    if (document instanceof IStructuredDocument) {
      IStructuredDocument structuredDocument = (IStructuredDocument) document;
      ITextRegion phpOpenRegion = null;
      for (int i = 0; i < partitions.length; i++) {
        ITypedRegion iTypedRegion = partitions[i];

        if (PHPPartitionTypes.PHP_DEFAULT
            .equals(iTypedRegion.getType())) {
          if (isInSingleLine(iTypedRegion.getOffset(),
              iTypedRegion.getLength())) {
            result.add(iTypedRegion);
            continue;
          }
          IStructuredDocumentRegion structuredDocumentRegion = structuredDocument
              .getRegionAtCharacterOffset(iTypedRegion
                  .getOffset());
          ITextRegionList regions = structuredDocumentRegion
              .getRegions();
          for (Iterator iterator = regions.iterator(); iterator
              .hasNext();) {
            ITextRegion iTypedRegion2 = (ITextRegion) iterator
                .next();
            // if (iTypedRegion2 instanceof ContextRegionContainer)
            // {
            // ContextRegionContainer new_name =
            // (ContextRegionContainer) iTypedRegion2;
            //
            // }
            if (PHPRegionContext.PHP_OPEN.equals(iTypedRegion2
                .getType())) {
              if (phpOpenRegion == null) {
                phpOpenRegion = iTypedRegion2;
              }
            } else if (PHPRegionContext.PHP_CLOSE
                .equals(iTypedRegion2.getType())) {
              if (phpOpenRegion != null) {
                IRegion region = new Region(
                    structuredDocumentRegion.getStart()
                        + phpOpenRegion.getStart(),
                    iTypedRegion2.getStart()
                        + iTypedRegion2.getLength());
                result.add(region);
                phpOpenRegion = null;
              }
            }
          }
        }
      }
      List<IRegion> temp = new ArrayList<IRegion>();
      for (Iterator<IRegion> iterator = result.iterator(); iterator
          .hasNext();) {
        IRegion iRegion = iterator.next();
        if (isInSingleLine(iRegion.getOffset(), iRegion.getLength())) {
          temp.add(iRegion);
        }
      }
      result = temp;
    }
    return result.toArray(new IRegion[result.size()]);
  }

  private boolean isInSingleLine(int start, int length) {
    try {
      String text = document.get(start, length);
      int index = text.indexOf("<?"); //$NON-NLS-1$
      start += index;
      if (text.lastIndexOf("?>") >= 0) { //$NON-NLS-1$
        length = text.lastIndexOf("?>") + 2 - index; //$NON-NLS-1$
      }
      if (document.getLineOfOffset(start) == document
          .getLineOfOffset(start + length)) {
        return true;
      }
    } catch (BadLocationException e) {
    }
    return false;
  }

  public static String join(Collection<String> s, String delimiter) {
    if (s == null || s.isEmpty())
      return ""; //$NON-NLS-1$
    Iterator<String> iter = s.iterator();
    StringBuilder builder = new StringBuilder(iter.next());
    while (iter.hasNext()) {
      builder.append(delimiter).append(iter.next());
    }
    return builder.toString();
  }

}
TOP

Related Classes of org.eclipse.php.formatter.core.CodeFormatterVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.