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    }