Package org.cfeclipse.cfml.editors.formatters

Source Code of org.cfeclipse.cfml.editors.formatters.XmlDocumentFormatter$TagReader

/*******************************************************************************
* Copyright (c) 2004, 2006s John-Mason P. Shackelford and others.
* 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:
*     John-Mason P. Shackelford - initial API and implementation
*      IBM Corporation - bug fixes
*******************************************************************************/

package org.cfeclipse.cfml.editors.formatters;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

import org.cfeclipse.cfml.CFMLPlugin;
import org.cfeclipse.cfml.preferences.EditorPreferenceConstants;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;

public class XmlDocumentFormatter {

    private static class CommentReader extends TagReader {

        private boolean complete = false;

        protected void clear() {
            this.complete = false;
        }

        public String getStartOfTag() {
            return "<!--"; //$NON-NLS-1$
        }

        protected String readTag() throws IOException {
            int intChar;
            char c;
            StringBuffer node = new StringBuffer();

            while (!complete && (intChar = reader.read()) != -1) {
                c = (char) intChar;

                node.append(c);

                if (c == '>' && node.toString().endsWith("-->")) { //$NON-NLS-1$
                    complete = true;
                }
            }
            return node.toString();
        }
    }

    private static class DoctypeDeclarationReader extends TagReader {

        private boolean complete = false;

        protected void clear() {
            this.complete = false;
        }

        public String getStartOfTag() {
            return "<!"; //$NON-NLS-1$
        }

        protected String readTag() throws IOException {
            int intChar;
            char c;
            StringBuffer node = new StringBuffer();

            while (!complete && (intChar = reader.read()) != -1) {
                c = (char) intChar;

                node.append(c);

                if (c == '>') {
                    complete = true;
                }
            }
            return node.toString();
        }

    }

    private static class ProcessingInstructionReader extends TagReader {

        private boolean complete = false;

        protected void clear() {
            this.complete = false;
        }

        public String getStartOfTag() {
            return "<?"; //$NON-NLS-1$
        }

        protected String readTag() throws IOException {
            int intChar;
            char c;
            StringBuffer node = new StringBuffer();

            while (!complete && (intChar = reader.read()) != -1) {
                c = (char) intChar;

                node.append(c);

                if (c == '>' && node.toString().endsWith("?>")) { //$NON-NLS-1$
                    complete = true;
                }
            }
            return node.toString();
        }
    }

    private static abstract class TagReader {

        protected Reader reader;

        private String tagText;

        protected abstract void clear();

        public int getPostTagDepthModifier() {
            return 0;
        }

        public int getPreTagDepthModifier() {
            return 0;
        }

        abstract public String getStartOfTag();

        public String getTagText() {
            return this.tagText;
        }

        public boolean isTextNode() {
            return false;
        }

        protected abstract String readTag() throws IOException;

        public boolean requiresInitialIndent() {
            return true;
        }

        public void setReader(Reader reader) throws IOException {
            this.reader = reader;
            this.clear();
            this.tagText = readTag();
        }

        public boolean startsOnNewline() {
            return true;
        }
    }

    private static class TagReaderFactory {

        // Warning: the order of the Array is important!
        private static TagReader[] tagReaders = new TagReader[]{new CommentReader(),
                                                                new DoctypeDeclarationReader(),
                                                                new ProcessingInstructionReader(),
                                                                new XmlElementReader()};

        private static TagReader textNodeReader = new TextReader();

        public static TagReader createTagReaderFor(Reader reader)
                                                                 throws IOException {

            char[] buf = new char[10];
            reader.mark(10);
            reader.read(buf, 0, 10);
            reader.reset();

            String startOfTag = String.valueOf(buf);

            for (int i = 0; i < tagReaders.length; i++) {
                if (startOfTag.startsWith(tagReaders[i].getStartOfTag())) {
                    tagReaders[i].setReader(reader);
                    return tagReaders[i];
                }
            }
            // else
            textNodeReader.setReader(reader);
            return textNodeReader;
        }
    }

    private static class TextReader extends TagReader {

        private boolean complete;

        private boolean isTextNode;

        protected void clear() {
            this.complete = false;
        }

        /* (non-Javadoc)
         * @see org.cfeclipse.cfml.editors.formatter.XmlDocumentFormatter.TagReader#getStartOfTag()
         */
        public String getStartOfTag() {
            return ""; //$NON-NLS-1$
        }

