001 package org.apache.commons.betwixt.digester; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 import java.beans.BeanInfo; 020 import java.beans.Introspector; 021 import java.beans.PropertyDescriptor; 022 import java.lang.reflect.Method; 023 024 import org.apache.commons.betwixt.AttributeDescriptor; 025 import org.apache.commons.betwixt.ElementDescriptor; 026 import org.apache.commons.betwixt.XMLUtils; 027 import org.apache.commons.betwixt.expression.ConstantExpression; 028 import org.apache.commons.betwixt.expression.MethodExpression; 029 import org.apache.commons.betwixt.expression.MethodUpdater; 030 import org.apache.commons.logging.Log; 031 import org.apache.commons.logging.LogFactory; 032 import org.xml.sax.Attributes; 033 import org.xml.sax.SAXException; 034 035 /** 036 * <p><code>AttributeRule</code> the digester Rule for parsing the 037 * <attribute> elements.</p> 038 * 039 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 040 * @version $Id: AttributeRule.java 438373 2006-08-30 05:17:21Z bayard $ 041 */ 042 public class AttributeRule extends RuleSupport { 043 044 /** Logger */ 045 private static final Log log = LogFactory.getLog( AttributeRule.class ); 046 /** This loads all classes created by name. Defaults to this class's classloader */ 047 private ClassLoader classLoader; 048 /** The <code>Class</code> whose .betwixt file is being digested */ 049 private Class beanClass; 050 051 /** Base constructor */ 052 public AttributeRule() { 053 this.classLoader = getClass().getClassLoader(); 054 } 055 056 // Rule interface 057 //------------------------------------------------------------------------- 058 059 /** 060 * Process the beginning of this element. 061 * 062 * @param attributes The attribute list of this element 063 * @throws SAXException 1. If the attribute tag is not inside an element tag. 064 * 2. If the name attribute is not valid XML attribute name. 065 */ 066 public void begin(String name, String namespace, Attributes attributes) throws SAXException { 067 068 AttributeDescriptor descriptor = new AttributeDescriptor(); 069 String nameAttributeValue = attributes.getValue( "name" ); 070 071 // check that name is well formed 072 if ( !XMLUtils.isWellFormedXMLName( nameAttributeValue ) ) { 073 throw new SAXException("'" + nameAttributeValue + "' would not be a well formed xml attribute name."); 074 } 075 076 String qName = nameAttributeValue; 077 descriptor.setLocalName( nameAttributeValue ); 078 String uri = attributes.getValue( "uri" ); 079 if ( uri != null ) { 080 descriptor.setURI( uri ); 081 String prefix = getXMLIntrospector().getConfiguration().getPrefixMapper().getPrefix(uri); 082 qName = prefix + ":" + nameAttributeValue; 083 } 084 descriptor.setQualifiedName( qName ); 085 086 String propertyName = attributes.getValue( "property" ); 087 descriptor.setPropertyName( propertyName ); 088 descriptor.setPropertyType( loadClass( attributes.getValue( "type" ) ) ); 089 090 if ( propertyName != null && propertyName.length() > 0 ) { 091 configureDescriptor(descriptor); 092 } else { 093 String value = attributes.getValue( "value" ); 094 if ( value != null ) { 095 descriptor.setTextExpression( new ConstantExpression( value ) ); 096 } 097 } 098 099 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 }