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 }