/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.sca.interfacedef.java.jaxws; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import javax.xml.bind.annotation.XmlAttachmentRef; import javax.xml.bind.annotation.XmlList; import javax.xml.bind.annotation.XmlMimeType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.ws.Holder; import org.apache.tuscany.sca.databinding.jaxb.XMLAdapterExtensionPoint; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public abstract class BaseBeanGenerator implements Opcodes { private static final Map<String, String> COLLECTION_CLASSES = new HashMap<String, String>(); static { COLLECTION_CLASSES.put("Ljava/util/Collection;", "java/util/ArrayList"); COLLECTION_CLASSES.put("Ljava/util/List;", "java/util/ArrayList"); COLLECTION_CLASSES.put("Ljava/util/Set;", "java/util/HashSet"); COLLECTION_CLASSES.put("Ljava/util/Queue;", "java/util/LinkedList"); } private final static Class[] KNOWN_JAXB_ANNOTATIONS = {XmlAttachmentRef.class, XmlMimeType.class, XmlJavaTypeAdapter.class, XmlList.class}; private static final Map<String, String> JAVA_KEYWORDS = new HashMap<String, String>(); static { JAVA_KEYWORDS.put("abstract", "_abstract"); JAVA_KEYWORDS.put("assert", "_assert"); JAVA_KEYWORDS.put("boolean", "_boolean"); JAVA_KEYWORDS.put("break", "_break"); JAVA_KEYWORDS.put("byte", "_byte"); JAVA_KEYWORDS.put("case", "_case"); JAVA_KEYWORDS.put("catch", "_catch"); JAVA_KEYWORDS.put("char", "_char"); JAVA_KEYWORDS.put("class", "_class"); JAVA_KEYWORDS.put("const", "_const"); JAVA_KEYWORDS.put("continue", "_continue"); JAVA_KEYWORDS.put("default", "_default"); JAVA_KEYWORDS.put("do", "_do"); JAVA_KEYWORDS.put("double", "_double"); JAVA_KEYWORDS.put("else", "_else"); JAVA_KEYWORDS.put("extends", "_extends"); JAVA_KEYWORDS.put("false", "_false"); JAVA_KEYWORDS.put("final", "_final"); JAVA_KEYWORDS.put("finally", "_finally"); JAVA_KEYWORDS.put("float", "_float"); JAVA_KEYWORDS.put("for", "_for"); JAVA_KEYWORDS.put("goto", "_goto"); JAVA_KEYWORDS.put("if", "_if"); JAVA_KEYWORDS.put("implements", "_implements"); JAVA_KEYWORDS.put("import", "_import"); JAVA_KEYWORDS.put("instanceof", "_instanceof"); JAVA_KEYWORDS.put("int", "_int"); JAVA_KEYWORDS.put("interface", "_interface"); JAVA_KEYWORDS.put("long", "_long"); JAVA_KEYWORDS.put("native", "_native"); JAVA_KEYWORDS.put("new", "_new"); JAVA_KEYWORDS.put("null", "_null"); JAVA_KEYWORDS.put("package", "_package"); JAVA_KEYWORDS.put("private", "_private"); JAVA_KEYWORDS.put("protected", "_protected"); JAVA_KEYWORDS.put("public", "_public"); JAVA_KEYWORDS.put("return", "_return"); JAVA_KEYWORDS.put("short", "_short"); JAVA_KEYWORDS.put("static", "_static"); JAVA_KEYWORDS.put("strictfp", "_strictfp"); JAVA_KEYWORDS.put("super", "_super"); JAVA_KEYWORDS.put("switch", "_switch"); JAVA_KEYWORDS.put("synchronized", "_synchronized"); JAVA_KEYWORDS.put("this", "_this"); JAVA_KEYWORDS.put("throw", "_throw"); JAVA_KEYWORDS.put("throws", "_throws"); JAVA_KEYWORDS.put("transient", "_transient"); JAVA_KEYWORDS.put("true", "_true"); JAVA_KEYWORDS.put("try", "_try"); JAVA_KEYWORDS.put("void", "_void"); JAVA_KEYWORDS.put("volatile", "_volatile"); JAVA_KEYWORDS.put("while", "_while"); JAVA_KEYWORDS.put("enum", "_enum"); } protected static final Map<Object, Class<?>> generatedClasses = Collections.synchronizedMap(new WeakHashMap<Object, Class<?>>()); protected XMLAdapterExtensionPoint xmlAdapters; public byte[] defineClass(ClassWriter cw, String classDescriptor, String classSignature, String namespace, String name, BeanProperty[] properties) { // Declare the class declareClass(cw, classDescriptor); // Compute the propOrder String[] propOrder = null; if (properties != null && properties.length > 0) { int size = properties.length; propOrder = new String[size]; for (int i = 0; i < size; i++) { propOrder[i] = getFieldName(properties[i].getName()); } } // Annotate the class annotateClass(cw, name, namespace, propOrder); // Decalre the default constructor declareConstructor(cw, classSignature); if (properties != null) { for (BeanProperty p : properties) { boolean isElement = p.isElement() && (!Map.class.isAssignableFrom(p.getType())); String xmlAdapterClassSignature = null; if (xmlAdapters != null) { Class<?> adapterClass = xmlAdapters.getAdapter(p.getType()); if (adapterClass != null) { xmlAdapterClassSignature = CodeGenerationHelper.getSignature(adapterClass); } } declareProperty(cw, classDescriptor, classSignature, p.getName(), p.getSignature(), p .getGenericSignature(), isElement, p.isNillable(), xmlAdapterClassSignature, p.getJaxbAnnotaions()); } } // Close the generation cw.visitEnd(); return cw.toByteArray(); } protected static boolean isHolder(java.lang.reflect.Type type) { if (type instanceof ParameterizedType) { Class<?> cls = CodeGenerationHelper.getErasure(type); return cls == Holder.class; } return false; } protected static java.lang.reflect.Type getHolderValueType(java.lang.reflect.Type paramType) { if (paramType instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType)paramType; Class<?> cls = CodeGenerationHelper.getErasure(p); if (cls == Holder.class) { return p.getActualTypeArguments()[0]; } } return paramType; } protected void declareProperty(ClassWriter cw, String classDescriptor, String classSignature, String propName, String propClassSignature, String propTypeSignature, boolean isElement, boolean isNillable, String xmlAdapterClassSignature, List<Annotation> jaxbAnnotations) { if (propClassSignature.equals(propTypeSignature)) { propTypeSignature = null; } declareField(cw, propName, propClassSignature, propTypeSignature, isElement, isNillable, xmlAdapterClassSignature, jaxbAnnotations); decalreGetter(cw, classDescriptor, classSignature, propName, propClassSignature, propTypeSignature); declareSetter(cw, classDescriptor, classSignature, propName, propClassSignature, propTypeSignature); } protected String getFieldName(String propName) { String name = JAVA_KEYWORDS.get(propName); return name != null ? name : propName; } protected void declareField(ClassWriter cw, String propName, String propClassSignature, String propTypeSignature, boolean isElement, boolean isNillable, String xmlAdapterClassSignature, List<Annotation> jaxbAnnotations) { FieldVisitor fv; AnnotationVisitor av0; fv = cw.visitField(ACC_PROTECTED, getFieldName(propName), propClassSignature, propTypeSignature, null); // For Map property, we cannot have the XmlElement annotation if (isElement && xmlAdapterClassSignature == null) { av0 = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlElement;", true); av0.visit("name", propName); av0.visit("namespace", ""); if (isNillable) { av0.visit("nillable", Boolean.TRUE); } // FIXME: // av0.visit("required", Boolean.FALSE); av0.visitEnd(); } if (xmlAdapterClassSignature != null) { av0 = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlAnyElement;", true); av0.visit("lax", Boolean.TRUE); av0.visitEnd(); av0 = fv.visitAnnotation("Ljavax/xml/bind/annotation/adapters/XmlJavaTypeAdapter;", true); av0.visit("value", org.objectweb.asm.Type.getType(xmlAdapterClassSignature)); av0.visitEnd(); } for (Annotation ann : jaxbAnnotations) { if (ann instanceof XmlMimeType) { AnnotationVisitor mime = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlMimeType;", true); mime.visit("value", ((XmlMimeType)ann).value()); mime.visitEnd(); } else if (ann instanceof XmlJavaTypeAdapter) { AnnotationVisitor ada = fv.visitAnnotation("Ljavax/xml/bind/annotation/adapters/XmlJavaTypeAdapter;", true); ada.visit("value", org.objectweb.asm.Type.getType(((XmlJavaTypeAdapter)ann).value())); ada.visit("type", org.objectweb.asm.Type.getType(((XmlJavaTypeAdapter)ann).type())); ada.visitEnd(); } else if (ann instanceof XmlAttachmentRef) { AnnotationVisitor att = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlAttachmentRef;", true); att.visitEnd(); } else if (ann instanceof XmlList) { AnnotationVisitor list = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlList;", true); list.visitEnd(); } } fv.visitEnd(); } protected void declareSetter(ClassWriter cw, String classDescriptor, String classSignature, String propName, String propClassSignature, String propTypeSignature) { if ("Ljava/util/List;".equals(propClassSignature)) { return; } MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "set" + capitalize(propName), "(" + propClassSignature + ")V", propTypeSignature == null ? null : "(" + propTypeSignature + ")V", null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); // mv.visitLineNumber(57, l0); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(CodeGenerationHelper.getLoadOPCode(propClassSignature), 1); mv.visitFieldInsn(PUTFIELD, classDescriptor, getFieldName(propName), propClassSignature); Label l1 = new Label(); mv.visitLabel(l1); // mv.visitLineNumber(58, l1); mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLocalVariable("this", classSignature, null, l0, l2, 0); mv.visitLocalVariable(getFieldName(propName), propClassSignature, propTypeSignature, l0, l2, 1); mv.visitMaxs(3, 3); mv.visitEnd(); } protected void decalreGetter(ClassWriter cw, String classDescriptor, String classSignature, String propName, String propClassSignature, String propTypeSignature) { String collectionImplClass = COLLECTION_CLASSES.get(propClassSignature); if (collectionImplClass != null) { decalreCollectionGetter(cw, classDescriptor, classSignature, propName, propClassSignature, propTypeSignature, collectionImplClass); return; } String getterName = ("Z".equals(propClassSignature) ? "is" : "get") + capitalize(propName); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, getterName, "()" + propClassSignature, propTypeSignature == null ? null : "()" + propTypeSignature, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); // mv.visitLineNumber(48, l0); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, classDescriptor, getFieldName(propName), propClassSignature); mv.visitInsn(CodeGenerationHelper.getReturnOPCode(propClassSignature)); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", classSignature, null, l0, l1, 0); mv.visitMaxs(2, 1); mv.visitEnd(); } protected void decalreCollectionGetter(ClassWriter cw, String classDescriptor, String classSignature, String propName, String propClassSignature, String propTypeSignature, String collectionImplClass) { String getterName = "get" + capitalize(propName); String fieldName = getFieldName(propName); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, getterName, "()" + propClassSignature, propTypeSignature == null ? null : "()" + propTypeSignature, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(63, l0); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, classDescriptor, fieldName, propClassSignature); Label l1 = new Label(); mv.visitJumpInsn(IFNONNULL, l1); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLineNumber(64, l2); mv.visitVarInsn(ALOAD, 0); mv.visitTypeInsn(NEW, collectionImplClass); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, collectionImplClass, "<init>", "()V"); mv.visitFieldInsn(PUTFIELD, classDescriptor, fieldName, propClassSignature); mv.visitLabel(l1); mv.visitLineNumber(66, l1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, classDescriptor, fieldName, propClassSignature); mv.visitInsn(ARETURN); Label l3 = new Label(); mv.visitLabel(l3); mv.visitLocalVariable("this", classSignature, null, l0, l3, 0); mv.visitMaxs(3, 1); mv.visitEnd(); } protected static String capitalize(String name) { if (name == null || name.length() == 0) { return name; } else { return Character.toUpperCase(name.charAt(0)) + name.substring(1); } } protected void declareConstructor(ClassWriter cw, String classSignature) { MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); // mv.visitLineNumber(37, l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mv.visitInsn(RETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", classSignature, null, l0, l1, 0); mv.visitMaxs(1, 1); mv.visitEnd(); } protected void declareClass(ClassWriter cw, String classDescriptor) { cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, classDescriptor, null, "java/lang/Object", null); } protected void annotateClass(ClassWriter cw, String name, String namespace, String[] propOrder) { AnnotationVisitor av0; // @XmlRootElement av0 = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlRootElement;", true); av0.visit("name", name); av0.visit("namespace", namespace); av0.visitEnd(); // @XmlAccessorType av0 = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlAccessorType;", true); av0.visitEnum("value", "Ljavax/xml/bind/annotation/XmlAccessType;", "FIELD"); av0.visitEnd(); // @XmlType av0 = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlType;", true); av0.visit("name", name); av0.visit("namespace", namespace); if (propOrder != null) { AnnotationVisitor pv = av0.visitArray("propOrder"); for (String p : propOrder) { pv.visit(null, p); } pv.visitEnd(); } av0.visitEnd(); } public Class<?> generate(String classDescriptor, String classSignature, String namespace, String name, BeanProperty[] properties, GeneratedClassLoader classLoader) { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); byte[] byteCode = defineClass(cw, classDescriptor, classSignature, namespace, name, properties); String className = classDescriptor.replace('/', '.'); Class<?> generated = classLoader.getGeneratedClass(className, byteCode); return generated; } public static class BeanProperty { private Class<?> type; private String namespace; private String name; private String signature; private String genericSignature; private List<Annotation> jaxbAnnotaions = new ArrayList<Annotation>(); private boolean element; private boolean nillable; public BeanProperty(String namespace, String name, Class<?> javaClass, Type type, boolean isElement) { super(); this.namespace = namespace; this.name = name; this.signature = CodeGenerationHelper.getJAXWSSignature(javaClass); this.type = javaClass; this.genericSignature = CodeGenerationHelper.getJAXWSSignature(type); this.element = isElement; // FIXME: How to test nillable? // this.nillable = (type instanceof GenericArrayType) || Collection.class.isAssignableFrom(javaClass) || javaClass.isArray(); // TUSCANY-2389: Set the nillable consistent with what wsgen produces this.nillable = javaClass.isArray(); } public String getName() { return name; } public String getSignature() { return signature; } public String getGenericSignature() { return genericSignature; } public Class<?> getType() { return type; } public List<Annotation> getJaxbAnnotaions() { return jaxbAnnotaions; } public String getNamespace() { return namespace; } public boolean isElement() { return element; } public boolean isNillable() { return nillable; } } public XMLAdapterExtensionPoint getXmlAdapters() { return xmlAdapters; } public void setXmlAdapters(XMLAdapterExtensionPoint xmlAdapters) { this.xmlAdapters = xmlAdapters; } protected static <T extends Annotation> T findAnnotation(Annotation[] anns, Class<T> annotationClass) { for (Annotation a : anns) { if (a.annotationType() == annotationClass) { return annotationClass.cast(a); } } return null; } protected static List<Annotation> findJAXBAnnotations(Annotation[] anns) { List<Annotation> jaxbAnnotation = new ArrayList<Annotation>(); for (Class<? extends Annotation> c : KNOWN_JAXB_ANNOTATIONS) { Annotation a = findAnnotation(anns, c); if (a != null) { jaxbAnnotation.add(a); } } return jaxbAnnotation; } protected List<Annotation> findJAXBAnnotations(Method method) { List<Annotation> anns = new ArrayList<Annotation>(); for (Class<? extends Annotation> c : KNOWN_JAXB_ANNOTATIONS) { Annotation ann = method.getAnnotation(c); if (ann != null) { anns.add(ann); } } return anns; } }