/* * JBoss, Home of Professional Open Source. * Copyright 2013, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.capedwarf.bytecode.endpoints; import java.util.ArrayList; import java.util.Collection; import java.util.Random; import com.google.api.server.spi.config.AnnotationBoolean; import com.google.api.server.spi.config.ApiResourceProperty; import com.google.appengine.repackaged.org.codehaus.jackson.map.annotate.JsonDeserialize; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import javassist.bytecode.SignatureAttribute; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.ClassMemberValue; import javassist.bytecode.annotation.StringMemberValue; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.jboss.capedwarf.bytecode.Annotator; import org.jboss.capedwarf.endpoints.EndpointsJsonDeserializer; import org.jboss.capedwarf.endpoints.EndpointsJsonSerializer; import org.jboss.capedwarf.shared.endpoints.Converters; /** * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ public class DtoAnnotator extends Annotator { public DtoAnnotator(CtClass clazz) { super(clazz); } public void addAnnotations() throws Exception { final Converters converters = Converters.getInstance(getClassLoader()); final boolean isResultType = converters.isResultType(getClazz()); if (isResultType == false) { return; } for (CtMethod method : getClazz().getDeclaredMethods()) { boolean ignored = false; Collection<Annotation> annotations = new ArrayList<>(); ApiResourceProperty apiRP = (ApiResourceProperty) method.getAnnotation(ApiResourceProperty.class); if (apiRP != null) { ignored = convertApiResourceProperty(apiRP, annotations); } if (ignored == false) { final CtClass returnType = method.getReturnType(); if (converters.hasConverter(returnType)) { addConverters(returnType, annotations); } } if (annotations.size() > 0) { addAnnotationsToMethod(method, annotations); } } } private boolean convertApiResourceProperty(ApiResourceProperty apiRP, Collection<Annotation> annotations) { boolean ignored = false; if (apiRP.ignored() == AnnotationBoolean.TRUE) { ignored = true; annotations.add(createAnnotation(JsonIgnore.class)); annotations.add(createAnnotation(com.google.appengine.repackaged.org.codehaus.jackson.annotate.JsonIgnore.class)); } final String name = apiRP.name(); if (name.length() > 0) { StringMemberValue value = memberValueOf(name); annotations.add(createAnnotation(JsonProperty.class, value)); annotations.add(createAnnotation(com.google.appengine.repackaged.org.codehaus.jackson.annotate.JsonProperty.class, value)); } return ignored; } private void addConverters(CtClass returnType, Collection<Annotation> annotations) { ClassMemberValue using1 = createClassMemberValue(EndpointsJsonSerializer.class); annotations.add(createAnnotation(JsonSerialize.class, "using", using1)); annotations.add(createAnnotation(com.google.appengine.repackaged.org.codehaus.jackson.map.annotate.JsonSerialize.class, "using", using1)); ClassMemberValue using2 = createClassMemberValue(generateDeserializer(returnType)); annotations.add(createAnnotation(JsonDeserialize.class, "using", using2)); annotations.add(createAnnotation(com.google.appengine.repackaged.org.codehaus.jackson.map.annotate.JsonDeserialize.class, "using", using2)); } protected String generateDeserializer(CtClass clazz) { try { return generateSimpleSub(EndpointsJsonDeserializer.class.getName(), clazz); } catch (Exception e) { throw new IllegalStateException(e); } } protected static String getSimpleName(String className) { int p = className.lastIndexOf("."); return (p > 0) ? className.substring(p + 1) : className; } protected static String toDesc(String className) { return "L" + className.replace('.', '/'); } protected String generateSimpleSub(String superClass, CtClass ctorParamClass) throws Exception { int p = superClass.lastIndexOf("."); String prefix = (p > 0) ? superClass.substring(0, p) : ""; String suffix = (p > 0) ? superClass.substring(p + 1) : superClass; String classname = prefix + "." + getSimpleName(ctorParamClass.getName()) + Math.abs(new Random().nextInt()) + suffix; ClassPool pool = getClazz().getClassPool(); CtClass newClass = pool.makeClass(classname); newClass.setSuperclass(pool.get(superClass)); SignatureAttribute.ClassSignature cs = SignatureAttribute.toClassSignature(toDesc(superClass) + "<" + toDesc(ctorParamClass.getName()) + ";>;"); newClass.setGenericSignature(cs.encode()); CtConstructor ctor = new CtConstructor(new CtClass[0], newClass); ctor.setBody(String.format("{super(%s.class);}", ctorParamClass.getName())); newClass.addConstructor(ctor); newClass.toClass(); return classname; } }