        /* (non-Javadoc)
         * @see org.cfeclipse.cfml.editors.formatter.XmlDocumentFormatter.TagReader#isTextNode()
         */
        public boolean isTextNode() {
            return this.isTextNode;
        }

        protected String readTag() throws IOException {

            StringBuffer node = new StringBuffer();

            while (!complete) {

                reader.mark(1);
                int intChar = reader.read();
                if (intChar == -1) break;

                char c = (char) intChar;
                if (c == '<') {
                    reader.reset();
                    complete = true;
                } else {
                    node.append(c);
                }
            }

            // if this text node is just whitespace
            // remove it, except for the newlines.
            if (node.length() < 1) {
                this.isTextNode = false;

            } else if (node.toString().trim().length() == 0) {
                String whitespace = node.toString();
                node = new StringBuffer();
                for (int i = 0; i < whitespace.length(); i++) {
                    char whitespaceCharacter = whitespace.charAt(i);
                    if (whitespaceCharacter == '\n' || whitespaceCharacter == '\r') {
                        node.append(whitespaceCharacter);
                    }
                }
                this.isTextNode = false;

            } else {
                this.isTextNode = true;
            }
            return node.toString();
        }

        /* (non-Javadoc)
         * @see org.cfeclipse.cfml.editors.formatter.XmlDocumentFormatter.TagReader#requiresInitialIndent()
         */
        public boolean requiresInitialIndent() {
            return false;
        }

        /* (non-Javadoc)
         * @see org.cfeclipse.cfml.editors.formatter.XmlDocumentFormatter.TagReader#startsOnNewline()
         */
        public boolean startsOnNewline() {
            return false;
        }
    }

    private static class XmlElementReader extends TagReader {

        private boolean complete = false;

        protected void clear() {
            this.complete = false;
        }

        public int getPostTagDepthModifier() {
            if (getTagText().endsWith("/>") || getTagText().endsWith("/ >")) { //$NON-NLS-1$ //$NON-NLS-2$
                return 0;
            } else if (getTagText().startsWith("</")) { //$NON-NLS-1$
                return 0;
            } else {
                return +1;
            }
        }

        public int getPreTagDepthModifier() {
            if (getTagText().startsWith("</")) { //$NON-NLS-1$
                return -1;
            }
            return 0;
        }

        public String getStartOfTag() {
            return "<"; //$NON-NLS-1$
        }

        protected String readTag() throws IOException {

            StringBuffer node = new StringBuffer();

            boolean insideQuote = false;
            int intChar;

            while (!complete && (intChar = reader.read()) != -1) {
                char c = (char) intChar;

                node.append(c);
                // TODO logic incorrectly assumes that " is quote character
                // when it could also be '
                if (c == '"') {
                    insideQuote = !insideQuote;
                }
                if (c == '>' && !insideQuote) {
                    complete = true;
                }
            }
            return node.toString();
        }
    }

    private int depth;
    private StringBuffer formattedXml;
    private boolean lastNodeWasText;
    private String fDefaultLineDelimiter;

    public XmlDocumentFormatter() {
    super();
    depth= -1;
  }

    private void copyNode(Reader reader, StringBuffer out, FormattingPreferences prefs) throws IOException {

        TagReader tag = TagReaderFactory.createTagReaderFor(reader);

        depth = depth + tag.getPreTagDepthModifier();

        if (!lastNodeWasText) {

            if (tag.startsOnNewline() && !hasNewlineAlready(out)) {
                out.append(fDefaultLineDelimiter);
            }

            if (tag.requiresInitialIndent()) {
                out.append(indent(prefs.getCanonicalIndent()));
            }
        }

        out.append(tag.getTagText());

        depth = depth + tag.getPostTagDepthModifier();

        lastNodeWasText = tag.isTextNode();

    }
   
    /**
   * Returns the indent of the given string.
   *
   * @param line the text line
   * @param tabWidth the width of the '\t' character.
   */
  public static int computeIndent(String line, int tabWidth) {
    int result= 0;
    int blanks= 0;
    int size= line.length();
    for (int i= 0; i < size; i++) {
      char c= line.charAt(i);
      if (c == '\t') {
        result++;
        blanks= 0;
      } else if (isIndentChar(c)) {
        blanks++;
        if (blanks == tabWidth) {
          result++;
          blanks= 0;
        }
      } else {
        return result;
      }
    }
    return result;
  }
 
