View Javadoc

1   /*
2    * Copyright 2004-2005, 2007-2008 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package net.sf.morph.lang.languages;
17  
18  import net.sf.composite.util.ObjectUtils;
19  import net.sf.morph.Defaults;
20  import net.sf.morph.lang.support.ExpressionParser;
21  import net.sf.morph.lang.support.SimpleExpressionParser;
22  import net.sf.morph.reflect.BeanReflector;
23  
24  /**
25   * <p>
26   * A simple language that is similar to the languages used by the JSTL EL, the
27   * Spring framework, Jakarta Commons BeanUtils, and Struts. Any "simple"
28   * expression in any of these languages should be recognized by this language.
29   * An example of a simple expression is <code>myBean.myProperty</code>. An
30   * example of a "non-simple" expression is
31   * </p>
32   *
33   * <p>
34   * <code>
35   * myBean.myProperty + myOtherBean.myOtherProperty
36   * </code>
37   * </p>
38   *
39   * <p>
40   * To be more precise, this language treats numbers as indexes into array-like
41   * objects and treats other alphanumeric identifiers as property names of
42   * bean-like objects. The following characters are all considered delimiters
43   * with no particularly special meaning: <code>[]().&quot;'</code>. Thus, all
44   * of the expressions in the table below have the same value in this language.
45   * </p>
46   *
47   * <table border="1">
48   * <tr>
49   * <th>Language</th>
50   * <th>Expression</th>
51   * </tr>
52   * <tr>
53   * <td><a
54   * href="http://bellsouthpwp.net/b/i/billsigg/jstl-quick-reference.pdf">JSTL EL
55   * </a>, Morph's simple language</td>
56   * <td>myBean.myMap["mapProperty"].myArray[0]</td>
57   * </tr>
58   * <tr>
59   * <td><a
60   * href="http://www.springframework.org/docs/reference/validation.html#beans-beans-conventions">Spring
61   * </a>, Morph's simple language</td>
62   * <td>myBean.myMap[mapProperty].myArray[0]</td>
63   * </tr>
64   * <tr>
65   * <td><a
66   * href="http://jakarta.apache.org/commons/beanutils/commons-beanutils-1.7.0/docs/api/org/apache/commons/beanutils/package-summary.html#standard.nested">Jakarta
67   * Commons BeanUtils </a>, <a
68   * href="http://struts.apache.org/faqs/indexedprops.html">Struts </a>, Morph's
69   * simple language</td>
70   * <td>myBean.myMap(mapProperty).myArray[0]</tr>
71   * <tr>
72   * <td>Morph's simple language</td>
73   * <td>myBean.myMap.mapProperty.myArray[0]</td>
74   * </tr>
75   * <tr>
76   * <td>Morph's simple language</td>
77   * <td>myBean.myMap.mapProperty.myArray.0</td>
78   * </tr>
79   * <tr>
80   * <td>Morph's simple language</td>
81   * <td>
82   * myBean&quot;myMap&quot;'''['.']'('.')mapProperty&quot;&quot;&quot;&quot;myArray[]0....
83   * </td>
84   * </tr>
85   * </table>
86   *
87   *
88   * <p>
89   * A simple language that will work for most use cases, but has the limitation
90   * that bean property names cannot be numbers. Numbers like 123 are always
91   * considered indices into an indexed object. For example, <code>123</code>
92   * is an invalid bean property name in this language, but <code>12Monkies</code>
93   * and <code>fridayThe13th</code> are valid property names.
94   * </p>
95   *
96   * @author Matt Sgarlata
97   * @since Nov 28, 2004
98   */
99  public class SimpleLanguage extends BaseLanguage {
100 
101 	private static final ExpressionParser DEFAULT_EXPRESSION_PARSER = new SimpleExpressionParser();
102 
103 	private ExpressionParser expressionParser;
104 	private BeanReflector reflector;
105 
106 	/**
107 	 * {@inheritDoc}
108 	 */
109 	protected boolean isPropertyImpl(String expression) throws Exception {
110 		return getExpressionParser().parse(expression).length == 1;
111 	}
112 
113 	/**
114 	 * {@inheritDoc}
115 	 */
116 	protected Class getTypeImpl(Object target, String expression) throws Exception {
117 		if (ObjectUtils.isEmpty(expression)) {
118 			return target.getClass();
119 		}
120 
121 		Object value = target;
122 
123 		String[] tokens = getExpressionParser().parse(expression);
124 		for (int i = 0; i < tokens.length - 1; i++) {
125 			String token = tokens[i];
126 			value = getReflector().get(value, token);
127 		}
128 
129 		String token = tokens[tokens.length - 1];
130 		return getReflector().getType(value, token);
131 	}
132 
133 	/**
134 	 * {@inheritDoc}
135 	 */
136 	protected Object getImpl(Object target, String expression) throws Exception {
137 		if (ObjectUtils.isEmpty(expression)) {
138 			return target;
139 		}
140 
141 		Object value = target;
142 
143 		String[] tokens = getExpressionParser().parse(expression);
144 		for (int i = 0; value != null && i < tokens.length; i++) {
145 			String token = tokens[i];
146 			value = getReflector().get(value, token);
147 		}
148 		return value;
149 	}
150 
151 	/**
152 	 * {@inheritDoc}
153 	 */
154 	protected void setImpl(Object target, String expression, Object value) throws Exception {
155 		if (ObjectUtils.isEmpty(expression)) {
156 			target = value;
157 		}
158 
159 		Object currentTarget = target;
160 
161 		String[] tokens = getExpressionParser().parse(expression);
162 		for (int i = 0; i < tokens.length; i++) {
163 			String token = tokens[i];
164 			if (i == tokens.length - 1) {
165 				getReflector().set(currentTarget, token, value);
166 			}
167 			else {
168 				currentTarget = getReflector().get(currentTarget, token);
169 			}
170 		}
171 	}
172 
173 	/**
174 	 * Get the expression parser.
175 	 * @return Returns the expressionParser.
176 	 */
177 	public synchronized ExpressionParser getExpressionParser() {
178 		if (expressionParser == null) {
179 			setExpressionParser(DEFAULT_EXPRESSION_PARSER);
180 		}
181 		return expressionParser;
182 	}
183 
184 	/**
185 	 * Set the expression parser.
186 	 * @param expressionParser The expressionParser to set.
187 	 */
188 	public synchronized void setExpressionParser(ExpressionParser expressionParser) {
189 		this.expressionParser = expressionParser;
190 	}
191 
192 	/**
193 	 * Get the reflector.
194 	 * @return BeanReflector
195 	 */
196 	public synchronized BeanReflector getReflector() {
197 		if (reflector == null) {
198 			setReflector(Defaults.createBeanReflector());
199 		}
200 		return reflector;
201 	}
202 
203 	/**
204 	 * Set the reflector.
205 	 * @param reflector BeanReflector to set
206 	 */
207 	public synchronized void setReflector(BeanReflector reflector) {
208 		this.reflector = reflector;
209 	}
210 }