org.datanucleus.enhancer.asm.primarykey.PrimaryKeyGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-enhancer
Show all versions of datanucleus-enhancer
DataNucleus Enhancer is a Java byte-code enhancer for use with DataNucleus.
/**********************************************************************
Copyright (c) 2009 Andy Jefferson and others. All rights reserved.
Licensed 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.
Contributors:
...
**********************************************************************/
package org.datanucleus.enhancer.asm.primarykey;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.TimeZone;
import java.util.UUID;
import org.datanucleus.enhancer.ClassEnhancer;
import org.datanucleus.enhancer.DataNucleusEnhancer;
import org.datanucleus.enhancer.asm.ASMClassEnhancer;
import org.datanucleus.enhancer.asm.ASMUtils;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.util.Localiser;
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;
/**
* Class to handle the generation of a PK class for a persistable class.
* The primary key class is generated as its own class, rather than as an inner class of the original class.
*/
public class PrimaryKeyGenerator
{
/** Localisation of messages */
protected static final Localiser LOCALISER=Localiser.getInstance(
"org.datanucleus.enhancer.Localisation", ClassEnhancer.class.getClassLoader());
/** Metadata for the class that needs a primary key class. */
final AbstractClassMetaData cmd;
/** Name of the primary key class ("mydomain.MyClass_PK"). */
final String pkClassName;
/** ASM name of the PK class ("mydomain/MyClass_PK"). */
final String className_ASM;
/** ASM type descriptor name of the PK class ("Lmydomain/MyClass_PK;"). */
final String className_DescName;
/** The enhancer being used. */
final ASMClassEnhancer classEnhancer;
String stringSeparator = ":";
/**
* Constructor for a PK generator for the specified class.
* @param cmd Metadata for the class that needs a primary key class
* @param enhancer The enhancer being used
*/
public PrimaryKeyGenerator(AbstractClassMetaData cmd, ASMClassEnhancer enhancer)
{
this.cmd = cmd;
this.classEnhancer = enhancer;
pkClassName = cmd.getFullClassName() + AbstractClassMetaData.GENERATED_PK_SUFFIX;
className_ASM = pkClassName.replace('.', '/');
className_DescName = "L" + className_ASM + ";";
// TODO If this is compound identity then we need to change the separator to be different to the
// related class so it is possible to separate them
}
/**
* Method to generate the primary key class.
* @return The bytes for this pk class
*/
public byte[] generate()
{
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, className_ASM, null,
"java/lang/Object", new String[] {"java/io/Serializable"});
// Add fields
addFields(cw);
// Add constructors
addDefaultConstructor(cw);
addStringConstructor(cw);
// Add methods
addMethodToString(cw);
addMethodEquals(cw);
addMethodHashCode(cw);
cw.visitEnd();
return cw.toByteArray();
}
/**
* Method to add fields to match the PK fields of the persistable class
* @param cw The ClassWriter to use
*/
protected void addFields(ClassWriter cw)
{
int[] pkPositions = cmd.getPKMemberPositions();
for (int i=0;i", "()V", null, null);
mv.visitCode();
Label startLabel = new Label();
mv.visitLabel(startLabel);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V");
mv.visitInsn(Opcodes.RETURN);
Label endLabel = new Label();
mv.visitLabel(endLabel);
mv.visitLocalVariable("this", className_DescName, null, startLabel, endLabel, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
/**
* Method to add a constructor taking in a String.
* @param cw The ClassWriter to use
*/
protected void addStringConstructor(ClassWriter cw)
{
if (DataNucleusEnhancer.LOGGER.isDebugEnabled())
{
DataNucleusEnhancer.LOGGER.debug(LOCALISER.msg("Enhancer.AddConstructor", pkClassName + "(String str)"));
}
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "(Ljava/lang/String;)V", null, null);
mv.visitCode();
int[] pkPositions = cmd.getPKMemberPositions();
Label[] fieldLabels = new Label[pkPositions.length];
Label startLabel = new Label();
mv.visitLabel(startLabel);
// Invoke default constructor of superclass (Object)
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V");
// StringTokenizer tokeniser = new StringTokenizer(str, {stringSeparator});
mv.visitTypeInsn(Opcodes.NEW, "java/util/StringTokenizer");
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitLdcInsn(stringSeparator);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/StringTokenizer", "",
"(Ljava/lang/String;Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ASTORE, 2);
Label l5 = new Label();
mv.visitLabel(l5);
// Get the next token and set the respective field from it
int astorePosition = 2;
for (int i=0;i", "(Ljava/lang/String;)V");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, typeName_ASM, "", "(J)V");
mv.visitFieldInsn(Opcodes.PUTFIELD, className_ASM, mmd.getName(),
ASMUtils.getTypeDescriptorForType(mmd.getTypeName()));
}
else if (Calendar.class.isAssignableFrom(mmd.getType()))
{
// fieldX = Calendar.getInstance();
// fieldX.setTimeInMillis(Long.valueOf(tokenX).longValue());
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Calendar", "getInstance", "()Ljava/util/Calendar;");
mv.visitFieldInsn(Opcodes.PUTFIELD, className_ASM, mmd.getName(), "Ljava/util/Calendar;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, className_ASM, mmd.getName(), "Ljava/util/Calendar;");
mv.visitVarInsn(Opcodes.ALOAD, astorePosition);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(Ljava/lang/String;)Ljava/lang/Long;");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/Calendar", "setTimeInMillis", "(J)V");
}
else
{
// "fieldX = new TypeX(tokenX)"
// TODO If this is compound identity and the related object uses SingleFieldIdentity then
// we need to use a different constructor with this value
String fieldTypeName = getTypeNameForField(mmd);
String fieldTypeName_ASM = fieldTypeName.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, fieldTypeName_ASM);
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, astorePosition);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, fieldTypeName_ASM, "",
"(Ljava/lang/String;)V");
mv.visitFieldInsn(Opcodes.PUTFIELD, className_ASM, mmd.getName(),
ASMUtils.getTypeDescriptorForType(fieldTypeName));
}
}
mv.visitInsn(Opcodes.RETURN);
Label endLabel = new Label();
mv.visitLabel(endLabel);
int variableNum = 0;
mv.visitLocalVariable("this", className_DescName, null, startLabel, endLabel, variableNum++);
mv.visitLocalVariable("str", "Ljava/lang/String;", null, startLabel, endLabel, variableNum++);
mv.visitLocalVariable("tokeniser", "Ljava/util/StringTokenizer;", null, l5, endLabel, variableNum++);
for (int i=0;i", "()V");
// Generates a String like "field1:field2:field3"
int[] pkPositions = cmd.getPKMemberPositions();
for (int i=0;i 0)
{
// "^"
mv.visitInsn(Opcodes.IXOR);
}
}
mv.visitInsn(Opcodes.IRETURN);
Label endLabel = new Label();
mv.visitLabel(endLabel);
mv.visitLocalVariable("this", className_DescName, null, startLabel, endLabel, 0);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
}