/** * * Copyright 2005 BEA Systems Inc. * Copyright 2005 International Business Machines Corporation * * 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 org.apache.tuscany.sdo.impl; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import org.osoa.sdo.DuplicateTypeException; import org.osoa.sdo.Property; import org.osoa.sdo.Type; import org.osoa.sdo.helper.TypeHelper; /** * @version $Rev$ $Date$ */ public class TypeHelperImpl implements TypeHelper { private final Map> typesByClass; private final Map> typesByName; private final ProxyClassLoader cl; @SuppressWarnings({"ClassLoader2Instantiation"}) public TypeHelperImpl(ClassLoader cl) { this.cl = new ProxyClassLoader(cl); typesByName = new HashMap>(); typesByClass = new HashMap>(); mapType(DataTypeImpl.BOOLEAN); mapType(DataTypeImpl.BYTE); mapType(DataTypeImpl.BYTES); mapType(DataTypeImpl.CHARACTER); mapType(DataTypeImpl.DATE); mapType(DataTypeImpl.DOUBLE); mapType(DataTypeImpl.FLOAT); mapType(DataTypeImpl.INT); mapType(DataTypeImpl.LONG); mapType(DataTypeImpl.SHORT); mapType(DataTypeImpl.STRING); mapType(DataTypeImpl.BOOLEAN_OBJECT); mapType(DataTypeImpl.CHARACTER_OBJECT); } private void mapType(Type type) { typesByName.put(type.getName(), type); java.lang.reflect.Type javaType = type.getJavaType(); if (javaType != null) { typesByClass.put(javaType, type); } } public Type define(Class interfaceClass) { if (typesByClass.containsKey(interfaceClass)) { throw new DuplicateTypeException(interfaceClass.getName()); } String namespace; String name; org.osoa.sdo.annotation.Type ann = interfaceClass.getAnnotation(org.osoa.sdo.annotation.Type.class); if (ann != null) { namespace = ann.namespace(); name = ann.name(); } else { namespace = Type.JAVA_NAMESPACE; name = interfaceClass.getName(); } return define(new QName(namespace, name), interfaceClass); } public Type define(QName typeName, Class interfaceClass) { if (!interfaceClass.isInterface()) { throw new IllegalArgumentException("Not an interface: " + interfaceClass); } Method[] methods = interfaceClass.getMethods(); Map props = new LinkedHashMap(methods.length >> 1); for (Method method : methods) { String methodName = method.getName(); Class[] params = method.getParameterTypes(); String propName; java.lang.reflect.Type propType; if (Void.TYPE.equals(method.getReturnType()) && methodName.startsWith("set") && methodName.length() > 3 && params.length == 1) { propName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4); propType = params[0]; } else if (Boolean.TYPE.equals(method.getReturnType()) && methodName.startsWith("is") && methodName.length() > 2 && params.length == 0) { propName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3); propType = Boolean.TYPE; } else if (!Void.TYPE.equals(method.getReturnType()) && methodName.startsWith("get") && methodName.length() > 3 && params.length == 0) { propName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4); propType = method.getGenericReturnType(); } else { throw new IllegalArgumentException("Non-accessor method on interface: " + method); } defineProperty(props, propName, propType); } return define(typeName, interfaceClass, new ArrayList(props.values())); } private void defineProperty(Map props, String propName, java.lang.reflect.Type propType) { Property prop = props.get(propName); if (prop != null) { /* TODO should check this if (!propClass.equals(prop.getType().getInstanceClass())) { throw new IllegalArgumentException("set/get types do not match for property: " + propName); } */ return; } int index = props.size(); if (propType instanceof Class) { prop = getPropertyFromClass(propName, index, propType); } else if (propType instanceof ParameterizedType) { prop = getPropertyFromParameterizedType(propName, index, propType); } else { throw new UnsupportedOperationException(); } props.put(propName, prop); } private Property getPropertyFromParameterizedType(String propName, int index, java.lang.reflect.Type propType) { ParameterizedType parameterizedType = (ParameterizedType) propType; if (List.class.equals(parameterizedType.getRawType())) { java.lang.reflect.Type actualPropType = parameterizedType.getActualTypeArguments()[0]; if (!(actualPropType instanceof Class)) { throw new IllegalArgumentException("Actual type of list property must not be generic: " + propName); } Class propClass = (Class) actualPropType; Type type = getType(propClass); if (type == null) { type = define(propClass); } return new ListPropertyImpl(propName, type, parameterizedType,index); } else { throw new IllegalArgumentException("Invalid generic type: " + parameterizedType); } } private Property getPropertyFromClass(String propName, int index, java.lang.reflect.Type propType) { Class propClass = (Class) propType; if (propClass.isArray() && !byte[].class.equals(propClass)) { throw new IllegalArgumentException("Property cannot be an array type: " + propName); } Type type = getType(propClass); if (type == null) { type = define(propClass); } return new PropertyImpl(propName, type, index); } private Type define(QName typeName, Class instanceClass, List properties) { if (typeName == null) { throw new IllegalArgumentException("typeName is null"); } String namespace = typeName.getNamespaceURI(); String name = typeName.getLocalPart(); if (namespace == null || Type.SDO_NAMESPACE.equals(namespace)) { throw new IllegalArgumentException("Invalid namespace: " + namespace); } if (name == null) { throw new IllegalArgumentException("name is null"); } if (Type.JAVA_NAMESPACE.equals(namespace) && !instanceClass.getName().equals(name)) { throw new IllegalArgumentException("in Java namespace, name must equal instanceClass name"); } SDOInstanceFactory instanceFactory = new SDOInstanceFactory(); Type type = new TypeImpl(typeName, instanceClass, properties, instanceFactory); SDOGenerator gen = new SDOGenerator(instanceClass); for (Property property : properties) { gen.addProperty(property); } byte[] bytes = gen.toByteArray(); // dumpClass(typeName.getLocalPart(), bytes); Class implementationClass = (Class) cl.addProxy(null, bytes); instanceFactory.setImplementationClass(implementationClass); mapType(type); return type; } public Type getType(Class interfaceClass) { return (Type) typesByClass.get(interfaceClass); } public Type getType(QName name) { return typesByName.get(name); } private static void dumpClass(String name, byte[] bytes) { File file = new File("/tmp/dump/" + name.replace('.', '/') + ".class"); file.getParentFile().mkdirs(); try { FileOutputStream fos = new FileOutputStream(file); fos.write(bytes); fos.close(); } catch (IOException e) { } } }