1
2
3
4
5
6
7
8
9
10
11
12
13
14
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>[]()."'</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"myMap"'''['.']'('.')mapProperty""""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 }