/****************************************************************************** | |
* Copyright (c) 2006, 2010 VMware Inc. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* and Apache License v2.0 which accompanies this distribution. | |
* The Eclipse Public License is available at | |
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0 | |
* is available at http://www.opensource.org/licenses/apache2.0.php. | |
* You may elect to redistribute this code under either of these licenses. | |
* | |
* Contributors: | |
* VMware Inc. | |
*****************************************************************************/ | |
package org.eclipse.gemini.blueprint.config.internal; | |
import java.util.Comparator; | |
import java.util.Locale; | |
import org.eclipse.gemini.blueprint.config.internal.util.AttributeCallback; | |
import org.eclipse.gemini.blueprint.config.internal.util.ParserUtils; | |
import org.eclipse.gemini.blueprint.service.importer.support.CollectionType; | |
import org.eclipse.gemini.blueprint.service.importer.support.MemberType; | |
import org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceCollectionProxyFactoryBean; | |
import org.eclipse.gemini.blueprint.service.importer.support.internal.util.ServiceReferenceComparator; | |
import org.springframework.beans.factory.support.BeanDefinitionBuilder; | |
import org.springframework.beans.factory.xml.ParserContext; | |
import org.springframework.util.xml.DomUtils; | |
import org.w3c.dom.Attr; | |
import org.w3c.dom.Element; | |
import org.w3c.dom.NamedNodeMap; | |
import org.w3c.dom.Node; | |
import org.w3c.dom.NodeList; | |
/** | |
* <osgi:list>, <osgi:set>, element parser. | |
* | |
* @author Costin Leau | |
* | |
*/ | |
public abstract class CollectionBeanDefinitionParser extends AbstractReferenceDefinitionParser { | |
/** | |
* Greedy proxy attribute callback. | |
* | |
* @author Costin Leau | |
*/ | |
static class CollectionAttributeCallback implements AttributeCallback { | |
public boolean process(Element parent, Attr attribute, BeanDefinitionBuilder builder) { | |
String name = attribute.getLocalName(); | |
if (MEMBER_TYPE.equals(name)) { | |
builder.addPropertyValue(MEMBER_TYPE_PROPERTY, MemberType.valueOf(attribute.getValue().toUpperCase() | |
.replace('-', '_'))); | |
return false; | |
} | |
return true; | |
} | |
} | |
private static final String NESTED_COMPARATOR = "comparator"; | |
private static final String INLINE_COMPARATOR_REF = "comparator-ref"; | |
private static final String COLLECTION_TYPE_PROP = "collectionType"; | |
private static final String COMPARATOR_PROPERTY = "comparator"; | |
private static final String SERVICE_ORDER = "service"; | |
private static final String SERVICE_REFERENCE_ORDER = "service-reference"; | |
private static final String MEMBER_TYPE = "member-type"; | |
private static final String MEMBER_TYPE_PROPERTY = "memberType"; | |
private static final Comparator SERVICE_REFERENCE_COMPARATOR = new ServiceReferenceComparator(); | |
private static final String NATURAL = "natural"; | |
private static final String BASIS = "basis"; | |
protected Class getBeanClass(Element element) { | |
return OsgiServiceCollectionProxyFactoryBean.class; | |
} | |
/** | |
* {@inheritDoc} | |
* | |
* Add support for 'greedy-proxying' attribute. | |
*/ | |
protected void parseAttributes(Element element, BeanDefinitionBuilder builder, AttributeCallback[] callbacks, | |
OsgiDefaultsDefinition defaults) { | |
// add timeout callback | |
CollectionAttributeCallback greedyProxyingCallback = new CollectionAttributeCallback(); | |
super.parseAttributes(element, builder, ParserUtils.mergeCallbacks(callbacks, | |
new AttributeCallback[] { greedyProxyingCallback }), defaults); | |
} | |
protected void parseNestedElements(Element element, ParserContext context, BeanDefinitionBuilder builder) { | |
super.parseNestedElements(element, context, builder); | |
parseComparator(element, context, builder); | |
} | |
/** | |
* Parse <comparator> element. | |
* | |
* @param element | |
* @param context | |
* @param builder | |
*/ | |
protected void parseComparator(Element element, ParserContext context, BeanDefinitionBuilder builder) { | |
boolean hasComparatorRef = element.hasAttribute(INLINE_COMPARATOR_REF); | |
// check nested comparator | |
Element comparatorElement = DomUtils.getChildElementByTagName(element, NESTED_COMPARATOR); | |
Object nestedComparator = null; | |
// comparator definition present | |
if (comparatorElement != null) { | |
// check duplicate nested and inline bean definition | |
if (hasComparatorRef) | |
context.getReaderContext().error( | |
"nested comparator declaration is not allowed if " + INLINE_COMPARATOR_REF | |
+ " attribute has been specified", comparatorElement); | |
NodeList nl = comparatorElement.getChildNodes(); | |
// take only elements | |
for (int i = 0; i < nl.getLength(); i++) { | |
Node nd = nl.item(i); | |
if (nd instanceof Element) { | |
Element beanDef = (Element) nd; | |
String name = beanDef.getLocalName(); | |
// check if we have a 'natural' tag (known comparator | |
// definitions) | |
if (NATURAL.equals(name)) | |
nestedComparator = parseNaturalComparator(beanDef); | |
else | |
// we have a nested definition | |
nestedComparator = parsePropertySubElement(context, beanDef, builder.getBeanDefinition()); | |
} | |
} | |
// set the reference to the nested comparator reference | |
if (nestedComparator != null) | |
builder.addPropertyValue(COMPARATOR_PROPERTY, nestedComparator); | |
} | |
// set collection type | |
// based on the existence of the comparator | |
// we treat the case where the comparator is natural which means the | |
// comparator | |
// instance is null however, we have to force a sorted collection to be | |
// used | |
// so that the object natural ordering is used. | |
if (comparatorElement != null || hasComparatorRef) { | |
if (CollectionType.LIST.equals(collectionType())) { | |
builder.addPropertyValue(COLLECTION_TYPE_PROP, CollectionType.SORTED_LIST); | |
} | |
if (CollectionType.SET.equals(collectionType())) { | |
builder.addPropertyValue(COLLECTION_TYPE_PROP, CollectionType.SORTED_SET); | |
} | |
} else { | |
builder.addPropertyValue(COLLECTION_TYPE_PROP, collectionType()); | |
} | |
} | |
/** | |
* Parse <osgi:natural> element. | |
* | |
* @param element | |
* @return | |
*/ | |
protected Comparator parseNaturalComparator(Element element) { | |
Comparator comparator = null; | |
NamedNodeMap attributes = element.getAttributes(); | |
for (int x = 0; x < attributes.getLength(); x++) { | |
Attr attribute = (Attr) attributes.item(x); | |
String name = attribute.getLocalName(); | |
String value = attribute.getValue(); | |
if (BASIS.equals(name)) { | |
if (SERVICE_REFERENCE_ORDER.equals(value)) | |
return SERVICE_REFERENCE_COMPARATOR; | |
// no comparator means relying on Comparable interface of the | |
// services | |
else if (SERVICE_ORDER.equals(value)) | |
return null; | |
} | |
} | |
return comparator; | |
} | |
/** | |
* Hook used for indicating the main collection type (set/list) on which this parser applies. | |
* | |
* @return service collection type | |
*/ | |
protected abstract CollectionType collectionType(); | |
} |