1 /***
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.xbean.spring.context.v2c;
18
19 import java.beans.BeanInfo;
20 import java.beans.PropertyDescriptor;
21 import java.beans.PropertyEditor;
22 import java.io.ByteArrayInputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Enumeration;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Properties;
35 import java.util.Set;
36
37 import javax.xml.XMLConstants;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.xbean.spring.context.impl.MappingMetaData;
42 import org.apache.xbean.spring.context.impl.NamedConstructorArgs;
43 import org.apache.xbean.spring.context.impl.NamespaceHelper;
44 import org.springframework.beans.PropertyValue;
45 import org.springframework.beans.PropertyEditorRegistrar;
46 import org.springframework.beans.PropertyEditorRegistry;
47 import org.springframework.beans.factory.BeanDefinitionStoreException;
48 import org.springframework.beans.factory.config.BeanDefinition;
49 import org.springframework.beans.factory.config.BeanDefinitionHolder;
50 import org.springframework.beans.factory.config.RuntimeBeanReference;
51 import org.springframework.beans.factory.parsing.BeanComponentDefinition;
52 import org.springframework.beans.factory.support.AbstractBeanDefinition;
53 import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
54 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
55 import org.springframework.beans.factory.support.ManagedList;
56 import org.springframework.beans.factory.support.ManagedMap;
57 import org.springframework.beans.factory.support.RootBeanDefinition;
58 import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
59 import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
60 import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
61 import org.springframework.beans.factory.xml.NamespaceHandler;
62 import org.springframework.beans.factory.xml.ParserContext;
63 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
64 import org.springframework.context.support.AbstractApplicationContext;
65 import org.springframework.util.StringUtils;
66
67 import org.w3c.dom.Attr;
68 import org.w3c.dom.Element;
69 import org.w3c.dom.NamedNodeMap;
70 import org.w3c.dom.Node;
71 import org.w3c.dom.NodeList;
72 import org.w3c.dom.Text;
73
74 /***
75 * An enhanced XML parser capable of handling custom XML schemas.
76 *
77 * @author James Strachan
78 * @version $Id$
79 * @since 2.0
80 */
81 public class XBeanNamespaceHandler implements NamespaceHandler {
82
83 public static final String SPRING_SCHEMA = "http://xbean.apache.org/schemas/spring/1.0";
84 public static final String SPRING_SCHEMA_COMPAT = "http://xbean.org/schemas/spring/1.0";
85
86 private static final Log log = LogFactory.getLog(XBeanNamespaceHandler.class);
87
88 private static final String QNAME_ELEMENT = "qname";
89
90 private static final String DESCRIPTION_ELEMENT = "description";
91
92 /***
93 * All the reserved Spring XML element names which cannot be overloaded by
94 * an XML extension
95 */
96 protected static final String[] RESERVED_ELEMENT_NAMES = {
97 "beans",
98 DESCRIPTION_ELEMENT,
99 DefaultBeanDefinitionDocumentReader.IMPORT_ELEMENT,
100 DefaultBeanDefinitionDocumentReader.ALIAS_ELEMENT,
101 DefaultBeanDefinitionDocumentReader.BEAN_ELEMENT,
102 BeanDefinitionParserDelegate.CONSTRUCTOR_ARG_ELEMENT,
103 BeanDefinitionParserDelegate.PROPERTY_ELEMENT,
104 BeanDefinitionParserDelegate.LOOKUP_METHOD_ELEMENT,
105 BeanDefinitionParserDelegate.REPLACED_METHOD_ELEMENT,
106 BeanDefinitionParserDelegate.ARG_TYPE_ELEMENT,
107 BeanDefinitionParserDelegate.REF_ELEMENT,
108 BeanDefinitionParserDelegate.IDREF_ELEMENT,
109 BeanDefinitionParserDelegate.VALUE_ELEMENT,
110 BeanDefinitionParserDelegate.NULL_ELEMENT,
111 BeanDefinitionParserDelegate.LIST_ELEMENT,
112 BeanDefinitionParserDelegate.SET_ELEMENT,
113 BeanDefinitionParserDelegate.MAP_ELEMENT,
114 BeanDefinitionParserDelegate.ENTRY_ELEMENT,
115 BeanDefinitionParserDelegate.KEY_ELEMENT,
116 BeanDefinitionParserDelegate.PROPS_ELEMENT,
117 BeanDefinitionParserDelegate.PROP_ELEMENT,
118 QNAME_ELEMENT };
119
120 protected static final String[] RESERVED_BEAN_ATTRIBUTE_NAMES = {
121 AbstractBeanDefinitionParser.ID_ATTRIBUTE,
122 BeanDefinitionParserDelegate.NAME_ATTRIBUTE,
123 BeanDefinitionParserDelegate.CLASS_ATTRIBUTE,
124 BeanDefinitionParserDelegate.PARENT_ATTRIBUTE,
125 BeanDefinitionParserDelegate.DEPENDS_ON_ATTRIBUTE,
126 BeanDefinitionParserDelegate.FACTORY_METHOD_ATTRIBUTE,
127 BeanDefinitionParserDelegate.FACTORY_BEAN_ATTRIBUTE,
128 BeanDefinitionParserDelegate.DEPENDENCY_CHECK_ATTRIBUTE,
129 BeanDefinitionParserDelegate.AUTOWIRE_ATTRIBUTE,
130 BeanDefinitionParserDelegate.INIT_METHOD_ATTRIBUTE,
131 BeanDefinitionParserDelegate.DESTROY_METHOD_ATTRIBUTE,
132 BeanDefinitionParserDelegate.ABSTRACT_ATTRIBUTE,
133 BeanDefinitionParserDelegate.SINGLETON_ATTRIBUTE,
134 BeanDefinitionParserDelegate.LAZY_INIT_ATTRIBUTE };
135
136 private static final String JAVA_PACKAGE_PREFIX = "java://";
137
138 private static final String BEAN_REFERENCE_PREFIX = "#";
139 private static final String NULL_REFERENCE = "#null";
140
141 private Set reservedElementNames = new HashSet(Arrays.asList(RESERVED_ELEMENT_NAMES));
142 private Set reservedBeanAttributeNames = new HashSet(Arrays.asList(RESERVED_BEAN_ATTRIBUTE_NAMES));
143 protected final NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs();
144
145 private ParserContext parserContext;
146
147 private XBeanQNameHelper qnameHelper;
148
149 public void init() {
150 }
151
152 public BeanDefinition parse(Element element, ParserContext parserContext) {
153 this.parserContext = parserContext;
154 this.qnameHelper = new XBeanQNameHelper(parserContext.getReaderContext());
155 BeanDefinitionHolder holder = parseBeanFromExtensionElement(element);
156
157 if (element.getParentNode() == element.getOwnerDocument() ||
158 element.getParentNode().getParentNode() == element.getOwnerDocument()) {
159 BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
160 BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
161 parserContext.getReaderContext().fireComponentRegistered(componentDefinition);
162 }
163 return holder.getBeanDefinition();
164 }
165
166 public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
167 if (node instanceof org.w3c.dom.Attr && XMLConstants.XMLNS_ATTRIBUTE.equals(node.getLocalName())) {
168 return definition;
169 }
170 throw new IllegalArgumentException("Cannot locate BeanDefinitionDecorator for "
171 + (node instanceof Element ? "element" : "attribute") + " [" +
172 node.getLocalName() + "].");
173 }
174
175 /***
176 * Configures the XmlBeanDefinitionReader to work nicely with extensible XML
177 * using this reader implementation.
178 */
179 public static void configure(AbstractApplicationContext context, XmlBeanDefinitionReader reader) {
180 reader.setNamespaceAware(true);
181 reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
182 }
183
184 /***
185 * Registers whatever custom editors we need
186 */
187 public static void registerCustomEditors(DefaultListableBeanFactory beanFactory) {
188 PropertyEditorRegistrar registrar = new PropertyEditorRegistrar() {
189 public void registerCustomEditors(PropertyEditorRegistry registry) {
190 registry.registerCustomEditor(java.io.File.class, new org.apache.xbean.spring.context.impl.FileEditor());
191 registry.registerCustomEditor(java.net.URI.class, new org.apache.xbean.spring.context.impl.URIEditor());
192 registry.registerCustomEditor(java.util.Date.class, new org.apache.xbean.spring.context.impl.DateEditor());
193 registry.registerCustomEditor(javax.management.ObjectName.class, new org.apache.xbean.spring.context.impl.ObjectNameEditor());
194 }
195 };
196
197 beanFactory.addPropertyEditorRegistrar(registrar);
198 }
199
200 /***
201 * Parses the non-standard XML element as a Spring bean definition
202 */
203 protected BeanDefinitionHolder parseBeanFromExtensionElement(Element element, String parentClass, String property) {
204 String uri = element.getNamespaceURI();
205 String localName = getLocalName(element);
206
207 MappingMetaData metadata = findNamespaceProperties(uri, localName);
208 if (metadata != null) {
209
210 String className = getPropertyDescriptor(parentClass, property).getPropertyType().getName();
211 if (className != null) {
212 return parseBeanFromExtensionElement(element, metadata, className);
213 }
214 }
215 return null;
216 }
217
218 private BeanDefinitionHolder parseBeanFromExtensionElement(Element element, MappingMetaData metadata, String className) {
219 Element original = cloneElement(element);
220
221 element.setAttributeNS(null, "class", className);
222 addSpringAttributeValues(className, element);
223 BeanDefinitionHolder definition = parserContext.getDelegate().parseBeanDefinitionElement(element, null);
224 addAttributeProperties(definition, metadata, className, original);
225 addContentProperty(definition, metadata, element);
226 addNestedPropertyElements(definition, metadata, className, element);
227 qnameHelper.coerceNamespaceAwarePropertyValues(definition.getBeanDefinition(), element);
228 declareLifecycleMethods(definition, metadata, element);
229 resolveBeanClass((AbstractBeanDefinition) definition.getBeanDefinition(), definition.getBeanName());
230 namedConstructorArgs.processParameters(definition, metadata);
231 return definition;
232 }
233
234 protected Class resolveBeanClass(AbstractBeanDefinition bd, String beanName) {
235 if (bd.hasBeanClass()) {
236 return bd.getBeanClass();
237 }
238 try {
239 ClassLoader cl = parserContext.getReaderContext().getReader().getBeanClassLoader();
240 if (cl == null) {
241 cl = Thread.currentThread().getContextClassLoader();
242 }
243 if (cl == null) {
244 cl = getClass().getClassLoader();
245 }
246 return bd.resolveBeanClass(cl);
247 }
248 catch (ClassNotFoundException ex) {
249 throw new BeanDefinitionStoreException(bd.getResourceDescription(),
250 beanName, "Bean class [" + bd.getBeanClassName() + "] not found", ex);
251 }
252 catch (NoClassDefFoundError err) {
253 throw new BeanDefinitionStoreException(bd.getResourceDescription(),
254 beanName, "Class that bean class [" + bd.getBeanClassName() + "] depends on not found", err);
255 }
256 }
257
258
259 /***
260 * Parses the non-standard XML element as a Spring bean definition
261 */
262 protected BeanDefinitionHolder parseBeanFromExtensionElement(Element element) {
263 String uri = element.getNamespaceURI();
264 String localName = getLocalName(element);
265
266 MappingMetaData metadata = findNamespaceProperties(uri, localName);
267 if (metadata != null) {
268
269 String className = metadata.getClassName(localName);
270 if (className != null) {
271 return parseBeanFromExtensionElement(element, metadata, className);
272 } else {
273 throw new BeanDefinitionStoreException("Unrecognized xbean element mapping: " + localName + " in namespace " + uri);
274 }
275 } else {
276 if (uri == null) throw new BeanDefinitionStoreException("Unrecognized Spring element: " + localName);
277 else throw new BeanDefinitionStoreException("Unrecognized xbean namespace mapping: " + uri);
278 }
279 }
280
281 protected void addSpringAttributeValues(String className, Element element) {
282 NamedNodeMap attributes = element.getAttributes();
283 for (int i = 0, size = attributes.getLength(); i < size; i++) {
284 Attr attribute = (Attr) attributes.item(i);
285 String uri = attribute.getNamespaceURI();
286 String localName = attribute.getLocalName();
287
288 if (uri != null && (uri.equals(SPRING_SCHEMA) || uri.equals(SPRING_SCHEMA_COMPAT))) {
289 element.setAttributeNS(null, localName, attribute.getNodeValue());
290 }
291 }
292 }
293
294 /***
295 * Creates a clone of the element and its attribute (though not its content)
296 */
297 protected Element cloneElement(Element element) {
298 Element answer = element.getOwnerDocument().createElementNS(element.getNamespaceURI(), element.getNodeName());
299 NamedNodeMap attributes = element.getAttributes();
300 for (int i = 0, size = attributes.getLength(); i < size; i++) {
301 Attr attribute = (Attr) attributes.item(i);
302 String uri = attribute.getNamespaceURI();
303 answer.setAttributeNS(uri, attribute.getName(), attribute.getNodeValue());
304 }
305 return answer;
306 }
307
308 /***
309 * Parses attribute names and values as being bean property expressions
310 */
311 protected void addAttributeProperties(BeanDefinitionHolder definition, MappingMetaData metadata, String className,
312 Element element) {
313 NamedNodeMap attributes = element.getAttributes();
314
315 for (int i = 0, size = attributes.getLength(); i < size; i++) {
316 Attr attribute = (Attr) attributes.item(i);
317 String uri = attribute.getNamespaceURI();
318 String localName = attribute.getLocalName();
319
320 if (localName == null || localName.equals("xmlns") || localName.startsWith("xmlns:")) {
321 continue;
322 }
323
324 if (isEmpty(uri) && !localName.equals("class")) {
325 boolean addProperty = true;
326 if (reservedBeanAttributeNames.contains(localName)) {
327
328 PropertyDescriptor descriptor = getPropertyDescriptor(className, localName);
329 addProperty = descriptor != null;
330 }
331 if (addProperty) {
332 addAttributeProperty(definition, metadata, element, attribute);
333 }
334 }
335 }
336
337 for (int i = 0, size = attributes.getLength(); i < size; i++) {
338 Attr attribute = (Attr) attributes.item(i);
339 String uri = attribute.getNamespaceURI();
340 String localName = attribute.getLocalName();
341
342 if (localName == null || localName.equals("xmlns") || localName.startsWith("xmlns:")) {
343 continue;
344 }
345
346 if (!isEmpty(uri) && uri.equals(element.getNamespaceURI())) {
347 boolean addProperty = true;
348 if (reservedBeanAttributeNames.contains(localName)) {
349
350 PropertyDescriptor descriptor = getPropertyDescriptor(className, localName);
351 addProperty = descriptor != null;
352 }
353 if (addProperty) {
354 addAttributeProperty(definition, metadata, element, attribute);
355 }
356 }
357 }
358 }
359
360 protected void addContentProperty(BeanDefinitionHolder definition, MappingMetaData metadata, Element element) {
361 String name = metadata.getContentProperty(getLocalName(element));
362 if (name != null) {
363 String value = getElementText(element);
364 addProperty(definition, metadata, element, name, value);
365 }
366 else {
367 StringBuffer buffer = new StringBuffer();
368 NodeList childNodes = element.getChildNodes();
369 for (int i = 0, size = childNodes.getLength(); i < size; i++) {
370 Node node = childNodes.item(i);
371 if (node instanceof Text) {
372 buffer.append(((Text) node).getData());
373 }
374 }
375
376 ByteArrayInputStream in = new ByteArrayInputStream(buffer.toString().getBytes());
377 Properties properties = new Properties();
378 try {
379 properties.load(in);
380 }
381 catch (IOException e) {
382 return;
383 }
384 Enumeration enumeration = properties.propertyNames();
385 while (enumeration.hasMoreElements()) {
386 String propertyName = (String) enumeration.nextElement();
387 String propertyEditor = metadata.getPropertyEditor(getLocalName(element), propertyName);
388
389 Object value = getValue(properties.getProperty(propertyName), propertyEditor);
390 definition.getBeanDefinition().getPropertyValues().addPropertyValue(propertyName, value);
391 }
392 }
393 }
394
395 protected void addAttributeProperty(BeanDefinitionHolder definition, MappingMetaData metadata, Element element,
396 Attr attribute) {
397 String localName = attribute.getLocalName();
398 String value = attribute.getValue();
399 addProperty(definition, metadata, element, localName, value);
400 }
401
402 /***
403 * Add a property onto the current BeanDefinition.
404 */
405 protected void addProperty(BeanDefinitionHolder definition, MappingMetaData metadata, Element element,
406 String localName, String value) {
407 String propertyName = metadata.getPropertyName(getLocalName(element), localName);
408 String propertyEditor = metadata.getPropertyEditor(getLocalName(element), propertyName);
409 if (propertyName != null) {
410 definition.getBeanDefinition().getPropertyValues().addPropertyValue(
411 propertyName, getValue(value,propertyEditor));
412 }
413 }
414
415 protected Object getValue(String value, String propertyEditor) {
416 if (value == null) return null;
417
418
419
420
421 if (NULL_REFERENCE.equals(value)) {
422 return null;
423 }
424
425
426
427
428 if (value.startsWith(BEAN_REFERENCE_PREFIX)) {
429
430 value = value.substring(BEAN_REFERENCE_PREFIX.length());
431
432
433 if (!value.startsWith(BEAN_REFERENCE_PREFIX)) {
434 return new RuntimeBeanReference(value);
435 }
436 }
437
438 if( propertyEditor!=null ) {
439 PropertyEditor p = createPropertyEditor(propertyEditor);
440
441 RootBeanDefinition def = new RootBeanDefinition();
442 def.setBeanClass(PropertyEditorFactory.class);
443 def.getPropertyValues().addPropertyValue("propertyEditor", p);
444 def.getPropertyValues().addPropertyValue("value", value);
445
446 return def;
447 }
448
449
450
451
452 return value;
453 }
454
455 protected PropertyEditor createPropertyEditor(String propertyEditor) {
456 ClassLoader cl = Thread.currentThread().getContextClassLoader();
457 if( cl==null ) {
458 cl = XBeanNamespaceHandler.class.getClassLoader();
459 }
460
461 try {
462 return (PropertyEditor)cl.loadClass(propertyEditor).newInstance();
463 } catch (Throwable e){
464 throw (IllegalArgumentException)new IllegalArgumentException("Could not load property editor: "+propertyEditor).initCause(e);
465 }
466 }
467
468 protected String getLocalName(Element element) {
469 String localName = element.getLocalName();
470 if (localName == null) {
471 localName = element.getNodeName();
472 }
473 return localName;
474 }
475
476 /***
477 * Lets iterate through the children of this element and create any nested
478 * child properties
479 */
480 protected void addNestedPropertyElements(BeanDefinitionHolder definition, MappingMetaData metadata,
481 String className, Element element) {
482 NodeList nl = element.getChildNodes();
483
484 for (int i = 0; i < nl.getLength(); i++) {
485 Node node = nl.item(i);
486 if (node instanceof Element) {
487 Element childElement = (Element) node;
488 String uri = childElement.getNamespaceURI();
489 String localName = childElement.getLocalName();
490
491 if (!isDefaultNamespace(uri) || !reservedElementNames.contains(localName)) {
492
493
494
495
496
497
498
499
500
501 Object value = null;
502 String propertyName = metadata.getNestedListProperty(getLocalName(element), localName);
503 if (propertyName != null) {
504 value = parseListElement(childElement, propertyName);
505 }
506 else {
507 propertyName = metadata.getFlatCollectionProperty(getLocalName(element), localName);
508 if (propertyName != null) {
509 Object def = parserContext.getDelegate().parseCustomElement(childElement);
510 PropertyValue pv = definition.getBeanDefinition().getPropertyValues().getPropertyValue(propertyName);
511 if (pv != null) {
512 Collection l = (Collection) pv.getValue();
513 l.add(def);
514 continue;
515 } else {
516 ManagedList l = new ManagedList();
517 l.add(def);
518 value = l;
519 }
520 } else {
521 propertyName = metadata.getNestedProperty(getLocalName(element), localName);
522 if (propertyName != null) {
523
524 value = parseChildExtensionBean(childElement);
525 }
526 }
527 }
528
529 if (propertyName == null && metadata.isFlatProperty(getLocalName(element), localName)) {
530 value = parseBeanFromExtensionElement(childElement, className, localName);
531 propertyName = localName;
532 }
533
534 if (propertyName == null) {
535 value = tryParseNestedPropertyViaIntrospection(metadata, className, childElement);
536 propertyName = localName;
537 }
538
539 if (value != null) {
540 definition.getBeanDefinition().getPropertyValues().addPropertyValue(propertyName, value);
541 }
542 else
543 {
544 /***
545 * In this case there is no nested property, so just do a normal
546 * addProperty like we do with attributes.
547 */
548 String text = getElementText(childElement);
549
550 if (text != null) {
551 addProperty(definition, metadata, element, localName, text);
552 }
553 }
554 }
555 }
556 }
557 }
558
559 /***
560 * Attempts to use introspection to parse the nested property element.
561 */
562 protected Object tryParseNestedPropertyViaIntrospection(MappingMetaData metadata, String className, Element element) {
563 String localName = getLocalName(element);
564 PropertyDescriptor descriptor = getPropertyDescriptor(className, localName);
565 if (descriptor != null) {
566 return parseNestedPropertyViaIntrospection(metadata, element, descriptor.getName(), descriptor.getPropertyType());
567 } else {
568 return parseNestedPropertyViaIntrospection(metadata, element, localName, Object.class);
569 }
570 }
571
572 /***
573 * Looks up the property decriptor for the given class and property name
574 */
575 protected PropertyDescriptor getPropertyDescriptor(String className, String localName) {
576 BeanInfo beanInfo = qnameHelper.getBeanInfo(className);
577 if (beanInfo != null) {
578 PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
579 for (int i = 0; i < descriptors.length; i++) {
580 PropertyDescriptor descriptor = descriptors[i];
581 String name = descriptor.getName();
582 if (name.equals(localName)) {
583 return descriptor;
584 }
585 }
586 }
587 return null;
588 }
589
590 /***
591 * Attempts to use introspection to parse the nested property element.
592 */
593 private Object parseNestedPropertyViaIntrospection(MappingMetaData metadata, Element element, String propertyName, Class propertyType) {
594 if (isMap(propertyType)) {
595 return parseCustomMapElement(metadata, element, propertyName);
596 } else if (isCollection(propertyType)) {
597 return parseListElement(element, propertyName);
598 } else {
599 return parseChildExtensionBean(element);
600 }
601 }
602
603 protected Object parseListElement(Element element, String name) {
604 return parserContext.getDelegate().parseListElement(element, null);
605 }
606
607 protected Object parseCustomMapElement(MappingMetaData metadata, Element element, String name) {
608 Map map = new ManagedMap();
609
610 Element parent = (Element) element.getParentNode();
611 String entryName = metadata.getMapEntryName(getLocalName(parent), name);
612 String keyName = metadata.getMapKeyName(getLocalName(parent), name);
613 String dups = metadata.getMapDupsMode(getLocalName(parent), name);
614 boolean flat = metadata.isFlatMap(getLocalName(parent), name);
615 String defaultKey = metadata.getMapDefaultKey(getLocalName(parent), name);
616
617 if (entryName == null) entryName = "property";
618 if (keyName == null) keyName = "key";
619 if (dups == null) dups = "replace";
620
621
622
623
624
625 NodeList nl = element.getChildNodes();
626 for (int i = 0; i < nl.getLength(); i++) {
627 Node node = nl.item(i);
628 if (node instanceof Element) {
629 Element childElement = (Element) node;
630
631 String localName = childElement.getLocalName();
632 String uri = childElement.getNamespaceURI();
633 if (localName == null || localName.equals("xmlns") || localName.startsWith("xmlns:")) {
634 continue;
635 }
636
637
638
639 if (!flat && !isEmpty(uri) && localName.equals(entryName)) {
640 String key = childElement.getAttribute(keyName);
641 if (key == null || key.length() == 0) {
642 key = defaultKey;
643 }
644 if (key == null) {
645 throw new RuntimeException("No key defined for map " + entryName);
646 }
647
648 Object keyValue = getValue(key, null);
649
650 Element valueElement = getFirstChildElement(childElement);
651 Object value;
652 if (valueElement != null) {
653 String valueElUri = valueElement.getNamespaceURI();
654 String valueElLocalName = valueElement.getLocalName();
655 if (valueElUri == null ||
656 valueElUri.equals(SPRING_SCHEMA) ||
657 valueElUri.equals(SPRING_SCHEMA_COMPAT) ||
658 valueElUri.equals(BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI)) {
659 if (BeanDefinitionParserDelegate.BEAN_ELEMENT.equals(valueElLocalName)) {
660 value = parserContext.getDelegate().parseBeanDefinitionElement(valueElement, null);
661 } else {
662 value = parserContext.getDelegate().parsePropertySubElement(valueElement, null);
663 }
664 } else {
665 value = parserContext.getDelegate().parseCustomElement(valueElement);
666 }
667 } else {
668 value = getElementText(childElement);
669 }
670
671 addValueToMap(map, keyValue, value, dups);
672 } else if (flat && !isEmpty(uri)) {
673 String key = childElement.getAttribute(keyName);
674 if (key == null || key.length() == 0) {
675 key = defaultKey;
676 }
677 if (key == null) {
678 throw new RuntimeException("No key defined for map entry " + entryName);
679 }
680 Object keyValue = getValue(key, null);
681 childElement.removeAttribute(keyName);
682 BeanDefinitionHolder bdh = parseBeanFromExtensionElement(childElement);
683 addValueToMap(map, keyValue, bdh, dups);
684 }
685 }
686 }
687 return map;
688 }
689
690 protected void addValueToMap(Map map, Object keyValue, Object value, String dups) {
691 if (map.containsKey(keyValue)) {
692 if ("discard".equalsIgnoreCase(dups)) {
693
694 } else if ("replace".equalsIgnoreCase(dups)) {
695 map.put(keyValue, value);
696 } else if ("allow".equalsIgnoreCase(dups)) {
697 List l = new ManagedList();
698 l.add(map.get(keyValue));
699 l.add(value);
700 map.put(keyValue, l);
701 } else if ("always".equalsIgnoreCase(dups)) {
702 List l = (List) map.get(keyValue);
703 l.add(value);
704 }
705 } else {
706 if ("always".equalsIgnoreCase(dups)) {
707 List l = (List) map.get(keyValue);
708 if (l == null) {
709 l = new ManagedList();
710 map.put(keyValue, l);
711 }
712 l.add(value);
713 } else {
714 map.put(keyValue, value);
715 }
716 }
717 }
718
719 protected Element getFirstChildElement(Element element) {
720 NodeList nl = element.getChildNodes();
721 for (int i = 0; i < nl.getLength(); i++) {
722 Node node = nl.item(i);
723 if (node instanceof Element) {
724 return (Element) node;
725 }
726 }
727 return null;
728 }
729
730 protected boolean isMap(Class type) {
731 return Map.class.isAssignableFrom(type);
732 }
733
734 /***
735 * Returns true if the given type is a collection type or an array
736 */
737 protected boolean isCollection(Class type) {
738 return type.isArray() || Collection.class.isAssignableFrom(type);
739 }
740
741 /***
742 * Iterates the children of this element to find the first nested bean
743 */
744 protected Object parseChildExtensionBean(Element element) {
745 NodeList nl = element.getChildNodes();
746 for (int i = 0; i < nl.getLength(); i++) {
747 Node node = nl.item(i);
748 if (node instanceof Element) {
749 Element childElement = (Element) node;
750 String uri = childElement.getNamespaceURI();
751 String localName = childElement.getLocalName();
752
753 if (uri == null ||
754 uri.equals(SPRING_SCHEMA) ||
755 uri.equals(SPRING_SCHEMA_COMPAT) ||
756 uri.equals(BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI)) {
757 if (BeanDefinitionParserDelegate.BEAN_ELEMENT.equals(localName)) {
758 return parserContext.getDelegate().parseBeanDefinitionElement(childElement, null);
759 } else {
760 return parserContext.getDelegate().parsePropertySubElement(childElement, null);
761 }
762 } else {
763 Object value = parserContext.getDelegate().parseCustomElement(childElement);
764 if (value != null) {
765 return value;
766 }
767 }
768 }
769 }
770 return null;
771 }
772
773 /***
774 * Uses META-INF/services discovery to find a Properties file with the XML
775 * marshaling configuration
776 *
777 * @param namespaceURI
778 * the namespace URI of the element
779 * @param localName
780 * the local name of the element
781 * @return the properties configuration of the namespace or null if none
782 * could be found
783 */
784 protected MappingMetaData findNamespaceProperties(String namespaceURI, String localName) {
785
786 if (namespaceURI != null && namespaceURI.startsWith(JAVA_PACKAGE_PREFIX)) {
787 String packageName = namespaceURI.substring(JAVA_PACKAGE_PREFIX.length());
788 return new MappingMetaData(packageName);
789 }
790
791 String uri = NamespaceHelper.createDiscoveryPathName(namespaceURI, localName);
792 InputStream in = loadResource(uri);
793 if (in == null) {
794 if (namespaceURI != null && namespaceURI.length() > 0) {
795 uri = NamespaceHelper.createDiscoveryPathName(namespaceURI);
796 in = loadResource(uri);
797 if (in == null) {
798 uri = NamespaceHelper.createDiscoveryOldPathName(namespaceURI);
799 in = loadResource(uri);
800 }
801 }
802 }
803
804 if (in != null) {
805 try {
806 Properties properties = new Properties();
807 properties.load(in);
808 return new MappingMetaData(properties);
809 }
810 catch (IOException e) {
811 log.warn("Failed to load resource from uri: " + uri, e);
812 }
813 }
814 return null;
815 }
816
817 /***
818 * Loads the resource from the given URI
819 */
820 protected InputStream loadResource(String uri) {
821 if (System.getProperty("xbean.dir") != null) {
822 File f = new File(System.getProperty("xbean.dir") + uri);
823 try {
824 return new FileInputStream(f);
825 } catch (FileNotFoundException e) {
826
827 }
828 }
829
830 InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(uri);
831 if (in == null) {
832 ClassLoader cl = parserContext.getReaderContext().getReader().getBeanClassLoader();
833 if (cl != null) {
834 in = cl.getResourceAsStream(uri);
835 }
836 if (in == null) {
837 in = getClass().getClassLoader().getResourceAsStream(uri);
838 if (in == null) {
839 log.debug("Could not find resource: " + uri);
840 }
841 }
842 }
843 return in;
844 }
845
846 protected boolean isEmpty(String uri) {
847 return uri == null || uri.length() == 0;
848 }
849
850 protected boolean isDefaultNamespace(String namespaceUri) {
851 return (!StringUtils.hasLength(namespaceUri) ||
852 BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI.equals(namespaceUri)) ||
853 SPRING_SCHEMA.equals(namespaceUri) ||
854 SPRING_SCHEMA_COMPAT.equals(namespaceUri);
855 }
856
857 protected void declareLifecycleMethods(BeanDefinitionHolder definitionHolder, MappingMetaData metaData,
858 Element element) {
859 BeanDefinition definition = definitionHolder.getBeanDefinition();
860 if (definition instanceof AbstractBeanDefinition) {
861 AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definition;
862 if (beanDefinition.getInitMethodName() == null) {
863 beanDefinition.setInitMethodName(metaData.getInitMethodName(getLocalName(element)));
864 }
865 if (beanDefinition.getDestroyMethodName() == null) {
866 beanDefinition.setDestroyMethodName(metaData.getDestroyMethodName(getLocalName(element)));
867 }
868 if (beanDefinition.getFactoryMethodName() == null) {
869 beanDefinition.setFactoryMethodName(metaData.getFactoryMethodName(getLocalName(element)));
870 }
871 }
872 }
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963 /***
964 * Returns the text of the element
965 */
966 protected String getElementText(Element element) {
967 StringBuffer buffer = new StringBuffer();
968 NodeList nodeList = element.getChildNodes();
969 for (int i = 0, size = nodeList.getLength(); i < size; i++) {
970 Node node = nodeList.item(i);
971 if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) {
972 buffer.append(node.getNodeValue());
973 }
974 }
975 return buffer.toString();
976 }
977 }