/**
  * @param offset
  * @param document
  * @return
  */
 private int getBeginWithoutWhiteSpaces(int offset, FormatterDocument document) {
   int length = document.getLength();
   while (offset < length) {
     if (!Character.isWhitespace(document.charAt(offset))) {
       break;
     }
     offset++;
   }
   return offset;
 }
 private int getBlockStartOffset(int offset, FormatterDocument document) {
   int length = document.getLength();
   while (offset < length) {
     if (document.charAt(offset) == '{') {
       break;
     }
     offset++;
   }
   return offset;
 }
 /**
  * Scan for given character located at the same line. Return the given offset if non is found.
  *
  * @param offset
  * @param document
  * @return The offset of the character; The given offset if character not found.
  */
 private int locateCharacterInSameLine(char character, int offset, FormatterDocument document) {
   for (int i = offset; i < document.getLength(); i++) {
     char c = document.charAt(i);
     if (c == character) {
       return i;
     }
     if (c == '\n' || c == '\r') {
       break;
     }
   }
   return offset;
 }
  /**
   * build
   *
   * @param parseResult
   * @param document
   * @return
   */
  public IFormatterContainerNode build(IParseNode parseResult, FormatterDocument document) {
    this._document = document;

    // create the formatter root node
    IFormatterContainerNode rootNode = new JSONRootFormatNode(document);

    // begin the transformation
    start(rootNode);

    JSONFormattingWalker walker = new JSONFormattingWalker();
    JSONParseRootNode jsonRootNode = (JSONParseRootNode) parseResult;
    jsonRootNode.accept(walker);

    // end the transformation
    checkedPop(rootNode, document.getLength());

    return rootNode;
  }
  // This is a temporary fix for custom at-rules. When the parser adds support to return the ruleID,
  // this will need to
  // be changed
  private void pushAtRuleNode(CSSNode atRuleNode) {

    int length = document.getLength();
    int selectorStartingOffset = atRuleNode.getStartingOffset();
    int selectEndingOffset = atRuleNode.getEndingOffset();

    // Locate first white space after the @rule
    while (selectorStartingOffset < length) {
      if (Character.isWhitespace(document.charAt(selectorStartingOffset))) {
        break;
      }
      selectorStartingOffset++;
    }
    // Find the starting offset for the selector
    selectorStartingOffset = getBeginWithoutWhiteSpaces(selectorStartingOffset, document);

    // Find the end offset for the selector
    while (selectEndingOffset >= selectorStartingOffset) {
      if (!Character.isWhitespace(document.charAt(selectEndingOffset - 1))) {
        break;
      }
      selectEndingOffset--;
    }
    // Push an At-Node to control the lines separators.
    FormatterCSSAtRuleNode atNode = new FormatterCSSAtRuleNode(document);
    atNode.setBegin(
        createTextNode(document, atRuleNode.getStartingOffset(), selectorStartingOffset));
    push(atNode);
    checkedPop(atNode, -1);

    // We use a selector node for now, we may want to create a new formatter node type for rule id
    FormatterBlockWithBeginNode formatterSelectorNode =
        new FormatterCSSSelectorNode(document, false, false);
    formatterSelectorNode.setBegin(
        createTextNode(
            document,
            getBeginWithoutWhiteSpaces(selectorStartingOffset, document),
            getEndWithoutWhiteSpaces(selectEndingOffset, document) + 1));
    push(formatterSelectorNode);

    findAndPushPunctuationNode(TypePunctuation.SEMICOLON, selectEndingOffset, false);

    checkedPop(formatterSelectorNode, -1);
  }
 /**
  * @param parseResult
  * @param document
  * @return
  */
 public IFormatterContainerNode build(IParseNode parseResult, FormatterDocument document) {
   this.document = document;
   final IFormatterContainerNode rootNode = new FormatterCSSRootNode(document);
   start(rootNode);
   IParseNode[] children = parseResult.getChildren();
   addNodes(children);
   checkedPop(rootNode, document.getLength());
   // Collect Off/On tags
   if (parseResult instanceof IParseRootNode) {
     setOffOnRegions(
         resolveOffOnRegions(
             (IParseRootNode) parseResult,
             document,
             CSSFormatterConstants.FORMATTER_OFF_ON_ENABLED,
             CSSFormatterConstants.FORMATTER_OFF,
             CSSFormatterConstants.FORMATTER_ON));
   }
   return rootNode;
 }