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-2007 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.cnd.apt.support;
043:
044: import antlr.Token;
045: import antlr.TokenStream;
046: import antlr.TokenStreamException;
047: import java.util.LinkedList;
048: import java.util.Stack;
049: import java.util.logging.Level;
050: import org.netbeans.modules.cnd.apt.debug.APTTraceFlags;
051: import org.netbeans.modules.cnd.apt.structure.APT;
052: import org.netbeans.modules.cnd.apt.structure.APTStream;
053: import org.netbeans.modules.cnd.apt.utils.APTTraceUtils;
054: import org.netbeans.modules.cnd.apt.utils.APTUtils;
055:
056: /**
057: * base Tree walker for APT
058: * @author Vladimir Voskresensky
059: */
060: public abstract class APTWalker {
061: private APTMacroMap macros;
062: private APT root;
063: private boolean walkerInUse = false;
064: private boolean stopped = false;
065:
066: /**
067: * Creates a new instance of APTWalker
068: */
069: public APTWalker(APT apt, APTMacroMap macros) {
070: assert (apt != null) : "how can we work on null tree?"; // NOI18N
071: this .root = apt;
072: this .macros = macros;
073: }
074:
075: /** fast visit APT without generating token stream */
076: public void visit() {
077: if (walkerInUse) {
078: throw new IllegalStateException(
079: "walker could be used only once"); // NOI18N
080: }
081: if (APTTraceFlags.APT_NON_RECURSE_VISIT) {
082: nonRecurseVisit();
083: } else {
084: walkerInUse = true;
085: visit(root.getFirstChild());
086: }
087: }
088:
089: public void nonRecurseVisit() {
090: init(false);
091: while (!finished()) {
092: toNextNode();
093: }
094: }
095:
096: public TokenStream getTokenStream() {
097: if (walkerInUse) {
098: throw new IllegalStateException(
099: "walker could be used only once"); // NOI18N
100: }
101: return new WalkerTokenStream();
102: }
103:
104: private class WalkerTokenStream implements TokenStream {
105: public WalkerTokenStream() {
106: init(true);
107: }
108:
109: public Token nextToken() throws TokenStreamException {
110: //Token token = null;
111: //do {
112: //token = nextTokenImpl();
113: //token = onToken(token); not used anywhere
114: //} while (token == null);
115: return nextTokenImpl();
116: }
117: }
118:
119: // /** walk APT and generate correspondent token stream */
120: // private Token nextToken() throws TokenStreamException {
121: //// if (!initedTokenStreamCreating) {
122: //// init();
123: //// }
124: // Token token = null;
125: // do {
126: // token = nextTokenImpl();
127: // token = onToken(token);
128: // } while (token == null);
129: // return token;
130: // }
131: //
132: // private int LA() throws TokenStreamException {
133: // if (!initedTokenStreamCreating) {
134: // init();
135: // }
136: // laToken = (laToken == null) ? nextTokenImpl() : laToken;
137: // return laToken.getType();
138: // }
139:
140: ////////////////////////////////////////////////////////////////////////////
141: // template methods to override by extensions
142:
143: protected abstract void onInclude(APT apt);
144:
145: protected abstract void onIncludeNext(APT apt);
146:
147: protected abstract void onDefine(APT apt);
148:
149: protected abstract void onUndef(APT apt);
150:
151: // preproc conditions
152:
153: /*
154: * @return true if infrastructure can continue visiting children
155: * false if infrastructure should not visit children
156: */
157: protected abstract boolean onIf(APT apt);
158:
159: /*
160: * @return true if infrastructure can continue visiting children
161: * false if infrastructure should not visit children
162: */
163: protected abstract boolean onIfdef(APT apt);
164:
165: /*
166: * @return true if infrastructure can continue visiting children
167: * false if infrastructure should not visit children
168: */
169: protected abstract boolean onIfndef(APT apt);
170:
171: /*
172: * @return true if infrastructure can continue visiting children
173: * false if infrastructure should not visit children
174: */
175: protected abstract boolean onElif(APT apt, boolean wasInPrevBranch);
176:
177: /*
178: * @return true if infrastructure can continue visiting children
179: * false if infrastructure should not visit children
180: */
181: protected abstract boolean onElse(APT apt, boolean wasInPrevBranch);
182:
183: protected abstract void onEndif(APT apt, boolean wasInBranch);
184:
185: // callback for stream node
186: protected void onStreamNode(APT apt) {
187: // do nothing
188: }
189:
190: /**
191: * Callback for #error node.
192: */
193: protected void onErrorNode(APT apt) {
194: // do nothing
195: }
196:
197: protected void onOtherNode(APT apt) {
198: // do nothing
199: }
200:
201: protected void onEOF() {
202: // do nothing
203: }
204:
205: /**
206: * Determines whether the walker should stop or proceed
207: * as soon as it encounteres #error directive
208: *
209: * @return true if the walker should stop on #error directive,
210: * otherwise false
211: */
212: protected boolean stopOnErrorDirective() {
213: return true;
214: }
215:
216: // tokens stream generating/callback for token
217: // protected abstract Token onToken(Token token);
218:
219: ////////////////////////////////////////////////////////////////////////////
220: // impl details
221:
222: /** fast recursive walker to be used when token stream is not interested */
223: private void visit(APT t) {
224: if (t == null || isStopped()) {
225: return;
226: }
227: boolean wasInChild = false;
228: for (APT node = t; node != null && !isStopped(); node = node
229: .getNextSibling()) {
230: if (node.getType() == APT.Type.CONDITION_CONTAINER) {
231: assert (node.getFirstChild() != null);
232: visit(node.getFirstChild());
233: } else {
234: boolean visitChild = onAPT(node, wasInChild);
235: if (visitChild && node.getFirstChild() != null) {
236: assert (APTUtils.isStartOrSwitchConditionNode(node
237: .getType()));
238: visit(node.getFirstChild());
239: }
240: wasInChild |= visitChild;
241: }
242: }
243: }
244:
245: protected boolean onAPT(APT node, boolean wasInBranch) {
246: boolean visitChild = false;
247: switch (node.getType()) {
248: case APT.Type.IF:
249: visitChild = onIf(node);
250: break;
251: case APT.Type.IFDEF:
252: visitChild = onIfdef(node);
253: break;
254: case APT.Type.IFNDEF:
255: visitChild = onIfndef(node);
256: break;
257: case APT.Type.ELIF:
258: visitChild = onElif(node, wasInBranch);
259: break;
260: case APT.Type.ELSE:
261: visitChild = onElse(node, wasInBranch);
262: break;
263: case APT.Type.ENDIF:
264: onEndif(node, wasInBranch);
265: break;
266: case APT.Type.DEFINE:
267: onDefine(node);
268: break;
269: case APT.Type.UNDEF:
270: onUndef(node);
271: break;
272: case APT.Type.INCLUDE:
273: onInclude(node);
274: break;
275: case APT.Type.INCLUDE_NEXT:
276: onIncludeNext(node);
277: break;
278: case APT.Type.TOKEN_STREAM:
279: // onStreamNode(node);
280: break;
281: case APT.Type.ERROR:
282: onErrorNode(node);
283: break;
284: case APT.Type.INVALID:
285: case APT.Type.LINE:
286: case APT.Type.PRAGMA:
287: case APT.Type.PREPROC_UNKNOWN:
288: onOtherNode(node);
289: break;
290: default:
291: assert (false) : "unsupported "
292: + APTTraceUtils.getTypeName(node); // NOI18N
293: }
294: APTUtils.LOG.log(Level.FINE, "onAPT: {0}; {1} {2}", // NOI18N
295: new Object[] { node,
296: (wasInBranch ? "Was before;" : ""), // NOI18N
297: (visitChild ? "Will visit children" : "") // NOI18N
298: });
299: return visitChild;
300: }
301:
302: private void pushState() {
303: visits.push(new WalkerState(curAPT, curWasInChild));
304: }
305:
306: private boolean popState() {
307: if (visits.isEmpty()) {
308: return false;
309: }
310: WalkerState state = visits.pop();
311: curAPT = state.lastNode;
312: curWasInChild = state.wasInChild;
313: return true;
314: }
315:
316: private void init(boolean needStream) {
317: curAPT = root.getFirstChild();
318: if (needStream) {
319: fillTokens();
320: }
321: curWasInChild = false;
322: pushState();
323: walkerInUse = true;
324: }
325:
326: private Token nextTokenImpl() throws TokenStreamException {
327: Token theRetToken = null;
328: tokenLoop: for (;;) {
329: while (!tokens.isEmpty()) {
330: TokenStream ts = tokens.peek();
331: theRetToken = ts.nextToken();
332: if (!APTUtils.isEOF(theRetToken)) {
333: return theRetToken;
334: } else {
335: tokens.removeFirst();
336: }
337: }
338: if (finished()) {
339: onEOF();
340: return APTUtils.EOF_TOKEN;
341: } else {
342: toNextNode();
343: fillTokens();
344: }
345: }
346: }
347:
348: private void toNextNode() {
349: popState();
350: if (finished()) {
351: return;
352: }
353: if (curAPT == null) {
354: // we are in APT of incomplete file
355: APTUtils.LOG.log(Level.SEVERE, "incomplete APT {0}",
356: new Object[] { root });// NOI18N
357: do {
358: popState();
359: curAPT = curAPT.getNextSibling();
360: } while (curAPT == null && !finished());
361:
362: if (curAPT == null) {
363: return;
364: }
365: }
366: if (curAPT.getType() == APT.Type.CONDITION_CONTAINER) {
367: // new conditional container node
368:
369: // if wasn't yet in any children
370: curWasInChild = false;
371: // push container to have possibility move on it's sibling after ENDIF
372: pushState();
373: // move to the first child of container
374: assert (curAPT.getFirstChild() != null);
375: curAPT = curAPT.getFirstChild();
376: }
377:
378: // allow any actions in extension for the current node
379: boolean visitChild = onAPT(curAPT, curWasInChild);
380: curWasInChild |= visitChild;
381: if (visitChild) {
382: // move on next node to visit
383: assert (APTUtils.isStartOrSwitchConditionNode(curAPT
384: .getType()));
385: if (curAPT.getFirstChild() != null) {
386: // node has children
387: // push to have possibility move on it's sibling after visited children
388: pushState();
389: curAPT = curAPT.getFirstChild();
390: curWasInChild = false;
391: } else {
392: // move on sibling, as cur node has empty children
393: curAPT = curAPT.getNextSibling();
394: }
395: } else {
396: if (curAPT.getType() == APT.Type.ENDIF) {
397: APT endif = curAPT;
398: // end of condition block
399: popState();
400: if (curAPT.getType() != APT.Type.CONDITION_CONTAINER) {
401: APTUtils.LOG
402: .log(
403: Level.SEVERE,
404: "#endif directive {0} without starting #if in APT {1}", // NOI18N
405: new Object[] { endif, root });
406: }
407: curWasInChild = false;
408: } else if (curAPT.getType() == APT.Type.ERROR) {
409: if (stopOnErrorDirective()) {
410: stop();
411: return;
412: }
413: }
414: curAPT = curAPT.getNextSibling();
415: while (curAPT == null && !finished()) {
416: popState();
417: curAPT = curAPT.getNextSibling();
418: }
419: }
420: if (!finished()) {
421: pushState();
422: }
423: }
424:
425: private void fillTokens() {
426: // only token stream nodes contain tokens as TokenStream
427: if (curAPT != null
428: && (curAPT.getType() == APT.Type.TOKEN_STREAM)) {
429: onStreamNode(curAPT);
430: TokenStream ts = ((APTStream) curAPT).getTokenStream();
431: tokens.addFirst(ts);
432: }
433: }
434:
435: private boolean finished() {
436: return (curAPT == null && visits.isEmpty()) || isStopped();
437: }
438:
439: protected APTMacroMap getMacroMap() {
440: return macros;
441: }
442:
443: protected APT getCurNode() {
444: return curAPT;
445: }
446:
447: // fields to be used when generating token stream
448: private APT curAPT;
449: private boolean curWasInChild;
450: private LinkedList<TokenStream> tokens = new LinkedList<TokenStream>();
451: private Stack<WalkerState> visits = new Stack<WalkerState>();
452:
453: private static final class WalkerState {
454: APT lastNode;
455: boolean wasInChild;
456:
457: private WalkerState(APT node, boolean wasInChildState) {
458: this .lastNode = node;
459: this .wasInChild = wasInChildState;
460: }
461: }
462:
463: protected final boolean isStopped() {
464: return stopped;
465: }
466:
467: protected final void stop() {
468: this .stopped = true;
469: }
470: }
|