Coverage Report - org.apache.commons.betwixt.expression.Context

Classes in this File Line Coverage Branch Coverage Complexity
Context
76% 
100% 
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 &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  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  
 }