View Javadoc

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