| /* |
| * 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.ref.WeakReference; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| 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, WeakReference<Class<?>>> generatedClasses = |
| Collections.synchronizedMap(new WeakHashMap<Object, WeakReference<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); |
| |
| // Declare 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", ""); |
| // TUSCANY-3283 - force not nillable if it isn't |
| if (isNillable) { |
| av0.visit("nillable", Boolean.TRUE); |
| } else { |
| av0.visit("nillable", Boolean.FALSE); |
| } |
| // 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) { |
| |
| // The reflection code here allows for toleration of older versions of ASM. |
| ClassWriter cw; |
| try { |
| Constructor<ClassWriter> c = ClassWriter.class.getConstructor(new Class[] {int.class}); |
| Field f = ClassWriter.class.getField("COMPUTE_MAXS"); |
| cw = c.newInstance(f.get(null)); |
| } catch ( Exception ex ) { |
| try { |
| Constructor<ClassWriter> c = ClassWriter.class.getConstructor(new Class[] {boolean.class}); |
| cw = c.newInstance(true); |
| } catch ( Exception ex2 ) { |
| throw new IllegalArgumentException(ex2); |
| } |
| |
| } |
| |
| 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; |
| } |
| |
| } |