org.xmen.xml.XMLNode.java Source code

Java tutorial

Introduction

Here is the source code for org.xmen.xml.XMLNode.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2003 Jens Lukowski and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *    Jens Lukowski - initial API and implementation
 *    Geoff Longman - heavily modified for Spindle
 *******************************************************************************/
package org.xmen.xml;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TypedPosition;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.xmen.internal.ui.text.ITypeConstants;
import org.xmen.internal.ui.text.XMLDocumentPartitioner;

import com.iw.plugins.spindle.UIPlugin;
import com.wutka.dtd.OrderPreservingMap;

/**
 * @author jll
 * 
 * To change the template for this generated type comment go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
public class XMLNode extends TypedPosition implements IAdaptable, IWorkbenchAdapter, Comparable {
    public static final int TAG = 0;
    public static final int ATTR = 1;
    public static final int DOUBLEQUOTE = 2;
    public static final int SINGLEQUOTE = 3;
    public static final int ATTRIBUTE = 4;
    public static final int ATT_VALUE = 5;
    public static final int AFTER_ATTRIBUTE = 6;
    public static final int AFTER_ATT_VALUE = 7;
    public static final int IN_TERMINATOR = 8;

    public static final Comparator COMPARATOR = new Comparator() {
        public int compare(Object o1, Object o2) {
            int offset1 = ((XMLNode) o1).getOffset();
            int offset2 = ((XMLNode) o2).getOffset();
            return (offset1 > offset2) ? 1 : ((offset1 < offset2) ? -1 : 0);
        }
    };

    static public final Map TERMINATORS;

    static {
        TERMINATORS = new HashMap();
        TERMINATORS.put(ITypeConstants.TAG, ">");
        TERMINATORS.put(ITypeConstants.ATTR, "");
        TERMINATORS.put(ITypeConstants.TEXT, "");
        TERMINATORS.put(ITypeConstants.PI, "?>");
        TERMINATORS.put(ITypeConstants.DECL, ">");
        TERMINATORS.put(ITypeConstants.ENDTAG, ">");
        TERMINATORS.put(ITypeConstants.COMMENT, "-->");
        TERMINATORS.put(ITypeConstants.EMPTYTAG, "/>");
    }

    public static XMLNode getArtifactAt(IDocument doc, int offset) {
        return getArtifactAt(XMLDocumentPartitioner.CONTENT_TYPES_CATEGORY, doc, offset);
    }

    public static XMLNode getArtifactAt(String positionCategory, IDocument doc, int offset) {
        try {
            Position[] pos = doc.getPositions(positionCategory);
            for (int i = 0; i < pos.length; i++) {
                if (offset >= pos[i].getOffset() && offset <= pos[i].getOffset() + pos[i].getLength())
                    return (XMLNode) pos[i];
            }
        } catch (BadPositionCategoryException e) {
            //do nothing
        }
        return null;
    }

    private boolean added = false;
    private boolean modified = false;
    private XMLNode parent = null;
    private List children = new ArrayList();
    private XMLNode correspondingNode = null;
    private IDocument document = null;
    public String publicId; //valid only for the root node
    public String rootNodeId; //valid only for the root
    // node

    /**
     * @param offset
     * @param length
     * @param type
     */
    public XMLNode(int offset, int length, String type, IDocument document) {
        super(offset, length, type);
        added = true;
        this.document = document;
    }

    /**
     * @param region
     */
    public XMLNode(ITypedRegion region) {
        super(region);
        added = true;
    }

    public boolean isTagPart() {
        String type = getType();
        return ITypeConstants.TAG.equals(type) || ITypeConstants.EMPTYTAG.equals(type)
                || ITypeConstants.ENDTAG.equals(type);
    }

    public boolean isTag() {
        return ITypeConstants.TAG.equals(getType());
    }

    public boolean isEmptyTag() {
        return ITypeConstants.EMPTYTAG.equals(getType());
    }

    public boolean isTagOrEmptyTag() {
        String type = getType();
        return ITypeConstants.TAG.equals(type) || ITypeConstants.EMPTYTAG.equals(type);
    }

    public boolean isText() {
        return ITypeConstants.TEXT.equals(getType());
    }

    public boolean isPI() {
        return ITypeConstants.PI.equals(getType());
    }

    public boolean isDECL() {
        return ITypeConstants.DECL.equals(getType());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.text.Position#setLength(int)
     */
    public void setLength(int length) {
        super.setLength(length);
        added = false;
        modified = true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.text.Position#setOffset(int)
     */
    public void setOffset(int offset) {
        super.setOffset(offset);
        added = false;
        modified = true;
    }

    /**
     * @return
     */
    public boolean isAdded() {
        return added;
    }

    /**
     * @return
     */
    public boolean isModified() {
        return modified;
    }

    /**
     * @param b
     */
    public void setAdded(boolean b) {
        added = b;
    }

    /**
     * @param b
     */
    public void setModified(boolean b) {
        modified = b;
    }

    /**
     * @return
     */
    public synchronized List getChildren() {
        return children;
    }

    /**
     * @return
     */
    public XMLNode getParent() {
        return parent;
    }

    /**
     * @param node
     */
    public void setParent(XMLNode node) {
        parent = node;
        if (parent != null && !parent.getChildren().contains(this)) {
            parent.addChild(this);
        }
    }

    public synchronized void addChild(XMLNode childArtifact) {
        for (int i = 0; i < children.size(); i++) {
            if (((XMLNode) children.get(i)).getOffset() > childArtifact.getOffset()) {
                children.add(i, childArtifact);
                break;
            }

        }
        if (!children.contains(childArtifact))
            children.add(childArtifact);
    }

    public List getChildrenAfter(XMLNode child) {
        List result = new ArrayList();
        for (int i = 0; i < children.size(); i++) {
            if (((XMLNode) children.get(i)).getOffset() > child.getOffset()) {
                result.add(child);
            }
        }

        return result;
    }

    public synchronized void removeChild(XMLNode child) {
        children.remove(child);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
        if (adapter.equals(IWorkbenchAdapter.class)) {
            return this;
        }

        return null;
    }

    public boolean containsOnlyWhitespaces() {
        return getContent().trim().length() == 0;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
     */
    public Object[] getChildren(Object obj) {
        if (obj instanceof XMLNode) {
            List result = new ArrayList();

            XMLNode artifact = (XMLNode) obj;
            for (Iterator it = artifact.getChildren().iterator(); it.hasNext();) {
                XMLNode child = (XMLNode) it.next();
                String childType = child.getType();
                if (childType == ITypeConstants.ENDTAG
                        || (childType == ITypeConstants.TEXT && child.containsOnlyWhitespaces()))
                    continue;
                result.add(child);

            }

            String type = artifact.getType();
            if (type == ITypeConstants.TAG || type == ITypeConstants.EMPTYTAG) {
                result.addAll(0, artifact.getAttributes());
            } else if (type == ITypeConstants.DECL) {
            }

            return result.toArray(new XMLNode[0]);
        }

        return null;
    }

    public String getContent() {
        String content = "";

        try {
            content = document.get(getOffset(), getLength());
        } catch (BadLocationException e) {
        }

        return content;
    }

    public String getContentTo(int to) {
        return getContentTo(to, false);
    }

    public String getContentTo(int to, boolean stripLeader) {
        try {
            String result = document.get(getOffset(), to - getOffset());
            if (stripLeader) {
                return result.substring(1);
            } else {
                return result;
            }

        } catch (BadLocationException e) {
            // do nothing
        }
        return "";
    }

    public String getContentFrom(int from) {
        String content = "";

        try {
            content = document.get(from, getOffset() - from + getLength() - 1);
        } catch (BadLocationException e) {
        }

        return content;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
     */
    public ImageDescriptor getImageDescriptor(Object object) {
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
     */
    public String getLabel(Object o) {
        return getName();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
     */
    public Object getParent(Object o) {
        // TODO Auto-generated method stub
        return this;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    public int compareTo(Object obj) {
        if (this == obj)
            return 0;

        if (!(obj instanceof XMLNode))
            return 0;

        XMLNode other = (XMLNode) obj;
        return (getOffset() > other.getOffset()) ? 1 : ((getOffset() < other.getOffset()) ? -1 : 0);
    }

    /**
     * @return
     */
    public XMLNode getCorrespondingNode() {
        return correspondingNode == null ? this : correspondingNode;
    }

    /**
     * @param node
     */
    public void setCorrespondingNode(XMLNode node) {
        correspondingNode = node;
    }

    public String getName() {
        String name = "unknown";

        String type = getType();
        if (type.equals(ITypeConstants.TEXT)) {
            name = "#TEXT";
        } else if (type.equals(ITypeConstants.COMMENT)) {
            name = "#COMMENT";
        } else if (getType().equals(ITypeConstants.ATTR)) {
            name = getAttributeName();
        } else if (type.equals(ITypeConstants.TAG) || type.equals(ITypeConstants.EMPTYTAG)
                || type.equals(ITypeConstants.ENDTAG) || type.equals(ITypeConstants.PI)
                || type.equals(ITypeConstants.DECL)) {
            name = getTagName();
        }

        return name;
    }

    public XMLNode getNextArtifact() {
        return getArtifactAt(document, getOffset() + getLength() + 1);
    }

    public XMLNode getPreviousArtifact() {
        return getArtifactAt(document, getOffset() - 1);
    }

    /** @deprecated not needed anymore? */
    public XMLNode getPreviousSibling() {
        if (parent == null)
            throw new IllegalStateException("create tree first");

        String myType = getType();
        if (parent.getType().equals("/"))
            return null;
        if (myType.equals(ITypeConstants.ENDTAG)) {
            if (correspondingNode != null) {
                return correspondingNode.getPreviousSibling();
            } else {
                return null;
            }
        }

        XMLNode candidate = getPreviousArtifact();
        if (candidate == null)
            return null;
        if (candidate.parent == parent)
            return candidate;
        return null;
    }

    public XMLNode getPreviousSiblingTag(String allowed) {
        if (parent == null || parent.getType().equals("/") || allowed == null)
            return null;
        Position[] pos = null;
        try {
            pos = document.getPositions(XMLDocumentPartitioner.CONTENT_TYPES_CATEGORY);
        } catch (BadPositionCategoryException e) {
            e.printStackTrace();
            return null;
        }
        Arrays.sort(pos, COMPARATOR);
        int index = 0;
        while (pos[index] != this)
            index++;
        if (index > 0) {
            XMLNode result = null;
            for (int i = index - 1; i >= 0; i--) {
                result = (XMLNode) pos[i];
                if (result == parent)
                    return null;
                String type = result.getType();
                if (result.getParent() != parent || (type.equals(ITypeConstants.TEXT)
                        || type.equals(ITypeConstants.COMMENT) || type.equals(ITypeConstants.DECL))) {
                    continue;
                }

                String name = result.getName();
                if (name == null)
                    continue;
                if (type.equals(ITypeConstants.ENDTAG)) {
                    XMLNode corresponding = result.getCorrespondingNode();
                    if (allowed.indexOf(corresponding.getName().toLowerCase()) >= 0)
                        return corresponding;
                } else if (allowed.indexOf(name) >= 0) {
                    return result;
                }
            }
        }

        return null;
    }

    public String getValue() {
        String name = "<unknown type>";

        if (getType().equals(ITypeConstants.TEXT)) {
        } else if (getType().equals(ITypeConstants.TAG)) {
            if (children.size() > 0 && ((XMLNode) children.get(0)).getType().equals(ITypeConstants.TEXT)) {
                name = ((XMLNode) children.get(0)).getContent().trim();
            }
        } else if (getType().equals(ITypeConstants.PI)) {
        } else if (getType().equals(ITypeConstants.ATTR)) {
            return getAttributeValue();
        } else if (getType().equals(ITypeConstants.COMMENT)) {
        } else if (getType().equals(ITypeConstants.DECL)) {
        } else if (getType().equals(ITypeConstants.ENDTAG)) {
        } else if (getType().equals(ITypeConstants.EMPTYTAG)) {
        }

        return name;
    }

    private String getAttributeName() {
        try {
            String content = document.get(getOffset(), getLength());
            int index = content.indexOf("=");
            if (index == -1) {
                index = content.indexOf("\"");
                if (index == -1) {
                    index = content.indexOf("'");
                    if (index == -1)
                        index = content.length();
                }
            }
            return content.substring(0, index).trim();
        } catch (BadLocationException e) {
            UIPlugin.log(e);
        }
        return null;
    }

    private String getTagName() {
        String content = null;
        String name = null;

        try {
            content = document.get(getOffset(), getLength());
        } catch (BadLocationException e) {
            //            e.printStackTrace();
            return null;
        }

        StringTokenizer st = new StringTokenizer(content, " \t\n\r<>/");

        if (st.hasMoreTokens()) {
            name = st.nextToken();
        }

        if (name == null) {
            name = "";
        }

        return name;
    }

    public String getAttributeValue() {

        if (!attributeHasValue())
            return null;
        try {
            int index = getAttributeValueStart();

            if (index < 0)
                return null;

            String content = document.get(getOffset(), getLength());
            content = content.substring(index).trim();

            if (content.length() < 2)
                return "";

            return content.substring(1, content.length() - 1);
        } catch (BadLocationException e) {
            UIPlugin.log(e);
        }

        return null;
    }

    private boolean attributeHasValue() {
        try {
            String content = document.get(getOffset(), getLength());
            return content.indexOf('=') > 0 && getAttributeValueStart() > 0;
        } catch (BadLocationException e) {
            UIPlugin.log(e);
        }
        return false;
    }

    private int getAttributeValueStart() throws BadLocationException {
        int index = 0;

        String content = document.get(getOffset(), getLength());

        int singleIndex = content.indexOf("\"");
        int doubleIndex = content.indexOf("'");

        if (singleIndex < 0 && doubleIndex < 0)
            return 0;

        if (singleIndex >= 0 && doubleIndex > 0) {

            index = Math.min(singleIndex, doubleIndex);
        } else {
            index = Math.max(singleIndex, doubleIndex);
        }

        return index;
    }

    public IRegion getAttributeValueRegion() {
        try {
            int index = getAttributeValueStart();

            if (index < 0)
                return null;

            if (index == 0)
                return new Region(getOffset(), getLength());

            String value = getAttributeValue();
            if (value != null)
                return new Region(getOffset() + index + 1, value.length());
        } catch (BadLocationException e) {
            // eat it
        }

        return null;
    }

    public XMLNode getAttributeAt(int offset) {
        List attrs = getAttributes();
        for (Iterator it = attrs.iterator(); it.hasNext();) {
            XMLNode artifact = (XMLNode) it.next();
            if (artifact.getOffset() <= offset && offset <= artifact.getOffset() + artifact.getLength())
                return artifact;
        }
        return null;
    }

    public List getAttributes() {
        List attrs = new ArrayList();
        String content = null;
        int state = TAG;
        int start = -1;
        int startLength = 0;
        int endLength = 0;
        if (ITypeConstants.PI.equals(getType())) {
            startLength = 2;
            endLength = 2;
        } else if (ITypeConstants.DECL.equals(getType())) {
            //      if ("!DOCTYPE".equals(getName()))
            //        return getDoctypeAttributes();
            startLength = 1;
            endLength = 1;
        } else if (ITypeConstants.TAG.equals(getType())) {
            startLength = 1;
            endLength = 1;
        } else if (ITypeConstants.EMPTYTAG.equals(getType())) {
            startLength = 1;
            endLength = 2;
        } else {
            return attrs;
        }

        try {
            content = document.get(getOffset(), getLength());
        } catch (BadLocationException e) {
            UIPlugin.log(e);
            return attrs;
        }

        String name = getName();
        int initial = name == null ? 0 : content.indexOf(name) + name.length();

        for (int i = startLength + initial; i < content.length() - endLength; i++) {
            char c = content.charAt(i);
            switch (c) {
            case '"':

                if (state == DOUBLEQUOTE) {
                    attrs.add(new XMLNode(getOffset() + start, i - start + 1, ITypeConstants.ATTR, document));
                    start = -1;
                    state = TAG;
                } else if (state == SINGLEQUOTE) {
                    break;
                } else if (state != ATTR) {
                    start = i;
                    state = DOUBLEQUOTE;
                } else {
                    state = DOUBLEQUOTE;
                }
                break;
            case '\'':
                if (state == SINGLEQUOTE) {
                    attrs.add(new XMLNode(getOffset() + start, i - start + 1, ITypeConstants.ATTR, document));
                    start = -1;
                    state = TAG;
                } else if (state == DOUBLEQUOTE) {
                    break;
                } else if (state != ATTR) {
                    start = i;
                    state = SINGLEQUOTE;
                } else {
                    state = SINGLEQUOTE;
                }
                break;
            default:
                if (!Character.isWhitespace(c)) {
                    if (state == TAG) {
                        start = i;
                        state = ATTR;
                    }
                } else if (state == ATTR) {
                    boolean stop = false;
                    int j = i;
                    // lookahead to see if this is an attribute name with no value
                    for (; j < content.length() - endLength; j++) {
                        char lookahead = content.charAt(j);

                        switch (lookahead) {
                        case '=':
                            break;
                        case '"':
                            break;
                        case '\'':
                            break;
                        default:
                            stop = !Character.isWhitespace(lookahead);
                            break;
                        }
                        if (stop)
                            break;
                    }
                    if (stop) {

                        attrs.add(new XMLNode(getOffset() + start, i - start + 1, ITypeConstants.ATTR, document));
                        start = -1;
                        state = TAG;
                    }
                }
            }
        }

        if (start != -1) {
            attrs.add(new XMLNode(getOffset() + start,
                    content.length() - startLength - start - (!getType().equals(ITypeConstants.TAG) ? 1 : 0),
                    ITypeConstants.ATTR, document));
        }
        for (Iterator iter = attrs.iterator(); iter.hasNext();) {
            XMLNode attr = (XMLNode) iter.next();
            attr.length = StringUtils.stripEnd(attr.getContent(), null).length();
        }

        return attrs;
    }

    //  /**
    //   * @return
    //   */
    //  private List getDoctypeAttributes(String content)
    //  {
    //       
    //    ArrayList attrs = new ArrayList();
    //    try
    //    {
    //      content = document.get(getOffset(), getLength());
    //    } catch (BadLocationException e)
    //    {
    //      UIPlugin.log(e);
    //      return attrs;
    //    }
    //    int initial = content.indexOf("!DOCTYPE") + "!DOCTYPE".length() + 1;
    //    int state = TAG;
    //    int start = -1;
    //    for (int i = initial; i < content.length() - 1; i++)
    //    {
    //      char c = content.charAt(i);
    //      switch (c)
    //      {
    //        case '"' :
    //          if (state == DOUBLEQUOTE || state == SINGLEQUOTE) {
    //            attrs.add(new XMLNode(
    //                getOffset() + start,
    //                i - start + 1,
    //                ITypeConstants.ATTR,
    //                document));
    //            start = -1;
    //            state = TAG;
    //          }
    //    }
    //    return null;
    //  }

    public Map getAttributesMap() {
        Map result = new OrderPreservingMap();
        for (Iterator iter = getAttributes().iterator(); iter.hasNext();) {
            XMLNode attr = (XMLNode) iter.next();
            result.put(attr.getName().toLowerCase(), attr);
        }
        return result;
    }

    public int getStateAt(int offset) {
        int state = TAG;
        String content = null;
        try {
            content = document.get(getOffset(), offset - getOffset());
        } catch (BadLocationException e) {
            UIPlugin.log(e);
            return TAG;
        }

        String name = getName();
        if (name == null)
            return TAG;

        String type = getType();
        if (type == "/")
            return -1;

        String terminator = (String) TERMINATORS.get(type);
        int terminatorLength = terminator.length();

        for (int i = name.length(); i < content.length(); i++) {
            char c = content.charAt(i);
            switch (c) {
            case '=':
                if (state == AFTER_ATTRIBUTE || state == ATTRIBUTE) {
                    state = ATT_VALUE;
                }
                break;
            case '"':
                if (state == DOUBLEQUOTE) {
                    state = AFTER_ATT_VALUE;
                } else if (state == SINGLEQUOTE) {
                    break;
                } else {
                    state = DOUBLEQUOTE;
                }
                break;
            case '\'':
                if (state == SINGLEQUOTE) {
                    state = AFTER_ATT_VALUE;
                } else if (state == DOUBLEQUOTE) {
                    break;
                } else {
                    state = SINGLEQUOTE;
                }
                break;
            default:
                if (Character.isWhitespace(c)) {
                    switch (state) {
                    case TAG:
                        state = ATTRIBUTE;
                        break;
                    case ATTR:
                        state = AFTER_ATTRIBUTE;
                        break;
                    case AFTER_ATT_VALUE:
                        state = ATTRIBUTE;
                        break;
                    }
                } else if (terminatorLength > 0) {
                    switch (state) {
                    case IN_TERMINATOR:
                        break;
                    case DOUBLEQUOTE:
                    case SINGLEQUOTE:
                        break;
                    default:
                        if (c == terminator.charAt(0))
                            state = IN_TERMINATOR;
                        break;
                    }
                }
            }
        }

        return state;
    }

    /**
     * For !DOCTYPE:
     * 
     * [28] doctypedecl ::= ' <!DOCTYPE' S Name (S ExternalID)? S? ('['
     * (markupdecl | DeclSep)* ']' S?)? '>' [28a] DeclSep ::= PEReference | S [29]
     * markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI |
     * Comment [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S
     * PubidLiteral S SystemLiteral
     */
    public String getDTDLocation() {
        //TODO: this must be changed to include inner DTDs
        String content = getContent();
        String location = null;
        int index = -1;
        int endIndex = -1;

        content = content.substring("<!DOCTYPE".length());

        index = content.indexOf("SYSTEM");

        if (index != -1) {
            index = content.indexOf("\"", index + "SYSTEM".length());

            if (index != -1) {
                endIndex = content.indexOf("\"", index + 1);
            } else {
                index = content.indexOf("'", index + "SYSTEM".length());
                if (index == -1) {
                    return null;
                }
                endIndex = content.indexOf("'", index + 1);
            }
        } else {
            index = content.indexOf("PUBLIC");
            if (index == -1) {
                return null;
            }
            index = content.indexOf("\"", index + "PUBLIC".length());
            if (index != -1) {
                // skip public ID
                index = content.indexOf("\"", index + 1);
            } else {
                index = content.indexOf("'", index + "PUBLIC".length());
                if (index == -1) {
                    return null;
                }
                // skip public ID
                index = content.indexOf("'", index + 1);
            }
            index = content.indexOf("\"", index + 1);
            if (index != -1) {
                endIndex = content.indexOf("\"", index + 1);
            } else {
                index = content.indexOf("'", index + 1);
                if (index != -1) {
                    endIndex = content.indexOf("'", index + 1);
                }
            }
        }
        if (index == -1 || endIndex == -1) {
            return null;
        }
        location = content.substring(index + 1, endIndex);

        return location;
    }

    /**
     * @return
     */
    public IDocument getDocument() {
        return document;
    }

    /**
     * @param document
     */
    public void setDocument(IDocument document) {
        this.document = document;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    public String toString() {
        String s = super.toString();
        s += "[";
        s += "name=" + getName() + ";";
        s += "type=" + getType() + ";";
        s += "content=" + getContent() + ";";
        s += "isDeleted=" + isDeleted() + ";";
        s += "[" + getOffset() + "," + getLength() + "]";
        s += "]";

        return s;
    }

    public boolean isTerminated() {
        if (getLength() == 0)
            return true;
        String type = getType();
        if (type.equals(ITypeConstants.TEXT))
            return true;

        if (type.equals(ITypeConstants.ATTR))
            return true;

        String terminator = (String) TERMINATORS.get(type);
        int length = terminator.length();
        try {
            return terminator.equals(document.get(getOffset() + getLength() - length, length));

        } catch (BadLocationException e) {
            // do nothing
        }
        return false;
    }

    public String readPublicId() {
        String content = getContent();
        int start = -1;
        int end = -1;
        int minLength = "<!DOCTYPE".length();
        if (content.length() < minLength)
            return "";

        content = content.substring("<!DOCTYPE".length());
        start = content.indexOf("PUBLIC");
        if (start >= 0) {
            start = content.indexOf("\"", start + "PUBLIC".length());
            if (start >= 0) {
                String test = content.substring(start + 1);
                end = content.indexOf("\"", ++start);

                if (end >= 0)
                    return content.substring(start, end);

            }
        }
        return null;
    }

    public String getRootNodeId() {
        String content = getContent();
        int index = -1;
        int end = -1;
        int minLength = "<!DOCTYPE".length();
        if (content.length() < minLength)
            return "";
        content = content.substring("<!DOCTYPE".length());

        index = content.indexOf("PUBLIC");
        if (index >= 0) {
            content = content.substring(0, index).trim();
            return content.length() == 0 ? null : content;
        } else {
            index = content.indexOf("SYSTEM");
            if (index >= 0) {
                content = content.substring(0, index).trim();
                return content.length() == 0 ? null : content;
            } else {
                content = content.trim();
                return content.length() == 0 ? null : content;
            }
        }
    }

}