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: /*
043: * File : ParseEventController.java
044: * Created on : Oct 27, 2003
045: * Author : Aztec
046: */
047: package org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework;
048:
049: import java.util.Stack;
050:
051: import org.netbeans.modules.uml.common.ETSystem;
052: import org.netbeans.modules.uml.core.support.umlutils.ETArrayList;
053: import org.netbeans.modules.uml.core.support.umlutils.ETList;
054:
055: import antlr.CommonAST;
056: import antlr.CommonASTWithHiddenTokens;
057: import antlr.collections.AST;
058: import antlr.CommonASTWithLocationsAndHidden;
059:
060: /**
061: * @author Aztec
062: */
063: public class ParserEventController implements IParserEventController {
064: private IStateListener m_StateListener;
065: private ITokenProcessor m_TokenProcessor;
066: private IStateFilter m_StateFilter;
067: private ITokenFilter m_TokenFilter;
068: private IErrorListener m_ErrorListener;
069:
070: private String m_Filename;
071: private String m_LanguageName;
072: private CommentGather m_CommentGather;
073:
074: //private Stack< String > stringStack;
075: private Stack<StateInformation> mStateStack = new Stack<StateInformation>();
076: private ETList<TokenInformation> mGuessingTokens = new ETArrayList<TokenInformation>();
077:
078: public String GUESSING_STATE = null;
079:
080: public ParserEventController() {
081: GUESSING_STATE = "Guessing";
082: m_CommentGather = null;
083: }
084:
085: public ParserEventController(CommentGather pGather, String langName) {
086: GUESSING_STATE = "Guessing";
087: m_CommentGather = pGather;
088: setLanguageName(langName);
089: }
090:
091: public ParserEventController(String filename,
092: CommentGather pGather, String langName) {
093: GUESSING_STATE = "Guessing";
094: m_CommentGather = pGather;
095: setLanguageName(langName);
096: setFilename(filename);
097: }
098:
099: public ParserEventController(ParserEventController rhs,
100: CommentGather pGather, String langName) {
101: copy(rhs);
102: GUESSING_STATE = "Guessing";
103: m_CommentGather = pGather;
104: setLanguageName(langName);
105: }
106:
107: /**
108: * Clear the internal information and prepare for the next parser instance.
109: */
110: public void clear() {
111: mStateStack.clear();
112: mGuessingTokens.clear();
113:
114: m_StateListener = null;
115: m_TokenProcessor = null;
116: m_StateFilter = null;
117: m_TokenFilter = null;
118: m_ErrorListener = null;
119:
120: m_Filename = "";
121: m_CommentGather = null;
122: }
123:
124: /**
125: * Sends an error event to the registered error listener.
126: *
127: * @param msg [in] The error message.
128: * @param line [in] The line number that contains the error.
129: * If the error was not a parser error the line
130: * number should be -1;
131: * @param column [in] The column number that contains the error.
132: * If the error was not a parser error the line
133: * number should be -1;
134: * @param filename [in] The name of the file that was being parsed.
135: */
136: public void errorFound(String msg, int line, int column,
137: String filename) {
138: // I am not going to wrap the calls with _VH because what should I do if an error
139: // occured. If I failed to create or initialize the error there is nothing to do.
140: if (m_ErrorListener != null) {
141: IErrorEvent pError = new ErrorEvent();
142:
143: pError.setErrorMessage(msg);
144: pError.setLineNumber(line);
145: pError.setColumnNumber(column);
146: pError.setFilename(filename);
147:
148: m_ErrorListener.onError(pError);
149: }
150: }
151:
152: /**
153: * Get the the interface that will recieve the error information
154: * will parsing the file.
155: *
156: * @param pVal [out] The error listener.
157: */
158: public IErrorListener getErrorListener() {
159: return m_ErrorListener;
160: }
161:
162: /* (non-Javadoc)
163: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#getFilename()
164: */
165: public String getFilename() {
166: return m_Filename;
167: }
168:
169: /* (non-Javadoc)
170: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#getLanguageName()
171: */
172: public String getLanguageName() {
173: return m_LanguageName;
174: }
175:
176: /* (non-Javadoc)
177: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#getStateFilter()
178: */
179: public IStateFilter getStateFilter() {
180: return m_StateFilter;
181: }
182:
183: /* (non-Javadoc)
184: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#getStateListener()
185: */
186: public IStateListener getStateListener() {
187: return m_StateListener;
188: }
189:
190: /* (non-Javadoc)
191: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#getTokenFilter()
192: */
193: public ITokenFilter getTokenFilter() {
194: return m_TokenFilter;
195: }
196:
197: /* (non-Javadoc)
198: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#getTokenProcessor()
199: */
200: public ITokenProcessor getTokenProcessor() {
201: return m_TokenProcessor;
202: }
203:
204: /* (non-Javadoc)
205: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#setErrorListener(org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IErrorListener)
206: */
207: public void setErrorListener(IErrorListener errorListener) {
208: m_ErrorListener = errorListener;
209: }
210:
211: /* (non-Javadoc)
212: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#setFilename(java.lang.String)
213: */
214: public void setFilename(String filename) {
215: m_Filename = filename;
216: }
217:
218: /* (non-Javadoc)
219: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#setLanguageName(java.lang.String)
220: */
221: public void setLanguageName(String name) {
222: m_LanguageName = name;
223: }
224:
225: /* (non-Javadoc)
226: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#setStateFilter(org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IStateFilter)
227: */
228: public void setStateFilter(IStateFilter stateFilter) {
229: m_StateFilter = stateFilter;
230: }
231:
232: /* (non-Javadoc)
233: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#setStateListener(org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IStateListener)
234: */
235: public void setStateListener(IStateListener stateListener) {
236: m_StateListener = stateListener;
237: }
238:
239: /* (non-Javadoc)
240: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#setTokenFilter(org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ITokenFilter)
241: */
242: public void setTokenFilter(ITokenFilter tokenFilter) {
243: m_TokenFilter = tokenFilter;
244: }
245:
246: /* (non-Javadoc)
247: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#setTokenProcessor(org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ITokenProcessor)
248: */
249: public void setTokenProcessor(ITokenProcessor tokenProcessor) {
250: m_TokenProcessor = tokenProcessor;
251: }
252:
253: /**
254: * Performs the processing of entering a new state in the parser. If the
255: * new state is <I>Guessing</I> then all discovered tokens will be
256: * queued until a concreate state is discovered.
257: * <p>
258: * The specified token will be the first token to be added sent to the
259: * token processors before any other tokens. Even if tokens where found
260: * while in a guessing state the specified token will be sent first.
261: */
262: public void stateBegin(String stateName, AST tok, String type) {
263: enteringState(stateName, false);
264: tokenFound(tok, type);
265:
266: // Since we have started a new state. I want to fire all the tokens
267: // that have been discovered while in a guessing state.
268: fireGuessingTokens();
269: }
270:
271: /* (non-Javadoc)
272: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#stateBegin(java.lang.String)
273: */
274: public void stateBegin(String stateName) {
275: enteringState(stateName, true);
276: }
277:
278: /**
279: * Performs the processing of exiting an existing state in the parser.
280: * <p>
281: * As new states are discovered the new state will be pushed onto a
282: * stack of states. The top state will be the current state. When
283: * the parser exits a state the top state will be removed from the
284: * stack and the top state will again be the top state.
285: * <I>Example:</I>
286: * Begin Class State
287: * Begin Attribute State
288: * < Some Tokens Discovered >
289: * End Attribute State
290: * End Class State
291: * <p>
292: * While discovering the class details the parser also entered and exited
293: * the attribute discovery state. While being in the attribute discovery
294: * state the parser was also in the class discovery state.
295: *
296: * @param stateName [in] The name the new state.
297: */
298: public void stateEnd() {
299: if (mStateStack.size() > 0) {
300: StateInformation oldState = mStateStack.peek();
301: mStateStack.pop();
302:
303: String temp = oldState.stateName;
304:
305: if (temp != GUESSING_STATE) {
306: fireEndState(oldState);
307: } else {
308: // If we found tokens will in the GUESSING_STATE state but never found
309: // found a new state means that we had already found the correct state
310: // for the tokens.
311: fireGuessingTokens();
312: }
313: }
314: }
315:
316: /* (non-Javadoc)
317: * @see org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.IParseEventController#tokenFound(antlr.collections.AST, java.lang.String)
318: */
319: public void tokenFound(AST tok, String type) {
320: if (mStateStack.size() > 0) {
321: StateInformation curState = mStateStack.peek();
322:
323: // If the current state is blocked I do not want to do any thing
324: // with the token. Basically the listener is not interested in the
325: // token so do not even queue up the token.
326: if (!curState.blocked) {
327: if (GUESSING_STATE.equals(curState.stateName)) {
328: TokenInformation info = new TokenInformation();
329: info.type = type;
330: info.token = tok;
331: mGuessingTokens.add(info);
332: } else {
333: // Go through with the send token logic. Check if the
334: // state is being monitored. Then check if the token
335: // is being monitored. If both test are passed then
336: // send the token to the listener.
337: fireToken(type, tok);
338: }
339: }
340: }
341: }
342:
343: public void copy(ParserEventController rhs) {
344: if (this != rhs) {
345: m_StateListener = rhs.m_StateListener;
346: m_TokenProcessor = rhs.m_TokenProcessor;
347: m_StateFilter = rhs.m_StateFilter;
348: m_TokenFilter = rhs.m_TokenFilter;
349: mStateStack = rhs.mStateStack;
350: mGuessingTokens = rhs.mGuessingTokens;
351: m_ErrorListener = rhs.m_ErrorListener;
352: m_Filename = rhs.m_Filename;
353: m_CommentGather = rhs.m_CommentGather;
354: m_LanguageName = rhs.m_LanguageName;
355: }
356: }
357:
358: private ITokenDescriptor addComment(AST pAST, ITokenDescriptor pDesc) {
359: if (m_CommentGather != null)
360: return m_CommentGather.gather(pAST, pDesc);
361: return pDesc;
362: }
363:
364: private ITokenDescriptor addFilename(ITokenDescriptor pDesc) {
365: String filename = getFilename();
366: if (filename != null) {
367: pDesc.addProperty("Filename", filename);
368: }
369: return pDesc;
370: }
371:
372: private void enteringState(String stateName, boolean flushGuessing) {
373: StateInformation pInfo = new StateInformation();
374: if (stateName != null) {
375: pInfo.stateName = stateName;
376: pInfo.blocked = false;
377:
378: // If current state is block then the new state is also blocked.
379: if (mStateStack.size() > 0) {
380: StateInformation curState = mStateStack.peek();
381: pInfo.blocked = curState.blocked;
382: }
383: fireBeginState(pInfo, flushGuessing);
384:
385: } else if (pInfo != null) {
386: pInfo.blocked = true;
387: }
388:
389: if (pInfo != null) {
390: mStateStack.push(pInfo);
391: }
392: }
393:
394: /**
395: * Fires the begin state event to all registered listeners. All token events that
396: * was discovered while in a <I>Guessing</I> state will also be fired. If the
397: * state is being blocked by the state filter then the event will not be sent and
398: * all the guessing tokens will be removed.
399: *
400: * @param stateName [in] The current state.
401: * @param flushGuessing [in] True - sends guessing tokens to the processor, false does
402: * not send guessing tokens to the processor
403: */
404: private void fireBeginState(StateInformation pInfo,
405: boolean flushGuessing) {
406: if (pInfo != null) {
407: pInfo.blocked = isStateFiltered(pInfo.stateName);
408: if (pInfo.blocked == false) {
409: if (m_StateListener != null) {
410: IStatePayload pPayload = new StatePayload();
411: m_StateListener.onBeginState(pInfo.stateName,
412: m_LanguageName, pPayload);
413: }
414:
415: // Since we have started a new state. I want to fire all the tokens
416: // that have been discovered while in a guessing state.
417: if (flushGuessing == true) {
418: fireGuessingTokens();
419: }
420: } else {
421: // Since the state was filtered I want to just remove all the
422: // guessing tokens.
423: mGuessingTokens.clear();
424: }
425: }
426: }
427:
428: /**
429: * Fires a OnToken event to all registered listeners.
430: *
431: * @param pDescriptor [in] The discovered token.
432: */
433: private void fireToken(String type, AST pToken) {
434: if ((m_TokenProcessor != null) /*&& (m_TopLevel != null)*/) {
435: if (isCurrentStateFiltered() == false) {
436: if (isTokenFiltered(type) == false) {
437: ITokenDescriptor pDescriptor = new TokenDescriptor();
438: pDescriptor = initializeTokenDescriptor(type,
439: pToken, pDescriptor);
440:
441: m_TokenProcessor.processToken(pDescriptor,
442: getLanguageName());
443: }
444: }
445: }
446: }
447:
448: /**
449: * Test if the specified token is filtered by the token filter.
450: *
451: * @out type [in] The new token.
452: * @return true if the specified token has been filtered, false otherwise.
453: */
454: private boolean isTokenFiltered(String type) {
455: boolean retVal = false;
456:
457: if (m_TokenFilter != null) {
458: StateInformation curState = null;
459: if (mStateStack.size() > 0) {
460: curState = mStateStack.peek();
461: retVal = curState.blocked;
462: }
463:
464: // If the current state is blocked do not even try to ask the token filter
465: // because it is required to be filtered. Matter a fact we should never
466: // get to this routine if the state is blocked, however I have added this
467: // check as as santity check.
468: if (!retVal && curState != null) {
469: retVal = !m_TokenFilter.isTokenValid(type,
470: curState.stateName, m_LanguageName);
471: }
472: }
473: return retVal;
474: }
475:
476: /**
477: * Test if the current state has been filtered. Since the filter
478: * structure is in a tree format if the states parent state is filtered the
479: * sub state is also filtered. Therefore, if the current state is filtered then
480: * the specified state will also be filtered.
481: *
482: * @return true if the current state has been filtered, false otherwise.
483: */
484: private boolean isCurrentStateFiltered() {
485: boolean retVal = false;
486:
487: if (mStateStack.size() > 0) {
488: StateInformation curState = mStateStack.peek();
489: retVal = curState.blocked;
490: }
491: return retVal;
492: }
493:
494: /**
495: * Test if the specified state is filtered by the state filter. Since the filter
496: * structure is in a tree format if the states parent state is filtered the
497: * sub state is also filtered. Therefore, if the current state is filtered then
498: * the specified state will also be filtered.
499: *
500: * @out info [in/out] The new state.
501: * @return true if the specified state has been filtered, false otherwise.
502: */
503: private boolean isStateFiltered(String stateName) {
504: boolean retVal = false;
505:
506: if (mStateStack.size() > 0) {
507: StateInformation curState = mStateStack.peek();
508: retVal = curState.blocked;
509: }
510:
511: // Now check if the filter wants to block this state. The if condition
512: // will only be entered if the parent state is not blocked.
513: if (!retVal) {
514: if (m_StateFilter != null) {
515: retVal = !m_StateFilter.processState(stateName,
516: m_LanguageName);
517: ;
518: }
519: }
520:
521: return retVal;
522: }
523:
524: /**
525: * Creates a ne ITokenDescriptor object. The ITokenDescritpor will be initialized with the
526: * information from the Antlr AST.
527: *
528: * @param type [in] The token type.
529: * @param pAST [in] The AST.
530: * @param pDesc [out] The ITokenDescriptor that will contain the token information.
531: */
532: private ITokenDescriptor initializeTokenDescriptor(String type,
533: AST pAST, ITokenDescriptor pDesc) {
534: if (pAST == null)
535: return null;
536:
537: CommonASTWithLocationsAndHidden pToken = null;
538:
539: try {
540: pToken = (CommonASTWithLocationsAndHidden) pAST;
541: } catch (ClassCastException e) {
542: ETSystem.out.println("Got " + pAST.getClass().getName()
543: + " instead of CommonASTWithVisibleTokens");
544: }
545: if (pToken == null)
546: return null;
547:
548: String text = pToken.getText();
549:
550: // pDesc = new TokenDescriptor();
551: pDesc.setType(type);
552: pDesc.setLine(pToken.getLineNumber());
553: pDesc.setColumn(pToken.getColumn());
554: pDesc.setPosition(pToken.getPosition());
555: pDesc.setValue(text);
556: pDesc.setLength(text.length());
557:
558: addComment(pToken, pDesc);
559: return addFilename(pDesc);
560: }
561:
562: /**
563: * Fires a OnToken event for every state that was discovered while in a
564: * <I>Guessing</I> state.
565: */
566: private void fireGuessingTokens() {
567: for (int i = 0; i < mGuessingTokens.size(); i++) {
568: TokenInformation info = mGuessingTokens.get(i);
569: fireToken(info.type, info.token);
570: }
571: mGuessingTokens.clear();
572: }
573:
574: /**
575: * Fires the end state event to all registered listeners.
576: *
577: * @param stateName [in] The state that is being exited.
578: */
579: private void fireEndState(StateInformation info) {
580: if (m_StateListener != null && info.blocked == false) {
581: m_StateListener.onEndState(info.stateName);
582: }
583: }
584:
585: class StateInformation {
586: StateInformation() {
587: stateName = "";
588: blocked = false;
589: }
590:
591: StateInformation(StateInformation rhs) {
592: if (this != rhs) {
593: if (rhs.stateName.length() > 0) {
594: stateName = rhs.stateName;
595: }
596: blocked = rhs.blocked;
597: }
598: }
599:
600: public String stateName;
601: public boolean blocked;
602: }
603:
604: class TokenInformation {
605: public String type;
606: public AST token;
607: }
608:
609: }
|