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