View Javadoc

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      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     // 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         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 &lt;-&gt; 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 }