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