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.java;
015:
016: import java.util.ArrayList;
017:
018: import org.netbeans.editor.EditorDebug;
019: import org.netbeans.editor.TokenID;
020:
021: /**
022: * Expression generated by parsing text by java completion
023: *
024: * @author Miloslav Metelka
025: * @version 1.00
026: */
027:
028: public class JCExpression {
029:
030: /** Invalid expression - this ID is used only internally */
031: private static final int INVALID = -1;
032: /** Constant - int/long/String/char etc. */
033: public static final int CONSTANT = 0;
034: /** Variable 'a' or 'a.b.c' */
035: public static final int VARIABLE = 1;
036: /** Operator '+' or '--' */
037: public static final int OPERATOR = 2;
038: /** Special value for unary operators */
039: public static final int UNARY_OPERATOR = 3;
040: /** Dot between method calls 'a().b()' or 'a().b.c.d(e, f)' */
041: public static final int DOT = 4;
042: /**
043: * Dot between method calls and dot at the end 'a().b().' or 'a().b.c.d(e,
044: * f).'
045: */
046: public static final int DOT_OPEN = 5;
047: /** Opened array 'a[0' or 'a.b.c[d.e' */
048: public static final int ARRAY_OPEN = 6;
049: /** Array 'a[0]' or 'a.b.c[d.e]' */
050: public static final int ARRAY = 7;
051: /** Left opened parentheses */
052: public static final int PARENTHESIS_OPEN = 8;
053: /** Closed parenthesis holding the subexpression or conversion */
054: public static final int PARENTHESIS = 9;
055: /** Opened method 'a(' or 'a.b.c(d, e' */
056: public static final int METHOD_OPEN = 10;
057: /** Method closed by right parentheses 'a()' or 'a.b.c(d, e, f)' */
058: public static final int METHOD = 11;
059: /**
060: * Constructor closed by right parentheses 'new String()' or 'new
061: * String("hello")'
062: */
063: // NOI18N
064: public static final int CONSTRUCTOR = 12;
065: /** Conversion '(int)a.b()' */
066: public static final int CONVERSION = 13;
067: /** Data type */
068: public static final int TYPE = 14;
069: /** 'new' keyword */
070: public static final int NEW = 15;
071: /** 'instanceof' operator */
072: public static final int INSTANCEOF = 16;
073:
074: private static final int javaTokenIDsLength = JavaTokenContext.context
075: .getTokenIDs().length;
076:
077: /**
078: * Array that holds the precedence of the operator and whether it's right
079: * associative or not.
080: */
081: private static final int[] OP = new int[javaTokenIDsLength
082: + INSTANCEOF + 1];
083:
084: /** Is the operator right associative? */
085: private static final int RIGHT_ASSOCIATIVE = 32;
086:
087: static {
088: OP[JavaTokenContext.EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
089: OP[JavaTokenContext.LT_ID] = 10;
090: OP[JavaTokenContext.GT_ID] = 10;
091: OP[JavaTokenContext.LSHIFT_ID] = 11;
092: OP[JavaTokenContext.RSSHIFT_ID] = 11;
093: OP[JavaTokenContext.RUSHIFT_ID] = 11;
094: OP[JavaTokenContext.PLUS_ID] = 12;
095: OP[JavaTokenContext.MINUS_ID] = 12;
096: OP[JavaTokenContext.MUL_ID] = 13;
097: OP[JavaTokenContext.DIV_ID] = 13;
098: OP[JavaTokenContext.AND_ID] = 8;
099: OP[JavaTokenContext.OR_ID] = 6;
100: OP[JavaTokenContext.XOR_ID] = 7;
101: OP[JavaTokenContext.MOD_ID] = 13;
102: OP[JavaTokenContext.NOT_ID] = 14;
103: OP[JavaTokenContext.NEG_ID] = 14;
104:
105: OP[JavaTokenContext.EQ_EQ_ID] = 9;
106: OP[JavaTokenContext.LT_EQ_ID] = 10;
107: OP[JavaTokenContext.GT_EQ_ID] = 10;
108: OP[JavaTokenContext.LSHIFT_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
109: OP[JavaTokenContext.RSSHIFT_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
110: OP[JavaTokenContext.RUSHIFT_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
111: OP[JavaTokenContext.PLUS_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
112: OP[JavaTokenContext.MINUS_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
113: OP[JavaTokenContext.MUL_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
114: OP[JavaTokenContext.DIV_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
115: OP[JavaTokenContext.AND_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
116: OP[JavaTokenContext.OR_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
117: OP[JavaTokenContext.XOR_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
118: OP[JavaTokenContext.MOD_EQ_ID] = 2 | RIGHT_ASSOCIATIVE;
119: OP[JavaTokenContext.NOT_EQ_ID] = 9;
120:
121: OP[JavaTokenContext.DOT_ID] = 15;
122: OP[JavaTokenContext.COLON_ID] = 3 | RIGHT_ASSOCIATIVE;
123: OP[JavaTokenContext.QUESTION_ID] = 3 | RIGHT_ASSOCIATIVE;
124: OP[JavaTokenContext.LBRACKET_ID] = 15;
125: OP[JavaTokenContext.RBRACKET_ID] = 0; // stop
126: OP[JavaTokenContext.PLUS_PLUS_ID] = 15;
127: OP[JavaTokenContext.MINUS_MINUS_ID] = 15;
128: OP[JavaTokenContext.AND_AND_ID] = 5;
129: OP[JavaTokenContext.OR_OR_ID] = 4;
130:
131: OP[JavaTokenContext.COMMA_ID] = 0; // stop
132: OP[JavaTokenContext.SEMICOLON_ID] = 0; // not-recognized
133: OP[JavaTokenContext.LPAREN_ID] = 16;
134: OP[JavaTokenContext.RPAREN_ID] = 0; // not-recognized
135: OP[JavaTokenContext.LBRACE_ID] = 0; // not-recognized
136: OP[JavaTokenContext.RBRACE_ID] = 0; // not-recognized
137:
138: OP[javaTokenIDsLength + INVALID] = 0;
139: OP[javaTokenIDsLength + CONSTANT] = 1;
140: OP[javaTokenIDsLength + VARIABLE] = 1;
141: OP[javaTokenIDsLength + UNARY_OPERATOR] = 15;
142: OP[javaTokenIDsLength + DOT] = 1;
143: OP[javaTokenIDsLength + DOT_OPEN] = 0; // stop
144: OP[javaTokenIDsLength + ARRAY_OPEN] = 0; // stop
145: OP[javaTokenIDsLength + ARRAY] = 1;
146: OP[javaTokenIDsLength + PARENTHESIS_OPEN] = 0; // stop
147: OP[javaTokenIDsLength + PARENTHESIS] = 1;
148: OP[javaTokenIDsLength + METHOD_OPEN] = 0; // stop
149: OP[javaTokenIDsLength + METHOD] = 1;
150: OP[javaTokenIDsLength + CONSTRUCTOR] = 1;
151: OP[javaTokenIDsLength + CONVERSION] = 1;
152: OP[javaTokenIDsLength + TYPE] = 0; // stop
153: OP[javaTokenIDsLength + NEW] = 0; // stop
154: OP[javaTokenIDsLength + INSTANCEOF] = 10;
155:
156: }
157:
158: private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
159:
160: private JCExpression parent;
161:
162: /** ID of the expression */
163: private int expID;
164:
165: /** Result type */
166: private JCType type;
167:
168: /** Current token count * 3 */
169: private int tokenCountM3;
170:
171: /**
172: * token info blocks containing tokenID token's text and the position of the
173: * token in the document
174: */
175: private Object[] tokenBlocks = EMPTY_OBJECT_ARRAY;
176:
177: /** List of parameters */
178: private ArrayList prmList;
179:
180: JCExpression(int expID) {
181: this .expID = expID;
182: }
183:
184: /** Create empty variable. */
185: static JCExpression createEmptyVariable(int pos) {
186: JCExpression empty = new JCExpression(VARIABLE);
187: empty.addToken(JavaTokenContext.IDENTIFIER, pos, "");
188: return empty;
189: }
190:
191: /**
192: * Return id of the operator or 'new' or 'instance' keywords or -1 for the
193: * rest.
194: */
195: static int getOperatorID(TokenID tokenID) {
196: int id = -1;
197:
198: if (tokenID.getCategory() == JavaTokenContext.OPERATORS) {
199: id = tokenID.getNumericID();
200:
201: } else {
202: switch (tokenID.getNumericID()) {
203: case JavaTokenContext.NEW_ID:
204: id = javaTokenIDsLength + NEW;
205: break;
206:
207: case JavaTokenContext.INSTANCEOF_ID:
208: id = javaTokenIDsLength + INSTANCEOF;
209: break;
210:
211: }
212: }
213:
214: return id;
215: }
216:
217: static int getOperatorID(JCExpression exp) {
218: int expID = (exp != null) ? exp.getExpID() : INVALID;
219: switch (expID) {
220: case OPERATOR:
221: return exp.getTokenID(0).getNumericID();
222: }
223: return javaTokenIDsLength + expID;
224: }
225:
226: static int getOperatorPrecedence(int opID) {
227: return OP[opID] & 31;
228: }
229:
230: static boolean isOperatorRightAssociative(int opID) {
231: return (OP[opID] & RIGHT_ASSOCIATIVE) != 0;
232: }
233:
234: /**
235: * Is the expression a valid type. It can be either datatype or array.
236: */
237: static boolean isValidType(JCExpression exp) {
238: switch (exp.getExpID()) {
239: case ARRAY:
240: if (exp.getParameterCount() == 1) {
241: return isValidType(exp.getParameter(0));
242: }
243: return false;
244:
245: case DOT:
246: int prmCnt = exp.getParameterCount();
247: for (int i = 0; i < prmCnt; i++) {
248: if (exp.getParameter(i).getExpID() != VARIABLE) {
249: return false;
250: }
251: }
252: return true;
253:
254: case TYPE:
255: case VARIABLE:
256: return true;
257: }
258:
259: return false;
260: }
261:
262: /** Get expression ID */
263: public int getExpID() {
264: return expID;
265: }
266:
267: /** Set expression ID */
268: void setExpID(int expID) {
269: this .expID = expID;
270: }
271:
272: public JCExpression getParent() {
273: return parent;
274: }
275:
276: void setParent(JCExpression parent) {
277: this .parent = parent;
278: }
279:
280: public JCType getType() {
281: return type;
282: }
283:
284: void setType(JCType type) {
285: this .type = type;
286: }
287:
288: public int getTokenCount() {
289: return tokenCountM3 / 3;
290: }
291:
292: public String getTokenText(int tokenInd) {
293: tokenInd *= 3;
294: return (String) tokenBlocks[tokenInd + 2];
295: }
296:
297: public int getTokenOffset(int tokenInd) {
298: tokenInd *= 3;
299: return ((Integer) tokenBlocks[tokenInd + 1]).intValue();
300: }
301:
302: public int getTokenLength(int tokenInd) {
303: tokenInd *= 3;
304: return ((String) tokenBlocks[tokenInd + 2]).length();
305: }
306:
307: public TokenID getTokenID(int tokenInd) {
308: tokenInd *= 3;
309: return (TokenID) tokenBlocks[tokenInd];
310: }
311:
312: void addToken(TokenID tokenID, int tokenOffset, String tokenText) {
313: if (tokenCountM3 == tokenBlocks.length) {
314: Object[] tmp = new Object[Math.max(3,
315: tokenBlocks.length * 2)];
316: if (tokenBlocks.length > 0) {
317: System.arraycopy(tokenBlocks, 0, tmp, 0,
318: tokenBlocks.length);
319: }
320: tokenBlocks = tmp;
321: }
322:
323: tokenBlocks[tokenCountM3++] = tokenID;
324: tokenBlocks[tokenCountM3++] = new Integer(tokenOffset);
325: tokenBlocks[tokenCountM3++] = tokenText;
326: }
327:
328: public int getParameterCount() {
329: return (prmList != null) ? prmList.size() : 0;
330: }
331:
332: public JCExpression getParameter(int index) {
333: return (JCExpression) prmList.get(index);
334: }
335:
336: void addParameter(JCExpression prm) {
337: if (prmList == null) {
338: prmList = new ArrayList();
339: }
340: prm.setParent(this );
341: prmList.add(prm);
342: }
343:
344: void swapOperatorParms() {
345: if ((expID == OPERATOR || expID == INSTANCEOF)
346: && getParameterCount() == 2) {
347: JCExpression exp1 = (JCExpression) prmList.remove(0);
348: prmList.add(exp1);
349: exp1.swapOperatorParms();
350: ((JCExpression) prmList.get(0)).swapOperatorParms();
351: }
352: }
353:
354: private static String getIndentString(int indent) {
355: StringBuffer sb = new StringBuffer();
356: while (indent-- > 0) {
357: sb.append(' ');
358: }
359: return sb.toString();
360: }
361:
362: static String getIDName(int expID) {
363: switch (expID) {
364: case CONSTANT:
365: return "CONSTANT"; // NOI18N
366: case VARIABLE:
367: return "VARIABLE"; // NOI18N
368: case OPERATOR:
369: return "OPERATOR"; // NOI18N
370: case UNARY_OPERATOR:
371: return "UNARY_OPERATOR"; // NOI18N
372: case DOT:
373: return "DOT"; // NOI18N
374: case DOT_OPEN:
375: return "DOT_OPEN"; // NOI18N
376: case ARRAY:
377: return "ARRAY"; // NOI18N
378: case ARRAY_OPEN:
379: return "ARRAY_OPEN"; // NOI18N
380: case PARENTHESIS_OPEN:
381: return "PARENTHESIS_OPEN"; // NOI18N
382: case PARENTHESIS:
383: return "PARENTHESIS"; // NOI18N
384: case METHOD_OPEN:
385: return "METHOD_OPEN"; // NOI18N
386: case METHOD:
387: return "METHOD"; // NOI18N
388: case CONSTRUCTOR:
389: return "CONSTRUCTOR"; // NOI18N
390: case CONVERSION:
391: return "CONVERSION"; // NOI18N
392: case TYPE:
393: return "TYPE"; // NOI18N
394: case NEW:
395: return "NEW"; // NOI18N
396: case INSTANCEOF:
397: return "INSTANCEOF"; // NOI18N
398: default:
399: return "Unknown expID " + expID; // NOI18N
400: }
401: }
402:
403: public String toString(int indent) {
404: String indentStr = getIndentString(indent);
405: StringBuffer sb = new StringBuffer();
406: sb.append("expID=" + getIDName(expID)); // NOI18N
407:
408: if (type != null) {
409: sb.append(", result type="); // NOI18N
410: sb.append(type);
411: }
412:
413: // Debug tokens
414: int tokenCnt = getTokenCount();
415: sb.append(", token count="); // NOI18N
416: sb.append(tokenCnt);
417: if (tokenCnt > 0) {
418: for (int i = 0; i < tokenCountM3;) {
419: TokenID tokenID = (TokenID) tokenBlocks[i++];
420: int tokenOffset = ((Integer) tokenBlocks[i++])
421: .intValue();
422: String tokenText = (String) tokenBlocks[i++];
423: sb.append(", token" + (i / 3 - 1) + "='"
424: + EditorDebug.debugString(tokenText) + "'"); // NOI18N
425: }
426: }
427:
428: // Debug parameters
429: int parmCnt = getParameterCount();
430: sb.append(", parm count="); // NOI18N
431: sb.append(parmCnt);
432: if (parmCnt > 0) {
433: for (int i = 0; i < parmCnt; i++) {
434: sb.append('\n');
435: sb.append(indentStr);
436: sb.append("parm" + i + "=["
437: + getParameter(i).toString(indent + 2) + "]"); // NOI18N
438: }
439: }
440: return sb.toString();
441: }
442:
443: public String toString() {
444: return toString(0);
445: }
446: }
|