001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.xml.xdm.visitor;
043:
044: import java.util.List;
045: import java.util.Stack;
046: import org.netbeans.modules.xml.xdm.nodes.Attribute;
047: import org.netbeans.modules.xml.xdm.nodes.Document;
048: import org.netbeans.modules.xml.xdm.nodes.Element;
049: import org.netbeans.modules.xml.xdm.nodes.Node;
050: import org.netbeans.modules.xml.xdm.nodes.NodeImpl;
051: import org.netbeans.modules.xml.xdm.nodes.Text;
052: import org.netbeans.modules.xml.xdm.nodes.Token;
053: import org.netbeans.modules.xml.xdm.nodes.TokenType;
054: import org.w3c.dom.NamedNodeMap;
055: import org.w3c.dom.NodeList;
056:
057: /**
058: *
059: * @author Ayub Khan
060: */
061: public class NodeByPositionVisitor implements XMLNodeVisitor {
062:
063: private int currentPos = 0;
064: private Node rootNode;
065: private boolean found;
066: private int position;
067: private Node foundNode;
068: private Stack<Element> stack = new Stack<Element>();
069:
070: public NodeByPositionVisitor(Node rootNode) {
071: this .rootNode = rootNode;
072: }
073:
074: public Element getContainingElement(int position) {
075: //Based on position, the containing node could be element, attr, text
076: Node node = getContainingNode(position);
077: if (node instanceof Attribute || node instanceof Text) {
078: if (stack.isEmpty())
079: return null;
080: return stack.peek();
081: }
082: return (Element) node;
083: }
084:
085: public Node getContainingNode(int position) {
086: reset();
087: this .position = position;
088: rootNode.accept(this );
089: return this .foundNode;
090: }
091:
092: public void reset() {
093: currentPos = 0;
094: found = false;
095: foundNode = null;
096: }
097:
098: public void visit(Document doc) {
099: //xml processing instruction
100: currentPos += getLengthOfTokens(doc);
101: NodeList nodes = doc.getChildNodes();
102: for (int i = 0; i < nodes.getLength(); i++) {
103: Node n = (Node) nodes.item(i);
104: n.accept(this );
105: if (found)
106: return;
107: }
108: }
109:
110: public void visit(Element e) {
111: stack.push(e);
112: int openStartElemPos = currentPos;
113: currentPos += getTokenLength(e, TokenType.TOKEN_WHITESPACE); //all whitespaces
114: currentPos += getElementStartTokenLength(e, true); //open start tag
115: NamedNodeMap attrs = e.getAttributes();
116: for (int i = 0; i < attrs.getLength(); i++) {
117: Node attr = (Node) attrs.item(i);
118: attr.accept(this );
119: if (found)
120: return;
121: }
122: currentPos++; //close of start tag
123: int closeStartElemPos = currentPos;
124: if ((position >= openStartElemPos && position < closeStartElemPos)) {
125: this .foundNode = e;
126: found = true;
127: }
128: NodeList children = e.getChildNodes();
129: for (int i = 0; i < children.getLength(); i++) {
130: Node n = (Node) children.item(i);
131: n.accept(this );
132: if (found)
133: return;
134: }
135: stack.pop();
136: int openEndElemPos = currentPos;
137: currentPos += getElementStartTokenLength(e, false); //open end tag
138: currentPos++; //close of end tag
139: int closeEndElemPos = currentPos;
140: if ((position >= openEndElemPos && position < closeEndElemPos)) {
141: this .foundNode = e;
142: found = true;
143: }
144: }
145:
146: public void visit(Text txt) {
147: int beginTextPos = currentPos;
148: // use token length here because of automatic conversion
149: // for < as well as handling of comment and cdata which need
150: // to include the start and end delimeter
151: currentPos += getLengthOfTokens(txt);
152: int endTextPos = currentPos;
153: if ((position >= beginTextPos && position < endTextPos)) {
154: this .foundNode = txt;
155: found = true;
156: }
157: }
158:
159: public void visit(Attribute attr) {
160: int beginAttrPos = currentPos;
161: currentPos += getLengthOfTokens(attr);
162: int endAttrPos = currentPos;
163: if ((position >= beginAttrPos && position < endAttrPos)) {
164: this .foundNode = attr;
165: found = true;
166: }
167: }
168:
169: /**
170: * Obtains the length of a start element, e.g., "<", or "<elementname",
171: * "</", or "</elementname".
172: * @param node The element being queried
173: * @param beginTag Is this for the start tag (<) or end tag (</)?
174: * @return length of start element
175: */
176: private int getElementStartTokenLength(Element element,
177: boolean beginTag) {
178: String value = "";
179: List<Token> tokens = element.getTokens();
180: for (Token token : tokens) {
181: if (token.getType() != TokenType.TOKEN_ELEMENT_START_TAG) {
182: continue;
183: }
184: String tokenValue = token.getValue();
185: if (beginTag) {
186: if (!tokenValue.startsWith("</")) {
187: value = tokenValue;
188: }
189: } else { //end tag
190: if (tokenValue.startsWith("</")) {
191: value = tokenValue;
192: }
193: }
194: }
195: return value.length();
196: }
197:
198: private int getTokenLength(NodeImpl node, TokenType type) {
199: StringBuffer buf = new StringBuffer("");
200: List<Token> tokens = node.getTokens();
201: for (Token token : tokens) {
202: if (token.getType() == type) {
203: buf.append(token.getValue());
204: }
205: }
206: return buf.toString().length();
207: }
208:
209: private int getLeadingWhiteSpaces(Attribute attr) {
210: Token firstToken = attr.getTokens().get(0); //get the first token
211: if (firstToken.getType() == TokenType.TOKEN_WHITESPACE) {
212: return firstToken.getValue().length();
213: }
214: return 0;
215: }
216:
217: private int getLengthOfTokens(NodeImpl node) {
218: StringBuffer buf = new StringBuffer();
219: List<Token> tokens = node.getTokens();
220: for (Token token : tokens) {
221: buf.append(token.getValue());
222: }
223: return buf.length();
224: }
225: }
|