protected String computeNestedTag(
      String regionType,
      String tagName,
      IStructuredDocumentRegion structuredDocumentRegion,
      ITextRegion region) {

    if (regionType.equals(TwigRegionTypes.TWIG_STMT_OPEN)) {

      try {

        String text = structuredDocumentRegion.getText();
        Object cached = cache.get(text);

        if (cached != null) {
          return (String) cached;
        }

        CharStream content = new ANTLRStringStream(text);
        TwigLexer lexer = new TwigLexer(content);

        TwigParser parser = new TwigParser(new CommonTokenStream(lexer));

        parser.setTreeAdaptor(new TwigCommonTreeAdaptor());
        TwigParser.twig_source_return root;

        root = parser.twig_source();
        TwigCommonTree tree = (TwigCommonTree) root.getTree();
        TwigStatementVisitor visitor = new TwigStatementVisitor();

        tree.accept(visitor);

        if (visitor.getStatementType() == TwigParser.TWIG_TAG) {
          String tag = visitor.getTag();
          cache.put(text, tag);
          return tag;
        }

      } catch (Exception e) {

        e.printStackTrace();
      }

      return TWIG_STMT_TAG;

    } else if (regionType.equals(TwigRegionTypes.TWIG_OPEN)) {

      return TWIG_PRINT_TAG;
    }

    return TWIG_STMT_TAG;
  }
  protected void addAttributeValueProposals(
      ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context) {
    IDOMNode node = (IDOMNode) contentAssistRequest.getNode();
    String tagName = node.getNodeName();
    IStructuredDocumentRegion open = node.getFirstStructuredDocumentRegion();
    ITextRegionList openRegions = open.getRegions();
    int i = openRegions.indexOf(contentAssistRequest.getRegion());
    if (i < 0) return;
    ITextRegion nameRegion = null;
    while (i >= 0) {
      nameRegion = openRegions.get(i--);
      if (nameRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) break;
    }

    // get the attribute in question (first attr name to the left of the cursor)
    String attributeName = null;
    if (nameRegion != null) attributeName = open.getText(nameRegion);

    ProposalType proposalType = resolveProposalType(tagName, attributeName);
    if (ProposalType.None.equals(proposalType)) {
      return;
    }

    String currentValue = null;
    if (contentAssistRequest.getRegion().getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)
      currentValue = contentAssistRequest.getText();
    else currentValue = "";

    String matchString = null;
    int matchStrLen = contentAssistRequest.getMatchString().length();
    int start = contentAssistRequest.getReplacementBeginPosition();
    int length = contentAssistRequest.getReplacementLength();
    if (currentValue.length() > StringUtils.strip(currentValue).length()
        && (currentValue.startsWith("\"") || currentValue.startsWith("'"))
        && matchStrLen > 0) {
      // Value is surrounded by (double) quotes.
      matchString = currentValue.substring(1, matchStrLen);
      start++;
      length = currentValue.length() - 2;
      currentValue = currentValue.substring(1, length + 1);
    } else {
      matchString = currentValue.substring(0, matchStrLen);
    }

    IJavaProject project = getJavaProject(contentAssistRequest);
    try {
      switch (proposalType) {
        case Package:
          proposePackage(contentAssistRequest, project, matchString, start, length);
          break;
        case TypeAlias:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeJavaType(project, start, length, false, matchString));
          break;
        case ResultType:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeJavaType(project, start, length, true, matchString));
          break;
        case ResultProperty:
          proposeProperty(contentAssistRequest, matchString, start, length, node);
          break;
        case TypeHandlerType:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeTypeHandler(project, start, length, matchString));
          break;
        case CacheType:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeCacheType(project, start, length, matchString));
          break;
        case SettingName:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeSettingName(start, length, matchString));
          break;
        case ObjectFactory:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeObjectFactory(project, start, length, matchString));
          break;
        case ObjectWrapperFactory:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeObjectWrapperFactory(
                  project, start, length, matchString));
          break;
        case StatementId:
          proposeStatementId(contentAssistRequest, project, matchString, start, length, node);
          break;
        case MapperNamespace:
          proposeMapperNamespace(contentAssistRequest, project, start, length);
          break;
        case ResultMap:
          String ownId =
              "resultMap".equals(tagName) && "extends".equals(attributeName)
                  ? XpathUtil.xpathString(node, "@id")
                  : null;
          addProposals(
              contentAssistRequest,
              proposeResultMapReference(
                  project,
                  node.getOwnerDocument(),
                  start,
                  currentValue,
                  matchString.length(),
                  ownId));
          break;
        case Include:
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeReference(
                  project, node.getOwnerDocument(), matchString, start, length, "sql", null));
          break;
        case SelectId:
          // TODO: include mapper methods with @Select.
          addProposals(
              contentAssistRequest,
              ProposalComputorHelper.proposeReference(
                  project, node.getOwnerDocument(), matchString, start, length, "select", null));
          break;
        case KeyProperty:
          String nodeName = node.getNodeName();
          Node statementNode =
              "update".equals(nodeName) || "insert".equals(nodeName)
                  ? node
                  : MybatipseXmlUtil.findEnclosingStatementNode(node.getParentNode());
          addProposals(
              contentAssistRequest,
              proposeParameter(project, start, length, statementNode, false, matchString));
          break;
        case ParamProperty:
          addProposals(
              contentAssistRequest,
              proposeParameter(
                  project,
                  start,
                  length,
                  MybatipseXmlUtil.findEnclosingStatementNode(node),
                  true,
                  matchString));
          break;
        case ParamPropertyPartial:
          AttrTextParser parser = new AttrTextParser(currentValue, matchString.length());
          addProposals(
              contentAssistRequest,
              proposeParameter(
                  project,
                  start + parser.getMatchStringStart(),
                  parser.getReplacementLength(),
                  MybatipseXmlUtil.findEnclosingStatementNode(node.getParentNode()),
                  true,
                  parser.getMatchString()));
          break;
        default:
          break;
      }
    } catch (Exception e) {
      Activator.log(Status.ERROR, e.getMessage(), e);
    }
  }
 private String getText(ITextRegion region) {
   return (fParentRegion != null) ? fParentRegion.getText(region) : ""; // $NON-NLS-1$
 }
  /** nodeRegions must be broken. If you need after, make copy of them. */
  private void setupValues(
      ICSSStyleDeclItem item, ITextRegionList nodeRegions, List propertyValueList) {
    if (item == null) {
      return;
    }

    ICSSPrimitiveValue value;
    ITextRegionList regionBuf = new TextRegionListImpl();

    String propertyName = item.getPropertyName().toLowerCase();
    boolean bFont = (propertyName.equals(PropCMProperty.P_FONT));
    // (short-hand) font
    String propertyValue = null;
    int status =
        (propertyName.equals(PropCMProperty.P_VOICE_FAMILY)
                || propertyName.equals(PropCMProperty.P_FONT_FAMILY))
            ? S_COMMA_SEPARATION
            : S_NORMAL;
    while (!nodeRegions.isEmpty()) {
      value = null;
      ITextRegion region = nodeRegions.remove(0);
      if (region == null) {
        continue;
      }
      if (propertyValueList != null && !propertyValueList.isEmpty()) {
        propertyValue = (String) propertyValueList.remove(0);
      }
      String type = region.getType();
      // if (type == CSSRegionContexts.CSS_DECLARATION_DELIMITER || type
      // == CSSRegionContexts.CSS_RBRACE) {
      // break;
      // }
      switch (status) {
        case S_NORMAL:
          if (type == CSSRegionContexts.CSS_DECLARATION_VALUE_FUNCTION) {
            regionBuf.add(region);
            status = S_FUNCTION;
          } else if (bFont
              && type == CSSRegionContexts.CSS_DECLARATION_VALUE_OPERATOR
              && fParentRegion.getText(region).equals("/")) { // $NON-NLS-1$
            value = createPrimitiveValue(region, propertyValue);
            status = S_FONT_SLASH;
          } else if (!isBlank(type)) {
            value = createPrimitiveValue(region, propertyValue);
          }
          break;
        case S_FUNCTION:
          if (type == CSSRegionContexts.CSS_DECLARATION_VALUE_PARENTHESIS_CLOSE) {
            regionBuf.add(region);
            value = createPrimitiveValue(regionBuf);
            regionBuf.clear();
            status = S_NORMAL;
          } else if (!isBlank(type)) {
            regionBuf.add(region);
          }
          break;
        case S_FONT_SLASH:
          if (type == CSSRegionContexts.CSS_DECLARATION_VALUE_DIMENSION) {
            value = createPrimitiveValue(region, propertyValue);
            status = S_COMMA_SEPARATION;
          } else if (!isBlank(type)) {
            value = createPrimitiveValue(region, propertyValue);
          }
          break;
        case S_COMMA_SEPARATION:
          if (type == CSSRegionContexts.CSS_DECLARATION_VALUE_OPERATOR
              && fParentRegion.getText(region).equals(",")) { // $NON-NLS-1$
            value = createPrimitiveValue(regionBuf);
            regionBuf.clear();
            if (value != null) {
              if (fUpdateContext == null || !fUpdateContext.isActive()) {
                item.appendValue(value);
              }
            }
            value = createPrimitiveValue(region, propertyValue);
          } else {
            regionBuf.add(region);
          }
          break;
        default:
          break;
      }
      if (value != null) {
        if (fUpdateContext == null || !fUpdateContext.isActive()) {
          item.appendValue(value);
        }
      }
    }
    if (!regionBuf.isEmpty()) {
      value = createPrimitiveValue(regionBuf);
      if (fUpdateContext == null || !fUpdateContext.isActive()) {
        item.appendValue(value);
      }
    }
  }
  public void customizeDocumentCommand(IDocument document, DocumentCommand c) {
    if (!isSmartInsertMode()) {
      return;
    }

    if (!(document instanceof IStructuredDocument)) {
      // This shouldn't happen unless this strategy is used on an invalid document
      return;
    }
    IStructuredDocument doc = (IStructuredDocument) document;

    // Handle newlines/indentation
    if (c.length == 0
        && c.text != null
        && TextUtilities.endsWith(doc.getLegalLineDelimiters(), c.text) != -1) {

      IModelManager modelManager = StructuredModelManager.getModelManager();
      IStructuredModel model = modelManager.getModelForRead(doc);
      if (model != null) {
        try {
          final int offset = c.offset;
          int lineStart = findLineStart(doc, offset);
          int textStart = findTextStart(doc, lineStart, offset);

          IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(textStart);
          if (region != null && region.getType().equals(XML_TAG_NAME)) {
            Pair<Integer, Integer> balance = getBalance(doc, textStart, offset);
            int tagBalance = balance.getFirst();
            int bracketBalance = balance.getSecond();

            String lineIndent = ""; // $NON-NLS-1$
            if (textStart > lineStart) {
              lineIndent = doc.get(lineStart, textStart - lineStart);
            }

            // We only care if tag or bracket balance is greater than 0;
            // we never *dedent* on negative balances
            boolean addIndent = false;
            if (bracketBalance < 0) {
              // Handle
              //    <foo
              //        ></foo>^
              // and
              //    <foo
              //        />^
              ITextRegion left = getRegionAt(doc, offset, true /*biasLeft*/);
              if (left != null
                  && (left.getType().equals(XML_TAG_CLOSE)
                      || left.getType().equals(XML_EMPTY_TAG_CLOSE))) {

                // Find the corresponding open tag...
                // The org.eclipse.wst.xml.ui.gotoMatchingTag frequently
                // doesn't work, it just says "No matching brace found"
                // (or I would use that here).

                int targetBalance = 0;
                ITextRegion right = getRegionAt(doc, offset, false /*biasLeft*/);
                if (right != null && right.getType().equals(XML_END_TAG_OPEN)) {
                  targetBalance = -1;
                }
                int openTag =
                    AndroidXmlCharacterMatcher.findTagBackwards(doc, offset, targetBalance);
                if (openTag != -1) {
                  // Look up the indentation of the given line
                  lineIndent = AndroidXmlEditor.getIndentAtOffset(doc, openTag);
                }
              }
            } else if (tagBalance > 0 || bracketBalance > 0) {
              // Add indentation
              addIndent = true;
            }

            StringBuilder sb = new StringBuilder(c.text);
            sb.append(lineIndent);
            String oneIndentUnit = XmlFormatPreferences.create().getOneIndentUnit();
            if (addIndent) {
              sb.append(oneIndentUnit);
            }

            // Handle
            //     <foo>^</foo>
            // turning into
            //     <foo>
            //         ^
            //     </foo>
            ITextRegion left = getRegionAt(doc, offset, true /*biasLeft*/);
            ITextRegion right = getRegionAt(doc, offset, false /*biasLeft*/);
            if (left != null
                && right != null
                && left.getType().equals(XML_TAG_CLOSE)
                && right.getType().equals(XML_END_TAG_OPEN)) {
              // Move end tag
              if (tagBalance > 0 && bracketBalance < 0) {
                sb.append(oneIndentUnit);
              }
              c.caretOffset = offset + sb.length();
              c.shiftsCaret = false;
              sb.append(TextUtilities.getDefaultLineDelimiter(doc));
              sb.append(lineIndent);
            }
            c.text = sb.toString();
          } else if (region != null && region.getType().equals(XML_CONTENT)) {
            // Indenting in text content. If you're in the middle of editing
            // text, just copy the current line indentation.
            // However, if you're editing in leading whitespace (e.g. you press
            // newline on a blank line following say an element) then figure
            // out the indentation as if the newline had been pressed at the
            // end of the element, and insert that amount of indentation.
            // In this case we need to also make sure to subtract any existing
            // whitespace on the current line such that if we have
            //
            // <foo>
            // ^   <bar/>
            // </foo>
            //
            // you end up with
            //
            // <foo>
            //
            //    ^<bar/>
            // </foo>
            //
            String text = region.getText();
            int regionStart = region.getStartOffset();
            int delta = offset - regionStart;
            boolean inWhitespacePrefix = true;
            for (int i = 0, n = Math.min(delta, text.length()); i < n; i++) {
              char ch = text.charAt(i);
              if (!Character.isWhitespace(ch)) {
                inWhitespacePrefix = false;
                break;
              }
            }
            if (inWhitespacePrefix) {
              IStructuredDocumentRegion previous = region.getPrevious();
              if (previous != null && previous.getType() == XML_TAG_NAME) {
                ITextRegionList subRegions = previous.getRegions();
                ITextRegion last = subRegions.get(subRegions.size() - 1);
                if (last.getType() == XML_TAG_CLOSE || last.getType() == XML_EMPTY_TAG_CLOSE) {
                  int begin =
                      AndroidXmlCharacterMatcher.findTagBackwards(
                          doc, previous.getStartOffset() + last.getStart(), 0);
                  int prevLineStart = findLineStart(doc, begin);
                  int prevTextStart = findTextStart(doc, prevLineStart, begin);

                  String lineIndent = ""; // $NON-NLS-1$
                  if (prevTextStart > prevLineStart) {
                    lineIndent = doc.get(prevLineStart, prevTextStart - prevLineStart);
                  }
                  StringBuilder sb = new StringBuilder(c.text);
                  sb.append(lineIndent);
                  String oneIndentUnit = XmlFormatPreferences.create().getOneIndentUnit();

                  // See if there is whitespace on the insert line that
                  // we should also remove
                  for (int i = delta, n = text.length(); i < n; i++) {
                    char ch = text.charAt(i);
                    if (ch == ' ') {
                      c.length++;
                    } else {
                      break;
                    }
                  }
                  boolean onClosingTagLine = false;
                  if (text.indexOf('\n', delta) == -1) {
                    IStructuredDocumentRegion next = region.getNext();
                    if (next != null && next.getType() == XML_TAG_NAME) {
                      String nextType = next.getRegions().get(0).getType();
                      if (nextType == XML_END_TAG_OPEN) {
                        onClosingTagLine = true;
                      }
                    }
                  }

                  boolean addIndent = (last.getType() == XML_TAG_CLOSE) && !onClosingTagLine;
                  if (addIndent) {
                    sb.append(oneIndentUnit);
                  }
                  c.text = sb.toString();

                  return;
                }
              }
            }
            copyPreviousLineIndentation(doc, c);
          } else {
            copyPreviousLineIndentation(doc, c);
          }
        } catch (BadLocationException e) {
          AdtPlugin.log(e, null);
        } finally {
          model.releaseFromRead();
        }
      }
    }
  }