/******************************************************************************* * Copyright (c) 2014, 2016 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Dmitry Kornilov - Initial implementation ******************************************************************************/ package org.eclipse.persistence.internal.jpa.weaving; import org.eclipse.persistence.dynamic.DynamicClassLoader; import org.eclipse.persistence.dynamic.EclipseLinkClassWriter; import org.eclipse.persistence.internal.libraries.asm.ClassWriter; import org.eclipse.persistence.internal.libraries.asm.FieldVisitor; import org.eclipse.persistence.internal.libraries.asm.Label; import org.eclipse.persistence.internal.libraries.asm.MethodVisitor; import org.eclipse.persistence.internal.libraries.asm.Opcodes; /** * Generates a subclass of given collection implementing CollectionProxy interface. * {@link org.eclipse.persistence.jpa.rs.util.CollectionProxy} * * @author Dmitry Kornilov * @since EclipseLink 2.6.0 */ public class CollectionProxyClassWriter implements EclipseLinkClassWriter, Opcodes { private static final String CLASS_NAME_SUFFIX = "CollectionProxy"; private static final String INTERFACE = "org/eclipse/persistence/jpa/rs/util/CollectionProxy"; private final String parentClassName; private final String entityName; private final String fieldName; /** * Creates a new CollectionProxyClassWriter for the given attribute of the given entity of given type. * * @param parentClassName the superclass name. * @param entityName entity name * @param fieldName entity attribute name */ public CollectionProxyClassWriter(String parentClassName, String entityName, String fieldName) { this.parentClassName = parentClassName; this.entityName = entityName; this.fieldName = fieldName; } /** * Returns a class name for CollectionProxy based on parent class name and field name * to generate proxy for. * The name is constructed as _<className>_<fieldName>_RestCollectionProxy. * * @param entityName full class name (including package) * @param fieldName field name * @return Rest collection proxy name. */ public static String getClassName(String entityName, String fieldName) { final int index = entityName.lastIndexOf('.'); final String packageName = index >= 0 ? entityName.substring(0, index) : ""; final String shortClassName = index >= 0 ? entityName.substring(index + 1) : entityName; return packageName + "._" + shortClassName + "_" + fieldName + "_" + CLASS_NAME_SUFFIX; } /** * Returns a class name for generated CollectionProxy. * {@link #getClassName(String, String)} * * @return Rest collection proxy name. */ public String getClassName() { return getClassName(entityName, fieldName); } /** * public class Proxy extends SuperType implements CollectionProxy { * private List<LinkV2> links; * * public CollectionProxy(Collection c) { * super(); * this.addAll(c); * } * * @Override * public List<LinkV2> getLinks() { * return links; * } * * @Override * public void setLinks(List<LinkV2> links) { * this.links = links; * } * } * * @param loader * @param className * @return */ @Override public byte[] writeClass(DynamicClassLoader loader, String className) { final ClassWriter cw = new ClassWriter(0); MethodVisitor mv; // public class Proxy extends SuperType implements CollectionProxy cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, getASMClassName(), null, getASMParentClassName(), new String[]{INTERFACE}); // private List<LinkV2> links; final FieldVisitor fv = cw.visitField(ACC_PRIVATE, "links", "Ljava/util/List;", "Ljava/util/List<Lorg/eclipse/persistence/internal/jpa/rs/metadata/model/LinkV2;>;", null); fv.visitEnd(); // public CollectionProxy(Collection c) { // super(); // this.addAll(c); // } { mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/util/Collection;)V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(15, l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, getASMParentClassName(), "<init>", "()V", false); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLineNumber(16, l1); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, getASMClassName(), "addAll", "(Ljava/util/Collection;)Z", false); mv.visitInsn(POP); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLineNumber(17, l2); mv.visitInsn(RETURN); Label l3 = new Label(); mv.visitLabel(l3); mv.visitLocalVariable("this", "L" + getASMClassName() + ";", null, l0, l3, 0); mv.visitLocalVariable("c", "Ljava/util/Collection;", null, l0, l3, 1); mv.visitMaxs(2, 2); mv.visitEnd(); } // @Override // public List<LinkV2> getLinks() { // return links; // } { mv = cw.visitMethod(ACC_PUBLIC, "getLinks", "()Ljava/util/List;", "()Ljava/util/List<Lorg/eclipse/persistence/internal/jpa/rs/metadata/model/LinkV2;>;", null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(21, l0); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, getASMClassName(), "links", "Ljava/util/List;"); mv.visitInsn(ARETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", "L" + getASMClassName() + ";", null, l0, l1, 0); mv.visitMaxs(1, 1); mv.visitEnd(); } // @Override // public void setLinks(List<LinkV2> links) { // this.links = links; // } { mv = cw.visitMethod(ACC_PUBLIC, "setLinks", "(Ljava/util/List;)V", "(Ljava/util/List<Lorg/eclipse/persistence/internal/jpa/rs/metadata/model/LinkV2;>;)V", null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(26, l0); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(PUTFIELD, getASMClassName(), "links", "Ljava/util/List;"); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLineNumber(27, l1); mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLocalVariable("this", "L" + getASMClassName()+ ";", null, l0, l2, 0); mv.visitLocalVariable("links", "Ljava/util/List;", "Ljava/util/List<Lorg/eclipse/persistence/internal/jpa/rs/metadata/model/LinkV2;>;", l0, l2, 1); mv.visitMaxs(2, 2); mv.visitEnd(); } return cw.toByteArray(); } @Override public boolean isCompatible(EclipseLinkClassWriter writer) { return getParentClassName().equals(writer.getParentClassName()); } @Override public Class<?> getParentClass() { return null; } @Override public String getParentClassName() { return parentClassName; } private String getASMClassName() { return getClassName().replace('.', '/'); } private String getASMParentClassName() { return parentClassName.replace('.', '/'); } }