View Javadoc

1   package org.apache.commons.betwixt.digester;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   * 
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */ 
19  import java.beans.BeanInfo;
20  import java.beans.Introspector;
21  import java.beans.PropertyDescriptor;
22  import java.lang.reflect.Method;
23  
24  import org.apache.commons.betwixt.AttributeDescriptor;
25  import org.apache.commons.betwixt.ElementDescriptor;
26  import org.apache.commons.betwixt.XMLUtils;
27  import org.apache.commons.betwixt.expression.ConstantExpression;
28  import org.apache.commons.betwixt.expression.MethodExpression;
29  import org.apache.commons.betwixt.expression.MethodUpdater;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.xml.sax.Attributes;
33  import org.xml.sax.SAXException;
34  
35  /** 
36    * <p><code>AttributeRule</code> the digester Rule for parsing the 
37    * &lt;attribute&gt; elements.</p>
38    *
39    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
40    * @version $Id: AttributeRule.java 438373 2006-08-30 05:17:21Z bayard $
41    */
42  public class AttributeRule extends RuleSupport {
43  
44      /** Logger */
45      private static final Log log = LogFactory.getLog( AttributeRule.class );
46      /** This loads all classes created by name. Defaults to this class's classloader */
47      private ClassLoader classLoader;
48      /** The <code>Class</code> whose .betwixt file is being digested */
49      private Class beanClass;
50      
51      /** Base constructor */
52      public AttributeRule() {
53          this.classLoader = getClass().getClassLoader();
54      }
55      
56      // Rule interface
57      //-------------------------------------------------------------------------    
58      
59      /**
60       * Process the beginning of this element.
61       *
62       * @param attributes The attribute list of this element
63       * @throws SAXException 1. If the attribute tag is not inside an element tag.
64       * 2. If the name attribute is not valid XML attribute name.
65       */
66      public void begin(String name, String namespace, Attributes attributes) throws SAXException {
67          
68          AttributeDescriptor descriptor = new AttributeDescriptor();
69          String nameAttributeValue = attributes.getValue( "name" );
70  
71          // check that name is well formed 
72          if ( !XMLUtils.isWellFormedXMLName( nameAttributeValue ) ) {
73              throw new SAXException("'" + nameAttributeValue + "' would not be a well formed xml attribute name.");
74          }
75          
76          String qName = nameAttributeValue;
77          descriptor.setLocalName( nameAttributeValue );
78          String uri = attributes.getValue( "uri" );
79          if ( uri != null ) {
80              descriptor.setURI( uri );  
81              String prefix = getXMLIntrospector().getConfiguration().getPrefixMapper().getPrefix(uri);
82              qName = prefix + ":" + nameAttributeValue; 
83          }
84          descriptor.setQualifiedName( qName );
85          
86          String propertyName = attributes.getValue( "property" );
87          descriptor.setPropertyName( propertyName );
88          descriptor.setPropertyType( loadClass( attributes.getValue( "type" ) ) );
89          
90          if ( propertyName != null && propertyName.length() > 0 ) {
91              configureDescriptor(descriptor);
92          } else {
93              String value = attributes.getValue( "value" );
94              if ( value != null ) {
95                  descriptor.setTextExpression( new ConstantExpression( value ) );
96              }
97          }
98  
99          Object top = digester.peek();
100         if ( top instanceof ElementDescriptor ) {
101             ElementDescriptor parent = (ElementDescriptor) top;
102             parent.addAttributeDescriptor( descriptor );
103         } else {
104             throw new SAXException( "Invalid use of <attribute>. It should " 
105                 + "be nested inside an <element> element" );
106         }            
107 
108         digester.push(descriptor);        
109     }
110 
111 
112     /**
113      * Process the end of this element.
114      */
115     public void end(String name, String namespace) {
116         AttributeDescriptor descriptor = (AttributeDescriptor)digester.pop();
117         ElementDescriptor parent = (ElementDescriptor)digester.peek();
118 
119         // check for attribute suppression
120         if( getXMLIntrospector().getConfiguration().getAttributeSuppressionStrategy().suppress(descriptor)) {
121             parent.removeAttributeDescriptor(descriptor);
122         }
123     }
124 
125     
126     // Implementation methods
127     //-------------------------------------------------------------------------    
128     /**
129      * Loads a class (using the appropriate classloader)
130      *
131      * @param name the name of the class to load
132      * @return the class instance loaded by the appropriate classloader
133      */
134     protected Class loadClass( String name ) {
135         // XXX: should use a ClassLoader to handle complex class loading situations
136         if ( name != null ) {
137             try {
138                 return classLoader.loadClass(name);
139             } catch (Exception e) { // SWALLOW
140             }
141         }
142         return null;            
143     }
144     
145     /** 
146      * Set the Expression and Updater from a bean property name 
147      * @param attributeDescriptor configure this <code>AttributeDescriptor</code> 
148      * from the property with a matching name in the bean class
149      */
150     protected void configureDescriptor(AttributeDescriptor attributeDescriptor) {
151         Class beanClass = getBeanClass();
152         if ( beanClass != null ) {
153             String name = attributeDescriptor.getPropertyName();
154             try {
155                 BeanInfo beanInfo;
156                 if( getXMLIntrospector().getConfiguration().ignoreAllBeanInfo() ) {
157                     beanInfo = Introspector.getBeanInfo( beanClass, Introspector.IGNORE_ALL_BEANINFO );
158                 }
159                 else {
160                     beanInfo = Introspector.getBeanInfo( beanClass );
161                 }
162                 PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
163                 if ( descriptors != null ) {
164                     for ( int i = 0, size = descriptors.length; i < size; i++ ) {
165                         PropertyDescriptor descriptor = descriptors[i];
166                         if ( name.equals( descriptor.getName() ) ) {
167                             configureProperty( attributeDescriptor, descriptor );
168                             getProcessedPropertyNameSet().add( name );
169                             break;
170                         }
171                     }
172                 }
173             } catch (Exception e) {
174                 log.warn( "Caught introspection exception", e );
175             }
176         }
177     }    
178     
179     /**
180      * Configure an <code>AttributeDescriptor</code> from a <code>PropertyDescriptor</code>
181      *
182      * @param attributeDescriptor configure this <code>AttributeDescriptor</code>
183      * @param propertyDescriptor configure from this <code>PropertyDescriptor</code>
184      */
185     private void configureProperty( 
186                                     AttributeDescriptor attributeDescriptor, 
187                                     PropertyDescriptor propertyDescriptor ) {
188         Class type = propertyDescriptor.getPropertyType();
189         Method readMethod = propertyDescriptor.getReadMethod();
190         Method writeMethod = propertyDescriptor.getWriteMethod();
191         
192         if ( readMethod == null ) {
193             log.trace( "No read method" );
194             return;
195         }
196         
197         if ( log.isTraceEnabled() ) {
198             log.trace( "Read method=" + readMethod );
199         }
200         
201         // choose response from property type
202         if ( getXMLIntrospector().isLoopType( type ) ) {
203             log.warn( "Using loop type for an attribute. Type = " 
204                     + type.getName() + " attribute: " + attributeDescriptor.getQualifiedName() );
205         }
206 
207         log.trace( "Standard property" );
208         attributeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
209         
210         if ( writeMethod != null ) {
211             attributeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
212         }
213         
214         attributeDescriptor.setPropertyName( propertyDescriptor.getName() );
215         attributeDescriptor.setPropertyType( type );        
216         
217         // XXX: associate more bean information with the descriptor?
218         //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
219         //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
220     }
221     
222 }