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 }