001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.xwork.util;
006:
007: import ognl.*;
008: import org.apache.commons.logging.Log;
009: import org.apache.commons.logging.LogFactory;
010:
011: import java.beans.IntrospectionException;
012: import java.beans.PropertyDescriptor;
013: import java.util.*;
014:
015: import com.opensymphony.xwork.XworkException;
016:
017: /**
018: * A stack that is able to call methods on objects in the stack.
019: *
020: * @author $Author: rainerh $
021: * @author Rainer Hermanns
022: * @version $Revision: 1071 $
023: */
024: public class CompoundRootAccessor implements PropertyAccessor,
025: MethodAccessor, ClassResolver {
026:
027: private final static Log log = LogFactory
028: .getLog(CompoundRootAccessor.class);
029: private static Map invalidMethods = new HashMap();
030:
031: public void setProperty(Map context, Object target, Object name,
032: Object value) throws OgnlException {
033: CompoundRoot root = (CompoundRoot) target;
034: OgnlContext ognlContext = (OgnlContext) context;
035:
036: for (Iterator iterator = root.iterator(); iterator.hasNext();) {
037: Object o = iterator.next();
038:
039: if (o == null) {
040: continue;
041: }
042:
043: try {
044: if (OgnlRuntime.hasSetProperty(ognlContext, o, name)) {
045: OgnlRuntime
046: .setProperty(ognlContext, o, name, value);
047:
048: return;
049: } else if (o instanceof Map) {
050: Map map = (Map) o;
051: map.put(name, value);
052: return;
053: }
054: // } catch (OgnlException e) {
055: // if (e.getReason() != null) {
056: // final String msg = "Caught an Ognl exception while setting property " + name;
057: // log.error(msg, e);
058: // throw new XworkException(msg, e.getReason());
059: // }
060: } catch (IntrospectionException e) {
061: // this is OK if this happens, we'll just keep trying the next
062: }
063: }
064:
065: Boolean reportError = (Boolean) context
066: .get(OgnlValueStack.REPORT_ERRORS_ON_NO_PROP);
067:
068: final String msg = "No object in the CompoundRoot has a publicly accessible property named '"
069: + name + "' (no setter could be found).";
070:
071: if ((reportError != null) && (reportError.booleanValue())) {
072: log.error(msg);
073: throw new XworkException(msg);
074: } else {
075: log.debug(msg);
076: }
077: }
078:
079: public Object getProperty(Map context, Object target, Object name)
080: throws OgnlException {
081: CompoundRoot root = (CompoundRoot) target;
082: OgnlContext ognlContext = (OgnlContext) context;
083:
084: if (name instanceof Integer) {
085: Integer index = (Integer) name;
086:
087: return root.cutStack(index.intValue());
088: } else if (name instanceof String) {
089: if ("top".equals(name)) {
090: if (root.size() > 0) {
091: return root.get(0);
092: } else {
093: return null;
094: }
095: }
096:
097: for (Iterator iterator = root.iterator(); iterator
098: .hasNext();) {
099: Object o = iterator.next();
100:
101: if (o == null) {
102: continue;
103: }
104:
105: try {
106: if ((OgnlRuntime.hasGetProperty(ognlContext, o,
107: name))
108: || ((o instanceof Map) && ((Map) o)
109: .containsKey(name))) {
110: return OgnlRuntime.getProperty(ognlContext, o,
111: name);
112: }
113: } catch (OgnlException e) {
114: if (e.getReason() != null) {
115: final String msg = "Caught an Ognl exception while getting property "
116: + name;
117: log.error(msg, e);
118: throw new XworkException(msg, e);
119: }
120: } catch (IntrospectionException e) {
121: // this is OK if this happens, we'll just keep trying the next
122: }
123: }
124:
125: return null;
126: } else {
127: return null;
128: }
129: }
130:
131: public Object callMethod(Map context, Object target, String name,
132: Object[] objects) throws MethodFailedException {
133: CompoundRoot root = (CompoundRoot) target;
134:
135: if ("describe".equals(name)) {
136: Object v;
137: if (objects != null && objects.length == 1) {
138: v = objects[0];
139: } else {
140: v = root.get(0);
141: }
142:
143: if (v instanceof Collection || v instanceof Map
144: || v.getClass().isArray()) {
145: return v.toString();
146: }
147:
148: try {
149: Map descriptors = OgnlRuntime.getPropertyDescriptors(v
150: .getClass());
151:
152: int maxSize = 0;
153: for (Iterator iterator = descriptors.keySet()
154: .iterator(); iterator.hasNext();) {
155: String pdName = (String) iterator.next();
156: if (pdName.length() > maxSize) {
157: maxSize = pdName.length();
158: }
159: }
160:
161: SortedSet set = new TreeSet();
162: StringBuffer sb = new StringBuffer();
163: for (Iterator iterator = descriptors.values()
164: .iterator(); iterator.hasNext();) {
165: PropertyDescriptor pd = (PropertyDescriptor) iterator
166: .next();
167:
168: sb.append(pd.getName()).append(": ");
169: int padding = maxSize - pd.getName().length();
170: for (int i = 0; i < padding; i++) {
171: sb.append(" ");
172: }
173: sb.append(pd.getPropertyType().getName());
174: set.add(sb.toString());
175:
176: sb = new StringBuffer();
177: }
178:
179: sb = new StringBuffer();
180: for (Iterator iterator = set.iterator(); iterator
181: .hasNext();) {
182: String s = (String) iterator.next();
183: sb.append(s).append("\n");
184: }
185:
186: return sb.toString();
187: } catch (IntrospectionException e) {
188: e.printStackTrace();
189: } catch (OgnlException e) {
190: e.printStackTrace();
191: }
192:
193: return null;
194: }
195:
196: for (Iterator iterator = root.iterator(); iterator.hasNext();) {
197: Object o = iterator.next();
198:
199: if (o == null) {
200: continue;
201: }
202:
203: Class clazz = o.getClass();
204: Class[] argTypes = getArgTypes(objects);
205:
206: CompoundRootAccessor.MethodCall mc = null;
207:
208: if (argTypes != null) {
209: mc = new CompoundRootAccessor.MethodCall(clazz, name,
210: argTypes);
211: }
212:
213: if ((argTypes == null) || !invalidMethods.containsKey(mc)) {
214: try {
215: Object value = OgnlRuntime.callMethod(
216: (OgnlContext) context, o, name, name,
217: objects);
218:
219: if (value != null) {
220: return value;
221: }
222: } catch (OgnlException e) {
223: // try the next one
224: Throwable reason = e.getReason();
225:
226: if ((mc != null)
227: && (reason != null)
228: && (reason.getClass() == NoSuchMethodException.class)) {
229: invalidMethods.put(mc, Boolean.TRUE);
230: } else if (reason != null) {
231: throw new MethodFailedException(o, name, e
232: .getReason());
233: }
234: }
235: }
236: }
237:
238: return null;
239: }
240:
241: public Object callStaticMethod(Map transientVars, Class aClass,
242: String s, Object[] objects) throws MethodFailedException {
243: return null;
244: }
245:
246: public Class classForName(String className, Map context)
247: throws ClassNotFoundException {
248: Object root = Ognl.getRoot(context);
249:
250: try {
251: if (root instanceof CompoundRoot) {
252: if (className.startsWith("vs")) {
253: CompoundRoot compoundRoot = (CompoundRoot) root;
254:
255: if (className.equals("vs")) {
256: return compoundRoot.peek().getClass();
257: }
258:
259: int index = Integer
260: .parseInt(className.substring(2));
261:
262: return compoundRoot.get(index - 1).getClass();
263: }
264: }
265: } catch (Exception e) {
266: // just try the old fashioned way
267: }
268:
269: return Thread.currentThread().getContextClassLoader()
270: .loadClass(className);
271: }
272:
273: private Class[] getArgTypes(Object[] args) {
274: if (args == null) {
275: return new Class[0];
276: }
277:
278: Class[] classes = new Class[args.length];
279:
280: for (int i = 0; i < args.length; i++) {
281: Object arg = args[i];
282: classes[i] = (arg != null) ? arg.getClass() : Object.class;
283: }
284:
285: return classes;
286: }
287:
288: static class MethodCall {
289: Class clazz;
290: String name;
291: Class[] args;
292: int hash;
293:
294: public MethodCall(Class clazz, String name, Class[] args) {
295: this .clazz = clazz;
296: this .name = name;
297: this .args = args;
298: this .hash = clazz.hashCode() + name.hashCode();
299:
300: for (int i = 0; i < args.length; i++) {
301: Class arg = args[i];
302: hash += arg.hashCode();
303: }
304: }
305:
306: public boolean equals(Object obj) {
307: MethodCall mc = (CompoundRootAccessor.MethodCall) obj;
308:
309: return (mc.clazz.equals(clazz) && mc.name.equals(name) && Arrays
310: .equals(mc.args, args));
311: }
312:
313: public int hashCode() {
314: return hash;
315: }
316: }
317: }
|