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();
        }
      }
    }
  }
  public void testDropZones() {
    List<Pair<Point, String[]>> zones = new ArrayList<Pair<Point, String[]>>();

    zones.add(
        Pair.of(
            new Point(51 + 10, 181 + 10),
            new String[] {"above=@+id/Centered", "toLeftOf=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(71 + 10, 181 + 10),
            new String[] {"above=@+id/Centered", "alignLeft=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(104 + 10, 181 + 10),
            new String[] {"above=@+id/Centered", "alignRight=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(137 + 10, 181 + 10),
            new String[] {"above=@+id/Centered", "alignRight=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(170 + 10, 181 + 10),
            new String[] {"above=@+id/Centered", "toRightOf=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(51 + 10, 279 + 10),
            new String[] {"below=@+id/Centered", "toLeftOf=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(71 + 10, 279 + 10),
            new String[] {"below=@+id/Centered", "alignLeft=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(104 + 10, 279 + 10),
            new String[] {"below=@+id/Centered", "alignLeft=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(137 + 10, 279 + 10),
            new String[] {"below=@+id/Centered", "alignRight=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(170 + 10, 279 + 10),
            new String[] {"below=@+id/Centered", "toRightOf=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(51 + 10, 201 + 10),
            new String[] {"toLeftOf=@+id/Centered", "alignTop=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(51 + 10, 227 + 10),
            new String[] {"toLeftOf=@+id/Centered", "alignTop=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(170 + 10, 201 + 10),
            new String[] {"toRightOf=@+id/Centered", "alignTop=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(51 + 10, 253 + 10),
            new String[] {"toLeftOf=@+id/Centered", "alignBottom=@+id/Centered"}));
    zones.add(
        Pair.of(
            new Point(170 + 10, 227 + 10),
            new String[] {
              "toRightOf=@+id/Centered", "alignTop=@+id/Centered", "alignBottom=@+id/Centered"
            }));
    zones.add(
        Pair.of(
            new Point(170 + 10, 253 + 10),
            new String[] {"toRightOf=@+id/Centered", "alignBottom=@+id/Centered"}));

    for (Pair<Point, String[]> zonePair : zones) {
      Point dropPoint = zonePair.getFirst();
      String[] attachments = zonePair.getSecond();
      // If we drag right into the button itself, not a valid drop position

      INode inserted =
          dragInto(
              new Rect(0, 0, 105, 80),
              new Point(120, 240),
              dropPoint,
              1,
              -1,
              attachments,

              // Bounds rectangle
              "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",

              // Drop zones
              "useStyle(DROP_ZONE), "
                  + "drawRect(Rect[51,181,20,20]), drawRect(Rect[71,181,33,20]), "
                  + "drawRect(Rect[104,181,33,20]), drawRect(Rect[137,181,33,20]), "
                  + "drawRect(Rect[170,181,20,20]), drawRect(Rect[51,279,20,20]), "
                  + "drawRect(Rect[71,279,33,20]), drawRect(Rect[104,279,33,20]), "
                  + "drawRect(Rect[137,279,33,20]), drawRect(Rect[170,279,20,20]), "
                  + "drawRect(Rect[51,201,20,26]), drawRect(Rect[51,227,20,26]), "
                  + "drawRect(Rect[51,253,20,26]), drawRect(Rect[170,201,20,26]), "
                  + "drawRect(Rect[170,227,20,26]), drawRect(Rect[170,253,20,26])");

      for (String attachment : attachments) {
        String[] elements = attachment.split("=");
        String name = "layout_" + elements[0];
        String value = elements[1];
        assertEquals(value, inserted.getStringAttr(ANDROID_URI, name));
      }
    }
  }