Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
Context |
|
| 1.1923076923076923;1.192 |
1 | /* |
|
2 | * Copyright 2001-2004 The Apache Software Foundation. |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of 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, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | package org.apache.commons.betwixt.expression; |
|
17 | ||
18 | import java.util.HashMap; |
|
19 | import java.util.Map; |
|
20 | ||
21 | import org.apache.commons.betwixt.BindingConfiguration; |
|
22 | import org.apache.commons.betwixt.Options; |
|
23 | import org.apache.commons.betwixt.strategy.IdStoringStrategy; |
|
24 | import org.apache.commons.betwixt.strategy.ObjectStringConverter; |
|
25 | import org.apache.commons.betwixt.strategy.ValueSuppressionStrategy; |
|
26 | import org.apache.commons.collections.ArrayStack; |
|
27 | import org.apache.commons.logging.Log; |
|
28 | import org.apache.commons.logging.LogFactory; |
|
29 | ||
30 | /** <p><code>Context</code> describes the context used to evaluate |
|
31 | * bean expressions. |
|
32 | * This is mostly a bean together with a number of context variables. |
|
33 | * Context variables are named objects. |
|
34 | * In other words, |
|
35 | * a context variable associates an object with a string.</p> |
|
36 | * |
|
37 | * <p> Logging during expression evaluation is done through the logging |
|
38 | * instance held by this class. |
|
39 | * The object initiating the evaluation should control this logging |
|
40 | * and so passing a <code>Log</code> instance is enforced by the constructors.</p> |
|
41 | * |
|
42 | * <p><code>Context</code> is a natural place to include shared evaluation code. |
|
43 | * One of the problems that you get with object graphs is that they can be cyclic. |
|
44 | * Xml cannot (directly) include cycles. |
|
45 | * Therefore <code>betwixt</code> needs to find and deal properly with cycles. |
|
46 | * The algorithm used is to check the parentage of a new child. |
|
47 | * If the child is a parent then that operation fails. </p> |
|
48 | * |
|
49 | * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> |
|
50 | */ |
|
51 | public class Context { |
|
52 | ||
53 | /** Evaluate this bean */ |
|
54 | private Object bean; |
|
55 | /** Variables map */ |
|
56 | private Map variables; |
|
57 | /** Store options */ |
|
58 | 14773 | private ArrayStack optionStack = new ArrayStack(); |
59 | /** |
|
60 | * Logging uses commons-logging <code>Log</code> |
|
61 | * named <code>org.apache.commons.betwixt</code> |
|
62 | */ |
|
63 | private Log log; |
|
64 | /** Configuration for dynamic binding properties */ |
|
65 | private BindingConfiguration bindingConfiguration; |
|
66 | ||
67 | /** |
|
68 | * Construct context with default log |
|
69 | */ |
|
70 | public Context() { |
|
71 | 205 | this( null, LogFactory.getLog( Context.class ) ); |
72 | 205 | } |
73 | ||
74 | /** Convenience constructor sets evaluted bean and log. |
|
75 | * |
|
76 | * @param bean evaluate expressions against this bean |
|
77 | * @param log log to this logger |
|
78 | * @deprecated 0.5 use constructor which takes a BindingConfiguration |
|
79 | */ |
|
80 | public Context(Object bean, Log log) { |
|
81 | 205 | this( bean, log, new BindingConfiguration() ); |
82 | 205 | } |
83 | ||
84 | ||
85 | /** Convenience constructor sets evaluted bean and log. |
|
86 | * |
|
87 | * @param bean evaluate expressions against this bean |
|
88 | * @param log log to this logger |
|
89 | * @param bindingConfiguration not null |
|
90 | */ |
|
91 | public Context(Object bean, Log log, BindingConfiguration bindingConfiguration) { |
|
92 | 3934 | this( bean, new HashMap(), log, bindingConfiguration ); |
93 | 3934 | } |
94 | ||
95 | /** |
|
96 | * Construct a cloned context. |
|
97 | * The constructed context should share bean, variables, log and binding configuration. |
|
98 | * @param context duplicate the attributes of this bean |
|
99 | */ |
|
100 | public Context( Context context ) { |
|
101 | 10839 | this(context.bean, context.variables, context.log, context.bindingConfiguration); |
102 | 10839 | } |
103 | ||
104 | ||
105 | /** Convenience constructor sets evaluted bean, context variables and log. |
|
106 | * |
|
107 | * @param bean evaluate expressions against this bean |
|
108 | * @param variables context variables |
|
109 | * @param log log to this logger |
|
110 | * @deprecated 0.5 use constructor which takes a converter |
|
111 | */ |
|
112 | public Context(Object bean, Map variables, Log log) { |
|
113 | 0 | this( bean, variables, log, new BindingConfiguration() ); |
114 | 0 | } |
115 | ||
116 | /** Convenience constructor sets evaluted bean, context variables and log. |
|
117 | * |
|
118 | * @param bean evaluate expressions against this bean |
|
119 | * @param variables context variables |
|
120 | * @param log log to this logger |
|
121 | * @param bindingConfiguration not null |
|
122 | */ |
|
123 | 14773 | public Context(Object bean, Map variables, Log log, BindingConfiguration bindingConfiguration) { |
124 | 14773 | this.bean = bean; |
125 | 14773 | this.variables = variables; |
126 | 14773 | this.log = log; |
127 | 14773 | this.bindingConfiguration = bindingConfiguration; |
128 | 14773 | } |
129 | ||
130 | /** Returns a new child context with the given bean but the same log and variables. |
|
131 | * |
|
132 | * @param newBean create a child context for this bean |
|
133 | * @return new Context with new bean but shared variables |
|
134 | */ |
|
135 | // TODO: need to think about whether this is a good idea and how subclasses |
|
136 | // should handle this |
|
137 | public Context newContext(Object newBean) { |
|
138 | 9300 | Context context = new Context(this); |
139 | 9300 | context.setBean( newBean ); |
140 | 9300 | return context; |
141 | } |
|
142 | ||
143 | /** |
|
144 | * Gets the current bean. |
|
145 | * @return the bean against which expressions are evaluated |
|
146 | */ |
|
147 | public Object getBean() { |
|
148 | 47842 | return bean; |
149 | } |
|
150 | ||
151 | /** |
|
152 | * Set the current bean. |
|
153 | * @param bean the Object against which expressions will be evaluated |
|
154 | */ |
|
155 | public void setBean(Object bean) { |
|
156 | 9526 | this.bean = bean; |
157 | 9526 | } |
158 | ||
159 | /** |
|
160 | * Gets context variables. |
|
161 | * @return map containing variable values keyed by variable name |
|
162 | */ |
|
163 | public Map getVariables() { |
|
164 | 0 | return variables; |
165 | } |
|
166 | ||
167 | /** |
|
168 | * Sets context variables. |
|
169 | * @param variables map containing variable values indexed by varibable name Strings |
|
170 | */ |
|
171 | public void setVariables(Map variables) { |
|
172 | 0 | this.variables = variables; |
173 | 0 | } |
174 | ||
175 | /** |
|
176 | * Gets the value of a particular context variable. |
|
177 | * @param name the name of the variable whose value is to be returned |
|
178 | * @return the variable value or null if the variable isn't set |
|
179 | */ |
|
180 | public Object getVariable(String name) { |
|
181 | 0 | return variables.get( name ); |
182 | } |
|
183 | ||
184 | /** |
|
185 | * Sets the value of a particular context variable. |
|
186 | * @param name the name of the variable |
|
187 | * @param value the value of the variable |
|
188 | */ |
|
189 | public void setVariable(String name, Object value) { |
|
190 | 0 | variables.put( name, value ); |
191 | 0 | } |
192 | ||
193 | /** |
|
194 | * Gets the current log. |
|
195 | * |
|
196 | * @return the implementation to which this class logs |
|
197 | */ |
|
198 | public Log getLog() { |
|
199 | 34238 | return log; |
200 | } |
|
201 | ||
202 | /** |
|
203 | * Set the log implementation to which this class logs |
|
204 | * |
|
205 | * @param log the implemetation that this class should log to |
|
206 | */ |
|
207 | public void setLog(Log log) { |
|
208 | 0 | this.log = log; |
209 | 0 | } |
210 | ||
211 | /** |
|
212 | * Gets object <-> string converter. |
|
213 | * @return the Converter to be used for conversions, not null |
|
214 | * @since 0.5 |
|
215 | */ |
|
216 | public ObjectStringConverter getObjectStringConverter() { |
|
217 | 5291 | return bindingConfiguration.getObjectStringConverter(); |
218 | } |
|
219 | ||
220 | /** |
|
221 | * Should <code>ID</code>'s and <code>IDREF</code> attributes |
|
222 | * be used to cross-reference matching objects? |
|
223 | * |
|
224 | * @return true if <code>ID</code> and <code>IDREF</code> |
|
225 | * attributes should be used to cross-reference instances |
|
226 | * @since 0.5 |
|
227 | */ |
|
228 | public boolean getMapIDs() { |
|
229 | 7126 | return bindingConfiguration.getMapIDs(); |
230 | } |
|
231 | ||
232 | /** |
|
233 | * The name of the attribute which can be specified in the XML to override the |
|
234 | * type of a bean used at a certain point in the schema. |
|
235 | * |
|
236 | * <p>The default value is 'className'.</p> |
|
237 | * |
|
238 | * @return The name of the attribute used to overload the class name of a bean |
|
239 | * @since 0.5 |
|
240 | */ |
|
241 | public String getClassNameAttribute() { |
|
242 | 3572 | return bindingConfiguration.getClassNameAttribute(); |
243 | } |
|
244 | ||
245 | /** |
|
246 | * Sets the name of the attribute which can be specified in |
|
247 | * the XML to override the type of a bean used at a certain |
|
248 | * point in the schema. |
|
249 | * |
|
250 | * <p>The default value is 'className'.</p> |
|
251 | * |
|
252 | * @param classNameAttribute The name of the attribute used to overload the class name of a bean |
|
253 | * @since 0.5 |
|
254 | */ |
|
255 | public void setClassNameAttribute(String classNameAttribute) { |
|
256 | 0 | bindingConfiguration.setClassNameAttribute( classNameAttribute ); |
257 | 0 | } |
258 | ||
259 | /** |
|
260 | * Gets the <code>ValueSuppressionStrategy</code>. |
|
261 | * This is used to control the expression of attributes with certain values. |
|
262 | * @since 0.7 |
|
263 | * @return <code>ValueSuppressionStrategy</code>, not null |
|
264 | */ |
|
265 | public ValueSuppressionStrategy getValueSuppressionStrategy() { |
|
266 | 7293 | return bindingConfiguration.getValueSuppressionStrategy(); |
267 | } |
|
268 | ||
269 | /** |
|
270 | * Sets the <code>ValueSuppressionStrategy</code>. |
|
271 | * This is used to control the expression of attributes with certain values. |
|
272 | * @since 0.7 |
|
273 | * @param valueSuppressionStrategy <code>ValueSuppressionStrategy</code>, not null |
|
274 | */ |
|
275 | public void setValueSuppressionStrategy( |
|
276 | ValueSuppressionStrategy valueSuppressionStrategy) { |
|
277 | 0 | bindingConfiguration.setValueSuppressionStrategy(valueSuppressionStrategy); |
278 | 0 | } |
279 | ||
280 | /** |
|
281 | * Gets the strategy used to manage storage and retrieval of id's. |
|
282 | * @since 0.7 |
|
283 | * @return Returns the idStoringStrategy, not null |
|
284 | */ |
|
285 | public IdStoringStrategy getIdMappingStrategy() { |
|
286 | 1838 | return bindingConfiguration.getIdMappingStrategy(); |
287 | } |
|
288 | ||
289 | /** |
|
290 | * Gets the current <code>Options</code>. |
|
291 | * @return <code>Options</code> that currently apply |
|
292 | * or null if there are no current options. |
|
293 | * @since 0.7 |
|
294 | */ |
|
295 | public Options getOptions() { |
|
296 | 37665 | Options results = null; |
297 | 37665 | if (!optionStack.isEmpty()) { |
298 | 35540 | results = (Options) optionStack.peek(); |
299 | } |
|
300 | 37665 | return results; |
301 | } |
|
302 | ||
303 | /** |
|
304 | * <p>Pushes the given <code>Options</code> onto the stack. |
|
305 | * </p><p> |
|
306 | * <strong>Note</strong> that code calling push should ensure that {@link #popOptions} |
|
307 | * is called once the options are no longer current. |
|
308 | * This ensures that the previous options are reinstated. |
|
309 | * </p> |
|
310 | * @since 0.7 |
|
311 | * @param options newly current <code>Options</code>, not null |
|
312 | */ |
|
313 | public void pushOptions(Options options) { |
|
314 | 32399 | optionStack.push(options); |
315 | 32399 | } |
316 | ||
317 | /** |
|
318 | * <p>Pops the current options from the stack. |
|
319 | * The previously current options (if any exist) |
|
320 | * will be reinstated by this method. |
|
321 | * </p><p> |
|
322 | * <stong>Note</strong> code calling this method should |
|
323 | * have previsouly called {@link #popOptions}. |
|
324 | * @since 0.7 |
|
325 | */ |
|
326 | public void popOptions() { |
|
327 | 36089 | if (optionStack.isEmpty()) { |
328 | 4405 | log.debug("Cannot pop options off empty stack"); |
329 | } else { |
|
330 | 31684 | optionStack.pop(); |
331 | } |
|
332 | 36089 | } |
333 | ||
334 | /** |
|
335 | * Gets the value of the first option with this name. |
|
336 | * The stack of inherited options is search (starting |
|
337 | * from the current option) until an option with a non-null |
|
338 | * value for the named option is found. |
|
339 | * |
|
340 | * @param name the name of the option to be found |
|
341 | * @return option value or null if this value is never set |
|
342 | */ |
|
343 | public String getInheritedOption(String name) { |
|
344 | 52 | String result = null; |
345 | 130 | for (int i=0; i<optionStack.size() ; i++) |
346 | { |
|
347 | 117 | Options options = (Options) optionStack.peek(i); |
348 | 117 | if (options != null) |
349 | { |
|
350 | 117 | result = options.getValue(name); |
351 | 117 | if (result != null) |
352 | { |
|
353 | 39 | break; |
354 | } |
|
355 | } |
|
356 | } |
|
357 | 52 | return result; |
358 | } |
|
359 | ||
360 | } |