001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.expression.jexl;
018:
019: import java.util.Enumeration;
020: import java.util.Iterator;
021:
022: import org.apache.commons.jexl.util.introspection.Info;
023: import org.apache.commons.jexl.util.introspection.UberspectImpl;
024: import org.apache.commons.jexl.util.introspection.VelMethod;
025: import org.apache.commons.jexl.util.introspection.VelPropertyGet;
026: import org.apache.commons.jexl.util.introspection.VelPropertySet;
027: import org.apache.commons.lang.StringUtils;
028: import org.mozilla.javascript.Context;
029: import org.mozilla.javascript.Function;
030: import org.mozilla.javascript.JavaScriptException;
031: import org.mozilla.javascript.NativeArray;
032: import org.mozilla.javascript.NativeJavaClass;
033: import org.mozilla.javascript.ScriptRuntime;
034: import org.mozilla.javascript.Scriptable;
035: import org.mozilla.javascript.ScriptableObject;
036: import org.mozilla.javascript.Undefined;
037: import org.mozilla.javascript.Wrapper;
038:
039: /**
040: * Jexl Introspector that supports Rhino JavaScript objects
041: * as well as Java Objects.
042: *
043: * @version $Id: JSIntrospector.java 449189 2006-09-23 06:52:29Z crossley $
044: */
045: public class JSIntrospector extends UberspectImpl {
046:
047: static class JSMethod implements VelMethod {
048:
049: Scriptable scope;
050: String name;
051:
052: public JSMethod(Scriptable scope, String name) {
053: this .scope = scope;
054: this .name = name;
055: }
056:
057: public Object invoke(Object this Arg, Object[] args)
058: throws Exception {
059: Context cx = Context.enter();
060: try {
061: Object result;
062: Scriptable this Obj = !(this Arg instanceof Scriptable) ? Context
063: .toObject(this Arg, scope)
064: : (Scriptable) this Arg;
065: result = ScriptableObject.getProperty(this Obj, name);
066: Object[] newArgs = null;
067: if (args != null) {
068: newArgs = new Object[args.length];
069: int len = args.length;
070: for (int i = 0; i < len; i++) {
071: newArgs[i] = args[i];
072: if (args[i] != null
073: && !(args[i] instanceof Number)
074: && !(args[i] instanceof Boolean)
075: && !(args[i] instanceof String)
076: && !(args[i] instanceof Scriptable)) {
077: newArgs[i] = Context.toObject(args[i],
078: scope);
079: }
080: }
081: }
082: result = ScriptRuntime.call(cx, result, this Obj,
083: newArgs, scope);
084: if (result == Undefined.instance
085: || result == Scriptable.NOT_FOUND) {
086: result = null;
087: } else if (!(result instanceof NativeJavaClass)) {
088: while (result instanceof Wrapper) {
089: result = ((Wrapper) result).unwrap();
090: }
091: }
092: return result;
093: } catch (JavaScriptException e) {
094: throw new java.lang.reflect.InvocationTargetException(e);
095: } finally {
096: Context.exit();
097: }
098: }
099:
100: public boolean isCacheable() {
101: return false;
102: }
103:
104: public String getMethodName() {
105: return name;
106: }
107:
108: public Class getReturnType() {
109: return Object.class;
110: }
111:
112: }
113:
114: static class JSPropertyGet implements VelPropertyGet {
115:
116: Scriptable scope;
117: String name;
118:
119: public JSPropertyGet(Scriptable scope, String name) {
120: this .scope = scope;
121: this .name = name;
122: }
123:
124: public Object invoke(Object this Arg) throws Exception {
125: Context cx = Context.enter();
126: try {
127: Scriptable this Obj = !(this Arg instanceof Scriptable) ? Context
128: .toObject(this Arg, scope)
129: : (Scriptable) this Arg;
130: Object result = ScriptableObject.getProperty(this Obj,
131: name);
132: if (result == Scriptable.NOT_FOUND) {
133: result = ScriptableObject.getProperty(this Obj,
134: "get" + StringUtils.capitalize(name));
135: if (result != Scriptable.NOT_FOUND
136: && result instanceof Function) {
137: try {
138: result = ((Function) result).call(cx,
139: ScriptableObject
140: .getTopLevelScope(this Obj),
141: this Obj, new Object[] {});
142: } catch (JavaScriptException exc) {
143: exc.printStackTrace();
144: result = null;
145: }
146: }
147: }
148: if (result == Scriptable.NOT_FOUND
149: || result == Undefined.instance) {
150: result = null;
151: } else if (result instanceof Wrapper
152: && !(result instanceof NativeJavaClass)) {
153: result = ((Wrapper) result).unwrap();
154: }
155: return result;
156: } finally {
157: Context.exit();
158: }
159: }
160:
161: public boolean isCacheable() {
162: return false;
163: }
164:
165: public String getMethodName() {
166: return name;
167: }
168: }
169:
170: static class JSPropertySet implements VelPropertySet {
171:
172: Scriptable scope;
173: String name;
174:
175: public JSPropertySet(Scriptable scope, String name) {
176: this .scope = scope;
177: this .name = name;
178: }
179:
180: public Object invoke(Object this Arg, Object rhs)
181: throws Exception {
182: Context.enter();
183: try {
184: Scriptable this Obj;
185: Object arg = rhs;
186: if (!(this Arg instanceof Scriptable)) {
187: this Obj = Context.toObject(this Arg, scope);
188: } else {
189: this Obj = (Scriptable) this Arg;
190: }
191: if (arg != null && !(arg instanceof Number)
192: && !(arg instanceof Boolean)
193: && !(arg instanceof String)
194: && !(arg instanceof Scriptable)) {
195: arg = Context.toObject(arg, scope);
196: }
197: ScriptableObject.putProperty(this Obj, name, arg);
198: return rhs;
199: } finally {
200: Context.exit();
201: }
202: }
203:
204: public boolean isCacheable() {
205: return false;
206: }
207:
208: public String getMethodName() {
209: return name;
210: }
211: }
212:
213: public static class NativeArrayIterator implements Iterator {
214:
215: NativeArray arr;
216: int index;
217:
218: public NativeArrayIterator(NativeArray arr) {
219: this .arr = arr;
220: this .index = 0;
221: }
222:
223: public boolean hasNext() {
224: return index < (int) arr.jsGet_length();
225: }
226:
227: public Object next() {
228: Context.enter();
229: try {
230: Object result = arr.get(index++, arr);
231: if (result == Undefined.instance
232: || result == Scriptable.NOT_FOUND) {
233: result = null;
234: } else {
235: if (!(result instanceof NativeJavaClass)) {
236: while (result instanceof Wrapper) {
237: result = ((Wrapper) result).unwrap();
238: }
239: }
240: }
241: return result;
242: } finally {
243: Context.exit();
244: }
245: }
246:
247: public void remove() {
248: arr.delete(index);
249: }
250: }
251:
252: static class ScriptableIterator implements Iterator {
253:
254: Scriptable scope;
255: Object[] ids;
256: int index;
257:
258: public ScriptableIterator(Scriptable scope) {
259: this .scope = scope;
260: this .ids = scope.getIds();
261: this .index = 0;
262: }
263:
264: public boolean hasNext() {
265: return index < ids.length;
266: }
267:
268: public Object next() {
269: Context.enter();
270: try {
271: Object result = ScriptableObject.getProperty(scope,
272: ids[index++].toString());
273: if (result == Undefined.instance
274: || result == Scriptable.NOT_FOUND) {
275: result = null;
276: } else if (!(result instanceof NativeJavaClass)) {
277: while (result instanceof Wrapper) {
278: result = ((Wrapper) result).unwrap();
279: }
280: }
281: return result;
282: } finally {
283: Context.exit();
284: }
285: }
286:
287: public void remove() {
288: Context.enter();
289: try {
290: scope.delete(ids[index].toString());
291: } finally {
292: Context.exit();
293: }
294: }
295: }
296:
297: public Iterator getIterator(Object obj, Info i) throws Exception {
298: if (!(obj instanceof Scriptable)) {
299: // support Enumeration
300: /*
301: Booth Enumeration and Iterator are supported in
302: Uberspect. The only difference is that they emit a
303: rather long warning message to commons logging, telling
304: that Enumerations and Iterator not are resettable and
305: cannot be reused.
306: */
307: if (obj instanceof Enumeration) {
308: final Enumeration e = (Enumeration) obj;
309: return new Iterator() {
310:
311: public boolean hasNext() {
312: return e.hasMoreElements();
313: }
314:
315: public Object next() {
316: return e.nextElement();
317: }
318:
319: public void remove() {
320: // no action
321: }
322:
323: };
324: }
325: if (obj instanceof Iterator) {
326: // support Iterator
327: return (Iterator) obj;
328: }
329: return super .getIterator(obj, i);
330: }
331: if (obj instanceof NativeArray) {
332: return new NativeArrayIterator((NativeArray) obj);
333: }
334: return new ScriptableIterator((Scriptable) obj);
335: }
336:
337: public VelMethod getMethod(Object obj, String methodName,
338: Object[] args, Info i) throws Exception {
339: return !(obj instanceof Scriptable) ? super .getMethod(obj,
340: methodName, args, i) : new JSMethod((Scriptable) obj,
341: methodName);
342: }
343:
344: public VelPropertyGet getPropertyGet(Object obj, String identifier,
345: Info i) throws Exception {
346: return !(obj instanceof Scriptable) ? super .getPropertyGet(obj,
347: identifier, i) : new JSPropertyGet((Scriptable) obj,
348: identifier);
349: }
350:
351: public VelPropertySet getPropertySet(Object obj, String identifier,
352: Object arg, Info i) throws Exception {
353: return !(obj instanceof Scriptable) ? super .getPropertySet(obj,
354: identifier, arg, i) : new JSPropertySet(
355: (Scriptable) obj, identifier);
356: }
357: }
|