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
046: .getLog(AttributeRule.class);
047: /** This loads all classes created by name. Defaults to this class's classloader */
048: private ClassLoader classLoader;
049: /** The <code>Class</code> whose .betwixt file is being digested */
050: private Class beanClass;
051:
052: /** Base constructor */
053: public AttributeRule() {
054: this .classLoader = getClass().getClassLoader();
055: }
056:
057: // Rule interface
058: //-------------------------------------------------------------------------
059:
060: /**
061: * Process the beginning of this element.
062: *
063: * @param attributes The attribute list of this element
064: * @throws SAXException 1. If the attribute tag is not inside an element tag.
065: * 2. If the name attribute is not valid XML attribute name.
066: */
067: public void begin(String name, String namespace,
068: Attributes attributes) throws SAXException {
069:
070: AttributeDescriptor descriptor = new AttributeDescriptor();
071: String nameAttributeValue = attributes.getValue("name");
072:
073: // check that name is well formed
074: if (!XMLUtils.isWellFormedXMLName(nameAttributeValue)) {
075: throw new SAXException(
076: "'"
077: + nameAttributeValue
078: + "' would not be a well formed xml attribute name.");
079: }
080:
081: String qName = nameAttributeValue;
082: descriptor.setLocalName(nameAttributeValue);
083: String uri = attributes.getValue("uri");
084: if (uri != null) {
085: descriptor.setURI(uri);
086: String prefix = getXMLIntrospector().getConfiguration()
087: .getPrefixMapper().getPrefix(uri);
088: qName = prefix + ":" + nameAttributeValue;
089: }
090: descriptor.setQualifiedName(qName);
091:
092: String propertyName = attributes.getValue("property");
093: descriptor.setPropertyName(propertyName);
094: descriptor.setPropertyType(loadClass(attributes
095: .getValue("type")));
096:
097: if (propertyName != null && propertyName.length() > 0) {
098: configureDescriptor(descriptor);
099: } else {
100: String value = attributes.getValue("value");
101: if (value != null) {
102: descriptor.setTextExpression(new ConstantExpression(
103: value));
104: }
105: }
106:
107: Object top = digester.peek();
108: if (top instanceof ElementDescriptor) {
109: ElementDescriptor parent = (ElementDescriptor) top;
110: parent.addAttributeDescriptor(descriptor);
111: } else {
112: throw new SAXException(
113: "Invalid use of <attribute>. It should "
114: + "be nested inside an <element> element");
115: }
116:
117: digester.push(descriptor);
118: }
119:
120: /**
121: * Process the end of this element.
122: */
123: public void end(String name, String namespace) {
124: AttributeDescriptor descriptor = (AttributeDescriptor) digester
125: .pop();
126: ElementDescriptor parent = (ElementDescriptor) digester.peek();
127:
128: // check for attribute suppression
129: if (getXMLIntrospector().getConfiguration()
130: .getAttributeSuppressionStrategy().suppress(descriptor)) {
131: parent.removeAttributeDescriptor(descriptor);
132: }
133: }
134:
135: // Implementation methods
136: //-------------------------------------------------------------------------
137: /**
138: * Loads a class (using the appropriate classloader)
139: *
140: * @param name the name of the class to load
141: * @return the class instance loaded by the appropriate classloader
142: */
143: protected Class loadClass(String name) {
144: // XXX: should use a ClassLoader to handle complex class loading situations
145: if (name != null) {
146: try {
147: return classLoader.loadClass(name);
148: } catch (Exception e) { // SWALLOW
149: }
150: }
151: return null;
152: }
153:
154: /**
155: * Set the Expression and Updater from a bean property name
156: * @param attributeDescriptor configure this <code>AttributeDescriptor</code>
157: * from the property with a matching name in the bean class
158: */
159: protected void configureDescriptor(
160: AttributeDescriptor attributeDescriptor) {
161: Class beanClass = getBeanClass();
162: if (beanClass != null) {
163: String name = attributeDescriptor.getPropertyName();
164: try {
165: BeanInfo beanInfo;
166: if (getXMLIntrospector().getConfiguration()
167: .ignoreAllBeanInfo()) {
168: beanInfo = Introspector.getBeanInfo(beanClass,
169: Introspector.IGNORE_ALL_BEANINFO);
170: } else {
171: beanInfo = Introspector.getBeanInfo(beanClass);
172: }
173: PropertyDescriptor[] descriptors = beanInfo
174: .getPropertyDescriptors();
175: if (descriptors != null) {
176: for (int i = 0, size = descriptors.length; i < size; i++) {
177: PropertyDescriptor descriptor = descriptors[i];
178: if (name.equals(descriptor.getName())) {
179: configureProperty(attributeDescriptor,
180: descriptor);
181: getProcessedPropertyNameSet().add(name);
182: break;
183: }
184: }
185: }
186: } catch (Exception e) {
187: log.warn("Caught introspection exception", e);
188: }
189: }
190: }
191:
192: /**
193: * Configure an <code>AttributeDescriptor</code> from a <code>PropertyDescriptor</code>
194: *
195: * @param attributeDescriptor configure this <code>AttributeDescriptor</code>
196: * @param propertyDescriptor configure from this <code>PropertyDescriptor</code>
197: */
198: private void configureProperty(
199: AttributeDescriptor attributeDescriptor,
200: PropertyDescriptor propertyDescriptor) {
201: Class type = propertyDescriptor.getPropertyType();
202: Method readMethod = propertyDescriptor.getReadMethod();
203: Method writeMethod = propertyDescriptor.getWriteMethod();
204:
205: if (readMethod == null) {
206: log.trace("No read method");
207: return;
208: }
209:
210: if (log.isTraceEnabled()) {
211: log.trace("Read method=" + readMethod);
212: }
213:
214: // choose response from property type
215: if (getXMLIntrospector().isLoopType(type)) {
216: log.warn("Using loop type for an attribute. Type = "
217: + type.getName() + " attribute: "
218: + attributeDescriptor.getQualifiedName());
219: }
220:
221: log.trace("Standard property");
222: attributeDescriptor.setTextExpression(new MethodExpression(
223: readMethod));
224:
225: if (writeMethod != null) {
226: attributeDescriptor.setUpdater(new MethodUpdater(
227: writeMethod));
228: }
229:
230: attributeDescriptor.setPropertyName(propertyDescriptor
231: .getName());
232: attributeDescriptor.setPropertyType(type);
233:
234: // XXX: associate more bean information with the descriptor?
235: //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
236: //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
237: }
238:
239: }
|