View Javadoc

1   /*
2    * Copyright 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.io.read;
17  
18  import java.util.Map;
19  
20  import org.apache.commons.betwixt.AttributeDescriptor;
21  import org.apache.commons.betwixt.ElementDescriptor;
22  import org.apache.commons.betwixt.TextDescriptor;
23  import org.apache.commons.betwixt.XMLBeanInfo;
24  import org.apache.commons.betwixt.expression.Updater;
25  import org.apache.commons.logging.Log;
26  import org.xml.sax.Attributes;
27  
28  /***
29   * Action that creates and binds a new bean instance.
30   * 
31   * @author <a href='http://jakarta.apache.org/'>Apache Commons Team</a>
32   * @version $Revision: 155402 $
33   */
34  public class BeanBindAction extends MappingAction.Base {
35  
36      /*** Singleton instance */
37      public static final BeanBindAction INSTANCE = new BeanBindAction();
38  
39      /***
40       * Begins a new element which is to be bound to a bean.
41       */
42      public MappingAction begin(
43          String namespace,
44          String name,
45          Attributes attributes,
46          ReadContext context)
47                      throws Exception {
48                          
49          Log log = context.getLog();
50  
51          ElementDescriptor computedDescriptor = context.getCurrentDescriptor();
52  
53          if (log.isTraceEnabled()) {
54              log.trace("Element Pushed: " + name);
55          }
56  
57          // default to ignoring the current element
58          MappingAction action = MappingAction.EMPTY;
59  
60          Object instance = null;
61          Class beanClass = null;
62          if (computedDescriptor == null) {
63              log.trace("No Descriptor");
64          } else {
65              beanClass = computedDescriptor.getSingularPropertyType();
66          }
67          // TODO: this is a bit of a workaround 
68          // need to come up with a better way of doing maps
69          if (beanClass != null && !Map.class.isAssignableFrom(beanClass)) {
70  
71              instance =
72                  createBean(
73                      namespace,
74                      name,
75                      attributes,
76                      computedDescriptor,
77                      context);
78                      
79              if (instance != null) {
80                  action = this;
81                  if (computedDescriptor.isUseBindTimeTypeForMapping())
82                  {
83                      beanClass = instance.getClass();
84                  }
85                  context.markClassMap(beanClass);
86  
87                  if (log.isTraceEnabled()) {
88                      log.trace("Marked: " + beanClass);
89                  }
90  
91                  context.pushBean(instance);
92  
93                  // if we are a reference to a type we should lookup the original
94                  // as this ElementDescriptor will be 'hollow' 
95                  // and have no child attributes/elements.
96                  // XXX: this should probably be done by the NodeDescriptors...
97                  ElementDescriptor typeDescriptor =
98                      getElementDescriptor(computedDescriptor, context);
99  
100                 // iterate through all attributes        
101                 AttributeDescriptor[] attributeDescriptors =
102                     typeDescriptor.getAttributeDescriptors();
103                 context.populateAttributes(attributeDescriptors, attributes);
104 
105                 if (log.isTraceEnabled()) {
106                     log.trace("Created bean " + instance);
107                 }
108 
109                 // add bean for ID matching
110                 if (context.getMapIDs()) {
111                     // XXX need to support custom ID attribute names
112                     // XXX i have a feeling that the current mechanism might need to change
113                     // XXX so i'm leaving this till later
114                     String id = attributes.getValue("id");
115                     if (id != null) {
116                         context.putBean(id, instance);
117                     }
118                 }
119             }
120         }
121         return action;
122     }
123 
124 
125     public void body(String text, ReadContext context) throws Exception {
126         Log log = context.getLog();
127         // Take the first content descriptor
128         ElementDescriptor currentDescriptor = context.getCurrentDescriptor();
129         if (currentDescriptor == null) {
130             if (log.isTraceEnabled()) {
131                 log.trace("path descriptor is null:");
132             }
133         } else {
134             TextDescriptor bodyTextdescriptor =
135                 currentDescriptor.getPrimaryBodyTextDescriptor();
136             if (bodyTextdescriptor != null) {
137                 if (log.isTraceEnabled()) {
138                     log.trace("Setting mixed content for:");
139                     log.trace(bodyTextdescriptor);
140                 }
141                 Updater updater = bodyTextdescriptor.getUpdater();
142                 if (log.isTraceEnabled())
143                 {    
144                     log.trace("Updating mixed content with:");
145                     log.trace(updater);
146                 }
147                 if (updater != null && text != null) {
148                     updater.update(context, text);
149                 }
150             }
151         }
152     }
153 
154     public void end(ReadContext context) throws Exception {
155         // force any setters of the parent bean to be called for this new bean instance
156         Object instance = context.popBean();
157         update(context, instance);
158     }
159 
160     private void update(ReadContext context, Object value) throws Exception {
161         Log log = context.getLog();
162 
163         Updater updater = context.getCurrentUpdater();
164         
165         if ( updater == null ) {
166             if ( context.getLog().isTraceEnabled() ) {
167                 context.getLog().trace("No updater for " + context.getCurrentElement());
168             }
169         } else {
170             updater.update(context, value);
171         }
172 
173         String poppedElement = context.popElement();
174     }
175 
176 
177 
178 
179     /*** 
180     * Factory method to create new bean instances 
181     *
182     * @param namespace the namespace for the element
183     * @param name the local name
184     * @param attributes the <code>Attributes</code> used to match <code>ID/IDREF</code>
185     * @return the created bean
186     */
187     protected Object createBean(
188         String namespace,
189         String name,
190         Attributes attributes,
191         ElementDescriptor descriptor,
192         ReadContext context) {
193         // TODO: recycle element mappings 
194         // Maybe should move the current mapping into the context
195         ElementMapping mapping = new ElementMapping();
196         Class beanClass = descriptor.getSingularPropertyType();
197         if (beanClass != null && beanClass.isArray()) {
198             beanClass = beanClass.getComponentType();
199         }
200 
201         // TODO: beanClass can be deduced from descriptor
202         // so this feels a little over-engineered
203         mapping.setType(beanClass);
204         mapping.setNamespace(namespace);
205         mapping.setName(name);
206         mapping.setAttributes(attributes);
207         mapping.setDescriptor(descriptor);
208 
209         Object newInstance =
210             context.getBeanCreationChain().create(mapping, context);
211 
212         return newInstance;
213     }
214 
215     /*** Allows the navigation from a reference to a property object to the 
216     * descriptor defining what the property is. i.e. doing the join from a reference 
217     * to a type to lookup its descriptor.
218     * This could be done automatically by the NodeDescriptors. 
219     * Refer to TODO.txt for more info.
220     *
221     * @param propertyDescriptor find descriptor for property object 
222     * referenced by this descriptor
223     * @return descriptor for the singular property class type referenced.
224     */
225     private ElementDescriptor getElementDescriptor(
226         ElementDescriptor propertyDescriptor,
227         ReadContext context) {
228         Log log = context.getLog();
229         Class beanClass = propertyDescriptor.getSingularPropertyType();
230         if (propertyDescriptor.isUseBindTimeTypeForMapping()) {
231             // use the actual bind time type
232             Object current = context.getBean();
233             if (current != null) {
234                 beanClass = current.getClass();
235             }
236         }
237         if (beanClass != null && !Map.class.isAssignableFrom(beanClass)) {
238             if (beanClass.isArray()) {
239                 beanClass = beanClass.getComponentType();
240             }
241             // support for derived beans
242             
243             
244             if (log.isTraceEnabled()) {
245                 log.trace("Filling descriptor for: " + beanClass);
246             }
247             try {
248                 XMLBeanInfo xmlInfo =
249                     context.getXMLIntrospector().introspect(beanClass);
250                 return xmlInfo.getElementDescriptor();
251 
252             } catch (Exception e) {
253                 log.warn("Could not introspect class: " + beanClass, e);
254             }
255         }
256         // could not find a better descriptor so use the one we've got
257         return propertyDescriptor;
258     }
259 
260 }