blob: 6109680c94d64af79058111c87cf4ff31e0e18cc [file] [log] [blame]
/*
* Copyright (c) 2014, 2021 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Dmitry Kornilov - Initial implementation
package org.eclipse.persistence.internal.jpa.rs.weaving;
import org.eclipse.persistence.dynamic.DynamicClassLoader;
import org.eclipse.persistence.dynamic.EclipseLinkClassWriter;
import org.eclipse.persistence.internal.libraries.asm.EclipseLinkASMClassWriter;
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;
* }
* }
*
*/
@Override
public byte[] writeClass(DynamicClassLoader loader, String className) {
final EclipseLinkASMClassWriter cw = new EclipseLinkASMClassWriter(0);
MethodVisitor mv;
// public class Proxy extends SuperType implements CollectionProxy
cw.visit(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('.', '/');
}
}