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.impl.structure;
043:
044: import antlr.Token;
045: import java.io.Serializable;
046: import java.util.ArrayList;
047: import java.util.Collection;
048: import java.util.Collections;
049: import java.util.List;
050: import java.util.logging.Level;
051: import org.netbeans.modules.cnd.apt.debug.DebugUtils;
052: import org.netbeans.modules.cnd.apt.support.APTTokenTypes;
053: import org.netbeans.modules.cnd.apt.structure.APT;
054: import org.netbeans.modules.cnd.apt.structure.APTDefine;
055: import org.netbeans.modules.cnd.apt.utils.APTUtils;
056: import org.netbeans.modules.cnd.apt.utils.ListBasedTokenStream;
057:
058: /**
059: * #define directive implementation
060: * @author Vladimir Voskresensky
061: */
062: public final class APTDefineNode extends APTMacroBaseNode implements
063: APTDefine, Serializable {
064: private static final long serialVersionUID = -99267816578145490L;
065:
066: private List<Token> params = null;
067: private List<Token> bodyTokens = null;
068:
069: private byte state = BEFORE_MACRO_NAME;
070:
071: private static final byte BEFORE_MACRO_NAME = 0;
072: private static final byte AFTER_MACRO_NAME = 1;
073: private static final byte IN_PARAMS = 2;
074: private static final byte IN_BODY = 3;
075: private static final byte IN_BODY_AFTER_SHARP = 4;
076: private static final byte ERROR = 5;
077:
078: /** Copy constructor */
079: /**package*/
080: APTDefineNode(APTDefineNode orig) {
081: super (orig);
082: this .params = orig.params;
083: this .bodyTokens = orig.bodyTokens;
084: this .state = orig.state;
085: }
086:
087: /** Constructor for serialization */
088: protected APTDefineNode() {
089: }
090:
091: /** Creates a new instance of APTDefineNode */
092: public APTDefineNode(Token token) {
093: super (token);
094: }
095:
096: public final int getType() {
097: return APT.Type.DEFINE;
098: }
099:
100: public Collection<Token> getParams() {
101: if (params == null) {
102: return null;
103: } else {
104: return Collections.<Token> unmodifiableList(params);// != null ? (Token[]) params.toArray(new Token[params.size()]) : null;
105: }
106: }
107:
108: public boolean isFunctionLike() {
109: return params != null;
110: }
111:
112: /**
113: * returns List of Tokens of macro body
114: */
115: public List<Token> getBody() {
116: return bodyTokens != null ? bodyTokens : Collections
117: .<Token> emptyList();
118: }
119:
120: /**
121: * returns true if #define directive is valid
122: */
123: public boolean isValid() {
124: return state != ERROR;
125: }
126:
127: public boolean accept(Token token) {
128: int ttype = token.getType();
129: if (APTUtils.isEndDirectiveToken(ttype)) {
130: return false;
131: } else {
132: switch (state) {
133: case BEFORE_MACRO_NAME: {
134: // allow base class to remember macro nam
135: boolean accepted = super .accept(token);
136: assert (accepted);
137: state = AFTER_MACRO_NAME;
138: break;
139: }
140: case AFTER_MACRO_NAME: {
141: if (token.getType() == APTTokenTypes.FUN_LIKE_MACRO_LPAREN) {
142: params = new ArrayList<Token>();
143: state = IN_PARAMS;
144: } else {
145: if (bodyTokens == null) {
146: bodyTokens = new ArrayList<Token>();
147: }
148: bodyTokens.add(token);
149: state = IN_BODY;
150: }
151: break;
152: }
153: case IN_PARAMS: {
154: switch (token.getType()) {
155: case APTTokenTypes.ID:
156: params.add(token);
157: // leave IN_PARAMS state
158: break;
159: case APTTokenTypes.RPAREN:
160: state = IN_BODY;
161: break;
162: case APTTokenTypes.ELLIPSIS:
163: // TODO: need to support ELLIPSIS for IZ#83949
164: params.add(APTUtils.VA_ARGS_TOKEN);
165: break;
166: default:
167: // eat comma and comments and leave IN_PARAMS state
168: if (!(token.getType() == APTTokenTypes.COMMA || APTUtils
169: .isCommentToken(token.getType()))) {
170: // error check
171: if (DebugUtils.STANDALONE) {
172: System.err
173: .printf(
174: "line %d: \"%s\" may not appear in macro parameter list\n", // NOI18N
175: getToken().getLine(), token
176: .getText());
177: } else {
178: APTUtils.LOG
179: .log(
180: Level.SEVERE,
181: "line {0}: {1} may not appear in macro parameter list", // NOI18N
182: new Object[] {
183: getToken()
184: .getLine(),
185: token.getText() });
186: }
187: state = ERROR;
188: }
189: break;
190: }
191: break;
192: }
193: case IN_BODY: {
194: // init body list if necessary
195: if (bodyTokens == null) {
196: bodyTokens = new ArrayList<Token>();
197: }
198: // check for errors:
199: if (token.getType() == APTTokenTypes.SHARP) {
200: state = IN_BODY_AFTER_SHARP;
201: }
202: bodyTokens.add(token);
203: break;
204: }
205: case IN_BODY_AFTER_SHARP: {
206: bodyTokens.add(token);
207: // skip comments
208: if (APTUtils.isCommentToken(token.getType())) {
209: // stay in the current state
210: } else if (token.getType() == APTTokenTypes.ID) {
211: // error check: token after # must be parameter
212: state = isInParamList(token) ? IN_BODY : ERROR;
213: } else {
214: // only id is accepted after #
215: state = ERROR;
216: }
217: if (state == ERROR) {
218: if (DebugUtils.STANDALONE) {
219: System.err
220: .printf(
221: "line %d: '#' is not followed by a macro parameter\n", // NOI18N
222: getToken().getLine());
223: } else {
224: APTUtils.LOG
225: .log(
226: Level.SEVERE,
227: "line {0}: '#' is not followed by a macro parameter", // NOI18N
228: new Object[] { getToken()
229: .getLine() });
230: }
231: }
232: break;
233: }
234: case ERROR: {
235: // eat all after error
236: break;
237: }
238: default:
239: assert (false) : "unexpected state"; // NOI18N
240: }
241: return true;
242: }
243: }
244:
245: private boolean isInParamList(Token id) {
246: assert id != null;
247: if (params == null) {
248: return false;
249: }
250: for (Token param : params) {
251: if (APTUtils.getTokenTextKey(param).equals(
252: APTUtils.getTokenTextKey(id))) {
253: return true;
254: }
255: }
256: return false;
257: }
258:
259: @Override
260: public String getText() {
261: String ret = super .getText();
262: String paramStr = ""; // NOI18N
263: if (params != null) {
264: paramStr = "PARAMS{"
265: + APTUtils.toString(new ListBasedTokenStream(
266: this .params)) + "}"; // NOI18N
267: }
268: String bodyStr;
269: if (bodyTokens != null) {
270: bodyStr = "BODY{"
271: + APTUtils.toString(new ListBasedTokenStream(
272: getBody())) + "}"; // NOI18N
273: } else {
274: bodyStr = "{NO BODY}"; // NOI18N
275: }
276: return ret + paramStr + bodyStr;
277: }
278: }
|