001: /**
002: * MVEL (The MVFLEX Expression Language)
003: *
004: * Copyright (C) 2007 Christopher Brock, MVFLEX/Valhalla Project and the Codehaus
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: */package org.mvel;
019:
020: import static org.mvel.PropertyAccessor.get;
021: import org.mvel.integration.VariableResolverFactory;
022: import org.mvel.optimizers.AccessorOptimizer;
023: import org.mvel.optimizers.OptimizationNotSupported;
024: import static org.mvel.optimizers.OptimizerFactory.*;
025: import static org.mvel.util.ArrayTools.findFirst;
026: import static org.mvel.util.ParseTools.handleEscapeSequence;
027: import static org.mvel.util.PropertyTools.handleNumericConversion;
028: import static org.mvel.util.PropertyTools.isNumber;
029: import org.mvel.util.ThisLiteral;
030:
031: import java.io.Serializable;
032: import static java.lang.Class.forName;
033: import static java.lang.System.arraycopy;
034: import java.lang.reflect.Method;
035:
036: public class ASTNode implements Cloneable, Serializable {
037: public static final int LITERAL = 1;
038: public static final int DEEP_PROPERTY = 1 << 1;
039: public static final int OPERATOR = 1 << 2;
040: public static final int IDENTIFIER = 1 << 3;
041: public static final int COMPILE_IMMEDIATE = 1 << 4;
042: public static final int NUMERIC = 1 << 5;
043: public static final int NEGATION = 1 << 6;
044: public static final int INVERT = 1 << 8;
045: public static final int FOLD = 1 << 9;
046: public static final int METHOD = 1 << 10;
047: public static final int ASSIGN = 1 << 11;
048: public static final int LOOKAHEAD = 1 << 12;
049: public static final int COLLECTION = 1 << 13;
050: public static final int THISREF = 1 << 14;
051: public static final int INLINE_COLLECTION = 1 << 15;
052: public static final int STR_LITERAL = 1 << 16;
053: public static final int BLOCK = 1 << 17;
054: public static final int BLOCK_IF = 1 << 18;
055: public static final int BLOCK_FOREACH = 1 << 19;
056: public static final int BLOCK_WITH = 1 << 20;
057: public static final int TYPED = 1 << 21;
058: public static final int RETURN = 1 << 22;
059: public static final int INTEGER32 = 1 << 23;
060:
061: protected int firstUnion;
062: protected int endOfName;
063:
064: protected int fields = 0;
065:
066: protected Class egressType;
067: protected char[] name;
068: protected String nameCache;
069:
070: protected Object literal;
071: protected Accessor accessor;
072:
073: protected int cursorPosition;
074: public ASTNode nextASTNode;
075: protected boolean discard;
076:
077: private int intRegister;
078:
079: public ASTNode() {
080: }
081:
082: public ASTNode(char[] expr, int start, int end, int fields) {
083: this .cursorPosition = start;
084: this .fields = fields;
085:
086: char[] name = new char[end - start];
087: arraycopy(expr, start, name, 0, end - start);
088: setName(name);
089: }
090:
091: public ASTNode(char[] expr, int fields) {
092: this .fields = fields;
093: this .name = expr;
094: }
095:
096: public ASTNode(int fields, Object literalValue) {
097: this .fields = fields;
098: this .literal = literalValue;
099: }
100:
101: protected String getAbsoluteRootElement() {
102: if ((fields & (DEEP_PROPERTY | COLLECTION)) != 0) {
103: return new String(name, 0, getAbsoluteFirstPart());
104: }
105: return null;
106: }
107:
108: public Class getEgressType() {
109: return egressType;
110: }
111:
112: public void setEgressType(Class egressType) {
113: this .egressType = egressType;
114: }
115:
116: protected String getAbsoluteRemainder() {
117: return (fields & COLLECTION) != 0 ? new String(name, endOfName,
118: name.length - endOfName)
119: : ((fields & DEEP_PROPERTY) != 0 ? new String(name,
120: firstUnion + 1, name.length - firstUnion - 1)
121: : null);
122: }
123:
124: public char[] getNameAsArray() {
125: return name;
126: }
127:
128: private int getAbsoluteFirstPart() {
129: if ((fields & COLLECTION) != 0) {
130: if (firstUnion < 0 || endOfName < firstUnion)
131: return endOfName;
132: else
133: return firstUnion;
134: } else if ((fields & DEEP_PROPERTY) != 0) {
135: return firstUnion;
136: } else {
137: return -1;
138: }
139:
140: }
141:
142: public String getAbsoluteName() {
143: if ((fields & (COLLECTION | DEEP_PROPERTY)) != 0) {
144: return new String(name, 0, getAbsoluteFirstPart());
145: } else {
146: return getName();
147: }
148: }
149:
150: public String getName() {
151: if (nameCache != null)
152: return nameCache;
153: else if (name != null)
154: return nameCache = new String(name);
155: return "";
156: }
157:
158: public Object getLiteralValue() {
159: return literal;
160: }
161:
162: public void setLiteralValue(Object literal) {
163: this .literal = literal;
164: }
165:
166: public Object getReducedValueAccelerated(Object ctx,
167: Object this Value, VariableResolverFactory factory) {
168: if ((fields & (LITERAL)) != 0) {
169: // if ((fields & THISREF) != 0)
170: // return thisValue;
171: // else
172: return literal;
173: }
174: try {
175: return valRet(accessor.getValue(ctx, this Value, factory));
176: } catch (NullPointerException e) {
177: //todo: FIX JIT, so we don't have to force safe reflective mode.
178: AccessorOptimizer optimizer;
179: Object retVal = null;
180:
181: if ((fields & FOLD) != 0) {
182: optimizer = getAccessorCompiler(SAFE_REFLECTIVE);
183: accessor = optimizer.optimizeFold(name, ctx, this Value,
184: factory);
185: retVal = accessor.getValue(ctx, this Value, factory);
186: } else {
187: try {
188: accessor = (optimizer = getThreadAccessorOptimizer())
189: .optimizeAccessor(name, ctx, this Value,
190: factory, true);
191: } catch (OptimizationNotSupported ne) {
192: accessor = (optimizer = getAccessorCompiler(SAFE_REFLECTIVE))
193: .optimizeAccessor(name, ctx, this Value,
194: factory, true);
195: }
196: }
197:
198: if (accessor == null)
199: throw new OptimizationFailure("failed optimization", e);
200:
201: if (retVal == null) {
202: retVal = optimizer.getResultOptPass();
203: }
204:
205: if (egressType == null) {
206: egressType = optimizer.getEgressType();
207: }
208:
209: return valRet(retVal);
210: }
211: }
212:
213: public Object getReducedValue(Object ctx, Object this Value,
214: VariableResolverFactory factory) {
215: String s;
216: if ((fields & (LITERAL)) != 0) {
217: if ((fields & THISREF) != 0) {
218: return this Value;
219: } else {
220: return literal;
221: }
222: } else if ((fields & FOLD) != 0) {
223: if (accessor == null) {
224: AccessorOptimizer optimizer = getAccessorCompiler(SAFE_REFLECTIVE);
225: accessor = optimizer.optimizeFold(name, ctx, this Value,
226: factory);
227:
228: return optimizer.getResultOptPass();
229: }
230: }
231:
232: if ((fields & DEEP_PROPERTY) != 0) {
233: /**
234: * The token is a DEEP PROPERTY (meaning it contains unions) in which case we need to traverse an object
235: * graph.
236: */
237: if (AbstractParser.LITERALS
238: .containsKey(s = getAbsoluteRootElement())) {
239: /**
240: * The root of the DEEP PROPERTY is a literal.
241: */
242: Object literal = AbstractParser.LITERALS.get(s);
243: if (literal == ThisLiteral.class)
244: literal = this Value;
245:
246: return valRet(get(getAbsoluteRemainder(), literal,
247: factory, this Value));
248: } else if (factory != null && factory.isResolveable(s)) {
249: /**
250: * The root of the DEEP PROPERTY is a local or global var.
251: */
252: return valRet(get(name, ctx, factory, this Value));
253:
254: } else if (ctx != null) {
255: /**
256: * We didn't resolve the root, yet, so we assume that if we have a VROOT then the property must be
257: * accessible as a field of the VROOT.
258: */
259:
260: try {
261: return valRet(get(name, ctx, factory, this Value));
262: } catch (PropertyAccessException e) {
263: /**
264: * No luck. Make a last-ditch effort to resolve this as a static-class reference.
265: */
266: Object sa = tryStaticAccess(ctx, factory);
267: if (sa == null)
268: throw e;
269:
270: /**
271: * Since this clearly is a class literal, we change the nature of theis node to
272: * make it a literal to prevent re-evaluation.
273: */
274: literal = valRet(sa);
275: fields |= LITERAL;
276:
277: return literal;
278: }
279: }
280: } else {
281: if (factory != null
282: && factory.isResolveable(s = getAbsoluteName())) {
283: /**
284: * The token is a local or global var.
285: */
286:
287: if (isCollection()) {
288: return valRet(get(new String(name, endOfName,
289: name.length - endOfName), factory
290: .getVariableResolver(s).getValue(),
291: factory, this Value));
292: }
293:
294: return valRet(factory.getVariableResolver(s).getValue());
295: } else if (ctx != null) {
296: /**
297: * Check to see if the var exists in the VROOT.
298: */
299: try {
300: return valRet(get(name, ctx, factory, this Value));
301: } catch (RuntimeException e) {
302: e.printStackTrace();
303: throw new UnresolveablePropertyException(this );
304: }
305: } else {
306: if (isOperator()) {
307: throw new CompileException("incomplete statement");
308: } else {
309: int mBegin = findFirst('(', name);
310: if (mBegin != -1) {
311: if (factory.isResolveable(s = new String(name,
312: 0, mBegin))) {
313: Method m = (Method) factory
314: .getVariableResolver(s).getValue();
315:
316: return valRet(get(m.getName()
317: + new String(name, mBegin,
318: name.length - mBegin), m
319: .getDeclaringClass(), factory,
320: this Value));
321: }
322: }
323: }
324:
325: throw new UnresolveablePropertyException(this );
326: }
327: }
328:
329: Object sa = tryStaticAccess(ctx, factory);
330: if (sa == null)
331: throw new UnresolveablePropertyException(this );
332: return valRet(sa);
333:
334: }
335:
336: protected Object valRet(final Object value) {
337: if ((fields & NEGATION) != 0) {
338: try {
339: return !((Boolean) value);
340: } catch (Exception e) {
341: throw new CompileException(
342: "illegal negation of non-boolean value");
343: }
344: } else if ((fields & INVERT) != 0) {
345: try {
346: return ~((Integer) value);
347: } catch (Exception e) {
348: throw new CompileException(
349: "bitwise (~) operator can only be applied to integers");
350: }
351: }
352:
353: return value;
354: }
355:
356: protected Object tryStaticAccess(Object this Ref,
357: VariableResolverFactory factory) {
358: try {
359: /**
360: * Try to resolve this *smartly* as a static class reference.
361: *
362: * This starts at the end of the token and starts to step backwards to figure out whether
363: * or not this may be a static class reference. We search for method calls simply by
364: * inspecting for ()'s. The first union area we come to where no brackets are present is our
365: * test-point for a class reference. If we find a class, we pass the reference to the
366: * property accessor along with trailing methods (if any).
367: *
368: */
369: boolean meth = false;
370: int depth = 0;
371: int last = name.length;
372: for (int i = last - 1; i > 0; i--) {
373: switch (name[i]) {
374: case '.':
375: if (!meth) {
376: try {
377: return get(new String(name, last,
378: name.length - last),
379: forName(new String(name, 0, last)),
380: factory, this Ref);
381: } catch (ClassNotFoundException e) {
382: return get(new String(name, i + 1,
383: name.length - i - 1),
384: forName(new String(name, 0, i)),
385: factory, this Ref);
386: }
387: }
388: meth = false;
389: last = i;
390: break;
391: case ')':
392: if (depth++ == 0)
393: meth = true;
394: break;
395: case '(':
396: depth--;
397: break;
398: }
399: }
400: } catch (Exception cnfe) {
401: // do nothing.
402: }
403:
404: return null;
405: }
406:
407: @SuppressWarnings({"SuspiciousMethodCalls"})
408: protected void setName(char[] name) {
409: if ((fields & STR_LITERAL) != 0) {
410: fields |= LITERAL;
411:
412: int escapes = 0;
413: for (int i = 0; i < name.length; i++) {
414: if (name[i] == '\\') {
415: name[i++] = 0;
416: name[i] = handleEscapeSequence(name[i]);
417: escapes++;
418: }
419: }
420:
421: char[] processedEscapeString = new char[name.length
422: - escapes];
423: int cursor = 0;
424: for (char aName : name) {
425: if (aName == 0) {
426: continue;
427: }
428: processedEscapeString[cursor++] = aName;
429: }
430:
431: this .literal = new String(this .name = processedEscapeString);
432:
433: } else {
434: this .literal = new String(this .name = name);
435: }
436:
437: if ((fields & (LITERAL)) != 0) {
438: // return;
439: } else if (AbstractParser.LITERALS.containsKey(literal)) {
440: fields |= LITERAL | IDENTIFIER;
441: if ((literal = AbstractParser.LITERALS.get(literal)) == ThisLiteral.class)
442: fields |= THISREF;
443: if (literal != null)
444: egressType = literal.getClass();
445: } else if (AbstractParser.OPERATORS.containsKey(literal)) {
446: fields |= OPERATOR;
447: literal = AbstractParser.OPERATORS.get(literal);
448: egressType = literal.getClass();
449: return;
450: } else if (isNumber(name)) {
451: fields |= NUMERIC | LITERAL | IDENTIFIER;
452: literal = handleNumericConversion(name);
453: egressType = literal.getClass();
454:
455: if ((fields & INVERT) != 0) {
456: try {
457: literal = ~((Integer) literal);
458: } catch (ClassCastException e) {
459: throw new CompileException(
460: "bitwise (~) operator can only be applied to integers");
461: }
462: }
463:
464: if (literal instanceof Integer) {
465: intRegister = (Integer) literal;
466: fields |= INTEGER32;
467:
468: }
469:
470: return;
471: } else if ((fields & INLINE_COLLECTION) != 0) {
472: return;
473: } else if ((firstUnion = findFirst('.', name)) > 0) {
474: if ((fields & METHOD) != 0) {
475: if (firstUnion < findFirst('(', name)) {
476: fields |= DEEP_PROPERTY | IDENTIFIER;
477: } else {
478: fields |= IDENTIFIER;
479: }
480: } else {
481: fields |= DEEP_PROPERTY | IDENTIFIER;
482: }
483:
484: } else {
485: fields |= IDENTIFIER;
486: }
487:
488: if ((endOfName = findFirst('[', name)) > 0)
489: fields |= COLLECTION;
490:
491: }
492:
493: public void setAccessor(Accessor accessor) {
494: this .accessor = accessor;
495: }
496:
497: public boolean isIdentifier() {
498: return (fields & IDENTIFIER) != 0;
499: }
500:
501: public boolean isLiteral() {
502: return (fields & LITERAL) != 0;
503: }
504:
505: public boolean isThisVal() {
506: return (fields & THISREF) != 0;
507: }
508:
509: public boolean isOperator() {
510: return (fields & OPERATOR) != 0;
511: }
512:
513: public boolean isOperator(Integer operator) {
514: return (fields & OPERATOR) != 0 && operator.equals(literal);
515: }
516:
517: public Integer getOperator() {
518: return (Integer) literal;
519: }
520:
521: protected boolean isCollection() {
522: return (fields & COLLECTION) != 0;
523: }
524:
525: public boolean isAssignment() {
526: return ((fields & ASSIGN) != 0);
527: }
528:
529: public boolean isDeepProperty() {
530: return ((fields & DEEP_PROPERTY) != 0);
531: }
532:
533: public void setAsLiteral() {
534: fields |= LITERAL;
535: }
536:
537: public int getCursorPosition() {
538: return cursorPosition;
539: }
540:
541: public void setCursorPosition(int cursorPosition) {
542: this .cursorPosition = cursorPosition;
543: }
544:
545: public boolean isDiscard() {
546: return discard;
547: }
548:
549: public void setDiscard(boolean discard) {
550: this .discard = discard;
551: }
552:
553: public boolean isDebuggingSymbol() {
554: return this .fields == -1;
555: }
556:
557: public int getIntRegister() {
558: return intRegister;
559: }
560:
561: public void setIntRegister(int intRegister) {
562: this .intRegister = intRegister;
563: }
564:
565: public int getFields() {
566: return fields;
567: }
568:
569: public Accessor getAccessor() {
570: return accessor;
571: }
572: }
|