  /**
   * Indent char is a space char but not a line delimiters.
   * <code>== Character.isWhitespace(ch) && ch != '\n' && ch != '\r'</code>
   */
  public static boolean isIndentChar(char ch) {
    return Character.isWhitespace(ch) && !isLineDelimiterChar(ch);
  }
 
  /**
   * Line delimiter chars are '\n' and '\r'.
   */
  public static boolean isLineDelimiterChar(char ch) {
    return ch == '\n' || ch == '\r';
 
 
    public String format(String documentText, FormattingPreferences prefs) {

        Assert.isNotNull(documentText);
        Assert.isNotNull(prefs);

        Reader reader = new StringReader(documentText);
        formattedXml = new StringBuffer();

        if (depth == -1) {
          depth = 0;
        }
        lastNodeWasText = false;
        try {
            while (true) {
                reader.mark(1);
                int intChar = reader.read();
                reader.reset();

                if (intChar != -1) {
                    copyNode(reader, formattedXml, prefs);
                } else {
                    break;
                }
            }
            reader.close();
        } catch (IOException e) {
          e.printStackTrace();
           //CFMLPlugin.log(e);
        }
        return formattedXml.toString();
    }

    private boolean hasNewlineAlready(StringBuffer out) {
        return out.lastIndexOf("\n") == formattedXml.length() - 1 //$NON-NLS-1$
               || out.lastIndexOf("\r") == formattedXml.length() - 1; //$NON-NLS-1$
    }

    private String indent(String canonicalIndent) {
        StringBuffer indent = new StringBuffer(30);
        for (int i = 0; i < depth; i++) {
            indent.append(canonicalIndent);
        }
        return indent.toString();
    }
   
    public void setInitialIndent(int indent) {
        depth= indent;
    }
  
  /**
   * Returns the indentation of the line at <code>offset</code> as a
   * <code>StringBuffer</code>. If the offset is not valid, the empty string
   * is returned.
   *
   * @param offset the offset in the document
   * @return the indentation (leading whitespace) of the line in which
   *        <code>offset</code> is located
   */
  public static StringBuffer getLeadingWhitespace(int offset, IDocument document) {
    StringBuffer indent= new StringBuffer();
    try {
      IRegion line= document.getLineInformationOfOffset(offset);
      int lineOffset= line.getOffset();
      int nonWS= findEndOfWhiteSpace(document, lineOffset, lineOffset + line.getLength());
      indent.append(document.get(lineOffset, nonWS - lineOffset));
      return indent;
    } catch (BadLocationException e) {
      return indent;
    }
  }
 
  /**
   * Returns the first offset greater than <code>offset</code> and smaller than
   * <code>end</code> whose character is not a space or tab character. If no such
   * offset is found, <code>end</code> is returned.
   *
   * @param document the document to search in
   * @param offset the offset at which searching start
   * @param end the offset at which searching stops
   * @return the offset in the specifed range whose character is not a space or tab
   * @exception BadLocationException if position is an invalid range in the given document
   */
  public static int findEndOfWhiteSpace(IDocument document, int offset, int end) throws BadLocationException {
    while (offset < end) {
      char c= document.getChar(offset);
      if (c != ' ' && c != '\t') {
        return offset;
      }
      offset++;
    }
    return end;
  }
 
  /**
   * Creates a string that represents one indent (can be
   * spaces or tabs..)
   *
   * @return one indentation
   */
  public static StringBuffer createIndent() {
    StringBuffer oneIndent= new StringBuffer();
    IPreferenceStore pluginPrefs= CFMLPlugin.getDefault().getPreferenceStore();   
    if (pluginPrefs.getBoolean(EditorPreferenceConstants.P_INSERT_SPACES_FOR_TABS)) {
      int tabLen= pluginPrefs.getInt(EditorPreferenceConstants.P_TAB_WIDTH);
      for (int i= 0; i < tabLen; i++) {
        oneIndent.append(' ');
      }
    } else {
      oneIndent.append('\t'); // default
    }
   
    return oneIndent;
  }

    public void setDefaultLineDelimiter(String defaultLineDelimiter) {
       fDefaultLineDelimiter= defaultLineDelimiter;
       
    }
}
TOP

Related Classes of org.cfeclipse.cfml.editors.formatters.XmlDocumentFormatter$TagReader

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.