/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.config.xml;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.logging.Logger;
import org.w3c.dom.Node;
import com.caucho.config.ConfigException;
import com.caucho.config.attribute.AddAttribute;
import com.caucho.config.attribute.Attribute;
import com.caucho.config.attribute.FlowAttribute;
import com.caucho.config.type.ConfigType;
import com.caucho.config.type.TypeFactory;
import com.caucho.util.L10N;
import com.caucho.xml.QName;
import com.caucho.xml.QNode;
/**
* Represents a CanDI-style introspected bean type for configuration.
*/
public class XmlBeanType<T> extends ConfigType<T>
{
private static final L10N L = new L10N(XmlBeanType.class);
private static final Logger log
= Logger.getLogger(XmlBeanType.class.getName());
private static final String RESIN_NS
= "http://caucho.com/ns/resin";
private static final String JAVAEE_NS
= "http://java.sun.com/xml/ns/javaee";
private static final QName TEXT = new QName("#text");
private static final QName W_VALUE = new QName("", "value", JAVAEE_NS);
private static final QName R_VALUE = new QName("", "value", RESIN_NS);
private static final QName A_VALUE = new QName("value", null);
private static final QName W_NEW = new QName("", "new", JAVAEE_NS);
private static final QName R_NEW = new QName("", "new", RESIN_NS);
private static final QName A_NEW = new QName("new", null);
private final Class<T> _beanClass;
private final ConfigType<T> _beanType;
private String _namespaceURI;
private HashMap<QName,Attribute> _nsAttributeMap
= new HashMap<QName,Attribute>();
public XmlBeanType(Class<T> beanClass)
{
_beanClass = beanClass;
_beanType = TypeFactory.getType(beanClass);
int p = beanClass.getName().lastIndexOf('.');
_namespaceURI = "urn:java:" + beanClass.getName().substring(0, p);
_nsAttributeMap.put(W_NEW, XmlBeanNewAttribute.ATTRIBUTE);
_nsAttributeMap.put(R_NEW, XmlBeanNewAttribute.ATTRIBUTE);
_nsAttributeMap.put(A_NEW, XmlBeanNewAttribute.ATTRIBUTE);
}
/**
* Returns the given type.
*/
@Override
public Class<T> getType()
{
return _beanClass;
}
/**
* Creates a new instance
*/
@Override
public Object create(Object parent, QName name)
{
return new XmlBeanConfig<T>(name, _beanClass, null);
}
/**
* Returns the attribute with the given name.
*/
public Attribute getAttribute(QName qName)
{
Attribute attr = _nsAttributeMap.get(qName);
if (attr == null) {
attr = getAttributeImpl(qName);
if (attr != null)
_nsAttributeMap.put(qName, attr);
}
return attr;
}
protected Attribute getAttributeImpl(QName qName)
{
Attribute attr = _beanType.getAttribute(qName);
if (attr != null) {
return XmlBeanProgramAttribute.ATTRIBUTE;
}
String uri = qName.getNamespaceURI();
if (uri == null)
return null;
else if (! uri.startsWith("urn:java:") && ! uri.equals(RESIN_NS))
return null;
Method method = null;
if (uri.equals(_namespaceURI)
&& (method = findMethod(qName.getLocalName())) != null) {
return new XmlBeanMethodAttribute(_beanClass, method);
}
Field field = null;
if (uri.equals(_namespaceURI)
&& (field = findField(qName.getLocalName())) != null) {
return new XmlBeanFieldAttribute(_beanClass, field);
}
/*
if ("value".equals(qName.getLocalName())
&& (uri.equals("urn:java:ee") || (uri.equals(RESIN_NS)))) {
// ioc/022k
return CustomBeanValueArgAttribute.ATTRIBUTE;
}
*/
if ("new".equals(qName.getLocalName())) {
return XmlBeanNewAttribute.ATTRIBUTE;
}
Attribute envAttr
= TypeFactory.getFactory().getEnvironmentAttribute(qName);
if (envAttr instanceof FlowAttribute) {
// || envAttr instanceof EnvironmentAttribute) {
// ioc/04c1
return envAttr;
}
ConfigType<?> type = TypeFactory.getFactory().getEnvironmentType(qName);
if (type == null) {
if (Character.isLowerCase(qName.getLocalName().charAt(0))) {
throw new ConfigException(L.l("'{0}' is an unknown field of {1}",
qName.getLocalName(),
_beanClass.getName()));
}
else {
throw new ConfigException(L.l("'{0}' cannot be instantiated because it does not map to a known class",
qName));
}
}
Class<?> cl = type.getType();
if (Annotation.class.isAssignableFrom(cl)) {
return new XmlBeanAnnotationAttribute(cl);
}
AddAttribute addAttribute = (AddAttribute) _beanType.getAddAttribute(cl);
if (addAttribute != null)
return new XmlBeanAddAttribute(cl);
throw new ConfigException(L.l("'{0}' is an unknown field or annotation",
qName));
// return new CustomBeanArgAttribute(cl);
}
@Override
public Attribute getProgramAttribute()
{
Attribute attr = _beanType.getProgramAttribute();
if (attr == null)
return null;
// server/1kl5
return XmlBeanProgramAttribute.ATTRIBUTE;
}
private Method findMethod(String name)
{
return findMethod(_beanClass, name);
}
private Method findMethod(Class<?> cl, String name)
{
if (cl == null || cl.equals(Object.class))
return null;
for (Method method : cl.getDeclaredMethods()) {
if (method.getName().equals(name))
return method;
}
return findMethod(cl.getSuperclass(), name);
}
private Field findField(String name)
{
return findField(_beanClass, name);
}
private Field findField(Class<?> cl, String name)
{
if (cl == null || cl.equals(Object.class))
return null;
for (Field field : cl.getDeclaredFields()) {
if (field.getName().equals(name))
return field;
}
return findField(cl.getSuperclass(), name);
}
private ConfigType createClass(QName name)
{
return TypeFactory.getFactory().getEnvironmentType(name);
/*
String uri = name.getNamespaceURI();
if (uri.equals(RESIN_NS)) {
return createResinClass(name.getLocalName());
}
if (! uri.startsWith("urn:java:"))
return null;
String pkg = uri.substring("urn:java:".length());
return TypeFactory.loadClass(pkg, name.getLocalName());
*/
}
private Class<?> createResinClass(String name)
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> cl = TypeFactory.loadClass("ee", name);
return cl;
}
/**
* Called before the children are configured. Also called for
* attribute configuration, e.g. for macros and web-app-default.
*/
@Override
public void beforeConfigure(XmlConfigContext builder, Object bean, Node node)
{
super.beforeConfigure(builder, bean, node);
if (bean instanceof XmlBeanConfig) {
XmlBeanConfig xmlBean = (XmlBeanConfig) bean;
if (node instanceof QNode) {
QNode qNode = (QNode) node;
String uri = qNode.getBaseURI();
int line = qNode.getLine();
xmlBean.setConfigLocation(uri, line);
}
}
}
/**
* Converts the string to a value of the type.
*/
public Object valueOf(String text)
{
throw new UnsupportedOperationException(getClass().getName());
}
/**
* Initialize the type
*/
@Override
public void init(Object bean)
{
XmlBeanConfig<?> customBean = (XmlBeanConfig<?>) bean;
customBean.init();
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + _beanClass.getName() + "]";
}
}