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.text.syntax;
043:
044: import java.lang.ref.WeakReference;
045: import java.util.*;
046:
047: import org.w3c.dom.*;
048:
049: import javax.swing.text.*;
050:
051: import org.netbeans.editor.ext.*;
052: import org.netbeans.editor.*;
053: import org.openide.ErrorManager;
054:
055: /**
056: *
057: * Instances are produced by {@link XMLSyntaxSupport}.
058: * <p>
059: * <b>Warning:</b> class is public only for private purposes!
060: *
061: * @author Petr Nejedly - original HTML design
062: * @author Sandeep Randhawa - XML port
063: * @author Petr Kuzel - DOM Nodes
064: *
065: * @version 1.0
066: */
067: public abstract class SyntaxElement {
068:
069: // to do do not handle prolog as text!
070: // support PIs
071:
072: protected XMLSyntaxSupport support; // it produced us
073:
074: private WeakReference<TokenItem> first; //a weak reference to the fist TokenItem of this SE
075:
076: private WeakReference<SyntaxElement> previous; // WR to the cached previous element
077: private WeakReference<SyntaxElement> next; // WR to the cached next element
078:
079: // let it be visible by static inner classes extending us
080: protected int offset; // original position in document //??? use item instead
081: protected int length; // original lenght in document
082:
083: /** Creates new SyntaxElement */
084: public SyntaxElement(XMLSyntaxSupport support, TokenItem first,
085: int to) {
086: this .support = support;
087: this .offset = first.getOffset();
088: this .length = to - offset;
089: this .first = new WeakReference(first);
090: }
091:
092: /** returns an instance of first TokenItem of this SyntaxElement.
093: * The instance is weakly held by this SyntaxElement instance, once
094: * it is GC'ed, a new one is created using the offset of the original one.
095: *
096: * The WeakReference is used here because of a huge deep memory consumption of
097: * a TokenItem-s under some circumstances (The SyntaxSupport chains the tokens
098: * so it happens that each SyntaxElement instance holds it's own long
099: * TokenItem-s chain.
100: *
101: * The current implementation lowers the CPU performance slightly, but
102: * allows to GC the TokenItem-s chains if necessary.
103: */
104: protected TokenItem first() {
105: TokenItem cached_first = first.get();
106: if (cached_first == null) {
107: try {
108: TokenItem new_first = support.getTokenChain(offset,
109: offset + 1); //it is a first token offset, so we shouldn't overlap the document length
110: first = new WeakReference(new_first);
111: return new_first;
112: } catch (BadLocationException e) {
113: return null;
114: }
115: } else {
116: return cached_first;
117: }
118: }
119:
120: public int getElementOffset() {
121:
122: return offset;
123: }
124:
125: public int getElementLength() {
126: return length;
127: }
128:
129: void setNext(SyntaxElement se) {
130: next = new WeakReference(se);
131: }
132:
133: void setPrevious(SyntaxElement se) {
134: previous = new WeakReference(se);
135: }
136:
137: /**
138: * Get previous SyntaxElement. Weakly cache results.
139: * @return previous SyntaxElement or <code>null</code> at document begining
140: * or illegal location.
141: */
142: public SyntaxElement getPrevious() {
143: try {
144: SyntaxElement cached_previous = previous == null ? null
145: : previous.get();
146: if (cached_previous == null) {
147: //we are on the beginning - no previous
148: if (offset == 0) {
149: return null;
150: }
151: //data not inialized yet or GC'ed already - we need to parse again
152: SyntaxElement new_previous = support
153: .getElementChain(getElementOffset() - 1);
154: if (new_previous != null) {
155: setPrevious(new_previous); //weakly cache the element
156: new_previous.setNext(this );
157: if (new_previous.offset == offset) {
158: Exception ex = new IllegalStateException(
159: "Previous cannot be the same as current element at offset "
160: + offset);
161: ErrorManager.getDefault().notify(
162: ErrorManager.INFORMATIONAL, ex);
163: return null;
164: }
165: }
166: return new_previous;
167: } else {
168: //use cached data
169: return cached_previous;
170: }
171: } catch (BadLocationException ex) {
172: return null;
173: }
174: }
175:
176: /**
177: * Get next SyntaxElement. Cache results.
178: * @return next SyntaxElement or <code>null</code> at document end
179: * or illegal location.
180: */
181: public SyntaxElement getNext() {
182: try {
183: SyntaxElement cached_next = next == null ? null : next
184: .get();
185: if (cached_next == null) {
186: //data not inialized yet or GC'ed already - we need to parse again
187: SyntaxElement new_next = support.getElementChain(offset
188: + length + 1);
189: if (new_next != null) {
190: setNext(new_next); //weakly cache the element
191: new_next.setPrevious(this );
192: if (new_next.offset == offset) {
193: // TODO see #43297 for causes and try to relax them
194: //Exception ex = new IllegalStateException("Next cannot be the same as current element at offset " + offset);
195: //ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
196: return null;
197: }
198: }
199: return new_next;
200: } else {
201: //use cached data
202: return cached_next;
203: }
204: } catch (BadLocationException ex) {
205: return null;
206: }
207: }
208:
209: /**
210: * Print element content for debug purposes.
211: */
212: public String toString() {
213: String content = "?";
214: try {
215: content = support.getDocument().getText(offset, length);
216: } catch (BadLocationException e) {
217: }
218: return "SyntaxElement [offset=" + offset + "; length=" + length
219: + " ;type = " + this .getClass().getName()
220: + "; content:" + content + "]";
221: }
222:
223: /**
224: *
225: */
226: public int hashCode() {
227: return super .hashCode() ^ offset ^ length;
228: }
229:
230: /**
231: * DOM Node equals. It's not compatible with Object's equals specs!
232: */
233: public boolean equals(Object obj) {
234: if (obj instanceof SyntaxElement) {
235: if (((SyntaxElement) obj).offset == offset)
236: return true;
237: }
238: return false;
239: }
240:
241: // Particular non-DOM syntax elements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
242:
243: /**
244: * It may stop some DOM traversing. //!!!
245: */
246: public static class Error extends SyntaxElement {
247:
248: public Error(XMLSyntaxSupport support, TokenItem from, int to) {
249: super (support, from, to);
250: }
251:
252: public String toString() {
253: return "Error" + super .toString(); // NOI18N
254: }
255: }
256:
257: }
|