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.ast;
019:
020: import org.mvel.CompileException;
021: import static org.mvel.DataConversion.convert;
022: import static org.mvel.MVEL.eval;
023: import org.mvel.ParserContext;
024: import org.mvel.PropertyAccessor;
025: import static org.mvel.compiler.AbstractParser.getCurrentThreadParserContext;
026: import org.mvel.compiler.Accessor;
027: import org.mvel.compiler.ExecutableStatement;
028: import org.mvel.integration.VariableResolverFactory;
029: import org.mvel.optimizers.AccessorOptimizer;
030: import static org.mvel.optimizers.OptimizerFactory.getThreadAccessorOptimizer;
031: import org.mvel.util.ArrayTools;
032: import static org.mvel.util.ArrayTools.findFirst;
033: import org.mvel.util.ParseTools;
034: import static org.mvel.util.ParseTools.*;
035: import static org.mvel.util.PropertyTools.getBaseComponentType;
036:
037: import java.io.Serializable;
038: import static java.lang.Character.isWhitespace;
039: import static java.lang.Thread.currentThread;
040: import java.lang.reflect.Array;
041: import java.lang.reflect.Constructor;
042: import java.util.Iterator;
043: import java.util.LinkedList;
044:
045: /**
046: * @author Christopher Brock
047: */
048: @SuppressWarnings({"ManualArrayCopy"})
049: public class NewObjectNode extends ASTNode {
050: private transient Accessor newObjectOptimizer;
051: private String className;
052:
053: private ArraySize[] arraySize;
054: private ExecutableStatement[] compiledArraySize;
055:
056: public NewObjectNode(char[] expr, int fields) {
057: this .name = expr;
058: updateClassName(this .fields = fields);
059:
060: if ((fields & COMPILE_IMMEDIATE) != 0) {
061: ParserContext pCtx = getCurrentThreadParserContext();
062: if (pCtx != null && pCtx.hasImport(className)) {
063: egressType = pCtx.getImport(className);
064: } else {
065: try {
066: egressType = currentThread()
067: .getContextClassLoader().loadClass(
068: className);
069: } catch (ClassNotFoundException e) {
070: // do nothing.
071: }
072: }
073:
074: if (egressType != null) {
075: rewriteClassReferenceToFQCN(fields);
076: if (arraySize != null) {
077: try {
078: egressType = findClass(null, repeatChar('[',
079: arraySize.length)
080: + "L" + egressType.getName() + ";");
081: } catch (Exception e) {
082: e.printStackTrace();
083: // for now, don't handle this.
084: }
085: }
086: }
087:
088: }
089:
090: }
091:
092: private void rewriteClassReferenceToFQCN(int fields) {
093: String FQCN = egressType.getName();
094:
095: if (className.indexOf('.') == -1) {
096: int idx = ArrayTools.findFirst('(', name);
097:
098: char[] fqcn = FQCN.toCharArray();
099:
100: if (idx == -1) {
101: this .name = new char[idx = fqcn.length];
102: for (int i = 0; i < idx; i++)
103: this .name[i] = fqcn[i];
104: } else {
105: char[] newName = new char[fqcn.length
106: + (name.length - idx)];
107:
108: for (int i = 0; i < fqcn.length; i++)
109: newName[i] = fqcn[i];
110:
111: int i0 = name.length - idx;
112: int i1 = fqcn.length;
113: for (int i = 0; i < i0; i++)
114: newName[i + i1] = name[i + idx];
115:
116: this .name = newName;
117: }
118: updateClassName(fields);
119: }
120: }
121:
122: private void updateClassName(int fields) {
123: int endRange = findFirst('(', name);
124: if (endRange == -1) {
125: if ((endRange = findFirst('[', name)) != -1) {
126: className = new String(name, 0, endRange);
127: int to;
128:
129: LinkedList<char[]> sizes = new LinkedList<char[]>();
130:
131: while (endRange < name.length) {
132: while (endRange < name.length
133: && isWhitespace(name[endRange]))
134: endRange++;
135:
136: if (endRange == name.length)
137: break;
138:
139: if (name[endRange] != '[')
140: throw new CompileException(
141: "unexpected token in contstructor",
142: name, endRange);
143:
144: to = balancedCapture(name, endRange, '[');
145:
146: sizes.add(ParseTools.subset(name, ++endRange, to
147: - endRange));
148: endRange = to + 1;
149: }
150:
151: Iterator<char[]> iter = sizes.iterator();
152: arraySize = new ArraySize[sizes.size()];
153: for (int i = 0; i < arraySize.length; i++)
154: arraySize[i] = new ArraySize(iter.next());
155:
156: if ((fields & COMPILE_IMMEDIATE) != 0) {
157: compiledArraySize = new ExecutableStatement[arraySize.length];
158: for (int i = 0; i < compiledArraySize.length; i++)
159: compiledArraySize[i] = (ExecutableStatement) subCompileExpression(arraySize[i].value);
160: }
161:
162: return;
163: }
164:
165: className = new String(name);
166: } else {
167: className = new String(name, 0, endRange);
168: }
169:
170: }
171:
172: public Object getReducedValueAccelerated(Object ctx,
173: Object this Value, VariableResolverFactory factory) {
174: if (newObjectOptimizer == null) {
175: if (egressType == null) {
176: /**
177: * This means we couldn't resolve the type at the time this AST node was created, which means
178: * we have to attempt runtime resolution.
179: */
180:
181: if (factory != null && factory.isResolveable(className)) {
182: try {
183: egressType = (Class) factory
184: .getVariableResolver(className)
185: .getValue();
186: rewriteClassReferenceToFQCN(COMPILE_IMMEDIATE);
187:
188: if (arraySize != null) {
189: try {
190: egressType = findClass(factory,
191: repeatChar('[',
192: arraySize.length)
193: + "L"
194: + egressType.getName()
195: + ";");
196: } catch (Exception e) {
197: // for now, don't handle this.
198: }
199: }
200:
201: } catch (ClassCastException e) {
202: throw new CompileException(
203: "cannot construct object: " + className
204: + " is not a class reference",
205: e);
206: }
207: }
208: }
209:
210: Class cls = Class[].class;
211:
212: if (arraySize != null) {
213: return (newObjectOptimizer = new NewObjectArray(
214: getBaseComponentType(egressType
215: .getComponentType()), compiledArraySize))
216: .getValue(ctx, this Value, factory);
217: }
218:
219: AccessorOptimizer optimizer = getThreadAccessorOptimizer();
220: newObjectOptimizer = optimizer.optimizeObjectCreation(name,
221: ctx, this Value, factory);
222:
223: /**
224: * Check to see if the optimizer actually produced the object during optimization. If so,
225: * we return that value now.
226: */
227: if (optimizer.getResultOptPass() != null) {
228: egressType = optimizer.getEgressType();
229: return optimizer.getResultOptPass();
230: }
231: }
232:
233: return newObjectOptimizer.getValue(ctx, this Value, factory);
234: }
235:
236: private static final Class[] EMPTYCLS = new Class[0];
237:
238: public Object getReducedValue(Object ctx, Object this Value,
239: VariableResolverFactory factory) {
240:
241: try {
242: if (arraySize != null) {
243: Class cls = findClass(factory, className);
244:
245: int[] s = new int[arraySize.length];
246: for (int i = 0; i < s.length; i++) {
247: s[i] = convert(eval(arraySize[i].value, ctx,
248: factory), Integer.class);
249: }
250:
251: return Array.newInstance(cls, s);
252: } else {
253: String[] cnsRes = captureContructorAndResidual(name);
254: String[] constructorParms = parseMethodOrConstructor(cnsRes[0]
255: .toCharArray());
256:
257: if (constructorParms != null) {
258: Class cls = findClass(factory, new String(subset(
259: name, 0, findFirst('(', name))));
260:
261: Object[] parms = new Object[constructorParms.length];
262: for (int i = 0; i < constructorParms.length; i++) {
263: parms[i] = eval(constructorParms[i], ctx,
264: factory);
265: }
266:
267: Constructor cns = getBestConstructorCanadidate(
268: parms, cls);
269:
270: if (cns == null)
271: throw new CompileException(
272: "unable to find constructor for: "
273: + cls.getName());
274:
275: for (int i = 0; i < parms.length; i++) {
276: //noinspection unchecked
277: parms[i] = convert(parms[i], cns
278: .getParameterTypes()[i]);
279: }
280:
281: if (cnsRes.length > 1) {
282: return PropertyAccessor
283: .get(cnsRes[1], cns.newInstance(parms),
284: factory, this Value);
285: } else {
286: return cns.newInstance(parms);
287: }
288: }
289:
290: else {
291: Constructor<?> cns = currentThread()
292: .getContextClassLoader().loadClass(
293: new String(name)).getConstructor(
294: EMPTYCLS);
295:
296: if (cnsRes.length > 1) {
297: return PropertyAccessor.get(cnsRes[1], cns
298: .newInstance(), factory, this Value);
299: } else {
300: return cns.newInstance();
301: }
302: }
303: }
304: } catch (ClassNotFoundException e) {
305: throw new CompileException("unable to resolve class: "
306: + e.getMessage());
307: } catch (NoSuchMethodException e) {
308: throw new CompileException("cannot resolve constructor: "
309: + e.getMessage());
310: } catch (Exception e) {
311: throw new CompileException("could not instantiate class: "
312: + e.getMessage());
313: }
314: }
315:
316: public Accessor getNewObjectOptimizer() {
317: return newObjectOptimizer;
318: }
319:
320: public static class ArraySize implements Serializable {
321: public ArraySize(char[] value) {
322: this .value = value;
323: }
324:
325: public char[] value;
326: }
327:
328: public static class NewObjectArray implements Accessor,
329: Serializable {
330: private ExecutableStatement[] sizes;
331: private Class arrayType;
332:
333: public NewObjectArray(Class arrayType,
334: ExecutableStatement[] sizes) {
335: this .arrayType = arrayType;
336: this .sizes = sizes;
337: }
338:
339: public Object getValue(Object ctx, Object elCtx,
340: VariableResolverFactory variableFactory) {
341: int[] s = new int[sizes.length];
342: for (int i = 0; i < s.length; i++) {
343: s[i] = convert(sizes[i].getValue(ctx, elCtx,
344: variableFactory), Integer.class);
345: }
346:
347: return Array.newInstance(arrayType, s);
348: }
349:
350: public Object setValue(Object ctx, Object elCtx,
351: VariableResolverFactory variableFactory, Object value) {
352: return null;
353: }
354: }
355: }
|