Coverage Report - org.apache.tapestry5.corelib.components.BeanEditor
 
Classes in this File Line Coverage Branch Coverage Complexity
BeanEditor
100%
29/29
100%
4/4
0
BeanEditor$1
67%
2/3
N/A
0
BeanEditor$CleanupEnvironment
75%
3/4
N/A
0
BeanEditor$Prepare
75%
3/4
N/A
0
 
 1  
 // Copyright 2007, 2008 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry5.corelib.components;
 16  
 
 17  
 import org.apache.tapestry5.BindingConstants;
 18  
 import org.apache.tapestry5.ComponentAction;
 19  
 import org.apache.tapestry5.ComponentResources;
 20  
 import org.apache.tapestry5.PropertyOverrides;
 21  
 import org.apache.tapestry5.annotations.Environmental;
 22  
 import org.apache.tapestry5.annotations.Parameter;
 23  
 import org.apache.tapestry5.annotations.Property;
 24  
 import org.apache.tapestry5.annotations.SupportsInformalParameters;
 25  
 import org.apache.tapestry5.beaneditor.BeanModel;
 26  
 import org.apache.tapestry5.corelib.internal.InternalMessages;
 27  
 import org.apache.tapestry5.internal.beaneditor.BeanModelUtils;
 28  
 import org.apache.tapestry5.ioc.annotations.Inject;
 29  
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 30  
 import org.apache.tapestry5.services.BeanEditContext;
 31  
 import org.apache.tapestry5.services.BeanModelSource;
 32  
 import org.apache.tapestry5.services.Environment;
 33  
 import org.apache.tapestry5.services.FormSupport;
 34  
 
 35  
 import java.lang.annotation.Annotation;
 36  
 
 37  
 /**
 38  
  * A component that generates a user interface for editing the properties of a bean. This is the central component of
 39  
  * the {@link BeanEditForm}, and utilizes a {@link PropertyEditor} for much of its functionality. This component places
 40  
  * a {@link BeanEditContext} into the environment.
 41  
  */
 42  
 @SupportsInformalParameters
 43  62
 public class BeanEditor
 44  
 {
 45  232
     public static class Prepare implements ComponentAction<BeanEditor>
 46  
     {
 47  
         private static final long serialVersionUID = 6273600092955522585L;
 48  
 
 49  
         public void execute(BeanEditor component)
 50  
         {
 51  138
             component.doPrepare();
 52  138
         }
 53  
 
 54  
         @Override
 55  
         public String toString()
 56  
         {
 57  0
             return "BeanEditor.Prepare";
 58  
         }
 59  
     }
 60  
 
 61  144
     static class CleanupEnvironment implements ComponentAction<BeanEditor>
 62  
     {
 63  
         private static final long serialVersionUID = 6867226962459227016L;
 64  
 
 65  
         public void execute(BeanEditor component)
 66  
         {
 67  138
             component.cleanupEnvironment();
 68  138
         }
 69  
 
 70  
         @Override
 71  
         public String toString()
 72  
         {
 73  0
             return "BeanEditor.CleanupEnvironment";
 74  
         }
 75  
     }
 76  
 
 77  6
     private static final ComponentAction<BeanEditor> CLEANUP_ENVIRONMENT = new CleanupEnvironment();
 78  
 
 79  
     /**
 80  
      * The object to be edited by the BeanEditor. This will be read when the component renders and updated when the form
 81  
      * for the component is submitted. Typically, the container will listen for a "prepare" event, in order to ensure
 82  
      * that a non-null value is ready to be read or updated.
 83  
      */
 84  
     @Parameter(autoconnect = true)
 85  
     private Object object;
 86  
 
 87  
     /**
 88  
      * A comma-separated list of property names to be retained from the {@link org.apache.tapestry5.beaneditor.BeanModel}.
 89  
      * Only these properties will be retained, and the properties will also be reordered. The names are
 90  
      * case-insensitive.
 91  
      */
 92  
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
 93  
     private String include;
 94  
 
 95  
     /**
 96  
      * A comma-separated list of property names to be removed from the {@link org.apache.tapestry5.beaneditor.BeanModel}.
 97  
      * The names are case-insensitive.
 98  
      */
 99  
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
 100  
     private String exclude;
 101  
 
 102  
     /**
 103  
      * A comma-separated list of property names indicating the order in which the properties should be presented. The
 104  
      * names are case insensitive. Any properties not indicated in the list will be appended to the end of the display
 105  
      * order.
 106  
      */
 107  
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
 108  
     private String reorder;
 109  
 
 110  
     /**
 111  
      * A comma-separated list of property names to be added to the {@link org.apache.tapestry5.beaneditor.BeanModel}.
 112  
      */
 113  
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
 114  
     private String add;
 115  
 
 116  
 
 117  
     /**
 118  
      * The model that identifies the parameters to be edited, their order, and every other aspect. If not specified, a
 119  
      * default bean model will be created from the type of the object bound to the object parameter.
 120  
      */
 121  
     @Parameter
 122  
     @Property(write = false)
 123  
     private BeanModel model;
 124  
 
 125  
     /**
 126  
      * Where to search for local overrides of property editing blocks as block parameters. Further, the container of the
 127  
      * overrides is used as the source for overridden validation messages. This is normally the BeanEditor component
 128  
      * itself, but when the component is used within a BeanEditForm, it will be the BeanEditForm's resources that will
 129  
      * be searched.
 130  
      */
 131  
     @Parameter(value = "this", allowNull = false)
 132  
     @Property(write = false)
 133  
     private PropertyOverrides overrides;
 134  
 
 135  
     @Inject
 136  
     private BeanModelSource modelSource;
 137  
 
 138  
     @Inject
 139  
     private ComponentResources resources;
 140  
 
 141  
     @Inject
 142  
     private Environment environment;
 143  
 
 144  
     @Environmental
 145  
     private FormSupport formSupport;
 146  
 
 147  
     // Value that change with each change to the current property:
 148  
 
 149  
     @Property
 150  
     private String propertyName;
 151  
 
 152  
     /**
 153  
      * To support nested BeanEditors, we need to cache the object value inside {@link #doPrepare()}. See TAPESTRY-2460.
 154  
      */
 155  
     private Object cachedObject;
 156  
 
 157  
     // Needed for testing as well
 158  
 
 159  
     public Object getObject()
 160  
     {
 161  312
         return cachedObject;
 162  
     }
 163  
 
 164  
     void setupRender()
 165  
     {
 166  94
         formSupport.storeAndExecute(this, new Prepare());
 167  94
     }
 168  
 
 169  
     void cleanupRender()
 170  
     {
 171  94
         formSupport.storeAndExecute(this, CLEANUP_ENVIRONMENT);
 172  94
     }
 173  
 
 174  
     /**
 175  
      * Used to initialize the model if necessary, to instantiate the object being edited if necessary, and to push the
 176  
      * BeanEditContext into the environment.
 177  
      */
 178  
     void doPrepare()
 179  
     {
 180  144
         if (model == null)
 181  
         {
 182  28
             Class type = resources.getBoundType("object");
 183  28
             model = modelSource.createEditModel(type, overrides.getOverrideMessages());
 184  
         }
 185  
 
 186  144
         BeanModelUtils.modify(model, add, include, exclude, reorder);
 187  
 
 188  
         // The only problem here is that if the bound property is backed by a persistent field, it
 189  
         // is assigned (and stored to the session, and propagated around the cluster) first,
 190  
         // before values are assigned.
 191  
 
 192  144
         if (object == null)
 193  
         {
 194  
             try
 195  
             {
 196  16
                 object = model.newInstance();
 197  
             }
 198  2
             catch (Exception ex)
 199  
             {
 200  2
                 String message = InternalMessages.failureInstantiatingObject(model.getBeanType(),
 201  
                                                                              resources.getCompleteId(),
 202  
                                                                              ex);
 203  2
                 throw new TapestryException(message, resources.getLocation(), ex);
 204  14
             }
 205  
         }
 206  
 
 207  142
         BeanEditContext context = new BeanEditContext()
 208  
         {
 209  
             public Class<?> getBeanClass()
 210  
             {
 211  4
                 return model.getBeanType();
 212  
             }
 213  
 
 214  142
             public <T extends Annotation> T getAnnotation(Class<T> type)
 215  
             {
 216  0
                 return getBeanClass().getAnnotation(type);
 217  
             }
 218  
         };
 219  
 
 220  
 
 221  142
         cachedObject = object;
 222  
 
 223  142
         environment.push(BeanEditContext.class, context);
 224  142
     }
 225  
 
 226  
     void cleanupEnvironment()
 227  
     {
 228  140
         environment.pop(BeanEditContext.class);
 229  140
     }
 230  
 
 231  
     // For testing
 232  
     void inject(ComponentResources resources, PropertyOverrides overrides, BeanModelSource source,
 233  
                 Environment environment)
 234  
     {
 235  8
         this.resources = resources;
 236  8
         this.overrides = overrides;
 237  8
         this.environment = environment;
 238  8
         modelSource = source;
 239  8
     }
 240  
 }