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.editor.ext;
043:
044: import java.util.Arrays;
045: import java.util.ArrayList;
046: import org.netbeans.editor.Syntax;
047:
048: /**
049: * Composition of several syntaxes together. There are several different
050: * situations in which this class can be used efficiently:
051: * 1) Syntax that wants to use some other syntax internally for
052: * recognizing one or more tokens. Example is java analyzer that would
053: * like to use html-syntax for detail parsing block comments.
054: *
055: * 2) Master syntax that will manage two or more slave syntaxes. Example is the
056: * master syntax managing java-syntax and html-syntax. The result would
057: * be the same like in the previous example but it's more independent.
058: *
059: * 3) Master syntax that handles switching of the two or more other syntaxes. Only one
060: * slave syntax is active at one time.
061: *
062: * 4) An aribitrary combination and nesting of the previous examples.
063: *
064: * @author Miloslav Metelka
065: * @version 1.00
066: */
067:
068: public class MultiSyntax extends Syntax {
069:
070: /** Slave syntaxes that can be used for scanning. They can
071: * be added by registerSyntax().
072: */
073: private SyntaxInfo slaveSyntaxChain;
074:
075: /** Last chain member of the slaveSyntaxChain */
076: private SyntaxInfo slaveSyntaxChainEnd;
077:
078: /** Register a particular slave syntax. */
079: protected void registerSyntax(Syntax slaveSyntax) {
080: slaveSyntaxChainEnd = new SyntaxInfo(slaveSyntax,
081: slaveSyntaxChainEnd);
082: if (slaveSyntaxChain == null) {
083: slaveSyntaxChain = slaveSyntaxChainEnd;
084: }
085: }
086:
087: /** Store state of this analyzer into given mark state. */
088: public void storeState(StateInfo stateInfo) {
089: super .storeState(stateInfo);
090: ((MultiStateInfo) stateInfo).store(this );
091: }
092:
093: public void loadInitState() {
094: super .loadInitState();
095: SyntaxInfo syntaxItem = slaveSyntaxChain;
096: while (syntaxItem != null) {
097: syntaxItem.syntax.loadInitState();
098: syntaxItem = syntaxItem.next;
099: }
100: }
101:
102: public void load(StateInfo stateInfo, char buffer[], int offset,
103: int len, boolean lastBuffer, int stopPosition) {
104: ((MultiStateInfo) stateInfo).load(this , buffer, offset, len,
105: lastBuffer, stopPosition);
106: super .load(stateInfo, buffer, offset, len, lastBuffer,
107: stopPosition);
108: }
109:
110: public StateInfo createStateInfo() {
111: return new MultiStateInfo();
112: }
113:
114: /** Compare state of this analyzer to given state info. The basic
115: * implementation does the following:
116: * 1. state info of the main syntax is compared
117: * 2. if the result is EQUAL_STATE then go through all the registered slave syntaxes:
118: * a) get the info
119: */
120: public int compareState(StateInfo stateInfo) {
121: int diff = super .compareState(stateInfo);
122: if (diff == EQUAL_STATE) {
123: diff = ((MultiStateInfo) stateInfo).compare(this );
124: }
125: return diff;
126: }
127:
128: /** Class that can contain any number of the additional state infos from
129: * other syntaxes. The state infos stored are identified
130: * by the their syntax classes.
131: */
132: public static class MultiStateInfo extends BaseStateInfo {
133:
134: private ChainItem stateInfoChain;
135:
136: /** Goes through all the syntaxes and inits them. If the multi-state-info has
137: * valid state-info for the given syntax the state-info is used.
138: * Otherwise the syntax is inited to the init state.
139: */
140: void load(MultiSyntax masterSyntax, char[] buffer, int offset,
141: int len, boolean lastBuffer, int stopPosition) {
142: SyntaxInfo syntaxItem = masterSyntax.slaveSyntaxChain;
143: while (syntaxItem != null) {
144: StateInfo loadInfo = null;
145: int masterOffsetDelta = 0;
146: Syntax s = syntaxItem.syntax;
147: if (syntaxItem.active) {
148: Class sc = s.getClass();
149: ChainItem item = stateInfoChain;
150: while (item != null) {
151: if (item.syntaxClass == sc && item.valid) {
152: loadInfo = item.stateInfo;
153: masterOffsetDelta = item.masterOffsetDelta;
154: break;
155: }
156: item = item.prev;
157: }
158: }
159: s.load(loadInfo, buffer, offset + masterOffsetDelta,
160: len - masterOffsetDelta, lastBuffer,
161: stopPosition);
162: syntaxItem = syntaxItem.next;
163: }
164: }
165:
166: void store(MultiSyntax masterSyntax) {
167: // Invalidate all state-info chain items
168: ChainItem item = stateInfoChain;
169: while (item != null) {
170: item.valid = false;
171: item = item.prev;
172: }
173:
174: // Go through active syntaxes and store their info and master-offset
175: SyntaxInfo syntaxItem = masterSyntax.slaveSyntaxChain;
176: while (syntaxItem != null) {
177: if (syntaxItem.active) {
178: Syntax s = syntaxItem.syntax;
179: Class sc = s.getClass();
180: item = stateInfoChain;
181: while (item != null) {
182: if (item.syntaxClass == sc) { // found right item
183: break;
184: }
185: item = item.prev;
186: }
187: if (item == null) { // not found, add new
188: item = stateInfoChain = new ChainItem(s
189: .createStateInfo(), sc, stateInfoChain);
190: }
191: // Store the state and compute masterOffsetDelta
192: s.storeState(item.stateInfo);
193: item.masterOffsetDelta = s.getOffset()
194: - masterSyntax.getOffset();
195: item.valid = true;
196: }
197: syntaxItem = syntaxItem.next;
198: }
199: }
200:
201: int compare(MultiSyntax masterSyntax) {
202: int ret = Syntax.EQUAL_STATE;
203: // Go through valid state-info chain items
204: ChainItem item = stateInfoChain;
205: while (item != null && ret == Syntax.EQUAL_STATE) {
206: if (item.valid) {
207: Class sc = item.syntaxClass;
208: SyntaxInfo syntaxItem = masterSyntax.slaveSyntaxChain;
209: while (syntaxItem != null) {
210: if (syntaxItem.syntax.getClass() == sc) {
211: if (syntaxItem.active) {
212: ret = syntaxItem.syntax
213: .compareState(item.stateInfo);
214: } else { // syntax not active but should be
215: ret = Syntax.DIFFERENT_STATE;
216: }
217: break;
218: }
219: syntaxItem = syntaxItem.next;
220: }
221: }
222: item = item.prev;
223: }
224: return ret;
225: }
226:
227: static class ChainItem {
228:
229: /** Whether this item is valid. It can become invalid if the syntax that it represents
230: * becomes inactive in this item.
231: */
232: boolean valid;
233:
234: /** State info of the particular slave syntax */
235: StateInfo stateInfo;
236:
237: /* Delta of the offset variable of the slave syntax against the offset
238: * variable of the master syntax.
239: */
240: int masterOffsetDelta;
241:
242: /** Class of the syntax this info is for */
243: Class syntaxClass;
244:
245: /** Previous chain item in the list */
246: ChainItem prev;
247:
248: ChainItem(StateInfo stateInfo, Class syntaxClass,
249: ChainItem prev) {
250: this .stateInfo = stateInfo;
251: this .syntaxClass = syntaxClass;
252: this .prev = prev;
253: }
254:
255: }
256:
257: }
258:
259: /** Extended info about one slave syntax */
260: static class SyntaxInfo {
261:
262: SyntaxInfo(Syntax syntax, SyntaxInfo prevChainEnd) {
263: this .syntax = syntax;
264:
265: if (prevChainEnd != null) {
266: prev = prevChainEnd;
267: prevChainEnd.next = this ;
268: }
269: }
270:
271: /** The slave syntax itself */
272: Syntax syntax;
273:
274: /** Whether this syntax is actively scanning the text. There can be possibly more
275: * syntaxes scanning the in a nested way.
276: */
277: boolean active;
278:
279: /** Next member in the chain */
280: SyntaxInfo next;
281:
282: /** Previous member in the chain */
283: SyntaxInfo prev;
284:
285: }
286:
287: }
|