001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    
021    package org.apache.xbean.blueprint.context.impl;
022    
023    import java.beans.BeanInfo;
024    import java.beans.IntrospectionException;
025    import java.beans.Introspector;
026    import java.beans.PropertyDescriptor;
027    import java.beans.PropertyEditor;
028    import java.io.ByteArrayInputStream;
029    import java.io.IOException;
030    import java.io.InputStream;
031    import java.lang.reflect.Method;
032    import java.net.URL;
033    import java.util.Collection;
034    import java.util.HashMap;
035    import java.util.HashSet;
036    import java.util.Map;
037    import java.util.Properties;
038    import java.util.Set;
039    
040    import javax.annotation.PostConstruct;
041    import javax.annotation.PreDestroy;
042    
043    import org.apache.aries.blueprint.NamespaceHandler;
044    import org.apache.aries.blueprint.ParserContext;
045    import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
046    import org.apache.aries.blueprint.mutable.MutableCollectionMetadata;
047    import org.apache.aries.blueprint.mutable.MutableMapMetadata;
048    import org.apache.aries.blueprint.mutable.MutableRefMetadata;
049    import org.apache.aries.blueprint.mutable.MutableValueMetadata;
050    import org.osgi.framework.Bundle;
051    import org.osgi.service.blueprint.container.ComponentDefinitionException;
052    import org.osgi.service.blueprint.reflect.BeanMetadata;
053    import org.osgi.service.blueprint.reflect.BeanProperty;
054    import org.osgi.service.blueprint.reflect.CollectionMetadata;
055    import org.osgi.service.blueprint.reflect.ComponentMetadata;
056    import org.osgi.service.blueprint.reflect.MapEntry;
057    import org.osgi.service.blueprint.reflect.Metadata;
058    import org.osgi.service.blueprint.reflect.NonNullMetadata;
059    import org.osgi.service.blueprint.reflect.NullMetadata;
060    import org.osgi.service.blueprint.reflect.RefMetadata;
061    import org.osgi.service.blueprint.reflect.ReferenceMetadata;
062    import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata;
063    import org.osgi.service.blueprint.reflect.ValueMetadata;
064    import org.slf4j.Logger;
065    import org.slf4j.LoggerFactory;
066    import org.w3c.dom.Attr;
067    import org.w3c.dom.Element;
068    import org.w3c.dom.NamedNodeMap;
069    import org.w3c.dom.Node;
070    import org.w3c.dom.NodeList;
071    
072    /**
073     * @version $Rev: 923807 $ $Date: 2010-03-16 11:30:46 -0400 (Tue, 16 Mar 2010) $
074     */
075    public class XBeanNamespaceHandler implements NamespaceHandler {
076    
077        private static final Logger LOGGER = LoggerFactory.getLogger(XBeanNamespaceHandler.class);
078    
079        public static final String BLUEPRINT_NAMESPACE = "http://www.osgi.org/xmlns/blueprint/v1.0.0";
080        private static final String BEAN_REFERENCE_PREFIX = "#";
081        private static final String NULL_REFERENCE = "#null";
082    
083        private final String namespace;
084        private final URL schemaLocation;
085        private final Set<Class> managedClasses;
086        private final MappingMetaData mappingMetaData;
087        private final Map<String, Class> managedClassesByName;
088        private final Map<String, Class<? extends PropertyEditor>> propertyEditors;
089        private final NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs();
090    
091        public XBeanNamespaceHandler(String namespace, URL schemaLocation, Set<Class> managedClasses, Map<String, Class<? extends PropertyEditor>> propertyEditors, Properties properties) {
092            this.namespace = namespace;
093            this.schemaLocation = schemaLocation;
094            this.managedClasses = managedClasses;
095            managedClassesByName = mapClasses(managedClasses);
096            this.propertyEditors = propertyEditors;
097            this.mappingMetaData = new MappingMetaData(properties);
098        }
099    
100        public XBeanNamespaceHandler(String namespace, String schemaLocation, Bundle bundle, String propertiesLocation) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
101            URL propertiesUrl = bundle.getResource(propertiesLocation);
102            InputStream in = propertiesUrl.openStream();
103            Properties properties = new Properties();
104            try {
105                properties.load(in);
106            } finally {
107                in.close();
108            }
109            this.namespace = namespace;
110            this.schemaLocation = bundle.getEntry(schemaLocation);
111            this.managedClasses = managedClassesFromProperties(bundle, properties);
112            managedClassesByName = mapClasses(managedClasses);
113            propertyEditors = propertyEditorsFromProperties(bundle, properties);
114            this.mappingMetaData = new MappingMetaData(properties);
115        }
116    
117        public XBeanNamespaceHandler(String namespace, String schemaLocation, String propertiesLocation) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
118            ClassLoader cl = getClass().getClassLoader();
119            URL propertiesUrl = cl.getResource(propertiesLocation);
120            if (propertiesUrl == null) {
121                throw new IOException("Could not locate properties at " + propertiesLocation);
122            }
123            InputStream in = propertiesUrl.openStream();
124            Properties properties = new Properties();
125            try {
126                properties.load(in);
127            } finally {
128                in.close();
129            }
130            this.namespace = namespace;
131            this.schemaLocation = cl.getResource(schemaLocation);
132            this.managedClasses = managedClassesFromProperties(cl, properties);
133            managedClassesByName = mapClasses(managedClasses);
134            propertyEditors = propertyEditorsFromProperties(cl, properties);
135            this.mappingMetaData = new MappingMetaData(properties);
136        }
137    
138        private static Set<Class> managedClassesFromProperties(ClassLoader cl, Properties properties) {
139            Set<Class> managedClasses = new HashSet<Class>();
140            Properties methods = new Properties();
141            for (Map.Entry entry : properties.entrySet()) {
142                String key = (String) entry.getKey();
143                if (key.indexOf(".") < 0) {
144                    String className = (String) entry.getValue();
145                    try {
146                        Class<?> beanClass = cl.loadClass(className);
147                        managedClasses.add(beanClass);
148                        findAnnotations(key, beanClass, methods);
149                    } catch (NoClassDefFoundError e) {
150                        LOGGER.warn("Could not load class: {} due to {}", className, e.getMessage());
151                    } catch (ClassNotFoundException e) {
152                        LOGGER.warn("Could not load class: {}", className);
153                    }
154                }
155            }
156            properties.putAll(methods);
157            return managedClasses;
158        }
159    
160        private static Set<Class> managedClassesFromProperties(Bundle bundle, Properties properties) {
161            Set<Class> managedClasses = new HashSet<Class>();
162            Properties methods = new Properties();
163            for (Map.Entry entry : properties.entrySet()) {
164                String key = (String) entry.getKey();
165                if (key.indexOf(".") < 0) {
166                    String className = (String) entry.getValue();
167                    try {
168                        Class<?> beanClass = bundle.loadClass(className);
169                        managedClasses.add(beanClass);
170                        findAnnotations(key, beanClass, methods);
171                    } catch (NoClassDefFoundError e) {
172                        LOGGER.warn("Could not load class: {} due to {}", className, e.getMessage());
173                    } catch (ClassNotFoundException e) {
174                        LOGGER.warn("Could not load class: {}", className);
175                    }
176                }
177            }
178            properties.putAll(methods);
179            return managedClasses;
180        }
181    
182        private static void findAnnotations(String key, Class<?> beanClass, Properties methods) {
183            for (Method m : beanClass.getMethods()) {
184                if (m.isAnnotationPresent(PostConstruct.class)) {
185                    methods.put(key + ".initMethod", m.getName());
186                }
187                if (m.isAnnotationPresent(PreDestroy.class)) {
188                    methods.put(key + ".destroyMethod", m.getName());
189                }
190            }
191        }
192    
193        private Map<String, Class<? extends PropertyEditor>> propertyEditorsFromProperties(Bundle bundle, Properties properties) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
194            Map<String, Class<? extends PropertyEditor>> propertyEditors = new HashMap<String, Class<? extends PropertyEditor>>();
195            for (Map.Entry entry : properties.entrySet()) {
196                String key = (String) entry.getKey();
197                if (key.endsWith(".propertyEditor")) {
198                    String className = (String) entry.getValue();
199                    Class<? extends PropertyEditor> clazz = bundle.loadClass(className).asSubclass(PropertyEditor.class);
200                    propertyEditors.put(className, clazz);
201                }
202            }
203            return propertyEditors;
204        }
205    
206        private Map<String, Class<? extends PropertyEditor>> propertyEditorsFromProperties(ClassLoader classLoader, Properties properties) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
207            Map<String, Class<? extends PropertyEditor>> propertyEditors = new HashMap<String, Class<? extends PropertyEditor>>();
208            for (Map.Entry entry : properties.entrySet()) {
209                String key = (String) entry.getKey();
210                if (key.endsWith(".propertyEditor")) {
211                    String className = (String) entry.getValue();
212                    Class<? extends PropertyEditor> clazz = classLoader.loadClass(className).asSubclass(PropertyEditor.class);
213                    propertyEditors.put(className, clazz);
214                }
215            }
216            return propertyEditors;
217        }
218    
219        private Map<String, Class> mapClasses(Set<Class> managedClasses) {
220            Map<String, Class> map = new HashMap<String, Class>();
221            for (Class clazz : managedClasses) {
222                map.put(clazz.getName(), clazz);
223            }
224            return map;
225        }
226    
227        public URL getSchemaLocation(String s) {
228            if (namespace.equals(s)) {
229                return schemaLocation;
230            }
231            return null;
232        }
233    
234        public Set<Class> getManagedClasses() {
235            return managedClasses;
236        }
237    
238        public Metadata parse(Element element, ParserContext parserContext) {
239            String beanTypeName = element.getLocalName();
240            String className = mappingMetaData.getClassName(beanTypeName);
241            if (className == null) {
242                throw new ComponentDefinitionException(beanTypeName + " not known to xbean namespace handler for " + namespace);
243            }
244            return parseInternal(element, parserContext, beanTypeName, className);
245        }
246    
247        private Metadata parseInternal(Element element, ParserContext parserContext, String beanTypeName, String className) {
248            MutableBeanMetadata beanMetaData = parserContext.createMetadata(MutableBeanMetadata.class);
249            beanMetaData.setClassName(className);
250            beanMetaData.setScope(BeanMetadata.SCOPE_SINGLETON);
251            beanMetaData.setActivation(BeanMetadata.ACTIVATION_EAGER);
252            beanMetaData.setRuntimeClass(managedClassesByName.get(className));
253            if (beanMetaData.getRuntimeClass() == null) {
254                throw new ComponentDefinitionException("Unknown bean class: " + className);
255            }
256    
257            if (element.hasAttributeNS(BLUEPRINT_NAMESPACE, "id")) {
258                String id = element.getAttributeNS(BLUEPRINT_NAMESPACE, "id");
259                beanMetaData.setId(id);
260            } else {
261                beanMetaData.setId(parserContext.generateId());
262            }
263    
264            lifecycleMethods(beanTypeName, beanMetaData);
265    
266            attributeProperties(element, parserContext, beanTypeName, beanMetaData);
267            contentProperty(beanMetaData, element, parserContext);
268            nestedProperties(beanMetaData, element, beanTypeName, className, parserContext);
269            //QName resolution
270            coerceNamespaceAwarePropertyValues(beanMetaData, element, parserContext);
271            namedConstructorArgs.processParameters(beanMetaData, mappingMetaData, parserContext);
272            return beanMetaData;
273        }
274    
275        private void lifecycleMethods(String beanTypeName, MutableBeanMetadata beanMetaData) {
276            String initMethod = mappingMetaData.getInitMethodName(beanTypeName);
277            if (initMethod != null) {
278                beanMetaData.setInitMethod(initMethod);
279            }
280            String destroyMethod = mappingMetaData.getDestroyMethodName(beanTypeName);
281            if (destroyMethod != null) {
282                beanMetaData.setDestroyMethod(destroyMethod);
283            }
284            String factoryMethod = mappingMetaData.getFactoryMethodName(beanTypeName);
285            if (factoryMethod != null) {
286                beanMetaData.setFactoryMethod(factoryMethod);
287            }
288        }
289    
290        private void attributeProperties(Element element, ParserContext parserContext, String beanTypeName, MutableBeanMetadata beanMetaData) {
291            NamedNodeMap attrs = element.getAttributes();
292            for (int i = 0; i < attrs.getLength(); i++) {
293                Attr attr = (Attr) attrs.item(i);
294                if (namespace.equals(attr.getNamespaceURI()) || attr.getNamespaceURI() == null) {
295                    String attrName = attr.getLocalName();
296                    String value = attr.getValue().trim();
297                    String propertyName = mappingMetaData.getPropertyName(beanTypeName, attrName);
298                    String propertyEditor = mappingMetaData.getPropertyEditor(beanTypeName, attrName);
299                    addProperty(propertyName, value, propertyEditor, beanMetaData, parserContext);
300                }
301            }
302        }
303    
304        private void contentProperty(MutableBeanMetadata definition, Element element, ParserContext parserContext) {
305            String name = mappingMetaData.getContentProperty(element.getLocalName());
306            String propertyEditor = mappingMetaData.getPropertyEditor(element.getLocalName(), name);
307            if (name != null) {
308                String value = getElementText(element).trim();
309                addProperty(name, value, propertyEditor, definition, parserContext);
310            } else {
311                ByteArrayInputStream in = new ByteArrayInputStream(getElementText(element).getBytes());
312                Properties properties = new Properties();
313                try {
314                    properties.load(in);
315                }
316                catch (IOException e) {
317                    return;
318                }
319                for (Map.Entry<Object, Object> entry : properties.entrySet()) {
320                    String key = (String) entry.getKey();
321                    String value = (String) entry.getValue();
322                    addProperty(key, value, propertyEditor, definition, parserContext);
323                }
324            }
325        }
326    
327        private void addProperty(String name, String value, String propertyEditor, MutableBeanMetadata definition, ParserContext parserContext) {
328            Metadata m = getValue(value, propertyEditor, parserContext);
329            definition.addProperty(name, m);
330        }
331    
332        private void nestedProperties(MutableBeanMetadata beanMetadata, Element element, String beanTypeName, String className, ParserContext parserContext) {
333            NodeList nodes = element.getChildNodes();
334            for (int i = 0; i < nodes.getLength(); i++) {
335                Node node = nodes.item(i);
336                if (node instanceof Element) {
337                    Element child = (Element) node;
338                    String childName = child.getLocalName();
339                    String namespace = child.getNamespaceURI();
340                    if (!this.namespace.equals(namespace)) {
341                        BeanProperty prop = parserContext.parseElement(BeanProperty.class, beanMetadata, child);
342                        beanMetadata.addProperty(prop);
343                        continue;
344                    }
345                    Metadata childMetadata = null;
346                    PropertyDescriptor pd = getPropertyDescriptor(mappingMetaData.getClassName(beanTypeName), childName);
347                    Class propertyType = pd == null ? null : pd.getPropertyType();
348                    String propertyName = mappingMetaData.getNestedListProperty(beanTypeName, childName);
349                    //explicit list
350                    if (propertyName != null || isCollectionType(propertyType)) {
351                        propertyName = propertyName == null ? childName : propertyName;
352                        childMetadata = parserContext.parseElement(CollectionMetadata.class, beanMetadata, child);
353                    } else if ((propertyName = mappingMetaData.getFlatCollectionProperty(beanTypeName, childName)) != null) {
354                        //flat collection
355                        Metadata elementMetadata = parse(child, parserContext);
356                        BeanProperty list = propertyByName(propertyName, beanMetadata);
357                        MutableCollectionMetadata listMeta;
358                        if (list == null) {
359                            listMeta = parserContext.createMetadata(MutableCollectionMetadata.class);
360                            childMetadata = listMeta;
361                        } else {
362                            listMeta = (MutableCollectionMetadata) list.getValue();
363                        }
364                        listMeta.addValue(elementMetadata);
365                    } else if ((propertyName = mappingMetaData.getNestedProperty(beanTypeName, childName)) != null) {
366                        // lets find the first child bean that parses fine
367                        childMetadata = parseChildExtensionBean(child, beanMetadata, parserContext);
368    
369                    } else if (mappingMetaData.isFlatProperty(beanTypeName, childName)) {
370                        propertyName = childName;
371                        String flatClassName = getPropertyDescriptor(mappingMetaData.getClassName(beanTypeName), childName).getPropertyType().getName();
372                        childMetadata = parseInternal(child, parserContext, childName, flatClassName);
373                    } else {
374                        childMetadata = tryParseNestedPropertyViaIntrospection(beanMetadata, className, child, parserContext);
375                        propertyName = childName;
376                    }
377                    if (childMetadata == null) {
378                        String text = getElementText(child);
379                        if (text != null) {
380                            MutableValueMetadata m = parserContext.createMetadata(MutableValueMetadata.class);
381                            m.setStringValue(text.trim());
382                            childMetadata = m;
383                        }
384    
385    
386    //                    propertyName = mappingMetaData.getPropertyName(beanTypeName, childName);
387    //                    NodeList childNodes = child.getChildNodes();
388    //                    StringBuilder buf = new StringBuilder();
389    //                    for (int j = 0; j < childNodes.getLength(); j++) {
390    //                        Node childNode = childNodes.item(j);
391    //                        if (childNode instanceof Element) {
392    //                            Element childElement = (Element) childNode;
393    //                            if (namespace.equals(childElement.getNamespaceURI())) {
394    //                                childMetadata = parse(childElement, parserContext);
395    //                            } else {
396    //                                try {
397    //                                    childMetadata = parserContext.parseElement(BeanMetadata.class, beanMetaData, childElement);
398    //                                } catch (Exception e) {
399    //                                    childMetadata = parserContext.parseElement(ValueMetadata.class, beanMetaData, childElement);
400    //                                }
401    //                            }
402    //
403    //                            break;
404    //                        } else if (childNode instanceof Text) {
405    //                            String value = childNode.getNodeValue();
406    //                            buf.append(value);
407    //                        }
408    //                    }
409    //                    if (childMetadata == null) {
410    //                        MutableValueMetadata m = parserContext.createMetadata(MutableValueMetadata.class);
411    //                        m.setStringValue(buf.toString().trim());
412    //                        childMetadata = m;
413    //                    }
414                    }
415                    if (childMetadata != null) {
416                        beanMetadata.addProperty(propertyName, childMetadata);
417                    }
418                }
419            }
420        }
421    
422        private Metadata parseChildExtensionBean(Element child, MutableBeanMetadata beanMetadata, ParserContext parserContext) {
423            NodeList nl = child.getChildNodes();
424            for (int i = 0; i < nl.getLength(); i++) {
425                Node node = nl.item(i);
426                if (node instanceof Element) {
427                    Element childElement = (Element) node;
428                    String uri = childElement.getNamespaceURI();
429                    String localName = childElement.getLocalName();
430                    Metadata value = parserContext.parseElement(Metadata.class, beanMetadata, childElement);
431                    if (value != null) {
432                        return value;
433                    }
434                    //TODO ARIES-111
435    //                if (uri == null ||
436    //                        uri.equals(BLUEPRINT_NAMESPACE)) {
437    //                    if ("bean".equals(localName)) {
438    //                        return parserContext.parseElement(BeanMetadata.class, beanMetadata, childElement);
439    //                    } else {
440    //                        return parserContext.parseElement(ValueMetadata.class, beanMetadata, childElement);
441    //                    }
442    //                } else {
443    //                    Metadata value = parse(childElement, parserContext);
444    //                    if (value != null) {
445    //                        return value;
446    //                    }
447    //                }
448                }
449            }
450            return null;
451        }
452    
453        private Metadata tryParseNestedPropertyViaIntrospection(MutableBeanMetadata beanMetadata, String className, Element element, ParserContext parserContext) {
454            String localName = element.getLocalName();
455            PropertyDescriptor descriptor = getPropertyDescriptor(className, localName);
456            if (descriptor != null) {
457                return parseNestedPropertyViaIntrospection(beanMetadata, element, descriptor.getName(), descriptor.getPropertyType(), parserContext);
458            } else {
459                return parseNestedPropertyViaIntrospection(beanMetadata, element, localName, Object.class, parserContext);
460            }
461        }
462    
463        private Metadata parseNestedPropertyViaIntrospection(MutableBeanMetadata beanMetadata, Element element, String propertyName, Class propertyType, ParserContext parserContext) {
464            if (isMap(propertyType)) {
465                return parseCustomMapElement(beanMetadata, element, propertyName, parserContext);
466            } else if (isCollection(propertyType)) {
467                return parserContext.parseElement(CollectionMetadata.class, beanMetadata, element);
468            } else {
469                return parseChildExtensionBean(element, beanMetadata, parserContext);
470            }
471        }
472    
473        private boolean isMap(Class type) {
474            return Map.class.isAssignableFrom(type);
475        }
476    
477        /**
478         * Returns true if the given type is a collection type or an array
479         */
480        private boolean isCollection(Class type) {
481            return type.isArray() || Collection.class.isAssignableFrom(type);
482        }
483    
484        protected String getLocalName(Element element) {
485            String localName = element.getLocalName();
486            if (localName == null) {
487                localName = element.getNodeName();
488            }
489            return localName;
490        }
491    
492        protected Metadata parseCustomMapElement(MutableBeanMetadata beanMetadata, Element element, String name, ParserContext parserContext) {
493            MutableMapMetadata map = parserContext.createMetadata(MutableMapMetadata.class);
494    
495            Element parent = (Element) element.getParentNode();
496            String entryName = mappingMetaData.getMapEntryName(getLocalName(parent), name);
497            String keyName = mappingMetaData.getMapKeyName(getLocalName(parent), name);
498            String dups = mappingMetaData.getMapDupsMode(getLocalName(parent), name);
499            boolean flat = mappingMetaData.isFlatMap(getLocalName(parent), name);
500            String defaultKey = mappingMetaData.getMapDefaultKey(getLocalName(parent), name);
501    
502            if (entryName == null) entryName = "property";
503            if (keyName == null) keyName = "key";
504            if (dups == null) dups = "replace";
505    
506            // TODO : support further customizations
507            //String valueName = "value";
508            //boolean keyIsAttr = true;
509            //boolean valueIsAttr = false;
510            NodeList nl = element.getChildNodes();
511            for (int i = 0; i < nl.getLength(); i++) {
512                Node node = nl.item(i);
513                if (node instanceof Element) {
514                    Element childElement = (Element) node;
515    
516                    String localName = childElement.getLocalName();
517                    String uri = childElement.getNamespaceURI();
518                    if (localName == null || localName.equals("xmlns") || localName.startsWith("xmlns:")) {
519                        continue;
520                    }
521    
522                    // we could use namespaced attributes to differentiate real spring
523                    // attributes from namespace-specific attributes
524                    if (!flat && !isEmpty(uri) && localName.equals(entryName)) {
525                        String key = childElement.getAttributeNS(uri, keyName);
526                        if (key == null || key.length() == 0) {
527                            key = defaultKey;
528                        }
529                        if (key == null) {
530                            throw new RuntimeException("No key defined for map " + entryName);
531                        }
532    
533                        NonNullMetadata keyValue = (NonNullMetadata) getValue(key, mappingMetaData.getPropertyEditor(localName, key), parserContext);
534    
535                        Element valueElement = getFirstChildElement(childElement);
536                        Metadata value;
537                        if (valueElement != null) {
538                            value = parserContext.parseElement(Metadata.class, beanMetadata, valueElement);
539    //                        String valueElUri = valueElement.getNamespaceURI();
540    //                        String valueElLocalName = valueElement.getLocalName();
541    //                        if (valueElUri == null ||
542    //                                valueElUri.equals(BLUEPRINT_NAMESPACE)) {
543    //                            if ("bean".equals(valueElLocalName)) {
544    //                                value = parserContext.parseElement(BeanMetadata.class, beanMetadata, valueElement);
545    //                            } else {
546    //                                value = parserContext.parseElement(BeanProperty.class, beanMetadata, valueElement).getValue();
547    //                            }
548    //                        } else {
549    //                            value = parserContext.parseElement(ValueMetadata.class, beanMetadata, valueElement);
550    //                        }
551                        } else {
552                            value = getValue(getElementText(childElement), mappingMetaData.getPropertyEditor(localName, key), parserContext);
553                        }
554    
555                        addValueToMap(map, keyValue, value, dups, parserContext);
556                    } else if (flat && !isEmpty(uri)) {
557                        String key = childElement.getAttributeNS(uri, keyName);
558                        if (key == null || key.length() == 0) {
559                            key = defaultKey;
560                        }
561                        if (key == null) {
562                            throw new RuntimeException("No key defined for map entry " + entryName);
563                        }
564                        NonNullMetadata keyValue = (NonNullMetadata) getValue(key, mappingMetaData.getPropertyEditor(localName, key), parserContext);
565                        childElement = cloneElement(childElement);
566                        childElement.removeAttributeNS(uri, keyName);
567                        Metadata bdh = parse(childElement, parserContext);
568                        addValueToMap(map, keyValue, bdh, dups, parserContext);
569                    }
570                }
571            }
572            return map;
573        }
574    
575        /**
576         * Creates a clone of the element and its attribute (though not its content)
577         */
578        protected Element cloneElement(Element element) {
579            Element answer = element.getOwnerDocument().createElementNS(element.getNamespaceURI(), element.getNodeName());
580            NamedNodeMap attributes = element.getAttributes();
581            for (int i = 0, size = attributes.getLength(); i < size; i++) {
582                Attr attribute = (Attr) attributes.item(i);
583                String uri = attribute.getNamespaceURI();
584                answer.setAttributeNS(uri, attribute.getName(), attribute.getNodeValue());
585            }
586            return answer;
587        }
588    
589    
590        protected void addValueToMap(MutableMapMetadata map, NonNullMetadata keyValue, Metadata value, String dups, ParserContext parserContext) {
591            if (hasKey(map, keyValue)) {
592                if ("discard".equalsIgnoreCase(dups)) {
593                    // Do nothing
594                } else if ("replace".equalsIgnoreCase(dups)) {
595                    map.addEntry(keyValue, value);
596                } else if ("allow".equalsIgnoreCase(dups)) {
597                    MutableCollectionMetadata l = parserContext.createMetadata(MutableCollectionMetadata.class);
598                    l.addValue(get(map, keyValue));
599                    l.addValue(value);
600                    map.addEntry(keyValue, l);
601                } else if ("always".equalsIgnoreCase(dups)) {
602                    MutableCollectionMetadata l = (MutableCollectionMetadata) get(map, keyValue);
603                    l.addValue(value);
604                }
605            } else {
606                if ("always".equalsIgnoreCase(dups)) {
607                    MutableCollectionMetadata l = (MutableCollectionMetadata) get(map, keyValue);
608                    if (l == null) {
609                        l = parserContext.createMetadata(MutableCollectionMetadata.class);
610                        map.addEntry(keyValue, l);
611                    }
612                    l.addValue(value);
613                } else {
614                    map.addEntry(keyValue, value);
615                }
616            }
617        }
618    
619        private Metadata get(MutableMapMetadata map, NonNullMetadata keyValue) {
620            for (Object entryo : map.getEntries()) {
621                MapEntry entry = (MapEntry)entryo;
622                if (equals(entry.getKey(), keyValue)) {
623                    return entry.getValue();
624                }
625            }
626            return null;
627        }
628    
629        private boolean equals(NonNullMetadata key1, NonNullMetadata key2) {
630            if (key1 == key2) return true;
631            if (key1.getClass() != key2.getClass()) return false;
632            if (key1 instanceof RefMetadata) return ((RefMetadata) key1).getComponentId().equals(((RefMetadata) key2).getComponentId());
633            if (key1 instanceof ReferenceMetadata) {
634                if (((ReferenceMetadata) key1).getTimeout() != ((ReferenceMetadata) key2).getTimeout()) return false;
635            }
636            if (key1 instanceof ServiceReferenceMetadata) {
637                ServiceReferenceMetadata sr1 = (ServiceReferenceMetadata) key1;
638                ServiceReferenceMetadata sr2 = (ServiceReferenceMetadata) key2;
639                return sr1.getAvailability() == sr2.getAvailability()
640                        && sr1.getInterface().equals(sr2.getInterface())
641                        && sr1.getComponentName().equals(sr2.getComponentName())
642                        && sr1.getFilter().equals(sr2.getFilter())
643                        && sr1.getReferenceListeners().equals(sr2.getReferenceListeners())
644    
645                        && sr1.getId().equals(sr2.getId())
646                        && sr1.getActivation() == sr2.getActivation()
647                        && sr1.getDependsOn().equals(sr2.getDependsOn());
648            }
649            if (key1 instanceof ValueMetadata) {
650                ValueMetadata v1 = (ValueMetadata) key1;
651                ValueMetadata v2 = (ValueMetadata) key2;
652                if (v1.getStringValue() != null ? v1.getStringValue().equals(v2.getStringValue()) : v2.getStringValue() == null
653                        && v1.getType() != null ? v1.getType().equals(v2.getType()) : v2.getType() == null) {
654                    return true;
655                }
656            }
657            return false;
658        }
659    
660        private boolean hasKey(MutableMapMetadata map, NonNullMetadata keyValue) {
661            return get(map, keyValue) != null;
662        }
663    
664        protected boolean isEmpty(String uri) {
665            return uri == null || uri.length() == 0;
666        }
667    
668        protected Metadata getValue(String value, String propertyEditorName, ParserContext parserContext) {
669            if (value == null) return null;
670    
671            //
672            // If value is #null then we are explicitly setting the value null instead of an empty string
673            //
674            if (NULL_REFERENCE.equals(value)) {
675                return parserContext.createMetadata(NullMetadata.class);
676            }
677    
678            //
679            // If value starts with # then we have a ref
680            //
681            if (value.startsWith(BEAN_REFERENCE_PREFIX)) {
682                // strip off the #
683                value = value.substring(BEAN_REFERENCE_PREFIX.length());
684    
685                // if the new value starts with a #, then we had an excaped value (e.g. ##value)
686                if (!value.startsWith(BEAN_REFERENCE_PREFIX)) {
687                    MutableRefMetadata ref = parserContext.createMetadata(MutableRefMetadata.class);
688                    ref.setComponentId(value);
689                    return ref;
690                }
691            }
692    
693    //        if( propertyEditor!=null ) {
694    //              PropertyEditor p = createPropertyEditor(propertyEditor);
695    //
696    //              RootBeanDefinition def = new RootBeanDefinition();
697    //              def.setBeanClass(PropertyEditorFactory.class);
698    //              def.getPropertyValues().addPropertyValue("propertyEditor", p);
699    //              def.getPropertyValues().addPropertyValue("value", value);
700    //
701    //              return def;
702    //        }
703    
704            //
705            // Neither null nor a reference
706            //
707            MutableValueMetadata metadata = parserContext.createMetadata(MutableValueMetadata.class);
708            if (propertyEditorName != null) {
709                PropertyEditor propertyEditor;
710                try {
711                    propertyEditor = propertyEditors.get(propertyEditorName).newInstance();
712                } catch (InstantiationException e) {
713                    throw new ComponentDefinitionException("Could not create a " + propertyEditorName + " to convert value " + value + " for namespace " + namespace);
714                } catch (IllegalAccessException e) {
715                    throw new ComponentDefinitionException("Could not create a " + propertyEditorName + " to convert value " + value + " for namespace " + namespace);
716                }
717                propertyEditor.setAsText(value);
718                value = propertyEditor.getAsText();
719            }
720            metadata.setStringValue(value);
721            return metadata;
722        }
723    
724        protected Element getFirstChildElement(Element element) {
725            NodeList nl = element.getChildNodes();
726            for (int i = 0; i < nl.getLength(); i++) {
727                Node node = nl.item(i);
728                if (node instanceof Element) {
729                    return (Element) node;
730                }
731            }
732            return null;
733        }
734    
735    
736        private boolean isCollectionType(Class propertyType) {
737            if (propertyType == null) {
738                return false;
739            }
740            if (Collection.class.isAssignableFrom(propertyType)) {
741                return true;
742            }
743            if (propertyType.isArray()) {
744                return true;
745            }
746            return false;
747        }
748    
749        public static BeanProperty propertyByName(String name, BeanMetadata meta) {
750            for (Object propo : meta.getProperties()) {
751                BeanProperty prop = (BeanProperty)propo;
752                if (name.equals(prop.getName())) {
753                    return prop;
754                }
755            }
756            return null;
757        }
758    
759        public ComponentMetadata decorate(Node node, ComponentMetadata componentMetadata, ParserContext parserContext) {
760            return componentMetadata;
761        }
762    
763        private void coerceNamespaceAwarePropertyValues(MutableBeanMetadata bd, Element element, ParserContext parserContext) {
764            // lets check for any QName types
765            BeanInfo beanInfo = getBeanInfo(getClass(bd.getClassName()));
766            if (beanInfo != null) {
767                PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
768                for (PropertyDescriptor descriptor : descriptors) {
769                    QNameHelper.coerceNamespaceAwarePropertyValues(bd, element, descriptor, parserContext);
770                }
771            }
772        }
773    
774        private PropertyDescriptor getPropertyDescriptor(String className, String localName) {
775            Class clazz = getClass(className);
776            return getPropertyDescriptor(clazz, localName);
777        }
778    
779        private PropertyDescriptor getPropertyDescriptor(Class clazz, String localName) {
780            BeanInfo beanInfo = getBeanInfo(clazz);
781            if (beanInfo != null) {
782                PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
783                for (int i = 0; i < descriptors.length; i++) {
784                    PropertyDescriptor descriptor = descriptors[i];
785                    String name = descriptor.getName();
786                    if (name.equals(localName)) {
787                        return descriptor;
788                    }
789                }
790            }
791            return null;
792        }
793    
794        private Class getClass(String className) throws ComponentDefinitionException {
795            if (className == null) {
796                return null;
797            }
798    
799            Class type = managedClassesByName.get(className);
800            if (type == null) {
801                throw new ComponentDefinitionException("Unknown type: " + className);
802            }
803            return type;
804        }
805    
806        private BeanInfo getBeanInfo(Class type) {
807            if (type == null) {
808                return null;
809            }
810            try {
811                return Introspector.getBeanInfo(type);
812            }
813            catch (IntrospectionException e) {
814                throw new ComponentDefinitionException("Failed to introspect type: " + type.getName() + ". Reason: " + e, e);
815            }
816        }
817    
818        private String getElementText(Element element) {
819            StringBuilder buffer = new StringBuilder();
820            NodeList nodeList = element.getChildNodes();
821            for (int i = 0, size = nodeList.getLength(); i < size; i++) {
822                Node node = nodeList.item(i);
823                if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) {
824                    buffer.append(node.getNodeValue());
825                }
826            }
827            return buffer.toString();
828        }
829    
830    }