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 */
017package org.apache.xbean.spring.context.impl;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021import org.springframework.beans.MutablePropertyValues;
022import org.springframework.beans.PropertyValue;
023import org.springframework.beans.factory.config.TypedStringValue;
024import org.springframework.beans.factory.support.AbstractBeanDefinition;
025import org.springframework.beans.factory.support.ManagedList;
026import org.w3c.dom.Element;
027import org.w3c.dom.Node;
028
029import javax.xml.namespace.QName;
030
031import java.beans.PropertyDescriptor;
032import java.lang.reflect.Method;
033import java.util.Iterator;
034import java.util.List;
035
036/**
037 * 
038 * @version $Revision: 1.1 $
039 */
040public class QNameHelper {
041    private static final Log log = LogFactory.getLog(QNameHelper.class);
042
043    public static QName createQName(Element element, String qualifiedName) {
044        int index = qualifiedName.indexOf(':');
045        if (index >= 0) {
046            String prefix = qualifiedName.substring(0, index);
047            String localName = qualifiedName.substring(index + 1);
048            String uri = recursiveGetAttributeValue(element, "xmlns:" + prefix);
049            return new QName(uri, localName, prefix);
050        }
051        else {
052            String uri = recursiveGetAttributeValue(element, "xmlns");
053            if (uri != null) {
054                return new QName(uri, qualifiedName);
055            }
056            return new QName(qualifiedName);
057        }
058    }
059
060    /**
061     * Recursive method to find a given attribute value
062     */
063    public static String recursiveGetAttributeValue(Element element, String attributeName) {
064        String answer = null;
065        try {
066            answer = element.getAttribute(attributeName);
067        }
068        catch (Exception e) {
069            if (log.isTraceEnabled()) {
070                log.trace("Caught exception looking up attribute: " + attributeName + " on element: " + element + ". Cause: " + e, e);
071            }
072        }
073        if (answer == null || answer.length() == 0) {
074            Node parentNode = element.getParentNode();
075            if (parentNode instanceof Element) {
076                return recursiveGetAttributeValue((Element) parentNode, attributeName);
077            }
078        }
079        return answer;
080    }
081
082    public static void coerceQNamePropertyValues(QNameReflectionParams params) {
083        coerceNamespaceAwarePropertyValues(params.getBeanDefinition(), params.getElement(), params.getDescriptors(), params.getIndex());
084    }
085    
086    public static void coerceNamespaceAwarePropertyValues(AbstractBeanDefinition bd, Element element, PropertyDescriptor[] descriptors, int i) {
087        PropertyDescriptor descriptor = descriptors[i];
088        // When the property is an indexed property, the getPropertyType can return null.
089        if (descriptor.getPropertyType() == null) {
090            return;
091        }
092        if (descriptor.getPropertyType().isAssignableFrom(QName.class)) {
093            String name = descriptor.getName();
094            MutablePropertyValues propertyValues = bd.getPropertyValues();
095            PropertyValue propertyValue = propertyValues.getPropertyValue(name);
096            if (propertyValue != null) {
097                Object value = propertyValue.getValue();
098                if (value instanceof String) {
099                    propertyValues.removePropertyValue(propertyValue);
100                    addPropertyValue(propertyValues, name, createQName(element, (String) value));
101                } else if (value instanceof TypedStringValue) {
102                    propertyValues.removePropertyValue(propertyValue);
103                    addPropertyValue(propertyValues, name, createQName(element, ((TypedStringValue) value).getValue()));
104                }
105            }
106        } else if (descriptor.getPropertyType().isAssignableFrom(QName[].class)) {
107            String name = descriptor.getName();
108            MutablePropertyValues propertyValues = bd.getPropertyValues();
109            PropertyValue propertyValue = propertyValues.getPropertyValue(name);
110            if (propertyValue != null) {
111                Object value = propertyValue.getValue();
112                if (value instanceof List) {
113                    List values = (List) value;
114                    List newValues = new ManagedList();
115                    for (Iterator iter = values.iterator(); iter.hasNext();) {
116                        Object v = iter.next();
117                        if (v instanceof String) {
118                            newValues.add(createQName(element, (String) v));
119                        } else {
120                            newValues.add(v);
121                        }
122                    }
123                    propertyValues.removePropertyValue(propertyValue);
124                    propertyValues.addPropertyValue(name, newValues);
125                }
126            }
127        }
128    }
129
130    // Fix Spring 1.2.6 to 1.2.7 binary incompatibility.
131    // The addPropertyValueMethod has changed to return a
132    // value instead of void.
133    // So use reflectiom to handle both cases.
134    private static final Method addPropertyValueMethod;
135    static {
136        try {
137            addPropertyValueMethod = MutablePropertyValues.class.getMethod(
138                        "addPropertyValue",
139                        new Class[] { String.class, Object.class });
140        } catch (Exception e) {
141            throw new RuntimeException("Unable to find MutablePropertyValues:addPropertyValue", e);
142        }
143    }
144    public static void addPropertyValue(MutablePropertyValues values, String name, Object value) {
145        try {
146            addPropertyValueMethod.invoke(values, new Object[] { name, value });
147        } catch (Exception e) {
148            throw new RuntimeException("Error adding property definition", e);
149        }
150    }
151
152}