001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.betwixt.digester;
018
019 import java.beans.BeanInfo;
020 import java.beans.Introspector;
021 import java.beans.PropertyDescriptor;
022 import java.util.Set;
023
024 import org.apache.commons.betwixt.AttributeDescriptor;
025 import org.apache.commons.betwixt.BeanProperty;
026 import org.apache.commons.betwixt.Descriptor;
027 import org.apache.commons.betwixt.ElementDescriptor;
028 import org.apache.commons.betwixt.NodeDescriptor;
029 import org.apache.commons.betwixt.XMLBeanInfo;
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 /** <p><code>AddDefaultsRule</code> appends all the default properties
036 * to the current element.</p>
037 *
038 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
039 */
040 public class AddDefaultsRule extends RuleSupport {
041
042 /** Logger */
043 private static final Log log = LogFactory.getLog( AddDefaultsRule.class );
044
045 /** Base constructor */
046 public AddDefaultsRule() {
047 }
048
049 // Rule interface
050 //-------------------------------------------------------------------------
051
052 /**
053 * Process the beginning of this element.
054 *
055 * @param attributes The attribute list of this element
056 * @throws Exception generally this will indicate an unrecoverable error
057 */
058 public void begin(String name, String namespace, Attributes attributes) throws Exception {
059 boolean addProperties = true;
060 String addPropertiesAttributeValue = attributes.getValue("add-properties");
061 if (addPropertiesAttributeValue != null)
062 {
063 addProperties = Boolean.valueOf(addPropertiesAttributeValue).booleanValue();
064 }
065
066 boolean addAdders = true;
067 String addAddersAttributeValue = attributes.getValue("add-adders");
068 if (addAddersAttributeValue != null)
069 {
070 addAdders = Boolean.valueOf(addAddersAttributeValue).booleanValue();
071 }
072
073 boolean guessNames = true;
074 String guessNamesAttributeValue = attributes.getValue("guess-names");
075 if (guessNamesAttributeValue != null)
076 {
077 guessNames = Boolean.valueOf(guessNamesAttributeValue).booleanValue();
078 }
079
080 if (addProperties) {
081 addDefaultProperties();
082 }
083
084 if (addAdders) {
085 addAdders(guessNames);
086 }
087 }
088
089 /**
090 * Adds default adder methods
091 */
092 private void addAdders(boolean guessNames) {
093 Class beanClass = getBeanClass();
094 // default any addProperty() methods
095 getXMLIntrospector().defaultAddMethods(
096 getRootElementDescriptor(),
097 beanClass, !guessNames);
098 }
099
100 /**
101 * Adds default property methods
102 *
103 */
104 private void addDefaultProperties() {
105 Class beanClass = getBeanClass();
106 Set processedProperties = getProcessedPropertyNameSet();
107 if ( beanClass != null ) {
108 try {
109 boolean attributesForPrimitives = getXMLInfoDigester().isAttributesForPrimitives();
110 BeanInfo beanInfo;
111 if( getXMLIntrospector().getConfiguration().ignoreAllBeanInfo() ) {
112 beanInfo = Introspector.getBeanInfo( beanClass, Introspector.IGNORE_ALL_BEANINFO );
113 }
114 else {
115 beanInfo = Introspector.getBeanInfo( beanClass );
116 }
117 PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
118 if ( descriptors != null ) {
119 for ( int i = 0, size = descriptors.length; i < size; i++ ) {
120 PropertyDescriptor descriptor = descriptors[i];
121 // have we already created a property for this
122 String propertyName = descriptor.getName();
123 if ( processedProperties.contains( propertyName ) ) {
124 continue;
125 }
126 if (!getXMLIntrospector().getConfiguration().getPropertySuppressionStrategy()
127 .suppressProperty(
128 beanClass,
129 descriptor.getPropertyType(),
130 descriptor.getName())) {
131 Descriptor nodeDescriptor =
132 getXMLIntrospector().createXMLDescriptor(new BeanProperty(descriptor));
133 if ( nodeDescriptor != null ) {
134 addDescriptor( nodeDescriptor );
135 }
136 }
137 }
138 }
139 } catch (Exception e) {
140 log.info( "Caught introspection exception", e );
141 }
142 }
143 }
144
145
146 // Implementation methods
147 //-------------------------------------------------------------------------
148
149 /**
150 * Add a desciptor to the top object on the Digester stack.
151 *
152 * @param nodeDescriptor add this <code>NodeDescriptor</code>. Must not be null.
153 * @throws SAXException if the parent for the addDefaults element is not a <element>
154 * or if the top object on the stack is not a <code>XMLBeanInfo</code> or a
155 * <code>ElementDescriptor</code>
156 * @deprecated 0.5 replaced {@link #addDescriptor( Descriptor )}
157 */
158 protected void addDescriptor( NodeDescriptor nodeDescriptor ) throws SAXException {
159 addDescriptor( (Descriptor) nodeDescriptor );
160 }
161
162 /**
163 * Add a desciptor to the top object on the Digester stack.
164 *
165 * @param nodeDescriptor add this <code>NodeDescriptor</code>. Must not be null.
166 * @throws SAXException if the parent for the addDefaults element is not a <element>
167 * or if the top object on the stack is not a <code>XMLBeanInfo</code> or a
168 * <code>ElementDescriptor</code>
169 * @since 0.5
170 */
171 protected void addDescriptor( Descriptor nodeDescriptor ) throws SAXException {
172 Object top = digester.peek();
173 if ( top instanceof XMLBeanInfo ) {
174 log.warn( "It is advisable to put an <addDefaults/> element inside an <element> tag" );
175
176 XMLBeanInfo beanInfo = (XMLBeanInfo) top;
177 // if there is already a root element descriptor then use it
178 // otherwise use this descriptor
179 if ( nodeDescriptor instanceof ElementDescriptor ) {
180 ElementDescriptor elementDescriptor = (ElementDescriptor) nodeDescriptor;
181 ElementDescriptor root = beanInfo.getElementDescriptor() ;
182 if ( root == null ) {
183 beanInfo.setElementDescriptor( elementDescriptor );
184 } else {
185 root.addElementDescriptor( elementDescriptor );
186 }
187 } else {
188 throw new SAXException(
189 "the <addDefaults> element should be within an <element> tag" );
190 }
191 } else if ( top instanceof ElementDescriptor ) {
192 ElementDescriptor parent = (ElementDescriptor) top;
193 if ( nodeDescriptor instanceof ElementDescriptor ) {
194 parent.addElementDescriptor( (ElementDescriptor) nodeDescriptor );
195 } else {
196 parent.addAttributeDescriptor( (AttributeDescriptor) nodeDescriptor );
197 }
198 } else {
199 throw new SAXException(
200 "Invalid use of <addDefaults>. It should be nested inside <element> element" );
201 }
202 }
203
204 /**
205 * Gets an <code>ElementDescriptor</code> for the top on digester's stack.
206 *
207 * @return the top object or the element description if the top object
208 * is an <code>ElementDescriptor</code> or a <code>XMLBeanInfo</code> class (respectively)
209 * Otherwise null.
210 */
211 protected ElementDescriptor getRootElementDescriptor() {
212 Object top = digester.peek();
213 if ( top instanceof XMLBeanInfo ) {
214 XMLBeanInfo beanInfo = (XMLBeanInfo) top;
215 return beanInfo.getElementDescriptor();
216
217 } else if ( top instanceof ElementDescriptor ) {
218 ElementDescriptor parent = (ElementDescriptor) top;
219 // XXX: could maybe walk up the parent hierarchy?
220 return parent;
221 }
222 return null;
223 }
224 }