/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.citrus.dev.handler.util;
import static com.alibaba.citrus.util.ArrayUtil.*;
import static com.alibaba.citrus.util.BasicConstant.*;
import static com.alibaba.citrus.util.ClassUtil.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static org.springframework.beans.factory.support.AbstractBeanDefinition.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.LookupOverride;
import org.springframework.beans.factory.support.MethodOverride;
import org.springframework.beans.factory.support.ReplaceOverride;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
/**
* 将<code>BeanDefinition</code>转换成xml。
*
* @author Michael Zhou
*/
public class BeanDefinitionReverseEngine {
private final AbstractBeanDefinition bd;
private final String name;
private final String[] aliases;
private final Element beanElement;
private final boolean innerBean;
public BeanDefinitionReverseEngine(AbstractBeanDefinition bd, String name, String[] aliases) {
this(bd, name, aliases, null);
}
public BeanDefinitionReverseEngine(AbstractBeanDefinition bd, String name, String[] aliases, Element parentElement) {
this.bd = bd;
this.name = trimToNull(name);
this.aliases = defaultIfEmptyArray(aliases, EMPTY_STRING_ARRAY);
this.beanElement = new Element("bean");
this.innerBean = parentElement != null;
if (innerBean) {
parentElement.subElements.add(beanElement);
}
doReverseEngineering();
}
private void doReverseEngineering() {
beanAttributes();
beanConstructorArgs();
beanProperties();
methodOverrides();
qualifiers();
}
private void beanAttributes() {
if (!innerBean && name != null) {
beanElement.addAttribute("id", new AnchorValue(name));
}
if (!innerBean && aliases.length > 0) {
beanElement.addAttribute("name", new AnchorValue(aliases));
}
if (bd.isAbstract()) {
beanElement.addAttribute("abstract", "true");
}
if (bd.getParentName() != null) {
beanElement.addAttribute("parent", new RefValue(bd.getParentName()));
}
if (bd.getBeanClassName() != null) {
beanElement.addAttribute("class", new ClassValue(bd.getBeanClassName()));
}
if (bd.isPrimary()) {
beanElement.addAttribute("primary", "true");
}
String autowireMode = null;
switch (bd.getAutowireMode()) {
case AUTOWIRE_AUTODETECT:
autowireMode = BeanDefinitionParserDelegate.AUTOWIRE_AUTODETECT_VALUE;
break;
case AUTOWIRE_BY_NAME:
autowireMode = BeanDefinitionParserDelegate.AUTOWIRE_BY_NAME_VALUE;
break;
case AUTOWIRE_BY_TYPE:
autowireMode = BeanDefinitionParserDelegate.AUTOWIRE_BY_TYPE_VALUE;
break;
case AUTOWIRE_CONSTRUCTOR:
autowireMode = BeanDefinitionParserDelegate.AUTOWIRE_CONSTRUCTOR_VALUE;
break;
}
if (autowireMode != null) {
beanElement.addAttribute("autowire", autowireMode);
}
if (!bd.isAutowireCandidate()) {
beanElement.addAttribute("autowire-candidate", "false");
}
String dependencyCheck = null;
switch (bd.getDependencyCheck()) {
case DEPENDENCY_CHECK_ALL:
dependencyCheck = BeanDefinitionParserDelegate.DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE;
break;
case DEPENDENCY_CHECK_OBJECTS:
dependencyCheck = BeanDefinitionParserDelegate.DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE;
break;
case DEPENDENCY_CHECK_SIMPLE:
dependencyCheck = BeanDefinitionParserDelegate.DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE;
break;
}
if (dependencyCheck != null) {
beanElement.addAttribute("dependency-check", dependencyCheck);
}
if (!isEmptyArray(bd.getDependsOn())) {
beanElement.addAttribute("depends-on", new RefValue(bd.getDependsOn()));
}
if (!isEmpty(bd.getScope()) && !"singleton".equals(bd.getScope())) {
beanElement.addAttribute("scope", bd.getScope());
}
if (bd.getInitMethodName() != null) {
beanElement.addAttribute("init-method", bd.getInitMethodName());
}
if (bd.getDestroyMethodName() != null) {
beanElement.addAttribute("destroy-method", bd.getDestroyMethodName());
}
if (bd.isLazyInit()) {
beanElement.addAttribute("lazy-init", "true");
}
if (bd.getFactoryBeanName() != null) {
beanElement.addAttribute("factory-bean", new RefValue(bd.getFactoryBeanName()));
}
if (bd.getFactoryMethodName() != null) {
beanElement.addAttribute("factory-method", bd.getFactoryMethodName());
}
}
private void beanConstructorArgs() {
ConstructorArgumentValues values = bd.getConstructorArgumentValues();
// indexed args
@SuppressWarnings("unchecked")
Map<Integer, ValueHolder> indexedArgs = new TreeMap<Integer, ValueHolder>(values.getIndexedArgumentValues());
for (Map.Entry<Integer, ValueHolder> entry : indexedArgs.entrySet()) {
Integer index = entry.getKey();
ValueHolder valueHolder = entry.getValue();
Element constructorArgElement = beanElement.newSubElement("constructor-arg");
constructorArgElement.addAttribute("index", index.toString());
beanConstructorArg(constructorArgElement, valueHolder);
}
// generic args
@SuppressWarnings("unchecked")
List<ValueHolder> genericArgs = values.getGenericArgumentValues();
for (ValueHolder valueHolder : genericArgs) {
Element constructorArgElement = beanElement.newSubElement("constructor-arg");
beanConstructorArg(constructorArgElement, valueHolder);
}
}
private void beanConstructorArg(Element constructorArgElement, ValueHolder valueHolder) {
if (valueHolder.getType() != null) {
constructorArgElement.addAttribute("type", new ClassValue(valueHolder.getType()));
}
value(constructorArgElement, valueHolder.getValue(), true, true, null, null);
}
private void beanProperties() {
PropertyValues values = bd.getPropertyValues();
List<PropertyValue> props = createArrayList(values.getPropertyValues());
Collections.sort(props, new Comparator<PropertyValue>() {
public int compare(PropertyValue o1, PropertyValue o2) {
String n1 = o1.getName();
String n2 = o2.getName();
if (n1 != null && n2 != null) {
return n1.compareTo(n2);
}
if (n1 == null && n2 == null) {
return 0;
}
if (n1 == null) {
return -1;
} else {
return 1;
}
}
});
for (PropertyValue prop : props) {
Element propertyElement = beanElement.newSubElement("property");
if (prop.getName() != null) {
propertyElement.addAttribute("name", prop.getName());
}
value(propertyElement, prop.getValue(), true, true, null, null);
}
}
private void value(Element element, Object value, boolean supportValueAttribute, boolean supportRefAttribute,
Element containerElement, String typeAttrName) {
// null
if (value == null || value instanceof TypedStringValue && ((TypedStringValue) value).getValue() == null) {
element.newSubElement("null");
return;
}
// ref
if (value instanceof RuntimeBeanReference) {
String refto = ((RuntimeBeanReference) value).getBeanName();
if (((RuntimeBeanReference) value).isToParent()) {
element.newSubElement("ref").addAttribute("parent", new RefValue(refto));
} else if (supportRefAttribute) {
element.addAttribute("ref", new RefValue(refto));
} else {
element.newSubElement("ref").addAttribute("bean", new RefValue(refto));
}
return;
}
// idref
if (value instanceof RuntimeBeanNameReference) {
element.newSubElement("idref").addAttribute("bean",
new RefValue(((RuntimeBeanNameReference) value).getBeanName()));
return;
}
// bean
BeanDefinition innerBd = null;
String innerName = null;
String[] innerAliases = null;
if (value instanceof BeanDefinitionHolder) {
innerBd = ((BeanDefinitionHolder) value).getBeanDefinition();
innerName = ((BeanDefinitionHolder) value).getBeanName();
innerAliases = ((BeanDefinitionHolder) value).getAliases();
} else if (value instanceof BeanDefinition) {
innerBd = (BeanDefinition) value;
}
if (innerBd != null) {
if (innerBd instanceof AbstractBeanDefinition) {
new BeanDefinitionReverseEngine((AbstractBeanDefinition) innerBd, innerName, innerAliases, element);
} else {
element.newSubElement("bean").addAttribute("unknownBeanDefinitionType",
new ClassValue(innerBd.getClass().getName()));
}
return;
}
// list or array
if (value.getClass().isArray()) {
value = createLinkedList(arrayAsIterable(Object.class, value));
}
if (value instanceof List<?>) {
Element listElement = element.newSubElement("list");
for (Object itemValue : (List<?>) value) {
value(listElement, itemValue, false, false, listElement, "value-type");
}
return;
}
// set
if (value instanceof Set<?>) {
Element setElement = element.newSubElement("set");
for (Object itemValue : (Set<?>) value) {
value(setElement, itemValue, false, false, setElement, "value-type");
}
return;
}
// props
if (value instanceof Properties) {
Element propsElement = element.newSubElement("props");
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
Element propElement = propsElement.newSubElement("prop");
propElement.addAttribute("key", String.valueOf(entry.getKey()));
propElement.setText(String.valueOf(entry.getValue()));
}
return;
}
// map
if (value instanceof Map<?, ?>) {
Element mapElement = element.newSubElement("map");
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
Element entryElement = mapElement.newSubElement("entry");
mapEntryKeyValue(entryElement, entry.getKey(), "key-ref", "key", "key", mapElement, "key-type");
mapEntryKeyValue(entryElement, entry.getValue(), "value-ref", "value", null, mapElement, "value-type");
}
return;
}
// typed string
if (value instanceof TypedStringValue) {
simpleValue(element, new TextValue(((TypedStringValue) value).getValue()),
((TypedStringValue) value).getTargetTypeName(), supportValueAttribute, containerElement,
typeAttrName);
return;
}
// simple raw data
if (value instanceof String || getPrimitiveType(value.getClass()) != null) {
simpleValue(element, new TextValue(value.toString()), null, supportValueAttribute, containerElement,
typeAttrName);
return;
}
// raw data
simpleValue(element, new RawValue(value.getClass(), value.toString()), null, supportValueAttribute,
containerElement, typeAttrName);
}
private void mapEntryKeyValue(Element entryElement, Object keyOrValue, String refAttrName, String valueAttrName,
String valueElementName, Element mapElement, String typeAttrName) {
if (keyOrValue != null) {
// ref
if (keyOrValue instanceof RuntimeBeanReference) {
String refto = ((RuntimeBeanReference) keyOrValue).getBeanName();
if (!((RuntimeBeanReference) keyOrValue).isToParent()) {
entryElement.addAttribute(refAttrName, new RefValue(refto));
return;
}
}
String simpleValue = null;
// typed string
if (keyOrValue instanceof TypedStringValue) {
String type = ((TypedStringValue) keyOrValue).getTargetTypeName();
if (type != null) {
// 如果<map>中没有type attribute,则把type设置到map中,作为默认的type。
if (mapElement != null && typeAttrName != null && !mapElement.hasAttribute(typeAttrName)) {
mapElement.addAttribute(typeAttrName, new ClassValue(type));
}
// 如果type和<map>中的默认type相同,则不显示
if (type.equals(mapElement.getAttribute(typeAttrName))) {
type = null;
}
}
if (type == null) {
simpleValue = ((TypedStringValue) keyOrValue).getValue();
}
}
// simple raw data
if (keyOrValue instanceof String || getPrimitiveType(keyOrValue.getClass()) != null) {
simpleValue = keyOrValue.toString();
}
if (simpleValue != null && !hasControlChars(simpleValue)) {
entryElement.addAttribute(valueAttrName, simpleValue);
return;
}
}
// complex key or value
if (valueElementName != null) {
value(entryElement.newSubElement(valueElementName), keyOrValue, false, false, null, null);
} else {
value(entryElement, keyOrValue, false, false, null, null);
}
}
private void simpleValue(Element element, StyledValue value, String type, boolean supportValueAttribute,
Element containerElement, String typeAttrName) {
if (type != null) {
// 如果<list>中没有type attribute,则把type设置到map中,作为默认的type。
if (containerElement != null && typeAttrName != null && !containerElement.hasAttribute(typeAttrName)) {
containerElement.addAttribute(typeAttrName, new ClassValue(type));
}
// 如果type和<list>中的默认type相同,则不显示
if (type.equals(containerElement.getAttribute(typeAttrName))) {
type = null;
}
}
if (type == null && supportValueAttribute && !hasControlChars(value.getText())) {
element.addAttribute("value", value);
return;
}
Element valueElement = element.newSubElement("value").setText(value);
if (type != null) {
valueElement.addAttribute("type", new ClassValue(type));
}
}
private void methodOverrides() {
@SuppressWarnings("unchecked")
Set<MethodOverride> overrides = bd.getMethodOverrides().getOverrides();
for (MethodOverride override : overrides) {
if (override instanceof LookupOverride) {
Element lookupMethodElement = beanElement.newSubElement("lookup-method");
String bean = ((LookupOverride) override).getBeanName();
String name = override.getMethodName();
if (bean != null) {
lookupMethodElement.addAttribute("bean", new RefValue(bean));
}
if (name != null) {
lookupMethodElement.addAttribute("name", name);
}
continue;
}
if (override instanceof ReplaceOverride) {
Element replacedMethodElement = beanElement.newSubElement("replaced-method");
String bean = ((ReplaceOverride) override).getMethodReplacerBeanName();
String name = override.getMethodName();
if (bean != null) {
replacedMethodElement.addAttribute("replacer", new RefValue(bean));
}
if (name != null) {
replacedMethodElement.addAttribute("name", name);
}
continue;
}
}
}
private void qualifiers() {
@SuppressWarnings("unchecked")
Set<AutowireCandidateQualifier> qualifiers = bd.getQualifiers();
for (AutowireCandidateQualifier q : qualifiers) {
Element qualifierElement = beanElement.newSubElement("qualifier");
if (q.getTypeName() != null) {
qualifierElement.addAttribute("type", new ClassValue(q.getTypeName()));
}
if (q.getAttribute("value") != null) {
qualifierElement.addAttribute("value", String.valueOf(q.getAttribute("value")));
}
for (String attrName : q.attributeNames()) {
if (!"value".equals(attrName)) {
Element attrElement = qualifierElement.newSubElement("attribute");
attrElement.addAttribute("key", String.valueOf(attrName));
attrElement.addAttribute("value", String.valueOf(q.getAttribute(attrName)));
}
}
}
}
private final static char[] controlChars;
static {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < ' '; i++) {
buf.append((char) i);
}
controlChars = buf.toString().toCharArray();
}
static boolean hasControlChars(String text) {
return !containsNone(text, controlChars);
}
public final Element toDom() {
return beanElement;
}
}