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.camel.spring.handler;
018    
019    import org.w3c.dom.Attr;
020    import org.w3c.dom.Element;
021    import org.w3c.dom.NamedNodeMap;
022    
023    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
024    import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
025    import org.springframework.core.Conventions;
026    import org.springframework.util.Assert;
027    import org.springframework.util.StringUtils;
028    
029    
030    /**
031     * A base class for a parser for a bean.
032     *
033     * @version $Revision: 747759 $
034     */
035    // TODO cannot use AbstractSimpleBeanDefinitionParser
036    // as doParse() is final and isEligableAttribute does not allow us to filter out attributes
037    // with the name "xmlns:"
038    public class BeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
039        private Class type;
040    
041        protected BeanDefinitionParser() {
042        }
043    
044        public BeanDefinitionParser(Class type) {
045            this.type = type;
046        }
047    
048        protected Class getBeanClass(Element element) {
049            if (type == null) {
050                type = loadType();
051            }
052            return type;
053        }
054    
055        protected Class loadType() {
056            throw new IllegalArgumentException("No type specified!");
057        }
058    
059        protected boolean isEligibleAttribute(String attributeName) {
060            return attributeName != null && !ID_ATTRIBUTE.equals(attributeName)
061                    && !attributeName.equals("xmlns") && !attributeName.startsWith("xmlns:");
062        }
063    
064        // TODO the following code is copied from AbstractSimpleBeanDefinitionParser
065        // it can be removed if ever the doParse() method is not final!
066        // or the Spring bug http://jira.springframework.org/browse/SPR-4599 is resolved
067    
068        /**
069         * Parse the supplied {@link Element} and populate the supplied
070         * {@link BeanDefinitionBuilder} as required.
071         * <p>This implementation maps any attributes present on the
072         * supplied element to {@link org.springframework.beans.PropertyValue}
073         * instances, and
074         * {@link BeanDefinitionBuilder#addPropertyValue(String, Object) adds them}
075         * to the
076         * {@link org.springframework.beans.factory.config.BeanDefinition builder}.
077         * <p>The {@link #extractPropertyName(String)} method is used to
078         * reconcile the name of an attribute with the name of a JavaBean
079         * property.
080         *
081         * @param element the XML element being parsed
082         * @param builder used to define the <code>BeanDefinition</code>
083         * @see #extractPropertyName(String)
084         */
085        protected final void doParse(Element element, BeanDefinitionBuilder builder) {
086            NamedNodeMap attributes = element.getAttributes();
087            for (int x = 0; x < attributes.getLength(); x++) {
088                Attr attribute = (Attr) attributes.item(x);
089                String name = attribute.getLocalName();
090                String fullName = attribute.getName();
091                if (!fullName.startsWith("xmlns:") && !fullName.equals("xmlns") && isEligibleAttribute(name)) {
092                    String propertyName = extractPropertyName(name);
093                    Assert.state(StringUtils.hasText(propertyName),
094                            "Illegal property name returned from 'extractPropertyName(String)': cannot be null or empty.");
095                    builder.addPropertyValue(propertyName, attribute.getValue());
096                }
097            }
098            postProcess(builder, element);
099        }
100    
101    
102        /**
103         * Extract a JavaBean property name from the supplied attribute name.
104         * <p>The default implementation uses the
105         * {@link Conventions#attributeNameToPropertyName(String)}
106         * method to perform the extraction.
107         * <p>The name returned must obey the standard JavaBean property name
108         * conventions. For example for a class with a setter method
109         * '<code>setBingoHallFavourite(String)</code>', the name returned had
110         * better be '<code>bingoHallFavourite</code>' (with that exact casing).
111         *
112         * @param attributeName the attribute name taken straight from the
113         *                      XML element being parsed (never <code>null</code>)
114         * @return the extracted JavaBean property name (must never be <code>null</code>)
115         */
116        protected String extractPropertyName(String attributeName) {
117            return Conventions.attributeNameToPropertyName(attributeName);
118        }
119    
120        /**
121         * Hook method that derived classes can implement to inspect/change a
122         * bean definition after parsing is complete.
123         * <p>The default implementation does nothing.
124         *
125         * @param beanDefinition the parsed (and probably totally defined) bean definition being built
126         * @param element        the XML element that was the source of the bean definition's metadata
127         */
128        protected void postProcess(BeanDefinitionBuilder beanDefinition, Element element) {
129        }
130    
131    }