1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 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 this( null, LogFactory.getLog( Context.class ) );
72 }
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 this( bean, log, new BindingConfiguration() );
82 }
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 this( bean, new HashMap(), log, bindingConfiguration );
93 }
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 this(context.bean, context.variables, context.log, context.bindingConfiguration);
102 }
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 this( bean, variables, log, new BindingConfiguration() );
114 }
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 public Context(Object bean, Map variables, Log log, BindingConfiguration bindingConfiguration) {
124 this.bean = bean;
125 this.variables = variables;
126 this.log = log;
127 this.bindingConfiguration = bindingConfiguration;
128 }
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
136
137 public Context newContext(Object newBean) {
138 Context context = new Context(this);
139 context.setBean( newBean );
140 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 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 this.bean = bean;
157 }
158
159 /***
160 * Gets context variables.
161 * @return map containing variable values keyed by variable name
162 */
163 public Map getVariables() {
164 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 this.variables = variables;
173 }
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 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 variables.put( name, value );
191 }
192
193 /***
194 * Gets the current log.
195 *
196 * @return the implementation to which this class logs
197 */
198 public Log getLog() {
199 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 this.log = log;
209 }
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 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 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 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 bindingConfiguration.setClassNameAttribute( classNameAttribute );
257 }
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 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 bindingConfiguration.setValueSuppressionStrategy(valueSuppressionStrategy);
278 }
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 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 Options results = null;
297 if (!optionStack.isEmpty()) {
298 results = (Options) optionStack.peek();
299 }
300 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 optionStack.push(options);
315 }
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 if (optionStack.isEmpty()) {
328 log.info("Cannot pop options off empty stack");
329 } else {
330 optionStack.pop();
331 }
332 }
333
334 }