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: * $Header:$
018: */
019: package org.apache.beehive.netui.script.el.util;
020:
021: import java.io.StringReader;
022: import java.lang.reflect.Array;
023: import java.lang.reflect.Method;
024: import java.util.HashMap;
025:
026: import org.apache.beehive.netui.script.el.parser.NetUIELParser;
027: import org.apache.beehive.netui.script.el.NetUIVariableResolver;
028: import org.apache.beehive.netui.script.el.ParsedExpression;
029: import org.apache.beehive.netui.script.el.ExpressionParseException;
030: import org.apache.beehive.netui.script.el.parser.TokenMgrError;
031: import org.apache.beehive.netui.script.el.parser.ParseException;
032: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
033: import org.apache.beehive.netui.util.internal.cache.PropertyCache;
034: import org.apache.beehive.netui.util.type.TypeUtils;
035: import org.apache.beehive.netui.util.logging.Logger;
036:
037: /**
038: *
039: */
040: public final class ParseUtils {
041:
042: private static final Logger LOGGER = Logger
043: .getInstance(ParseUtils.class);
044: private static final HashMap/*<String, ParsedExpression>*/PARSED_CACHE = new HashMap();
045:
046: /* do not construct */
047: private ParseUtils() {
048: }
049:
050: /**
051: * Parse the givne String into a {@link ParsedExpression}.
052: * @param expression the expression
053: * @return the parsed expression
054: */
055: public static ParsedExpression parse(String expression) {
056: ParsedExpression pe = (ParsedExpression) PARSED_CACHE
057: .get(expression);
058:
059: if (pe != null)
060: return pe;
061:
062: try {
063: NetUIELParser learn = new NetUIELParser(new StringReader(
064: expression));
065:
066: ParsedExpression expr = learn.parse();
067: expr.seal();
068:
069: /* infrequent; this should only happen when there is a cache miss */
070: synchronized (PARSED_CACHE) {
071: PARSED_CACHE.put(expression, expr);
072: }
073:
074: return expr;
075: } catch (ParseException e) {
076: String msg = "Error occurred parsing expression \""
077: + expression + "\".";
078: LOGGER.error(msg, e);
079: throw new ExpressionParseException(msg, e);
080: } catch (TokenMgrError tm) {
081: String msg = "Error occurred parsing expression \""
082: + expression + "\".";
083: LOGGER.error(msg, tm);
084: throw new ExpressionParseException(msg, tm);
085: }
086: }
087:
088: public static Object evaluate(String exprStr,
089: NetUIVariableResolver vr) {
090: ParsedExpression expr = parse(exprStr);
091: assert expr != null;
092: return expr.evaluate(vr);
093: }
094:
095: public static void update(String exprStr, Object value,
096: NetUIVariableResolver vr) {
097: ParsedExpression expr = parse(exprStr);
098: assert expr != null;
099: expr.update(value, vr);
100: }
101:
102: public static Class getPropertyType(Object value, String name,
103: PropertyCache cache) {
104: assert value != null;
105: assert cache != null;
106:
107: Class type = value.getClass();
108:
109: Method m = cache.getPropertySetter(type, name);
110: if (m == null) {
111: String msg = "Can not find setter method for property \""
112: + name + "\" on object of type \""
113: + value.getClass() + "\".";
114: LOGGER.error(msg);
115: throw new RuntimeException(msg);
116: }
117: // PropertyCache guarantees that props are found and match JavaBean naming rules
118: else {
119: assert m.getParameterTypes().length == 1;
120: return m.getParameterTypes()[0];
121: }
122: }
123:
124: public static Object getProperty(Object value, String name,
125: PropertyCache cache) {
126: assert value != null;
127: assert cache != null;
128:
129: Class type = value.getClass();
130:
131: Method m = cache.getPropertyGetter(type, name);
132: if (m != null) {
133: try {
134: return m.invoke(value, (Object[]) null);
135: } catch (Exception e) {
136: String msg = "An error occurred invoking a getter for the property \""
137: + name
138: + "\" on an object of type \""
139: + type
140: + "\".";
141: LOGGER.error(msg, e);
142: throw new RuntimeException(msg, e);
143: }
144: }
145:
146: String msg = "Could not find JavaBean property named \"" + name
147: + "\" on object of type \"" + type + "\"";
148: LOGGER.error(msg);
149: throw new RuntimeException(msg);
150: }
151:
152: public static final Object convertType(Object value, Class toType) {
153: assert toType != null;
154:
155: try {
156: boolean sourceIsArray = false;
157:
158: /* only convert String types; other Object types are already assumed to be in their target types. */
159: if (value != null
160: && !(value instanceof String || (sourceIsArray = (value instanceof String[]))))
161: return value;
162:
163: /* for a String[], convert each item in the array into its target type and return the resulting array. */
164: if (toType.isArray()) {
165: if (value == null)
166: return null;
167: else {
168: Class compType = toType.getComponentType();
169:
170: String[] strs = null;
171: if (value.getClass().isArray())
172: strs = (String[]) value;
173: else
174: strs = new String[] { (String) value };
175:
176: Object tgt = Array.newInstance(compType,
177: strs.length);
178:
179: for (int i = 0; i < strs.length; i++) {
180: Object o = null;
181: try {
182: /* todo: support getting the Locale here in an ExpressionContext object */
183: o = TypeUtils.convertToObject(strs[i],
184: compType);
185: } catch (IllegalArgumentException e) {
186: String msg = "Can not set Object types via expressions that are not supported by the set of registered type converters. Cause: "
187: + e;
188: LOGGER.error(msg, e);
189: throw new RuntimeException(msg, e);
190: }
191:
192: Array.set(tgt, i, o);
193: }
194:
195: return tgt;
196: }
197: }
198: // convert the String into its target type and return the result
199: else {
200: // If the "value" is multi-valued (String[]), it needs to be converted into a single-valued object.
201: // There is no policy defined for how we do this right now, so the first one will always win when
202: // multiple expressions reference the same property. When that property is a String type, the result
203: // is an HttpServletRequest that contains a String[], and here, we'll always the String[0].
204: if (sourceIsArray) {
205: assert value instanceof String[];
206: assert Array.getLength(value) > 0
207: && Array.getLength(value) - 1 >= 0;
208:
209: value = Array
210: .get(value, Array.getLength(value) - 1);
211: }
212:
213: try {
214: assert value == null || value instanceof String;
215:
216: return TypeUtils.convertToObject((String) value,
217: toType);
218: } catch (IllegalArgumentException e) {
219: String msg = "The type \""
220: + toType.getName()
221: + "\" can not be set through the NetUI expression language.";
222: LOGGER.error(msg, e);
223: throw new RuntimeException(msg, e);
224: }
225: }
226: } catch (Exception e) {
227: String msg = "Unable to convert a value of type \""
228: + value.getClass()
229: + "\" to the array element type of \"" + toType
230: + "\". Cause: " + e;
231: LOGGER.error(msg, e);
232: throw new RuntimeException(msg, e);
233: }
234: }
235:
236: public static String getContextString(String[] contexts) {
237: InternalStringBuilder builder = new InternalStringBuilder();
238: builder.append("[");
239: if (contexts != null) {
240: for (int i = 0; i < contexts.length; i++) {
241: if (i > 0)
242: builder.append(", ");
243: builder.append(contexts[i]);
244: }
245: }
246: builder.append("]");
247: return builder.toString();
248: }
249:
250: public static Throwable getRootCause(Throwable t) {
251: Throwable root = t;
252: while (root.getCause() != null)
253: root = root.getCause();
254: return root;
255: }
256: }
|