From 641a7812d439daca3045e7471b604beb807c1891 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 1 Apr 2007 00:00:07 +0000 Subject: Move Storable code generation support to separate package. --- .../com/amazon/carbonado/spi/CodeBuilderUtil.java | 574 --- .../amazon/carbonado/spi/CommonMethodNames.java | 87 - .../amazon/carbonado/spi/ConversionComparator.java | 212 -- .../com/amazon/carbonado/spi/MasterFeature.java | 59 - .../carbonado/spi/MasterStorableGenerator.java | 835 ----- .../com/amazon/carbonado/spi/MasterSupport.java | 39 - .../amazon/carbonado/spi/StorableGenerator.java | 3671 -------------------- .../amazon/carbonado/spi/StorableSerializer.java | 337 -- .../com/amazon/carbonado/spi/StorableSupport.java | 41 - .../com/amazon/carbonado/spi/TriggerSupport.java | 50 - .../com/amazon/carbonado/spi/WrappedStorage.java | 3 + .../com/amazon/carbonado/spi/WrappedSupport.java | 75 - .../com/amazon/carbonado/spi/package-info.java | 6 +- 13 files changed, 6 insertions(+), 5983 deletions(-) delete mode 100644 src/main/java/com/amazon/carbonado/spi/CodeBuilderUtil.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/CommonMethodNames.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/ConversionComparator.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/MasterFeature.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/MasterSupport.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/StorableGenerator.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/StorableSerializer.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/StorableSupport.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/TriggerSupport.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/WrappedSupport.java (limited to 'src/main/java/com/amazon/carbonado/spi') diff --git a/src/main/java/com/amazon/carbonado/spi/CodeBuilderUtil.java b/src/main/java/com/amazon/carbonado/spi/CodeBuilderUtil.java deleted file mode 100644 index 0623f94..0000000 --- a/src/main/java/com/amazon/carbonado/spi/CodeBuilderUtil.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ -package com.amazon.carbonado.spi; - -import java.util.HashSet; -import java.util.Set; -import java.util.Map; -import java.util.HashMap; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import org.cojen.classfile.ClassFile; -import org.cojen.classfile.Modifiers; -import org.cojen.classfile.CodeBuilder; -import org.cojen.classfile.MethodInfo; -import org.cojen.classfile.Label; -import org.cojen.classfile.TypeDesc; -import org.cojen.classfile.LocalVariable; -import org.cojen.classfile.MethodDesc; -import org.cojen.classfile.Opcode; -import org.cojen.util.ClassInjector; - -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.SupportException; - -import static com.amazon.carbonado.spi.CommonMethodNames.*; - -/** - * Collection of useful utilities for generating Carbonado code. - * - * @author Don Schneider - * @author Brian S O'Neill - */ -public class CodeBuilderUtil { - - /** - * Generate code to throw an exception if a parameter is null - * @param b CodeBuilder into which to append the code - * @param paramIndex index of the parameter to check - */ - public static void assertParameterNotNull(CodeBuilder b, int paramIndex) { - b.loadLocal(b.getParameter(paramIndex)); - Label notNull = b.createLabel(); - b.ifNullBranch(notNull, false); - throwException(b, IllegalArgumentException.class, null); - notNull.setLocation(); - } - - /** - * Generate code to create a local variable containing the specified parameter coerced - * to the specified type. This is useful for re-interpreting erased generics into - * the more specific genericized type. - * - * @param b CodeBuilder into which to append the code - * @param paramType the more specific type which was erased during compilation - * @param paramIndex index of the parameter to unerase - * @return a local variable referencing the type-cast parameter - */ - public static LocalVariable uneraseGenericParameter( - CodeBuilder b, TypeDesc paramType, final int paramIndex) - { - b.loadLocal(b.getParameter(paramIndex)); - b.checkCast(paramType); - LocalVariable result = b.createLocalVariable(null, paramType); - b.storeLocal(result); - return result; - } - - /** - * Generate code to throw an exception with an optional message. - * @param b {@link CodeBuilder} to which to add code - * @param type type of the object to throw - * @param message optional message to provide to the constructor - */ - public static void throwException(CodeBuilder b, Class type, String message) { - TypeDesc desc = TypeDesc.forClass(type); - b.newObject(desc); - b.dup(); - if (message == null) { - b.invokeConstructor(desc, null); - } else { - b.loadConstant(message); - b.invokeConstructor(desc, new TypeDesc[] {TypeDesc.STRING}); - } - b.throwObject(); - } - - /** - * Collect a set of all the interfaces and recursively all superclasses for the leaf - * (genericised class) and root (genericised base class). Eg, for Object, all - * classes and implemented interfaces for every superclass between foo (the leaf) and - * Object (the base). - *

A copy must be coercible into any of these types, and copy bridge methods must be - * provided to do so. - * - *

Note that the official documentation for this is in draft form, and you have to be - * psychic to have figured out the necessity in the first place. - * - * @param set set into which the class types will be collected - * @param leaf leaf class - * @return same set as was passed in - */ - public static Set gatherAllBridgeTypes(Set set, Class leaf) { - set.add(leaf); - for (Class c : leaf.getInterfaces()) { - gatherAllBridgeTypes(set, c); - } - if ((leaf = leaf.getSuperclass()) != null) { - gatherAllBridgeTypes(set, leaf); - } - return set; - } - - /** - * Add copy bridge methods for all classes/interfaces between the leaf (genericised class) - * and the root (genericised baseclass). - * - * @param cf file to which to add the copy bridge - * @param leaf leaf class - */ - public static void defineCopyBridges(ClassFile cf, Class leaf) { - for (Class c : gatherAllBridgeTypes(new HashSet(), leaf)) { - if (c != Object.class) { - defineCopyBridge(cf, leaf, c); - } - } - } - - /** - * Add a copy bridge method to the classfile for the given type. This is needed to allow - * the genericised class make a copy itself -- which will be erased to the base type -- and - * return it as the correct type. - * - * @param cf file to which to add the copy bridge - * @param leaf leaf class - * @param returnClass type returned from generated bridge method - */ - private static void defineCopyBridge(ClassFile cf, Class leaf, Class returnClass) { - TypeDesc returnType = TypeDesc.forClass(returnClass); - - if (isPublicMethodFinal(leaf, COPY_METHOD_NAME, returnType, null)) { - // Cannot override. - return; - } - - MethodInfo mi = cf.addMethod(Modifiers.PUBLIC.toBridge(true), - COPY_METHOD_NAME, returnType, null); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.invokeVirtual(COPY_METHOD_NAME, cf.getType(), null); - b.returnValue(returnType); - } - - /** - * Returns a new modifiable mapping of method signatures to methods. - * - * @return map of {@link #createSig signatures} to methods - */ - public static Map gatherAllDeclaredMethods(Class clazz) { - Map methods = new HashMap(); - gatherAllDeclaredMethods(methods, clazz); - return methods; - } - - private static void gatherAllDeclaredMethods(Map methods, Class clazz) { - for (Method m : clazz.getDeclaredMethods()) { - String desc = createSig(m); - if (!methods.containsKey(desc)) { - methods.put(desc, m); - } - } - - Class superclass = clazz.getSuperclass(); - if (superclass != null) { - gatherAllDeclaredMethods(methods, superclass); - } - for (Class c : clazz.getInterfaces()) { - gatherAllDeclaredMethods(methods, c); - } - } - - /** - * Returns true if a public final method exists which matches the given - * specification. - */ - public static boolean isPublicMethodFinal(Class clazz, String name, - TypeDesc retType, TypeDesc[] params) - { - if (!clazz.isInterface()) { - Class[] paramClasses; - if (params == null || params.length == 0) { - paramClasses = null; - } else { - paramClasses = new Class[params.length]; - for (int i=0; i - *

  • implements Storable
  • - *
  • implements Cloneable - *
  • abstract if appropriate - *
  • marked synthetic - *
  • targetted for java version 1.5 - * - * @param ci ClassInjector for the storable - * @param type specific Storable implementation to generate - * @param isAbstract true if the class should be abstract - * @param aSourcefileName identifier for the classfile, typically the factory class name - * @return ClassFile object ready to have methods added. - */ - public static ClassFile createStorableClassFile( - ClassInjector ci, Class type, boolean isAbstract, String aSourcefileName) - { - ClassFile cf; - if (type.isInterface()) { - cf = new ClassFile(ci.getClassName()); - cf.addInterface(type); - } else { - cf = new ClassFile(ci.getClassName(), type); - } - - if (isAbstract) { - Modifiers modifiers = cf.getModifiers().toAbstract(true); - cf.setModifiers(modifiers); - } - cf.addInterface(Storable.class); - cf.addInterface(Cloneable.class); - cf.markSynthetic(); - cf.setSourceFile(aSourcefileName); - cf.setTarget("1.5"); - return cf; - } - - /** - * Generates code to compare a field in this object against the same one in a - * different instance. Branch to the provided Label if they are not equal. - * - * @param b {@link CodeBuilder} to which to add the code - * @param fieldName the name of the field - * @param fieldType the type of the field - * @param testForNull if true and the values are references, they will be considered - * unequal unless neither or both are null. If false, assume neither is null. - * @param fail the label to branch to - * @param other the other instance to test - */ - public static void addEqualsCall(CodeBuilder b, - String fieldName, - TypeDesc fieldType, - boolean testForNull, - Label fail, - LocalVariable other) - { - b.loadThis(); - b.loadField(fieldName, fieldType); - - b.loadLocal(other); - b.loadField(fieldName, fieldType); - - addValuesEqualCall(b, fieldType, testForNull, fail, false); - } - - /** - * Generates code to compare two values on the stack, and branch to the - * provided Label if they are not equal. Both values must be of the same type. - * - *

    The generated instruction consumes both values on the stack. - * - * @param b {@link CodeBuilder} to which to add the code - * @param valueType the type of the values - * @param testForNull if true and the values are references, they will be considered - * unequal unless neither or both are null. If false, assume neither is null. - * @param label the label to branch to - * @param choice when true, branch to label if values are equal, else - * branch to label if values are unequal. - */ - public static void addValuesEqualCall(final CodeBuilder b, - final TypeDesc valueType, - final boolean testForNull, - final Label label, - final boolean choice) - { - if (valueType.getTypeCode() != TypeDesc.OBJECT_CODE) { - b.ifComparisonBranch(label, choice ? "==" : "!=", valueType); - return; - } - - // Equals method returns zero for false, so if choice is true, branch - // if not zero. Note that operator selection is opposite when invoking - // a direct ifComparisonBranch method. - String equalsBranchOp = choice ? "!=" : "=="; - - if (!testForNull) { - addEqualsCallTo(b, valueType); - b.ifZeroComparisonBranch(label, equalsBranchOp); - return; - } - - Label isNotNull = b.createLabel(); - LocalVariable value = b.createLocalVariable(null, valueType); - b.storeLocal(value); - b.loadLocal(value); - b.ifNullBranch(isNotNull, false); - - // First value popped off stack is null. Just test remaining one for null. - b.ifNullBranch(label, choice); - Label cont = b.createLabel(); - b.branch(cont); - - // First value popped off stack is not null, but second one might - // be. Call equals method, but swap values so that the second value is - // an argument into the equals method. - isNotNull.setLocation(); - b.loadLocal(value); - b.swap(); - addEqualsCallTo(b, valueType); - b.ifZeroComparisonBranch(label, equalsBranchOp); - - cont.setLocation(); - } - - public static void addEqualsCallTo(CodeBuilder b, TypeDesc fieldType) { - if (fieldType.isArray()) { - if (!fieldType.getComponentType().isPrimitive()) { - TypeDesc type = TypeDesc.forClass(Object[].class); - b.invokeStatic("java.util.Arrays", "deepEquals", - TypeDesc.BOOLEAN, new TypeDesc[] {type, type}); - } else { - b.invokeStatic("java.util.Arrays", "equals", - TypeDesc.BOOLEAN, new TypeDesc[] {fieldType, fieldType}); - } - } else { - TypeDesc[] params = {TypeDesc.OBJECT}; - if (fieldType.toClass() != null) { - if (fieldType.toClass().isInterface()) { - b.invokeInterface(fieldType, "equals", TypeDesc.BOOLEAN, params); - } else { - b.invokeVirtual(fieldType, "equals", TypeDesc.BOOLEAN, params); - } - } else { - b.invokeVirtual(TypeDesc.OBJECT, "equals", TypeDesc.BOOLEAN, params); - } - } - } - - /** - * Create a representation of the signature which includes the method name. - * This uniquely identifies the method. - * - * @param m method to describe - */ - public static String createSig(Method m) { - return m.getName() + ':' + MethodDesc.forMethod(m).getDescriptor(); - } - - /** - * Converts a value on the stack. If "to" type is a String, then conversion - * may call the String.valueOf(from). - */ - public static void convertValue(CodeBuilder b, Class from, Class to) { - if (from == to) { - return; - } - - TypeDesc fromType = TypeDesc.forClass(from); - TypeDesc toType = TypeDesc.forClass(to); - - // Let CodeBuilder have a crack at the conversion first. - try { - b.convert(fromType, toType); - return; - } catch (IllegalArgumentException e) { - if (to != String.class && to != Object.class && to != CharSequence.class) { - throw e; - } - } - - // Fallback case is to convert to a String. - - if (fromType.isPrimitive()) { - b.invokeStatic(TypeDesc.STRING, "valueOf", TypeDesc.STRING, new TypeDesc[]{fromType}); - } else { - // If object on stack is null, then just leave it alone. - b.dup(); - Label isNull = b.createLabel(); - b.ifNullBranch(isNull, true); - b.invokeStatic(TypeDesc.STRING, "valueOf", TypeDesc.STRING, - new TypeDesc[]{TypeDesc.OBJECT}); - isNull.setLocation(); - } - } - - /** - * Generates code to push an initial version property value on the stack. - * - * @throws SupportException if version type is not supported - */ - public static void initialVersion(CodeBuilder b, TypeDesc type, int value) - throws SupportException - { - adjustVersion(b, type, value, false); - } - - /** - * Generates code to increment a version property value, already on the stack. - * - * @throws SupportException if version type is not supported - */ - public static void incrementVersion(CodeBuilder b, TypeDesc type) - throws SupportException - { - adjustVersion(b, type, 0, true); - } - - private static void adjustVersion(CodeBuilder b, TypeDesc type, int value, boolean increment) - throws SupportException - { - TypeDesc primitiveType = type.toPrimitiveType(); - supportCheck: { - if (primitiveType != null) { - switch (primitiveType.getTypeCode()) { - case TypeDesc.INT_CODE: - case TypeDesc.LONG_CODE: - break supportCheck; - } - } - throw new SupportException("Unsupported version type: " + type.getFullName()); - } - - if (!increment) { - if (primitiveType == TypeDesc.LONG) { - b.loadConstant((long) value); - } else { - b.loadConstant(value); - } - } else { - Label setVersion = b.createLabel(); - if (!type.isPrimitive()) { - b.dup(); - Label versionNotNull = b.createLabel(); - b.ifNullBranch(versionNotNull, false); - b.pop(); - if (primitiveType == TypeDesc.LONG) { - b.loadConstant(1L); - } else { - b.loadConstant(1); - } - b.branch(setVersion); - versionNotNull.setLocation(); - b.convert(type, primitiveType); - } - if (primitiveType == TypeDesc.LONG) { - b.loadConstant(1L); - b.math(Opcode.LADD); - } else { - b.loadConstant(1); - b.math(Opcode.IADD); - } - setVersion.setLocation(); - } - - b.convert(primitiveType, type); - } - - /** - * Determines which overloaded "with" method on Query should be bound to. - */ - public static TypeDesc bindQueryParam(Class clazz) { - // This method is a bit vestigial. Once upon a time the Query class did - // not support all primitive types. - if (clazz.isPrimitive()) { - TypeDesc type = TypeDesc.forClass(clazz); - switch (type.getTypeCode()) { - case TypeDesc.INT_CODE: - case TypeDesc.LONG_CODE: - case TypeDesc.FLOAT_CODE: - case TypeDesc.DOUBLE_CODE: - case TypeDesc.BOOLEAN_CODE: - case TypeDesc.CHAR_CODE: - case TypeDesc.BYTE_CODE: - case TypeDesc.SHORT_CODE: - return type; - } - } - return TypeDesc.OBJECT; - } - - /** - * Appends a String to a StringBuilder. A StringBuilder and String must be - * on the stack, and a StringBuilder is left on the stack after the call. - */ - public static void callStringBuilderAppendString(CodeBuilder b) { - // Because of JDK1.5 bug which exposes AbstractStringBuilder class, - // cannot use reflection to get method signature. - TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class); - b.invokeVirtual(stringBuilder, "append", stringBuilder, new TypeDesc[] {TypeDesc.STRING}); - } - - /** - * Appends a char to a StringBuilder. A StringBuilder and char must be on - * the stack, and a StringBuilder is left on the stack after the call. - */ - public static void callStringBuilderAppendChar(CodeBuilder b) { - // Because of JDK1.5 bug which exposes AbstractStringBuilder class, - // cannot use reflection to get method signature. - TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class); - b.invokeVirtual(stringBuilder, "append", stringBuilder, new TypeDesc[] {TypeDesc.CHAR}); - } - - /** - * Calls length on a StringBuilder on the stack, leaving an int on the stack. - */ - public static void callStringBuilderLength(CodeBuilder b) { - // Because of JDK1.5 bug which exposes AbstractStringBuilder class, - // cannot use reflection to get method signature. - TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class); - b.invokeVirtual(stringBuilder, "length", TypeDesc.INT, null); - } - - /** - * Calls setLength on a StringBuilder. A StringBuilder and int must be on - * the stack, and both are consumed after the call. - */ - public static void callStringBuilderSetLength(CodeBuilder b) { - // Because of JDK1.5 bug which exposes AbstractStringBuilder class, - // cannot use reflection to get method signature. - TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class); - b.invokeVirtual(stringBuilder, "setLength", null, new TypeDesc[] {TypeDesc.INT}); - } - - /** - * Calls toString on a StringBuilder. A StringBuilder must be on the stack, - * and a String is left on the stack after the call. - */ - public static void callStringBuilderToString(CodeBuilder b) { - // Because of JDK1.5 bug which exposes AbstractStringBuilder class, - // cannot use reflection to get method signature. - TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class); - b.invokeVirtual(stringBuilder, "toString", TypeDesc.STRING, null); - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/CommonMethodNames.java b/src/main/java/com/amazon/carbonado/spi/CommonMethodNames.java deleted file mode 100644 index 8a011c9..0000000 --- a/src/main/java/com/amazon/carbonado/spi/CommonMethodNames.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -/** - * Collection of constant method names for the public API. - * - * @author Brian S O'Neill - */ -public class CommonMethodNames { - /** Storable API method name */ - public static final String - LOAD_METHOD_NAME = "load", - INSERT_METHOD_NAME = "insert", - UPDATE_METHOD_NAME = "update", - DELETE_METHOD_NAME = "delete", - TRY_LOAD_METHOD_NAME = "tryLoad", - TRY_INSERT_METHOD_NAME = "tryInsert", - TRY_UPDATE_METHOD_NAME = "tryUpdate", - TRY_DELETE_METHOD_NAME = "tryDelete", - STORABLE_TYPE_METHOD_NAME = "storableType", - COPY_METHOD_NAME = "copy", - CLONE_METHOD_NAME = "clone", - COPY_ALL_PROPERTIES = "copyAllProperties", - COPY_PRIMARY_KEY_PROPERTIES = "copyPrimaryKeyProperties", - COPY_VERSION_PROPERTY = "copyVersionProperty", - COPY_UNEQUAL_PROPERTIES = "copyUnequalProperties", - COPY_DIRTY_PROPERTIES = "copyDirtyProperties", - HAS_DIRTY_PROPERTIES = "hasDirtyProperties", - MARK_PROPERTIES_CLEAN = "markPropertiesClean", - MARK_ALL_PROPERTIES_CLEAN = "markAllPropertiesClean", - MARK_PROPERTIES_DIRTY = "markPropertiesDirty", - MARK_ALL_PROPERTIES_DIRTY = "markAllPropertiesDirty", - IS_PROPERTY_UNINITIALIZED = "isPropertyUninitialized", - IS_PROPERTY_DIRTY = "isPropertyDirty", - IS_PROPERTY_CLEAN = "isPropertyClean", - IS_PROPERTY_SUPPORTED = "isPropertySupported", - TO_STRING_KEY_ONLY_METHOD_NAME = "toStringKeyOnly", - TO_STRING_METHOD_NAME = "toString", - HASHCODE_METHOD_NAME = "hashCode", - EQUALS_METHOD_NAME = "equals", - EQUAL_PRIMARY_KEYS_METHOD_NAME = "equalPrimaryKeys", - EQUAL_PROPERTIES_METHOD_NAME = "equalProperties"; - - /** Storage API method name */ - public static final String - QUERY_METHOD_NAME = "query", - PREPARE_METHOD_NAME = "prepare"; - - /** Query API method name */ - public static final String - LOAD_ONE_METHOD_NAME = "loadOne", - TRY_LOAD_ONE_METHOD_NAME = "tryLoadOne", - WITH_METHOD_NAME = "with", - FETCH_METHOD_NAME = "fetch"; - - /** Repository API method name */ - public static final String - STORAGE_FOR_METHOD_NAME = "storageFor", - ENTER_TRANSACTION_METHOD_NAME = "enterTransaction", - GET_TRANSACTION_ISOLATION_LEVEL_METHOD_NAME = "getTransactionIsolationLevel"; - - /** Transaction API method name */ - public static final String - SET_FOR_UPDATE_METHOD_NAME = "setForUpdate", - COMMIT_METHOD_NAME = "commit", - EXIT_METHOD_NAME = "exit"; - - /** WrappedStorage.Support API method name */ - public static final String CREATE_WRAPPED_SUPPORT_METHOD_NAME = "createSupport"; -} diff --git a/src/main/java/com/amazon/carbonado/spi/ConversionComparator.java b/src/main/java/com/amazon/carbonado/spi/ConversionComparator.java deleted file mode 100644 index aed00b1..0000000 --- a/src/main/java/com/amazon/carbonado/spi/ConversionComparator.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import java.util.Comparator; - -import org.cojen.classfile.TypeDesc; - -/** - * Compares type conversions, finding the one that is nearest. - * - * @author Brian S O'Neill - */ -public class ConversionComparator implements Comparator { - private final TypeDesc mFrom; - - public ConversionComparator(Class fromType) { - mFrom = TypeDesc.forClass(fromType); - } - - /** - * Returns true if a coversion is possible to the given type. - */ - public boolean isConversionPossible(Class toType) { - return isConversionPossible(mFrom, TypeDesc.forClass(toType)); - } - - @SuppressWarnings("unchecked") - private static boolean isConversionPossible(TypeDesc from, TypeDesc to) { - if (from == to) { - return true; - } - - if (from.toPrimitiveType() != null && to.toPrimitiveType() != null) { - from = from.toPrimitiveType(); - to = to.toPrimitiveType(); - } else { - from = from.toObjectType(); - to = to.toObjectType(); - } - - switch (from.getTypeCode()) { - case TypeDesc.OBJECT_CODE: default: - return to.toClass().isAssignableFrom(from.toClass()); - case TypeDesc.BOOLEAN_CODE: - return to == TypeDesc.BOOLEAN; - case TypeDesc.BYTE_CODE: - return to == TypeDesc.BYTE || to == TypeDesc.SHORT - || to == TypeDesc.INT || to == TypeDesc.LONG - || to == TypeDesc.FLOAT || to == TypeDesc.DOUBLE; - case TypeDesc.SHORT_CODE: - return to == TypeDesc.SHORT - || to == TypeDesc.INT || to == TypeDesc.LONG - || to == TypeDesc.FLOAT || to == TypeDesc.DOUBLE; - case TypeDesc.CHAR_CODE: - return to == TypeDesc.CHAR; - case TypeDesc.INT_CODE: - return to == TypeDesc.INT || to == TypeDesc.LONG || to == TypeDesc.DOUBLE; - case TypeDesc.FLOAT_CODE: - return to == TypeDesc.FLOAT || to == TypeDesc.DOUBLE; - case TypeDesc.LONG_CODE: - return to == TypeDesc.LONG; - case TypeDesc.DOUBLE_CODE: - return to == TypeDesc.DOUBLE; - } - } - - /** - * Evaluates two types, to see which one is nearest to the from type. - * Return <0 if "a" is nearest, 0 if both are equally good, >0 if "b" is - * nearest. - */ - public int compare(Class toType_a, Class toType_b) { - TypeDesc from = mFrom; - TypeDesc a = TypeDesc.forClass(toType_a); - TypeDesc b = TypeDesc.forClass(toType_b); - - if (from == a) { - if (from == b) { - return 0; - } - return -1; - } else if (from == b) { - return 1; - } - - int result = compare(from, a, b); - if (result != 0) { - return result; - } - - if (from.isPrimitive()) { - // Try boxing. - if (from.toObjectType() != null) { - from = from.toObjectType(); - return compare(from, a, b); - } - } else { - // Try unboxing. - if (from.toPrimitiveType() != null) { - from = from.toPrimitiveType(); - result = compare(from, a, b); - if (result != 0) { - return result; - } - // Try boxing back up. Test by unboxing 'to' types. - if (!toType_a.isPrimitive() && a.toPrimitiveType() != null) { - a = a.toPrimitiveType(); - } - if (!toType_b.isPrimitive() && b.toPrimitiveType() != null) { - b = b.toPrimitiveType(); - } - return compare(from, a, b); - } - } - - return 0; - } - - private static int compare(TypeDesc from, TypeDesc a, TypeDesc b) { - if (isConversionPossible(from, a)) { - if (isConversionPossible(from, b)) { - if (from.isPrimitive()) { - if (a.isPrimitive()) { - if (b.isPrimitive()) { - // Choose the one with the least amount of widening. - return primitiveWidth(a) - primitiveWidth(b); - } else { - return -1; - } - } else if (b.isPrimitive()) { - return 1; - } - } else { - // Choose the one with the shortest distance up the class - // hierarchy. - Class fromClass = from.toClass(); - if (!fromClass.isInterface()) { - if (a.toClass().isInterface()) { - if (!b.toClass().isInterface()) { - return -1; - } - } else if (b.toClass().isInterface()) { - return 1; - } else { - return distance(fromClass, a.toClass()) - - distance(fromClass, b.toClass()); - } - } - } - } else { - return -1; - } - } else if (isConversionPossible(from, b)) { - return 1; - } - - return 0; - } - - // 1 = boolean, 2 = byte, 3 = short, 4 = char, 5 = int, 6 = float, 7 = long, 8 = double - private static int primitiveWidth(TypeDesc type) { - switch (type.getTypeCode()) { - default: - return 0; - case TypeDesc.BOOLEAN_CODE: - return 1; - case TypeDesc.BYTE_CODE: - return 2; - case TypeDesc.SHORT_CODE: - return 3; - case TypeDesc.CHAR_CODE: - return 4; - case TypeDesc.INT_CODE: - return 5; - case TypeDesc.FLOAT_CODE: - return 6; - case TypeDesc.LONG_CODE: - return 7; - case TypeDesc.DOUBLE_CODE: - return 8; - } - } - - private static int distance(Class from, Class to) { - int distance = 0; - while (from != to) { - from = from.getSuperclass(); - if (from == null) { - return Integer.MAX_VALUE; - } - distance++; - } - return distance; - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/MasterFeature.java b/src/main/java/com/amazon/carbonado/spi/MasterFeature.java deleted file mode 100644 index 1ec1fc3..0000000 --- a/src/main/java/com/amazon/carbonado/spi/MasterFeature.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -/** - * Master feature to enable when using {@link MasterStorableGenerator}. - * - * @author Brian S O'Neill - */ -public enum MasterFeature { - /** Insert and update operations implement record versioning, if version property exists */ - VERSIONING, - - /** Update operations load clean copy first, to prevent destructive update */ - UPDATE_FULL, - - /** Ensure update operation always is in a transaction */ - UPDATE_TXN, - - /** Ensure update operation always is in a transaction, "for update" */ - UPDATE_TXN_FOR_UPDATE, - - /** Insert operation applies any sequences to unset properties */ - INSERT_SEQUENCES, - - /** - * Insert operation checks that all required data properties have been set, - * excluding automatic properties and version property. - */ - INSERT_CHECK_REQUIRED, - - /** Ensure insert operation always is in a transaction */ - INSERT_TXN, - - /** Ensure insert operation always is in a transaction, "for update" */ - INSERT_TXN_FOR_UPDATE, - - /** Ensure delete operation always is in a transaction */ - DELETE_TXN, - - /** Ensure delete operation always is in a transaction, "for update" */ - DELETE_TXN_FOR_UPDATE, -} diff --git a/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java deleted file mode 100644 index 3de8fb5..0000000 --- a/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java +++ /dev/null @@ -1,835 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import java.lang.reflect.Method; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Map; - -import org.cojen.classfile.ClassFile; -import org.cojen.classfile.CodeBuilder; -import org.cojen.classfile.Label; -import org.cojen.classfile.LocalVariable; -import org.cojen.classfile.MethodInfo; -import org.cojen.classfile.Modifiers; -import org.cojen.classfile.Opcode; -import org.cojen.classfile.TypeDesc; -import org.cojen.util.ClassInjector; -import org.cojen.util.KeyFactory; -import org.cojen.util.SoftValuedHashMap; - -import com.amazon.carbonado.ConstraintException; -import com.amazon.carbonado.OptimisticLockException; -import com.amazon.carbonado.PersistException; -import com.amazon.carbonado.Repository; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.SupportException; -import com.amazon.carbonado.Transaction; - -import com.amazon.carbonado.info.StorableInfo; -import com.amazon.carbonado.info.StorableIntrospector; -import com.amazon.carbonado.info.StorableProperty; - -import com.amazon.carbonado.sequence.SequenceValueProducer; - -import static com.amazon.carbonado.spi.CommonMethodNames.*; - -/** - * Generates and caches abstract implementations of {@link Storable} types - * suitable for use by master repositories. The generated classes extend those - * generated by {@link StorableGenerator}. Subclasses need not worry about - * transactions since this class takes care of that. - * - * @author Brian S O'Neill - */ -public final class MasterStorableGenerator { - // Note: All generated fields/methods have a "$" character in them to - // prevent name collisions with any inherited fields/methods. User storable - // properties are defined as fields which exactly match the property - // name. We don't want collisions with those either. Legal bean properties - // cannot have "$" in them, so there's nothing to worry about. - - /** Name of protected abstract method in generated storable */ - public static final String - DO_TRY_LOAD_MASTER_METHOD_NAME = StorableGenerator.DO_TRY_LOAD_METHOD_NAME, - DO_TRY_INSERT_MASTER_METHOD_NAME = "doTryInsert$master", - DO_TRY_UPDATE_MASTER_METHOD_NAME = "doTryUpdate$master", - DO_TRY_DELETE_MASTER_METHOD_NAME = "doTryDelete$master"; - - private static final String APPEND_UNINIT_PROPERTY = "appendUninitializedPropertyName$"; - - private static final String INSERT_OP = "Insert"; - private static final String UPDATE_OP = "Update"; - private static final String DELETE_OP = "Delete"; - - // Cache of generated abstract classes. - private static Map> cCache = new SoftValuedHashMap(); - - /** - * Returns an abstract implementation of the given Storable type, which - * is fully thread-safe. The Storable type itself may be an interface or - * a class. If it is a class, then it must not be final, and it must have a - * public, no-arg constructor. The constructor for the returned abstract - * class looks like this: - * - *

    -     * public <init>(MasterSupport);
    -     * 
    - * - * Subclasses must implement the following abstract protected methods, - * whose exact names are defined by constants in this class: - * - *
    -     * // Load the object by examining the primary key.
    -     * protected abstract boolean doTryLoad() throws FetchException;
    -     *
    -     * // Insert the object into the storage layer.
    -     * protected abstract boolean doTryInsert_master() throws PersistException;
    -     *
    -     * // Update the object in the storage.
    -     * protected abstract boolean doTryUpdate_master() throws PersistException;
    -     *
    -     * // Delete the object from the storage layer by the primary key.
    -     * protected abstract boolean doTryDelete_master() throws PersistException;
    -     * 
    - * - * Subclasses can access the MasterSupport instance via the protected field - * named by {@link StorableGenerator#SUPPORT_FIELD_NAME SUPPORT_FIELD_NAME}. - * - * @throws com.amazon.carbonado.MalformedTypeException if Storable type is not well-formed - * @throws IllegalArgumentException if type is null - * @see MasterSupport - */ - public static Class - getAbstractClass(Class type, EnumSet features) - throws SupportException, IllegalArgumentException - { - StorableInfo info = StorableIntrospector.examine(type); - - anySequences: - if (features.contains(MasterFeature.INSERT_SEQUENCES)) { - for (StorableProperty property : info.getAllProperties().values()) { - if (property.getSequenceName() != null) { - break anySequences; - } - } - features.remove(MasterFeature.INSERT_SEQUENCES); - } - - if (info.getVersionProperty() == null) { - features.remove(MasterFeature.VERSIONING); - } - - if (features.contains(MasterFeature.VERSIONING)) { - // Implied feature. - features.add(MasterFeature.UPDATE_FULL); - } - - if (alwaysHasTxn(INSERT_OP, features)) { - // Implied feature. - features.add(MasterFeature.INSERT_TXN); - } - if (alwaysHasTxn(UPDATE_OP, features)) { - // Implied feature. - features.add(MasterFeature.UPDATE_TXN); - } - if (alwaysHasTxn(DELETE_OP, features)) { - // Implied feature. - features.add(MasterFeature.DELETE_TXN); - } - - if (requiresTxnForUpdate(INSERT_OP, features)) { - // Implied feature. - features.add(MasterFeature.INSERT_TXN_FOR_UPDATE); - } - if (requiresTxnForUpdate(UPDATE_OP, features)) { - // Implied feature. - features.add(MasterFeature.UPDATE_TXN_FOR_UPDATE); - } - if (requiresTxnForUpdate(DELETE_OP, features)) { - // Implied feature. - features.add(MasterFeature.DELETE_TXN_FOR_UPDATE); - } - - Object key = KeyFactory.createKey(new Object[] {type, features}); - - synchronized (cCache) { - Class abstractClass = (Class) cCache.get(key); - if (abstractClass != null) { - return abstractClass; - } - abstractClass = - new MasterStorableGenerator(type, features).generateAndInjectClass(); - cCache.put(key, abstractClass); - return abstractClass; - } - } - - private final EnumSet mFeatures; - private final StorableInfo mInfo; - private final Map> mAllProperties; - - private final ClassInjector mClassInjector; - private final ClassFile mClassFile; - - private MasterStorableGenerator(Class storableType, EnumSet features) { - mFeatures = features; - mInfo = StorableIntrospector.examine(storableType); - mAllProperties = mInfo.getAllProperties(); - - final Class abstractClass = StorableGenerator.getAbstractClass(storableType); - - mClassInjector = ClassInjector.create - (storableType.getName(), abstractClass.getClassLoader()); - - mClassFile = new ClassFile(mClassInjector.getClassName(), abstractClass); - mClassFile.setModifiers(mClassFile.getModifiers().toAbstract(true)); - mClassFile.markSynthetic(); - mClassFile.setSourceFile(MasterStorableGenerator.class.getName()); - mClassFile.setTarget("1.5"); - } - - private Class generateAndInjectClass() throws SupportException { - generateClass(); - Class abstractClass = mClassInjector.defineClass(mClassFile); - return (Class) abstractClass; - } - - private void generateClass() throws SupportException { - // Declare some types. - final TypeDesc storableType = TypeDesc.forClass(Storable.class); - final TypeDesc triggerSupportType = TypeDesc.forClass(TriggerSupport.class); - final TypeDesc masterSupportType = TypeDesc.forClass(MasterSupport.class); - final TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - final TypeDesc optimisticLockType = TypeDesc.forClass(OptimisticLockException.class); - final TypeDesc persistExceptionType = TypeDesc.forClass(PersistException.class); - - // Add constructor that accepts a MasterSupport. - { - TypeDesc[] params = {masterSupportType}; - MethodInfo mi = mClassFile.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokeSuperConstructor(new TypeDesc[] {triggerSupportType}); - - b.returnVoid(); - } - - // Declare protected abstract methods. - { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED.toAbstract(true), - DO_TRY_INSERT_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(persistExceptionType); - - mi = mClassFile.addMethod - (Modifiers.PROTECTED.toAbstract(true), - DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(persistExceptionType); - - mi = mClassFile.addMethod - (Modifiers.PROTECTED.toAbstract(true), - DO_TRY_DELETE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(persistExceptionType); - } - - // Add required protected doTryInsert method. - { - // If sequence support requested, implement special insert hook to - // call sequences for properties which are UNINITIALIZED. User may - // provide explicit values for properties with sequences. - - if (mFeatures.contains(MasterFeature.INSERT_SEQUENCES)) { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED, - StorableGenerator.CHECK_PK_FOR_INSERT_METHOD_NAME, - null, null); - CodeBuilder b = new CodeBuilder(mi); - - int ordinal = 0; - for (StorableProperty property : mAllProperties.values()) { - if (property.getSequenceName() != null) { - // Check the state of this property, to see if it is - // uninitialized. Uninitialized state has value zero. - - String stateFieldName = - StorableGenerator.PROPERTY_STATE_FIELD_NAME + (ordinal >> 4); - - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - int shift = (ordinal & 0xf) * 2; - b.loadConstant(StorableGenerator.PROPERTY_STATE_MASK << shift); - b.math(Opcode.IAND); - - Label isInitialized = b.createLabel(); - b.ifZeroComparisonBranch(isInitialized, "!="); - - // Load this in preparation for storing value to property. - b.loadThis(); - - // Call MasterSupport.getSequenceValueProducer(String). - TypeDesc seqValueProdType = TypeDesc.forClass(SequenceValueProducer.class); - b.loadThis(); - b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); - b.checkCast(masterSupportType); - b.loadConstant(property.getSequenceName()); - b.invokeInterface - (masterSupportType, "getSequenceValueProducer", - seqValueProdType, new TypeDesc[] {TypeDesc.STRING}); - - // Find appropriate method to call for getting next sequence value. - TypeDesc propertyType = TypeDesc.forClass(property.getType()); - TypeDesc propertyObjType = propertyType.toObjectType(); - Method method; - - try { - if (propertyObjType == TypeDesc.LONG.toObjectType()) { - method = SequenceValueProducer.class - .getMethod("nextLongValue", (Class[]) null); - } else if (propertyObjType == TypeDesc.INT.toObjectType()) { - method = SequenceValueProducer.class - .getMethod("nextIntValue", (Class[]) null); - } else if (propertyObjType == TypeDesc.STRING) { - method = SequenceValueProducer.class - .getMethod("nextDecimalValue", (Class[]) null); - } else { - throw new SupportException - ("Unable to support sequence of type \"" + - property.getType().getName() + "\" for property: " + - property.getName()); - } - } catch (NoSuchMethodException e) { - Error err = new NoSuchMethodError(); - err.initCause(e); - throw err; - } - - b.invoke(method); - b.convert(TypeDesc.forClass(method.getReturnType()), propertyType); - - // Store property - b.storeField(property.getName(), propertyType); - - // Set state to dirty. - b.loadThis(); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(StorableGenerator.PROPERTY_STATE_DIRTY << shift); - b.math(Opcode.IOR); - b.storeField(stateFieldName, TypeDesc.INT); - - isInitialized.setLocation(); - } - - ordinal++; - } - - // We've tried our best to fill in missing values, now run the - // original check method. - b.loadThis(); - b.invokeSuper(mClassFile.getSuperClassName(), - StorableGenerator.CHECK_PK_FOR_INSERT_METHOD_NAME, - null, null); - b.returnVoid(); - } - - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED.toFinal(true), - StorableGenerator.DO_TRY_INSERT_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(persistExceptionType); - CodeBuilder b = new CodeBuilder(mi); - - LocalVariable txnVar = b.createLocalVariable(null, transactionType); - - Label tryStart = addEnterTransaction(b, INSERT_OP, txnVar); - - if (mFeatures.contains(MasterFeature.VERSIONING)) { - // Only set if uninitialized. - b.loadThis(); - b.invokeVirtual(StorableGenerator.IS_VERSION_INITIALIZED_METHOD_NAME, - TypeDesc.BOOLEAN, null); - Label isInitialized = b.createLabel(); - b.ifZeroComparisonBranch(isInitialized, "!="); - addAdjustVersionProperty(b, null, 1); - isInitialized.setLocation(); - } - - if (mFeatures.contains(MasterFeature.INSERT_CHECK_REQUIRED)) { - // Ensure that required properties have been set. - b.loadThis(); - b.invokeVirtual(StorableGenerator.IS_REQUIRED_DATA_INITIALIZED_METHOD_NAME, - TypeDesc.BOOLEAN, null); - Label isInitialized = b.createLabel(); - b.ifZeroComparisonBranch(isInitialized, "!="); - - // Throw a ConstraintException. - TypeDesc exType = TypeDesc.forClass(ConstraintException.class); - b.newObject(exType); - b.dup(); - - // Append all the uninitialized property names to the exception message. - - LocalVariable countVar = b.createLocalVariable(null, TypeDesc.INT); - b.loadConstant(0); - b.storeLocal(countVar); - - TypeDesc sbType = TypeDesc.forClass(StringBuilder.class); - b.newObject(sbType); - b.dup(); - b.loadConstant("Not all required properties have been set: "); - TypeDesc[] stringParam = {TypeDesc.STRING}; - b.invokeConstructor(sbType, stringParam); - LocalVariable sbVar = b.createLocalVariable(null, sbType); - b.storeLocal(sbVar); - - int ordinal = -1; - - HashSet stateAppendMethods = new HashSet(); - - // Parameters are: StringBuilder, count, mask, property name - TypeDesc[] appendParams = {sbType, TypeDesc.INT, TypeDesc.INT, TypeDesc.STRING}; - - for (StorableProperty property : mAllProperties.values()) { - ordinal++; - - if (property.isJoin() || property.isPrimaryKeyMember() - || property.isNullable() - || property.isAutomatic() || property.isVersion()) - { - continue; - } - - int stateField = ordinal >> 4; - - String stateAppendMethodName = APPEND_UNINIT_PROPERTY + stateField; - - if (!stateAppendMethods.contains(stateField)) { - stateAppendMethods.add(stateField); - - MethodInfo mi2 = mClassFile.addMethod - (Modifiers.PRIVATE, stateAppendMethodName, TypeDesc.INT, appendParams); - - CodeBuilder b2 = new CodeBuilder(mi2); - - // Load the StringBuilder parameter. - b2.loadLocal(b2.getParameter(0)); - - String stateFieldName = - StorableGenerator.PROPERTY_STATE_FIELD_NAME + (ordinal >> 4); - - b2.loadThis(); - b2.loadField(stateFieldName, TypeDesc.INT); - // Load the mask parameter. - b2.loadLocal(b2.getParameter(2)); - b2.math(Opcode.IAND); - - Label propIsInitialized = b2.createLabel(); - b2.ifZeroComparisonBranch(propIsInitialized, "!="); - - // Load the count parameter. - b2.loadLocal(b2.getParameter(1)); - Label noComma = b2.createLabel(); - b2.ifZeroComparisonBranch(noComma, "=="); - b2.loadConstant(", "); - b2.invokeVirtual(sbType, "append", sbType, stringParam); - noComma.setLocation(); - - // Load the property name parameter. - b2.loadLocal(b2.getParameter(3)); - b2.invokeVirtual(sbType, "append", sbType, stringParam); - - // Increment the count parameter. - b2.integerIncrement(b2.getParameter(1), 1); - - propIsInitialized.setLocation(); - - // Return the possibly updated count. - b2.loadLocal(b2.getParameter(1)); - b2.returnValue(TypeDesc.INT); - } - - b.loadThis(); - // Parameters are: StringBuilder, count, mask, property name - b.loadLocal(sbVar); - b.loadLocal(countVar); - b.loadConstant(StorableGenerator.PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2)); - b.loadConstant(property.getName()); - b.invokePrivate(stateAppendMethodName, TypeDesc.INT, appendParams); - b.storeLocal(countVar); - } - - b.loadLocal(sbVar); - b.invokeVirtual(sbType, "toString", TypeDesc.STRING, null); - b.invokeConstructor(exType, new TypeDesc[] {TypeDesc.STRING}); - b.throwObject(); - - isInitialized.setLocation(); - } - - b.loadThis(); - b.invokeVirtual(DO_TRY_INSERT_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - - if (tryStart == null) { - b.returnValue(TypeDesc.BOOLEAN); - } else { - Label failed = b.createLabel(); - b.ifZeroComparisonBranch(failed, "=="); - - addCommitAndExitTransaction(b, INSERT_OP, txnVar); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - failed.setLocation(); - addExitTransaction(b, INSERT_OP, txnVar); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - addExitTransaction(b, INSERT_OP, txnVar, tryStart); - } - } - - // Add required protected doTryUpdate method. - addDoTryUpdate: { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED.toFinal(true), - StorableGenerator.DO_TRY_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(persistExceptionType); - CodeBuilder b = new CodeBuilder(mi); - - if ((!mFeatures.contains(MasterFeature.VERSIONING)) && - (!mFeatures.contains(MasterFeature.UPDATE_FULL)) && - (!mFeatures.contains(MasterFeature.UPDATE_TXN))) - { - // Nothing special needs to be done, so just delegate and return. - b.loadThis(); - b.invokeVirtual(DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - b.returnValue(TypeDesc.BOOLEAN); - break addDoTryUpdate; - } - - LocalVariable txnVar = b.createLocalVariable(null, transactionType); - LocalVariable savedVar = null; - - Label tryStart = addEnterTransaction(b, UPDATE_OP, txnVar); - - Label failed = b.createLabel(); - - if (mFeatures.contains(MasterFeature.UPDATE_FULL)) { - // Storable saved = copy(); - b.loadThis(); - b.invokeVirtual(COPY_METHOD_NAME, storableType, null); - b.checkCast(mClassFile.getType()); - savedVar = b.createLocalVariable(null, mClassFile.getType()); - b.storeLocal(savedVar); - - // if (!saved.tryLoad()) { - // goto failed; - // } - b.loadLocal(savedVar); - b.invokeInterface(storableType, TRY_LOAD_METHOD_NAME, TypeDesc.BOOLEAN, null); - b.ifZeroComparisonBranch(failed, "=="); - - // if (version support enabled) { - // if (this.getVersionNumber() != saved.getVersionNumber()) { - // throw new OptimisticLockException - // (this.getVersionNumber(), saved.getVersionNumber(), this); - // } - // } - if (mFeatures.contains(MasterFeature.VERSIONING)) { - TypeDesc versionType = TypeDesc.forClass(mInfo.getVersionProperty().getType()); - b.loadThis(); - b.invoke(mInfo.getVersionProperty().getReadMethod()); - b.loadLocal(savedVar); - b.invoke(mInfo.getVersionProperty().getReadMethod()); - Label sameVersion = b.createLabel(); - CodeBuilderUtil.addValuesEqualCall(b, versionType, true, sameVersion, true); - b.newObject(optimisticLockType); - b.dup(); - b.loadThis(); - b.invoke(mInfo.getVersionProperty().getReadMethod()); - b.convert(versionType, TypeDesc.OBJECT); - b.loadLocal(savedVar); - b.invoke(mInfo.getVersionProperty().getReadMethod()); - b.convert(versionType, TypeDesc.OBJECT); - b.loadThis(); - b.invokeConstructor - (optimisticLockType, - new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.OBJECT, storableType}); - b.throwObject(); - sameVersion.setLocation(); - } - - // this.copyDirtyProperties(saved); - // if (version support enabled) { - // saved.setVersionNumber(saved.getVersionNumber() + 1); - // } - b.loadThis(); - b.loadLocal(savedVar); - b.invokeVirtual(COPY_DIRTY_PROPERTIES, null, new TypeDesc[] {storableType}); - if (mFeatures.contains(MasterFeature.VERSIONING)) { - addAdjustVersionProperty(b, savedVar, -1); - } - - // if (!saved.doTryUpdateMaster()) { - // goto failed; - // } - b.loadLocal(savedVar); - b.invokeVirtual(DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - b.ifZeroComparisonBranch(failed, "=="); - - // saved.copyUnequalProperties(this); - b.loadLocal(savedVar); - b.loadThis(); - b.invokeInterface - (storableType, COPY_UNEQUAL_PROPERTIES, null, new TypeDesc[] {storableType}); - } else { - // if (!this.doTryUpdateMaster()) { - // goto failed; - // } - b.loadThis(); - b.invokeVirtual(DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - b.ifZeroComparisonBranch(failed, "=="); - } - - // txn.commit(); - // txn.exit(); - // return true; - addCommitAndExitTransaction(b, UPDATE_OP, txnVar); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - // failed: - // txn.exit(); - failed.setLocation(); - addExitTransaction(b, UPDATE_OP, txnVar); - // return false; - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - addExitTransaction(b, UPDATE_OP, txnVar, tryStart); - } - - // Add required protected doTryDelete method. - { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED.toFinal(true), - StorableGenerator.DO_TRY_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(persistExceptionType); - CodeBuilder b = new CodeBuilder(mi); - - LocalVariable txnVar = b.createLocalVariable(null, transactionType); - - Label tryStart = addEnterTransaction(b, DELETE_OP, txnVar); - - b.loadThis(); - b.invokeVirtual(DO_TRY_DELETE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - - if (tryStart == null) { - b.returnValue(TypeDesc.BOOLEAN); - } else { - Label failed = b.createLabel(); - b.ifZeroComparisonBranch(failed, "=="); - addCommitAndExitTransaction(b, DELETE_OP, txnVar); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - failed.setLocation(); - addExitTransaction(b, DELETE_OP, txnVar); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - addExitTransaction(b, DELETE_OP, txnVar, tryStart); - } - } - } - - /** - * Generates code to enter a transaction, if required. - * - * @param opType type of operation, Insert, Update, or Delete - * @param txnVar required variable of type Transaction for storing transaction - * @return optional try start label for transaction - */ - private Label addEnterTransaction(CodeBuilder b, String opType, LocalVariable txnVar) { - if (!alwaysHasTxn(opType)) { - return null; - } - - // txn = masterSupport.getRootRepository().enterTransaction(); - - TypeDesc repositoryType = TypeDesc.forClass(Repository.class); - TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - TypeDesc triggerSupportType = TypeDesc.forClass(TriggerSupport.class); - TypeDesc masterSupportType = TypeDesc.forClass(MasterSupport.class); - - b.loadThis(); - b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); - b.invokeInterface(masterSupportType, "getRootRepository", - repositoryType, null); - b.invokeInterface(repositoryType, ENTER_TRANSACTION_METHOD_NAME, - transactionType, null); - b.storeLocal(txnVar); - if (requiresTxnForUpdate(opType)) { - // txn.setForUpdate(true); - b.loadLocal(txnVar); - b.loadConstant(true); - b.invokeInterface(transactionType, SET_FOR_UPDATE_METHOD_NAME, null, - new TypeDesc[] {TypeDesc.BOOLEAN}); - } - - return b.createLabel().setLocation(); - } - - private boolean alwaysHasTxn(String opType) { - return alwaysHasTxn(opType, mFeatures); - } - - private static boolean alwaysHasTxn(String opType, EnumSet features) { - if (opType == UPDATE_OP) { - return - features.contains(MasterFeature.UPDATE_TXN) || - features.contains(MasterFeature.UPDATE_TXN_FOR_UPDATE) || - features.contains(MasterFeature.VERSIONING) || - features.contains(MasterFeature.UPDATE_FULL); - } else if (opType == INSERT_OP) { - return - features.contains(MasterFeature.INSERT_TXN) || - features.contains(MasterFeature.INSERT_TXN_FOR_UPDATE); - } else if (opType == DELETE_OP) { - return - features.contains(MasterFeature.DELETE_TXN) || - features.contains(MasterFeature.DELETE_TXN_FOR_UPDATE); - } - return false; - } - - private boolean requiresTxnForUpdate(String opType) { - return requiresTxnForUpdate(opType, mFeatures); - } - - private static boolean requiresTxnForUpdate(String opType, EnumSet features) { - if (opType == UPDATE_OP) { - return - features.contains(MasterFeature.UPDATE_TXN_FOR_UPDATE) || - features.contains(MasterFeature.VERSIONING) || - features.contains(MasterFeature.UPDATE_FULL); - } else if (opType == INSERT_OP) { - return features.contains(MasterFeature.INSERT_TXN_FOR_UPDATE); - } else if (opType == DELETE_OP) { - return features.contains(MasterFeature.DELETE_TXN_FOR_UPDATE); - } - return false; - } - - private void addCommitAndExitTransaction(CodeBuilder b, String opType, LocalVariable txnVar) { - if (!alwaysHasTxn(opType)) { - return; - } - - TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - - // txn.commit(); - // txn.exit(); - b.loadLocal(txnVar); - b.invokeInterface(transactionType, COMMIT_METHOD_NAME, null, null); - b.loadLocal(txnVar); - b.invokeInterface(transactionType, EXIT_METHOD_NAME, null, null); - } - - /** - * - * @param opType type of operation, Insert, Update, or Delete - */ - private void addExitTransaction(CodeBuilder b, String opType, LocalVariable txnVar) { - if (!alwaysHasTxn(opType)) { - return; - } - - TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - - // txn.exit(); - b.loadLocal(txnVar); - b.invokeInterface(transactionType, EXIT_METHOD_NAME, null, null); - } - - /** - * - * @param opType type of operation, Insert, Update, or Delete - */ - private void addExitTransaction(CodeBuilder b, String opType, LocalVariable txnVar, - Label tryStart) - { - if (tryStart == null) { - addExitTransaction(b, opType, txnVar); - return; - } - - // } catch (... e) { - // txn.exit(); - // throw e; - // } - - Label tryEnd = b.createLabel().setLocation(); - b.exceptionHandler(tryStart, tryEnd, null); - addExitTransaction(b, opType, txnVar); - b.throwObject(); - } - - /* - * Generates code to adjust the version property. If value parameter is negative, then - * version is incremented as follows: - * - * storable.setVersionNumber(storable.getVersionNumber() + 1); - * - * Otherwise, the version is set: - * - * storable.setVersionNumber(value); - * - * @param storableVar references storable instance, or null if this - * @param value if negative, increment version, else, set version to this value - */ - private void addAdjustVersionProperty(CodeBuilder b, - LocalVariable storableVar, - int value) - throws SupportException - { - // Push storable to stack in preparation for calling set method below. - if (storableVar == null) { - b.loadThis(); - } else { - b.loadLocal(storableVar); - } - - StorableProperty versionProperty = mInfo.getVersionProperty(); - TypeDesc versionType = TypeDesc.forClass(versionProperty.getType()); - - if (value >= 0) { - CodeBuilderUtil.initialVersion(b, versionType, value); - } else { - // Load current property value. - b.dup(); - b.invoke(versionProperty.getReadMethod()); - CodeBuilderUtil.incrementVersion(b, versionType); - } - - b.invoke(versionProperty.getWriteMethod()); - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/MasterSupport.java b/src/main/java/com/amazon/carbonado/spi/MasterSupport.java deleted file mode 100644 index dd48194..0000000 --- a/src/main/java/com/amazon/carbonado/spi/MasterSupport.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import com.amazon.carbonado.PersistException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.sequence.SequenceValueProducer; - -/** - * Provides runtime support for Storable classes generated by {@link MasterStorableGenerator}. - * - * @author Brian S O'Neill - */ -public interface MasterSupport extends TriggerSupport { - /** - * Returns a sequence value producer by name, or throw PersistException if not found. - * - *

    Note: this method throws PersistException even for fetch failures - * since this method is called by insert operations. Insert operations can - * only throw a PersistException. - */ - SequenceValueProducer getSequenceValueProducer(String name) throws PersistException; -} diff --git a/src/main/java/com/amazon/carbonado/spi/StorableGenerator.java b/src/main/java/com/amazon/carbonado/spi/StorableGenerator.java deleted file mode 100644 index 0c2d50d..0000000 --- a/src/main/java/com/amazon/carbonado/spi/StorableGenerator.java +++ /dev/null @@ -1,3671 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import java.lang.annotation.Annotation; -import java.lang.ref.Reference; -import java.lang.ref.SoftReference; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.math.BigInteger; - -import org.cojen.classfile.ClassFile; -import org.cojen.classfile.CodeBuilder; -import org.cojen.classfile.Label; -import org.cojen.classfile.LocalVariable; -import org.cojen.classfile.MethodDesc; -import org.cojen.classfile.MethodInfo; -import org.cojen.classfile.Modifiers; -import org.cojen.classfile.Opcode; -import org.cojen.classfile.TypeDesc; -import org.cojen.util.ClassInjector; -import org.cojen.util.WeakIdentityMap; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.FetchNoneException; -import com.amazon.carbonado.PersistException; -import com.amazon.carbonado.PersistNoneException; -import com.amazon.carbonado.Query; -import com.amazon.carbonado.Repository; -import com.amazon.carbonado.RepositoryException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; -import com.amazon.carbonado.Transaction; -import com.amazon.carbonado.Trigger; -import com.amazon.carbonado.UniqueConstraintException; - -import com.amazon.carbonado.lob.Lob; - -import com.amazon.carbonado.info.ChainedProperty; -import com.amazon.carbonado.info.OrderedProperty; -import com.amazon.carbonado.info.StorableInfo; -import com.amazon.carbonado.info.StorableIntrospector; -import com.amazon.carbonado.info.StorableKey; -import com.amazon.carbonado.info.StorableProperty; -import com.amazon.carbonado.info.StorablePropertyAdapter; -import com.amazon.carbonado.info.StorablePropertyAnnotation; -import com.amazon.carbonado.info.StorablePropertyConstraint; - -import static com.amazon.carbonado.spi.CommonMethodNames.*; - -/** - * Generates and caches abstract and wrapped implementations of {@link - * Storable} types. This greatly simplifies the process of defining new kinds - * of {@link Repository Repositories}, since most of the mundane code - * generation is taken care of. - * - * @author Brian S O'Neill - * @author Don Schneider - * @see MasterStorableGenerator - */ -public final class StorableGenerator { - - // Note: All generated fields/methods have a "$" character in them to - // prevent name collisions with any inherited fields/methods. User storable - // properties are defined as fields which exactly match the property - // name. We don't want collisions with those either. Legal bean properties - // cannot have "$" in them, so there's nothing to worry about. - - /** Name of protected abstract method in generated storable */ - public static final String - DO_TRY_LOAD_METHOD_NAME = "doTryLoad$", - DO_TRY_INSERT_METHOD_NAME = "doTryInsert$", - DO_TRY_UPDATE_METHOD_NAME = "doTryUpdate$", - DO_TRY_DELETE_METHOD_NAME = "doTryDelete$"; - - /** - * Name of protected method in generated storable which checks that - * primary keys are initialized, throwing an exception otherwise. - */ - public static final String - CHECK_PK_FOR_INSERT_METHOD_NAME = "checkPkForInsert$", - CHECK_PK_FOR_UPDATE_METHOD_NAME = "checkPkForUpdate$", - CHECK_PK_FOR_DELETE_METHOD_NAME = "checkPkForDelete$"; - - /** - * Name of protected method in generated storable that returns false if any - * primary keys are uninitialized. - */ - public static final String IS_PK_INITIALIZED_METHOD_NAME = "isPkInitialized$"; - - /** - * Name prefix of protected method in generated storable that returns false - * if a specific alternate key is uninitialized. The complete name is - * formed by the prefix appended with the zero-based alternate key ordinal. - */ - public static final String IS_ALT_KEY_INITIALIZED_PREFIX = "isAltKeyInitialized$"; - - /** - * Name of protected method in generated storable that returns false if any - * non-nullable, non-pk properties are uninitialized. - */ - public static final String IS_REQUIRED_DATA_INITIALIZED_METHOD_NAME = - "isRequiredDataInitialized$"; - - /** - * Name of protected method in generated storable that returns false if - * version property is uninitialized. If no version property exists, then - * this method is not defined. - */ - public static final String IS_VERSION_INITIALIZED_METHOD_NAME = "isVersionInitialized$"; - - /** - * Prefix of protected field in generated storable that holds property - * states. Each property consumes two bits to hold its state, and so each - * 32-bit field holds states for up to 16 properties. - */ - public static final String PROPERTY_STATE_FIELD_NAME = "propertyState$"; - - /** Adapter field names are propertyName + "$adapter$" + ordinal */ - public static final String ADAPTER_FIELD_ELEMENT = "$adapter$"; - - /** Constraint field names are propertyName + "$constraint$" + ordinal */ - public static final String CONSTRAINT_FIELD_ELEMENT = "$constraint$"; - - /** Reference to TriggerSupport or WrappedSupport instance */ - public static final String SUPPORT_FIELD_NAME = "support$"; - - /** Property state indicating that property has never been set, loaded, or saved */ - public static final int PROPERTY_STATE_UNINITIALIZED = 0; - /** Property state indicating that property has been set, but not saved */ - public static final int PROPERTY_STATE_DIRTY = 3; - /** Property state indicating that property value reflects a clean value */ - public static final int PROPERTY_STATE_CLEAN = 1; - /** Property state mask is 3, to cover the two bits used by a property state */ - public static final int PROPERTY_STATE_MASK = 3; - - // Private method which returns a property's state. - private static final String PROPERTY_STATE_EXTRACT_METHOD_NAME = "extractState$"; - - private static final String PRIVATE_INSERT_METHOD_NAME = "insert$"; - private static final String PRIVATE_UPDATE_METHOD_NAME = "update$"; - private static final String PRIVATE_DELETE_METHOD_NAME = "delete$"; - - // Cache of generated abstract classes. - private static Map>> cAbstractCache; - // Cache of generated wrapped classes. - private static Map>> cWrappedCache; - - static { - cAbstractCache = new WeakIdentityMap(); - cWrappedCache = new WeakIdentityMap(); - } - - // There are three flavors of equals methods, used by addEqualsMethod. - private static final int EQUAL_KEYS = 0; - private static final int EQUAL_PROPERTIES = 1; - private static final int EQUAL_FULL = 2; - - // Operation mode for generating Storable. - private static final int GEN_ABSTRACT = 1; - private static final int GEN_WRAPPED = 2; - - private static final String WRAPPED_STORABLE_FIELD_NAME = "wrappedStorable$"; - - private static final String UNCAUGHT_METHOD_NAME = "uncaught$"; - - private static final String INSERT_OP = "Insert"; - private static final String UPDATE_OP = "Update"; - private static final String DELETE_OP = "Delete"; - - /** - * Returns an abstract implementation of the given Storable type, which is - * fully thread-safe. The Storable type itself may be an interface or a - * class. If it is a class, then it must not be final, and it must have a - * public, no-arg constructor. The constructor signature for the returned - * abstract is defined as follows: - * - *

    -     * /**
    -     *  * @param support  Access to triggers
    -     *  */
    -     * public <init>(TriggerSupport support);
    -     * 
    - * - *

    Subclasses must implement the following abstract protected methods, - * whose exact names are defined by constants in this class: - * - *

    -     * // Load the object by examining the primary key.
    -     * protected abstract boolean doTryLoad() throws FetchException;
    -     *
    -     * // Insert the object into the storage layer.
    -     * protected abstract boolean doTryInsert() throws PersistException;
    -     *
    -     * // Update the object in the storage.
    -     * protected abstract boolean doTryUpdate() throws PersistException;
    -     *
    -     * // Delete the object from the storage layer by the primary key.
    -     * protected abstract boolean doTryDelete() throws PersistException;
    -     * 
    - * - * A set of protected hook methods are provided which ensure that all - * primary keys are initialized before performing a repository - * operation. Subclasses may override them, if they are capable of filling - * in unspecified primary keys. One such example is applying a sequence on - * insert. - * - *
    -     * // Throws exception if any primary keys are uninitialized.
    -     * // Actual method name defined by CHECK_PK_FOR_INSERT_METHOD_NAME.
    -     * protected void checkPkForInsert() throws IllegalStateException;
    -     *
    -     * // Throws exception if any primary keys are uninitialized.
    -     * // Actual method name defined by CHECK_PK_FOR_UPDATE_METHOD_NAME.
    -     * protected void checkPkForUpdate() throws IllegalStateException;
    -     *
    -     * // Throws exception if any primary keys are uninitialized.
    -     * // Actual method name defined by CHECK_PK_FOR_DELETE_METHOD_NAME.
    -     * protected void checkPkForDelete() throws IllegalStateException;
    -     * 
    - * - * Each property value is defined as a protected field whose name and type - * matches the property. Subclasses should access these fields directly - * during loading and storing. For loading, it bypasses constraint - * checks. For both, it provides better performance. - * - *

    Subclasses also have access to a set of property state bits stored - * in protected int fields. Subclasses are not responsible for updating - * these values. The intention is that these states may be used by - * subclasses to support partial updates. They may otherwise be ignored. - * - *

    As a convenience, protected methods are provided to test and alter - * the property state bits. Subclass constructors that fill all properties - * with loaded values must call markAllPropertiesClean to ensure all - * properties are identified as being valid. - * - *

    -     * // Returns true if all primary key properties have been set.
    -     * protected boolean isPkInitialized();
    -     *
    -     * // Returns true if all required data properties are set.
    -     * // A required data property is a non-nullable, non-primary key.
    -     * protected boolean isRequiredDataInitialized();
    -     *
    -     * // Returns true if a version property has been set.
    -     * // Note: This method is not generated if there is no version property.
    -     * protected boolean isVersionInitialized();
    -     * 
    - * - * Property state field names are defined by the concatenation of - * {@code PROPERTY_STATE_FIELD_NAME} and a zero-based decimal - * ordinal. To determine which field holds a particular property's state, - * the field ordinal is computed as the property ordinal divided by 16. The - * specific two-bit state position is the remainder of this division times 2. - * - * @throws com.amazon.carbonado.MalformedTypeException if Storable type is not well-formed - * @throws IllegalArgumentException if type is null - */ - @SuppressWarnings("unchecked") - public static Class getAbstractClass(Class type) - throws IllegalArgumentException - { - synchronized (cAbstractCache) { - Class abstractClass; - Reference> ref = cAbstractCache.get(type); - if (ref != null) { - abstractClass = (Class) ref.get(); - if (abstractClass != null) { - return abstractClass; - } - } - abstractClass = new StorableGenerator(type, GEN_ABSTRACT).generateAndInjectClass(); - cAbstractCache.put(type, new SoftReference>(abstractClass)); - return abstractClass; - } - } - - /** - * Returns a concrete Storable implementation of the given type which wraps - * another Storable. The Storable type itself may be an interface or a - * class. If it is a class, then it must not be final, and it must have a - * public, no-arg constructor. The constructor signature for the returned - * class is defined as follows: - * - *
    -     * /**
    -     *  * @param support  Custom implementation for Storable CRUD operations
    -     *  * @param storable Storable being wrapped
    -     *  */
    -     * public <init>(WrappedSupport support, Storable storable);
    -     * 
    - * - *

    Instances of the wrapped Storable delegate to the WrappedSupport for - * all CRUD operations: - * - *

      - *
    • load and tryLoad - *
    • insert and tryInsert - *
    • update and tryUpdate - *
    • delete and tryDelete - *
    - * - *

    Methods which delegate to wrapped Storable: - * - *

      - *
    • all ordinary user-defined properties - *
    • copyAllProperties - *
    • copyPrimaryKeyProperties - *
    • copyVersionProperty - *
    • copyUnequalProperties - *
    • copyDirtyProperties - *
    • hasDirtyProperties - *
    • markPropertiesClean - *
    • markAllPropertiesClean - *
    • markPropertiesDirty - *
    • markAllPropertiesDirty - *
    • hashCode - *
    • equalPrimaryKeys - *
    • equalProperties - *
    • toString - *
    • toStringKeyOnly - *
    - * - *

    Methods with special implementation: - * - *

      - *
    • all user-defined join properties (join properties query using wrapper's Storage) - *
    • storage (returns Storage used by wrapper) - *
    • storableType (returns literal class) - *
    • copy (delegates to wrapped storable, but bridge methods must be defined as well) - *
    • equals (compares Storage instance and properties) - *
    - * - * @throws com.amazon.carbonado.MalformedTypeException if Storable type is not well-formed - * @throws IllegalArgumentException if type is null - */ - @SuppressWarnings("unchecked") - public static Class getWrappedClass(Class type) - throws IllegalArgumentException - { - synchronized (cWrappedCache) { - Class wrappedClass; - Reference> ref = cWrappedCache.get(type); - if (ref != null) { - wrappedClass = (Class) ref.get(); - if (wrappedClass != null) { - return wrappedClass; - } - } - wrappedClass = new StorableGenerator(type, GEN_WRAPPED).generateAndInjectClass(); - cWrappedCache.put(type, new SoftReference>(wrappedClass)); - return wrappedClass; - } - } - - private final Class mStorableType; - private final int mGenMode; - private final TypeDesc mSupportType; - private final StorableInfo mInfo; - private final Map> mAllProperties; - private final boolean mHasJoins; - - private final ClassInjector mClassInjector; - private final ClassFile mClassFile; - - private StorableGenerator(Class storableType, int genMode) { - mStorableType = storableType; - mGenMode = genMode; - if (genMode == GEN_WRAPPED) { - mSupportType = TypeDesc.forClass(WrappedSupport.class); - } else { - mSupportType = TypeDesc.forClass(TriggerSupport.class); - } - mInfo = StorableIntrospector.examine(storableType); - mAllProperties = mInfo.getAllProperties(); - - boolean hasJoins = false; - for (StorableProperty property : mAllProperties.values()) { - if (property.isJoin()) { - hasJoins = true; - break; - } - } - mHasJoins = hasJoins; - - mClassInjector = ClassInjector.create - (storableType.getName(), storableType.getClassLoader()); - mClassFile = CodeBuilderUtil.createStorableClassFile - (mClassInjector, storableType, genMode == GEN_ABSTRACT, - StorableGenerator.class.getName()); - } - - private Class generateAndInjectClass() { - generateClass(); - Class abstractClass = mClassInjector.defineClass(mClassFile); - return (Class) abstractClass; - } - - private void generateClass() { - // Use this static method for passing uncaught exceptions. - defineUncaughtExceptionHandler(); - - // private final TriggerSupport support; - // Field is not final for GEN_WRAPPED, so that copy method can - // change WrappedSupport after calling clone. - mClassFile.addField(Modifiers.PROTECTED.toFinal(mGenMode == GEN_ABSTRACT), - SUPPORT_FIELD_NAME, - mSupportType); - - if (mGenMode == GEN_WRAPPED) { - // Add a few more fields to hold arguments passed from constructor. - - // private final wrappedStorable; - // Field is not final for GEN_WRAPPED, so that copy method can - // change wrapped Storable after calling clone. - mClassFile.addField(Modifiers.PRIVATE.toFinal(false), - WRAPPED_STORABLE_FIELD_NAME, - TypeDesc.forClass(mStorableType)); - } - - if (mGenMode == GEN_ABSTRACT) { - // Add protected constructor. - TypeDesc[] params = {mSupportType}; - - final int supportParam = 0; - MethodInfo mi = mClassFile.addConstructor(Modifiers.PROTECTED, params); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.invokeSuperConstructor(null); - - //// this.support = support - b.loadThis(); - b.loadLocal(b.getParameter(supportParam)); - b.storeField(SUPPORT_FIELD_NAME, mSupportType); - - b.returnVoid(); - } else if (mGenMode == GEN_WRAPPED) { - // Add public constructor. - TypeDesc[] params = {mSupportType, TypeDesc.forClass(Storable.class)}; - - final int wrappedSupportParam = 0; - final int wrappedStorableParam = 1; - MethodInfo mi = mClassFile.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.invokeSuperConstructor(null); - - //// this.wrappedSupport = wrappedSupport - b.loadThis(); - b.loadLocal(b.getParameter(wrappedSupportParam)); - b.storeField(SUPPORT_FIELD_NAME, mSupportType); - - //// this.wrappedStorable = wrappedStorable - b.loadThis(); - b.loadLocal(b.getParameter(wrappedStorableParam)); - b.checkCast(TypeDesc.forClass(mStorableType)); - b.storeField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - - b.returnVoid(); - } - - // Add static fields for adapters and constraints, and create static - // initializer to populate fields. - if (mGenMode == GEN_ABSTRACT) { - // CodeBuilder for static initializer, defined only if there's - // something to put in it. - CodeBuilder clinit = null; - - // Adapter and constraint fields are protected static. - final Modifiers fieldModifiers = Modifiers.PROTECTED.toStatic(true).toFinal(true); - - // Add adapter field. - for (StorableProperty property : mAllProperties.values()) { - StorablePropertyAdapter spa = property.getAdapter(); - if (spa == null) { - continue; - } - - String fieldName = property.getName() + ADAPTER_FIELD_ELEMENT + 0; - TypeDesc adapterType = TypeDesc.forClass - (spa.getAdapterConstructor().getDeclaringClass()); - mClassFile.addField(fieldModifiers, fieldName, adapterType); - - if (clinit == null) { - clinit = new CodeBuilder(mClassFile.addInitializer()); - } - - // Assign value to new field. - // admin$adapter$0 = new YesNoAdapter.Adapter - // (UserInfo.class, "admin", annotation); - - clinit.newObject(adapterType); - clinit.dup(); - clinit.loadConstant(TypeDesc.forClass(mStorableType)); - clinit.loadConstant(property.getName()); - - // Generate code to load property annotation third parameter. - loadPropertyAnnotation(clinit, property, spa.getAnnotation()); - - clinit.invoke(spa.getAdapterConstructor()); - clinit.storeStaticField(fieldName, adapterType); - } - - // Add contraint fields. - for (StorableProperty property : mAllProperties.values()) { - int count = property.getConstraintCount(); - for (int i=0; i property : mAllProperties.values()) { - ordinal++; - - if (property.isVersion()) { - versionOrdinal = ordinal; - } - - final String name = property.getName(); - final TypeDesc type = TypeDesc.forClass(property.getType()); - - if (property.isJoin()) { - // If generating wrapper, property access is not guarded by - // synchronization. Mark as volatile instead. - mClassFile.addField(Modifiers.PRIVATE.toVolatile(mGenMode == GEN_WRAPPED), - name, type); - requireStateField = true; - } else if (mGenMode == GEN_ABSTRACT) { - // Only define regular property fields if abstract - // class. Wrapped class doesn't reference them. Double - // words are volatile to prevent word tearing without - // explicit synchronization. - mClassFile.addField(Modifiers.PROTECTED.toVolatile(type.isDoubleWord()), - name, type); - requireStateField = true; - } - - final String stateFieldName = PROPERTY_STATE_FIELD_NAME + (ordinal >> 4); - if (ordinal == maxOrdinal || ((ordinal & 0xf) == 0xf)) { - if (requireStateField) { - // If generating wrapper, property state access is not guarded by - // synchronization. Mark as volatile instead. - mClassFile.addField - (Modifiers.PROTECTED.toVolatile(mGenMode == GEN_WRAPPED), - stateFieldName, TypeDesc.INT); - } - requireStateField = false; - } - - // Add read method. - buildReadMethod: { - Method readMethod = property.getReadMethod(); - - MethodInfo mi; - if (readMethod != null) { - mi = mClassFile.addMethod(readMethod); - } else { - // Add a synthetic protected read method. - String readName = property.getReadMethodName(); - mi = mClassFile.addMethod(Modifiers.PROTECTED, readName, type, null); - mi.markSynthetic(); - if (property.isJoin()) { - mi.addException(TypeDesc.forClass(FetchException.class)); - } - } - - if (mGenMode == GEN_ABSTRACT && property.isJoin()) { - // Synchronization is required for join property - // accessors, as they may alter bit masks. - mi.setModifiers(mi.getModifiers().toSynchronized(true)); - } - - // Now add code that actually gets the property value. - CodeBuilder b = new CodeBuilder(mi); - - if (property.isJoin()) { - // Join properties support on-demand loading. - - // Check if property has been loaded. - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2)); - b.math(Opcode.IAND); - Label isLoaded = b.createLabel(); - b.ifZeroComparisonBranch(isLoaded, "!="); - - // Store loaded join result here. - LocalVariable join = b.createLocalVariable(name, type); - - // Check if any internal properties are nullable, but - // the matching external property is not. If so, load - // each of these special internal values and check if - // null. If null, short-circuit the load and use null - // as the join result. - - Label shortCircuit = b.createLabel(); - buildShortCircuit: { - int count = property.getJoinElementCount(); - nullPossible: { - for (int i=0; i internal = property.getInternalJoinElement(i); - if (mGenMode == GEN_ABSTRACT) { - b.loadThis(); - b.loadField(internal.getName(), - TypeDesc.forClass(internal.getType())); - } else { - b.loadThis(); - b.loadField(WRAPPED_STORABLE_FIELD_NAME, - TypeDesc.forClass(mStorableType)); - b.invoke(internal.getReadMethod()); - } - TypeDesc bindType = - CodeBuilderUtil.bindQueryParam(internal.getType()); - CodeBuilderUtil.convertValue - (b, internal.getType(), bindType.toClass()); - b.invokeInterface(queryType, WITH_METHOD_NAME, queryType, - new TypeDesc[]{bindType}); - } - - // Now run the query. - if (property.isQuery()) { - // Just save and return the query. - b.storeLocal(join); - } else { - String loadMethod = - property.isNullable() ? - TRY_LOAD_ONE_METHOD_NAME : - LOAD_ONE_METHOD_NAME; - b.invokeInterface(queryType, loadMethod, storableDesc, null); - b.checkCast(type); - b.storeLocal(join); - } - } - - // Store loaded property. - shortCircuit.setLocation(); - b.loadThis(); - b.loadLocal(join); - b.storeField(property.getName(), type); - - // Add code to identify this property as being loaded. - b.loadThis(); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2)); - b.math(Opcode.IOR); - b.storeField(stateFieldName, TypeDesc.INT); - - isLoaded.setLocation(); - } - - // Load property value and return it. - - if (mGenMode == GEN_ABSTRACT || property.isJoin()) { - b.loadThis(); - b.loadField(property.getName(), type); - } else { - b.loadThis(); - b.loadField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - b.invoke(readMethod); - } - - b.returnValue(type); - } - - // Add write method. - if (!property.isQuery()) { - Method writeMethod = property.getWriteMethod(); - - MethodInfo mi; - if (writeMethod != null) { - mi = mClassFile.addMethod(writeMethod); - } else { - // Add a synthetic protected write method. - String writeName = property.getWriteMethodName(); - mi = mClassFile.addMethod(Modifiers.PROTECTED, writeName, null, - new TypeDesc[]{type}); - mi.markSynthetic(); - } - - if (mGenMode == GEN_ABSTRACT) { - mi.setModifiers(mi.getModifiers().toSynchronized(true)); - } - CodeBuilder b = new CodeBuilder(mi); - - // Primary keys cannot be altered if state is "clean". - if (mGenMode == GEN_ABSTRACT && property.isPrimaryKeyMember()) { - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2)); - b.math(Opcode.IAND); - b.loadConstant(PROPERTY_STATE_CLEAN << ((ordinal & 0xf) * 2)); - Label isMutable = b.createLabel(); - b.ifComparisonBranch(isMutable, "!="); - CodeBuilderUtil.throwException - (b, IllegalStateException.class, "Cannot alter primary key"); - isMutable.setLocation(); - } - - int spcCount = property.getConstraintCount(); - - boolean nullNotAllowed = - !property.getType().isPrimitive() && - !property.isJoin() && !property.isNullable(); - - if (mGenMode == GEN_ABSTRACT && (nullNotAllowed || spcCount > 0)) { - // Add constraint checks. - Label skipConstraints = b.createLabel(); - - if (nullNotAllowed) { - // Don't allow null value to be set. - b.loadLocal(b.getParameter(0)); - Label notNull = b.createLabel(); - b.ifNullBranch(notNull, false); - CodeBuilderUtil.throwException - (b, IllegalArgumentException.class, - "Cannot set property \"" + property.getName() + - "\" to null"); - notNull.setLocation(); - } else { - // Don't invoke constraints if value is null. - if (!property.getType().isPrimitive()) { - b.loadLocal(b.getParameter(0)); - b.ifNullBranch(skipConstraints, true); - } - } - - // Add code to invoke constraints. - - for (int spcIndex = 0; spcIndex < spcCount; spcIndex++) { - StorablePropertyConstraint spc = property.getConstraint(spcIndex); - String fieldName = - property.getName() + CONSTRAINT_FIELD_ELEMENT + spcIndex; - TypeDesc constraintType = TypeDesc.forClass - (spc.getConstraintConstructor().getDeclaringClass()); - b.loadStaticField(fieldName, constraintType); - b.loadLocal(b.getParameter(0)); - b.convert - (b.getParameter(0).getType(), TypeDesc.forClass - (spc.getConstrainMethod().getParameterTypes()[0])); - b.invoke(spc.getConstrainMethod()); - } - - skipConstraints.setLocation(); - } - - Label setValue = b.createLabel(); - - if (!property.isJoin() || Lob.class.isAssignableFrom(property.getType())) { - if (mGenMode == GEN_ABSTRACT) { - if (Lob.class.isAssignableFrom(property.getType())) { - // Contrary to how standard properties are managed, - // only mark dirty if value changed. - b.loadThis(); - b.loadField(property.getName(), type); - b.loadLocal(b.getParameter(0)); - CodeBuilderUtil.addValuesEqualCall(b, type, true, setValue, true); - } - } - - markOrdinaryPropertyDirty(b, property); - } else { - // If passed value is null, throw an - // IllegalArgumentException. Passing in null could also - // indicate that the property should be unloaded, but - // that is non-intuitive. - - b.loadLocal(b.getParameter(0)); - Label notNull = b.createLabel(); - b.ifNullBranch(notNull, false); - CodeBuilderUtil.throwException(b, IllegalArgumentException.class, null); - notNull.setLocation(); - - // Copy internal properties from joined object. - int count = property.getJoinElementCount(); - for (int i=0; i> 4), TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_MASK << ((ord & 0xf) * 2)); - b.math(Opcode.IAND); - b.loadConstant(PROPERTY_STATE_CLEAN << ((ord & 0xf) * 2)); - // If not clean, skip equal check. - b.ifComparisonBranch(setInternalProp, "!="); - } else { - // Call the public isPropertyClean method since - // the raw state bits are hidden. - b.loadThis(); - b.loadConstant(internal.getName()); - b.invokeVirtual(IS_PROPERTY_CLEAN, TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.STRING}); - // If not clean, skip equal check. - b.ifZeroComparisonBranch(setInternalProp, "=="); - } - - // If new internal property value is equal to - // existing value, skip setting it. - b.loadThis(); - b.invoke(internal.getReadMethod()); - b.loadLocal(newInternalPropVar); - Label skipSetInternalProp = b.createLabel(); - CodeBuilderUtil.addValuesEqualCall - (b, TypeDesc.forClass(internal.getType()), - true, skipSetInternalProp, true); - - setInternalProp.setLocation(); - - // Call set method to ensure that state bits are - // properly adjusted. - b.loadThis(); - b.loadLocal(newInternalPropVar); - b.invoke(internal.getWriteMethod()); - - skipSetInternalProp.setLocation(); - } - - // Add code to identify this property as being loaded. - b.loadThis(); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2)); - b.math(Opcode.IOR); - b.storeField(stateFieldName, TypeDesc.INT); - } - - // Now add code that actually sets the property value. - - setValue.setLocation(); - - if (mGenMode == GEN_ABSTRACT || property.isJoin()) { - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.storeField(property.getName(), type); - } else { - b.loadThis(); - b.loadField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - b.loadLocal(b.getParameter(0)); - b.invoke(writeMethod); - } - - b.returnVoid(); - } - - // Add optional protected adapted read methods. - if (mGenMode == GEN_ABSTRACT && property.getAdapter() != null) { - // End name with '$' to prevent any possible collisions. - String readName = property.getReadMethodName() + '$'; - - StorablePropertyAdapter adapter = property.getAdapter(); - - for (Method adaptMethod : adapter.findAdaptMethodsFrom(type.toClass())) { - TypeDesc toType = TypeDesc.forClass(adaptMethod.getReturnType()); - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED, readName, toType, null); - mi.markSynthetic(); - - // Now add code that actually gets the property value and - // then invokes adapt method. - CodeBuilder b = new CodeBuilder(mi); - - // Push adapter class to stack. - String fieldName = property.getName() + ADAPTER_FIELD_ELEMENT + 0; - TypeDesc adapterType = TypeDesc.forClass - (adapter.getAdapterConstructor().getDeclaringClass()); - b.loadStaticField(fieldName, adapterType); - - // Load property value. - b.loadThis(); - b.loadField(property.getName(), type); - - b.invoke(adaptMethod); - b.returnValue(toType); - } - } - - // Add optional protected adapted write methods. - - // Note: Calling these methods does not affect any state bits. - // They are only intended to be used by subclasses during loading. - - if (mGenMode == GEN_ABSTRACT && property.getAdapter() != null) { - // End name with '$' to prevent any possible collisions. - String writeName = property.getWriteMethodName() + '$'; - - StorablePropertyAdapter adapter = property.getAdapter(); - - for (Method adaptMethod : adapter.findAdaptMethodsTo(type.toClass())) { - TypeDesc fromType = TypeDesc.forClass(adaptMethod.getParameterTypes()[0]); - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED, writeName, null, new TypeDesc[] {fromType}); - mi.markSynthetic(); - mi.setModifiers(mi.getModifiers().toSynchronized(true)); - - // Now add code that actually adapts parameter and then - // stores the property value. - CodeBuilder b = new CodeBuilder(mi); - - // Push this in preparation for storing a field. - b.loadThis(); - - // Push adapter class to stack. - String fieldName = property.getName() + ADAPTER_FIELD_ELEMENT + 0; - TypeDesc adapterType = TypeDesc.forClass - (adapter.getAdapterConstructor().getDeclaringClass()); - b.loadStaticField(fieldName, adapterType); - - b.loadLocal(b.getParameter(0)); - b.invoke(adaptMethod); - b.storeField(property.getName(), type); - - b.returnVoid(); - } - } - } - } - - // Add tryLoad method which delegates to abstract doTryLoad method. - addTryLoad: { - // Define the tryLoad method. - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC.toSynchronized(mGenMode == GEN_ABSTRACT), - TRY_LOAD_METHOD_NAME, TypeDesc.BOOLEAN, null); - - if (mi == null) { - break addTryLoad; - } - - mi.addException(TypeDesc.forClass(FetchException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, null, false, null); - break addTryLoad; - } - - CodeBuilder b = new CodeBuilder(mi); - - // Check that primary key is initialized. - b.loadThis(); - b.invokeVirtual(IS_PK_INITIALIZED_METHOD_NAME, TypeDesc.BOOLEAN, null); - Label pkInitialized = b.createLabel(); - b.ifZeroComparisonBranch(pkInitialized, "!="); - - Label loaded = b.createLabel(); - Label notLoaded = b.createLabel(); - - if (mInfo.getAlternateKeyCount() == 0) { - CodeBuilderUtil.throwException(b, IllegalStateException.class, - "Primary key not fully specified"); - } else { - // If any alternate keys, check them too. - - // Load our Storage, in preparation for query against it. - loadStorageForFetch(b, TypeDesc.forClass(mStorableType)); - - Label runQuery = b.createLabel(); - TypeDesc queryType = TypeDesc.forClass(Query.class); - - for (int i=0; i altKey = mInfo.getAlternateKey(i); - - // Form query filter. - StringBuilder queryBuilder = new StringBuilder(); - for (OrderedProperty op : altKey.getProperties()) { - if (queryBuilder.length() > 0) { - queryBuilder.append(" & "); - } - queryBuilder.append(op.getChainedProperty().toString()); - queryBuilder.append(" = ?"); - } - - // Get query instance from Storage already loaded on stack. - b.loadConstant(queryBuilder.toString()); - b.invokeInterface(TypeDesc.forClass(Storage.class), - QUERY_METHOD_NAME, queryType, - new TypeDesc[]{TypeDesc.STRING}); - - // Now fill in the parameters of the query. - for (OrderedProperty op : altKey.getProperties()) { - StorableProperty prop = op.getChainedProperty().getPrimeProperty(); - b.loadThis(); - TypeDesc propType = TypeDesc.forClass(prop.getType()); - b.loadField(prop.getName(), propType); - TypeDesc bindType = CodeBuilderUtil.bindQueryParam(prop.getType()); - CodeBuilderUtil.convertValue(b, prop.getType(), bindType.toClass()); - b.invokeInterface(queryType, WITH_METHOD_NAME, queryType, - new TypeDesc[]{bindType}); - } - - b.branch(runQuery); - - noAltKey.setLocation(); - } - - CodeBuilderUtil.throwException(b, IllegalStateException.class, - "Primary or alternate key not fully specified"); - - // Run query sitting on the stack. - runQuery.setLocation(); - - b.invokeInterface(queryType, TRY_LOAD_ONE_METHOD_NAME, - TypeDesc.forClass(Storable.class), null); - LocalVariable fetchedVar = b.createLocalVariable(null, TypeDesc.OBJECT); - b.storeLocal(fetchedVar); - - // If query fetch is null, then object not found. Return false. - b.loadLocal(fetchedVar); - b.ifNullBranch(notLoaded, true); - - // Copy all properties from fetched object into this one. - - // Allow copy to destroy everything, including primary key. - b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_DIRTY, null, null); - - b.loadLocal(fetchedVar); - b.checkCast(TypeDesc.forClass(mStorableType)); - b.loadThis(); - b.invokeInterface(TypeDesc.forClass(Storable.class), - COPY_ALL_PROPERTIES, null, - new TypeDesc[] {TypeDesc.forClass(Storable.class)}); - - b.branch(loaded); - } - - pkInitialized.setLocation(); - - // Call doTryLoad and mark all properties as clean if load succeeded. - b.loadThis(); - b.invokeVirtual(DO_TRY_LOAD_METHOD_NAME, TypeDesc.BOOLEAN, null); - - b.ifZeroComparisonBranch(notLoaded, "=="); - - loaded.setLocation(); - // Only mark properties clean if doTryLoad returned true. - b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - notLoaded.setLocation(); - // Mark properties dirty, to be consistent with a delete side-effect. - b.loadThis(); - b.invokeVirtual(MARK_PROPERTIES_DIRTY, null, null); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - if (mGenMode == GEN_ABSTRACT) { - // Define the abstract method. - mi = mClassFile.addMethod - (Modifiers.PROTECTED.toAbstract(true), - DO_TRY_LOAD_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(FetchException.class)); - } - } - - // Add load method which calls tryLoad. - addLoad: { - // Define the load method. - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC.toSynchronized(mGenMode == GEN_ABSTRACT), - LOAD_METHOD_NAME, null, null); - - if (mi == null) { - break addLoad; - } - - mi.addException(TypeDesc.forClass(FetchException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, null, false, FetchNoneException.class); - break addLoad; - } - - CodeBuilder b = new CodeBuilder(mi); - - // Call tryLoad and throw an exception if false returned. - b.loadThis(); - b.invokeVirtual(TRY_LOAD_METHOD_NAME, TypeDesc.BOOLEAN, null); - - Label wasNotLoaded = b.createLabel(); - b.ifZeroComparisonBranch(wasNotLoaded, "=="); - b.returnVoid(); - - wasNotLoaded.setLocation(); - - TypeDesc noMatchesType = TypeDesc.forClass(FetchNoneException.class); - b.newObject(noMatchesType); - b.dup(); - b.loadThis(); - b.invokeVirtual(TO_STRING_KEY_ONLY_METHOD_NAME, TypeDesc.STRING, null); - b.invokeConstructor(noMatchesType, new TypeDesc[] {TypeDesc.STRING}); - b.throwObject(); - } - - final TypeDesc triggerType = TypeDesc.forClass(Trigger.class); - final TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - - // Add insert(boolean forTry) method which delegates to abstract doTryInsert method. - if (mGenMode == GEN_ABSTRACT) { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PRIVATE.toSynchronized(true), - PRIVATE_INSERT_METHOD_NAME, TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.BOOLEAN}); - mi.addException(TypeDesc.forClass(PersistException.class)); - - CodeBuilder b = new CodeBuilder(mi); - - LocalVariable forTryVar = b.getParameter(0); - LocalVariable triggerVar = b.createLocalVariable(null, triggerType); - LocalVariable txnVar = b.createLocalVariable(null, transactionType); - LocalVariable stateVar = b.createLocalVariable(null, TypeDesc.OBJECT); - - Label tryStart = addGetTriggerAndEnterTxn - (b, INSERT_OP, forTryVar, false, triggerVar, txnVar, stateVar); - - // Perform pk check after trigger has run, to allow it to define pk. - requirePkInitialized(b, CHECK_PK_FOR_INSERT_METHOD_NAME); - - // Call doTryInsert. - b.loadThis(); - b.invokeVirtual(DO_TRY_INSERT_METHOD_NAME, TypeDesc.BOOLEAN, null); - - Label notInserted = b.createLabel(); - b.ifZeroComparisonBranch(notInserted, "=="); - - addTriggerAfterAndExitTxn - (b, INSERT_OP, forTryVar, false, triggerVar, txnVar, stateVar); - - // Only mark properties clean if doTryInsert returned true. - b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - notInserted.setLocation(); - addTriggerFailedAndExitTxn(b, INSERT_OP, triggerVar, txnVar, stateVar); - - b.loadLocal(forTryVar); - Label isForTry = b.createLabel(); - b.ifZeroComparisonBranch(isForTry, "!="); - - TypeDesc constraintType = TypeDesc.forClass(UniqueConstraintException.class); - b.newObject(constraintType); - b.dup(); - b.loadThis(); - b.invokeVirtual(TO_STRING_METHOD_NAME, TypeDesc.STRING, null); - b.invokeConstructor(constraintType, new TypeDesc[] {TypeDesc.STRING}); - b.throwObject(); - - isForTry.setLocation(); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - addTriggerFailedAndExitTxn - (b, INSERT_OP, forTryVar, false, triggerVar, txnVar, stateVar, tryStart); - - // Define the abstract method. - mi = mClassFile.addMethod - (Modifiers.PROTECTED.toAbstract(true), - DO_TRY_INSERT_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(PersistException.class)); - } - - // Add insert method which calls insert(forTry = false) - addInsert: { - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, INSERT_METHOD_NAME, null, null); - - if (mi == null) { - break addInsert; - } - - mi.addException(TypeDesc.forClass(PersistException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, INSERT_OP, false, UniqueConstraintException.class); - break addInsert; - } - - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadConstant(false); - b.invokePrivate(PRIVATE_INSERT_METHOD_NAME, TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.BOOLEAN}); - b.pop(); - b.returnVoid(); - } - - // Add tryInsert method which calls insert(forTry = true) - addTryInsert: { - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, TRY_INSERT_METHOD_NAME, TypeDesc.BOOLEAN, null); - - if (mi == null) { - break addTryInsert; - } - - mi.addException(TypeDesc.forClass(PersistException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, INSERT_OP, true, null); - break addTryInsert; - } - - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadConstant(true); - b.invokePrivate(PRIVATE_INSERT_METHOD_NAME, TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.BOOLEAN}); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Add update(boolean forTry) method which delegates to abstract doTryUpdate method. - if (mGenMode == GEN_ABSTRACT) { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PRIVATE.toSynchronized(true), - PRIVATE_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.BOOLEAN}); - mi.addException(TypeDesc.forClass(PersistException.class)); - - CodeBuilder b = new CodeBuilder(mi); - - requirePkInitialized(b, CHECK_PK_FOR_UPDATE_METHOD_NAME); - - // If version property is present, it too must be initialized. The - // versionOrdinal variable was set earlier, when properties were defined. - if (versionOrdinal >= 0) { - b.loadThis(); - b.loadField(PROPERTY_STATE_FIELD_NAME + (versionOrdinal >> 4), TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_MASK << ((versionOrdinal & 0xf) * 2)); - b.math(Opcode.IAND); - Label versionIsSet = b.createLabel(); - b.ifZeroComparisonBranch(versionIsSet, "!="); - CodeBuilderUtil.throwException - (b, IllegalStateException.class, "Version not set"); - versionIsSet.setLocation(); - } - - LocalVariable forTryVar = b.getParameter(0); - LocalVariable triggerVar = b.createLocalVariable(null, triggerType); - LocalVariable txnVar = b.createLocalVariable(null, transactionType); - LocalVariable stateVar = b.createLocalVariable(null, TypeDesc.OBJECT); - - Label tryStart = addGetTriggerAndEnterTxn - (b, UPDATE_OP, forTryVar, false, triggerVar, txnVar, stateVar); - - // If no properties are dirty, then don't update. - Label doUpdate = b.createLabel(); - branchIfDirty(b, true, doUpdate); - - // Even though there was no update, still need tryLoad side-effect. - { - Label tryStart2 = b.createLabel().setLocation(); - b.loadThis(); - b.invokeVirtual(DO_TRY_LOAD_METHOD_NAME, TypeDesc.BOOLEAN, null); - - Label notUpdated = b.createLabel(); - b.ifZeroComparisonBranch(notUpdated, "=="); - - // Only mark properties clean if doTryLoad returned true. - b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - notUpdated.setLocation(); - - // Mark properties dirty, to be consistent with a delete side-effect. - b.loadThis(); - b.invokeVirtual(MARK_PROPERTIES_DIRTY, null, null); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - Label tryEnd = b.createLabel().setLocation(); - b.exceptionHandler(tryStart2, tryEnd, FetchException.class.getName()); - b.invokeVirtual(FetchException.class.getName(), "toPersistException", - TypeDesc.forClass(PersistException.class), null); - b.throwObject(); - } - - doUpdate.setLocation(); - - // Call doTryUpdate. - b.loadThis(); - b.invokeVirtual(DO_TRY_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, null); - - Label notUpdated = b.createLabel(); - b.ifZeroComparisonBranch(notUpdated, "=="); - - addTriggerAfterAndExitTxn - (b, UPDATE_OP, forTryVar, false, triggerVar, txnVar, stateVar); - - // Only mark properties clean if doUpdate returned true. - b.loadThis(); - // Note: all properties marked clean because doUpdate should have - // loaded values for all properties. - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - notUpdated.setLocation(); - addTriggerFailedAndExitTxn(b, UPDATE_OP, triggerVar, txnVar, stateVar); - - // Mark properties dirty, to be consistent with a delete side-effect. - b.loadThis(); - b.invokeVirtual(MARK_PROPERTIES_DIRTY, null, null); - - b.loadLocal(forTryVar); - Label isForTry = b.createLabel(); - b.ifZeroComparisonBranch(isForTry, "!="); - - TypeDesc persistNoneType = TypeDesc.forClass(PersistNoneException.class); - b.newObject(persistNoneType); - b.dup(); - b.loadConstant("Cannot update missing object: "); - b.loadThis(); - b.invokeVirtual(TO_STRING_METHOD_NAME, TypeDesc.STRING, null); - b.invokeVirtual(TypeDesc.STRING, "concat", - TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING}); - b.invokeConstructor(persistNoneType, new TypeDesc[] {TypeDesc.STRING}); - b.throwObject(); - - isForTry.setLocation(); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - addTriggerFailedAndExitTxn - (b, UPDATE_OP, forTryVar, false, triggerVar, txnVar, stateVar, tryStart); - - // Define the abstract method. - mi = mClassFile.addMethod - (Modifiers.PROTECTED.toAbstract(true), - DO_TRY_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(PersistException.class)); - } - - // Add update method which calls update(forTry = false) - addUpdate: { - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, UPDATE_METHOD_NAME, null, null); - - if (mi == null) { - break addUpdate; - } - - mi.addException(TypeDesc.forClass(PersistException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, UPDATE_OP, false, PersistNoneException.class); - break addUpdate; - } - - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadConstant(false); - b.invokePrivate(PRIVATE_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.BOOLEAN}); - b.pop(); - b.returnVoid(); - } - - // Add tryUpdate method which calls update(forTry = true) - addTryUpdate: { - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, TRY_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, null); - - if (mi == null) { - break addTryUpdate; - } - - mi.addException(TypeDesc.forClass(PersistException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, UPDATE_OP, true, null); - break addTryUpdate; - } - - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadConstant(true); - b.invokePrivate(PRIVATE_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.BOOLEAN}); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Add delete(boolean forTry) method which delegates to abstract doTryDelete method. - if (mGenMode == GEN_ABSTRACT) { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PRIVATE.toSynchronized(true), - PRIVATE_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.BOOLEAN}); - mi.addException(TypeDesc.forClass(PersistException.class)); - - CodeBuilder b = new CodeBuilder(mi); - - requirePkInitialized(b, CHECK_PK_FOR_DELETE_METHOD_NAME); - - LocalVariable forTryVar = b.getParameter(0); - LocalVariable triggerVar = b.createLocalVariable(null, triggerType); - LocalVariable txnVar = b.createLocalVariable(null, transactionType); - LocalVariable stateVar = b.createLocalVariable(null, TypeDesc.OBJECT); - - Label tryStart = addGetTriggerAndEnterTxn - (b, DELETE_OP, forTryVar, false, triggerVar, txnVar, stateVar); - - // Call doTryDelete. - b.loadThis(); - b.invokeVirtual(DO_TRY_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, null); - - b.loadThis(); - b.invokeVirtual(MARK_PROPERTIES_DIRTY, null, null); - - Label notDeleted = b.createLabel(); - b.ifZeroComparisonBranch(notDeleted, "=="); - - addTriggerAfterAndExitTxn - (b, DELETE_OP, forTryVar, false, triggerVar, txnVar, stateVar); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - notDeleted.setLocation(); - addTriggerFailedAndExitTxn(b, DELETE_OP, triggerVar, txnVar, stateVar); - - b.loadLocal(forTryVar); - Label isForTry = b.createLabel(); - b.ifZeroComparisonBranch(isForTry, "!="); - - TypeDesc persistNoneType = TypeDesc.forClass(PersistNoneException.class); - b.newObject(persistNoneType); - b.dup(); - b.loadConstant("Cannot delete missing object: "); - b.loadThis(); - b.invokeVirtual(TO_STRING_METHOD_NAME, TypeDesc.STRING, null); - b.invokeVirtual(TypeDesc.STRING, "concat", - TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING}); - b.invokeConstructor(persistNoneType, new TypeDesc[] {TypeDesc.STRING}); - b.throwObject(); - - isForTry.setLocation(); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - - addTriggerFailedAndExitTxn - (b, DELETE_OP, forTryVar, false, triggerVar, txnVar, stateVar, tryStart); - - // Define the abstract method. - mi = mClassFile.addMethod - (Modifiers.PROTECTED.toAbstract(true), - DO_TRY_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(PersistException.class)); - } - - // Add delete method which calls delete(forTry = false) - addDelete: { - // Define the delete method. - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, DELETE_METHOD_NAME, null, null); - - if (mi == null) { - break addDelete; - } - - mi.addException(TypeDesc.forClass(PersistException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, DELETE_OP, false, PersistNoneException.class); - break addDelete; - } - - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadConstant(false); - b.invokePrivate(PRIVATE_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.BOOLEAN}); - b.pop(); - b.returnVoid(); - } - - // Add tryDelete method which calls delete(forTry = true) - addTryDelete: { - // Define the delete method. - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, TRY_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, null); - - if (mi == null) { - break addTryDelete; - } - - mi.addException(TypeDesc.forClass(PersistException.class)); - - if (mGenMode == GEN_WRAPPED) { - callWrappedSupport(mi, DELETE_OP, true, null); - break addTryDelete; - } - - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadConstant(true); - b.invokePrivate(PRIVATE_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.BOOLEAN}); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Add storableType method - addStorableType: { - final TypeDesc type = TypeDesc.forClass(mStorableType); - final TypeDesc storableClassType = TypeDesc.forClass(Class.class); - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, STORABLE_TYPE_METHOD_NAME, storableClassType, null); - - if (mi == null) { - break addStorableType; - } - - CodeBuilder b = new CodeBuilder(mi); - b.loadConstant(type); - b.returnValue(storableClassType); - } - - // Add copy method. - addCopy: { - TypeDesc type = TypeDesc.forClass(mInfo.getStorableType()); - - // Add copy method. - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC.toSynchronized(mGenMode == GEN_ABSTRACT), - COPY_METHOD_NAME, mClassFile.getType(), null); - - if (mi == null) { - break addCopy; - } - - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.invokeVirtual(CLONE_METHOD_NAME, TypeDesc.OBJECT, null); - b.checkCast(mClassFile.getType()); - - if (mGenMode == GEN_WRAPPED) { - // Need to do a deeper copy. - - LocalVariable copiedVar = b.createLocalVariable(null, mClassFile.getType()); - b.storeLocal(copiedVar); - - // First copy the wrapped Storable. - b.loadLocal(copiedVar); // storeField later - b.loadThis(); - b.loadField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - b.invoke(lookupMethod(mStorableType, COPY_METHOD_NAME, null)); - b.checkCast(TypeDesc.forClass(mStorableType)); - b.storeField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - - // Replace the WrappedSupport, passing in copy of wrapped Storable. - b.loadLocal(copiedVar); // storeField later - b.loadThis(); - b.loadField(SUPPORT_FIELD_NAME, mSupportType); - b.loadLocal(copiedVar); - b.loadField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - - b.invokeInterface(WrappedSupport.class.getName(), - CREATE_WRAPPED_SUPPORT_METHOD_NAME, - mSupportType, - new TypeDesc[] {TypeDesc.forClass(Storable.class)}); - - // Store new WrappedSupport in copy. - b.storeField(SUPPORT_FIELD_NAME, mSupportType); - - b.loadLocal(copiedVar); - } - - b.returnValue(type); - } - - // Part of properly defining copy method, except needs to be added even - // if copy method was not added because it is inherited and final. - CodeBuilderUtil.defineCopyBridges(mClassFile, mInfo.getStorableType()); - - // Create all the property copier methods. - // Boolean params: pkProperties, versionProperty, dataProperties, unequalOnly, dirtyOnly - addCopyPropertiesMethod(COPY_ALL_PROPERTIES, - true, true, true, false, false); - addCopyPropertiesMethod(COPY_PRIMARY_KEY_PROPERTIES, - true, false, false, false, false); - addCopyPropertiesMethod(COPY_VERSION_PROPERTY, - false, true, false, false, false); - addCopyPropertiesMethod(COPY_UNEQUAL_PROPERTIES, - false, true, true, true, false); - addCopyPropertiesMethod(COPY_DIRTY_PROPERTIES, - false, true, true, false, true); - - // Define hasDirtyProperties method. - addHasDirtyProps: { - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, HAS_DIRTY_PROPERTIES, TypeDesc.BOOLEAN, null); - - if (mi == null) { - break addHasDirtyProps; - } - - if (mGenMode == GEN_WRAPPED) { - callWrappedStorable(mi); - break addHasDirtyProps; - } - - CodeBuilder b = new CodeBuilder(mi); - Label isDirty = b.createLabel(); - branchIfDirty(b, false, isDirty); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - isDirty.setLocation(); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Define isPropertyUninitialized, isPropertyDirty, and isPropertyClean methods. - addPropertyStateExtractMethod(); - addPropertyStateCheckMethod(IS_PROPERTY_UNINITIALIZED, PROPERTY_STATE_UNINITIALIZED); - addPropertyStateCheckMethod(IS_PROPERTY_DIRTY, PROPERTY_STATE_DIRTY); - addPropertyStateCheckMethod(IS_PROPERTY_CLEAN, PROPERTY_STATE_CLEAN); - - // Define isPropertySupported method. - addIsPropertySupported: { - MethodInfo mi = addMethodIfNotFinal - (Modifiers.PUBLIC, IS_PROPERTY_SUPPORTED, - TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING}); - - if (mi == null) { - break addIsPropertySupported; - } - - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadField(SUPPORT_FIELD_NAME, mSupportType); - b.loadLocal(b.getParameter(0)); - b.invokeInterface(mSupportType, "isPropertySupported", TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.STRING}); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Define standard object methods. - addHashCodeMethod(); - addEqualsMethod(EQUAL_FULL); - addEqualsMethod(EQUAL_KEYS); - addEqualsMethod(EQUAL_PROPERTIES); - addToStringMethod(false); - addToStringMethod(true); - - addMarkCleanMethod(MARK_PROPERTIES_CLEAN); - addMarkCleanMethod(MARK_ALL_PROPERTIES_CLEAN); - addMarkDirtyMethod(MARK_PROPERTIES_DIRTY); - addMarkDirtyMethod(MARK_ALL_PROPERTIES_DIRTY); - - if (mGenMode == GEN_ABSTRACT) { - // Define protected isPkInitialized method. - addIsInitializedMethod - (IS_PK_INITIALIZED_METHOD_NAME, mInfo.getPrimaryKeyProperties()); - - // Define protected methods to check if alternate key is initialized. - addAltKeyMethods: - for (int i=0; i> altProps = - new HashMap>(); - - StorableKey altKey = mInfo.getAlternateKey(i); - - for (OrderedProperty op : altKey.getProperties()) { - ChainedProperty cp = op.getChainedProperty(); - if (cp.getChainCount() > 0) { - // This should not be possible. - continue addAltKeyMethods; - } - StorableProperty property = cp.getPrimeProperty(); - altProps.put(property.getName(), property); - } - - addIsInitializedMethod(IS_ALT_KEY_INITIALIZED_PREFIX + i, altProps); - } - - // Define protected isRequiredDataInitialized method. - defineIsRequiredDataInitialized: { - Map> requiredProperties = - new HashMap>(); - - for (StorableProperty property : mAllProperties.values()) { - if (!property.isPrimaryKeyMember() && - !property.isJoin() && - !property.isNullable()) { - - requiredProperties.put(property.getName(), property); - } - } - - addIsInitializedMethod - (IS_REQUIRED_DATA_INITIALIZED_METHOD_NAME, requiredProperties); - } - - // Define optional protected isVersionInitialized method. The - // versionOrdinal variable was set earlier, when properties were defined. - if (versionOrdinal >= 0) { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED, IS_VERSION_INITIALIZED_METHOD_NAME, - TypeDesc.BOOLEAN, null); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.loadField(PROPERTY_STATE_FIELD_NAME + (versionOrdinal >> 4), TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_MASK << ((versionOrdinal & 0xf) * 2)); - b.math(Opcode.IAND); - // zero == false, not zero == true - b.returnValue(TypeDesc.BOOLEAN); - } - } - } - - /** - * If GEN_WRAPPED, generates a method implementation which delgates to the - * WrappedSupport. Also clears join property state if called method - * returns normally. - * - * @param opType optional, is one of INSERT_OP, UPDATE_OP, or DELETE_OP, for trigger support - * @param forTry used for INSERT_OP, UPDATE_OP, or DELETE_OP - * @param exceptionType optional - if called method throws this exception, - * join property state is still cleared. - */ - private void callWrappedSupport(MethodInfo mi, - String opType, - boolean forTry, - Class exceptionType) - { - if (mGenMode == GEN_ABSTRACT || !mHasJoins) { - // Don't need to clear state bits. - exceptionType = null; - } - - CodeBuilder b = new CodeBuilder(mi); - - final TypeDesc triggerType = TypeDesc.forClass(Trigger.class); - final TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - - LocalVariable triggerVar = b.createLocalVariable(null, triggerType); - LocalVariable txnVar = b.createLocalVariable(null, transactionType); - LocalVariable stateVar = b.createLocalVariable(null, TypeDesc.OBJECT); - - Label tryStart; - if (opType == null) { - tryStart = b.createLabel().setLocation(); - } else { - tryStart = addGetTriggerAndEnterTxn - (b, opType, null, forTry, triggerVar, txnVar, stateVar); - } - - b.loadThis(); - b.loadField(SUPPORT_FIELD_NAME, mSupportType); - Method method = lookupMethod(WrappedSupport.class, mi); - b.invoke(method); - - Label tryEnd = b.createLabel().setLocation(); - - clearState(b); - - if (method.getReturnType() == void.class) { - if (opType != null) { - addTriggerAfterAndExitTxn(b, opType, null, forTry, triggerVar, txnVar, stateVar); - } - b.returnVoid(); - } else { - if (opType != null) { - Label notDone = b.createLabel(); - b.ifZeroComparisonBranch(notDone, "=="); - addTriggerAfterAndExitTxn(b, opType, null, forTry, triggerVar, txnVar, stateVar); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - notDone.setLocation(); - addTriggerFailedAndExitTxn(b, opType, triggerVar, txnVar, stateVar); - b.loadConstant(false); - } - b.returnValue(TypeDesc.forClass(method.getReturnType())); - } - - if (opType != null) { - addTriggerFailedAndExitTxn - (b, opType, null, forTry, triggerVar, txnVar, stateVar, tryStart); - } - - if (exceptionType != null) { - b.exceptionHandler(tryStart, tryEnd, exceptionType.getName()); - clearState(b); - b.throwObject(); - } - } - - /** - * If GEN_WRAPPED, generates a method implementation which delgates to the - * wrapped Storable. - */ - private void callWrappedStorable(MethodInfo mi) { - callWrappedStorable(mi, new CodeBuilder(mi)); - } - - /** - * If GEN_WRAPPED, generates a method implementation which delgates to the - * wrapped Storable. - */ - private void callWrappedStorable(MethodInfo mi, CodeBuilder b) { - b.loadThis(); - b.loadField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - - int count = mi.getMethodDescriptor().getParameterCount(); - for (int j=0; j> 4); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.storeLocal(stateBits); - } - - Label skipCopy = b.createLabel(); - - // Check if independent property is supported, and skip if not. - if (property.isIndependent()) { - addSkipIndependent(b, target, property, skipCopy); - } - - // Skip property if uninitialized. - b.loadLocal(stateBits); - b.loadConstant(mask); - b.math(Opcode.IAND); - b.ifZeroComparisonBranch(skipCopy, "=="); - - if (dirtyOnly) { - // Add code to find out if property has been dirty. - b.loadLocal(stateBits); - b.loadConstant(mask); - b.math(Opcode.IAND); - b.loadConstant(PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2)); - b.ifComparisonBranch(skipCopy, "!="); - } - - TypeDesc type = TypeDesc.forClass(property.getType()); - - if (unequalOnly) { - // Add code to find out if they're equal. - b.loadThis(); - b.loadField(property.getName(), type); // [this.propValue - b.loadLocal(target); // [this.propValue, target - b.invoke(property.getReadMethod()); // [this.propValue, target.propValue - CodeBuilderUtil.addValuesEqualCall - (b, TypeDesc.forClass(property.getType()), true, skipCopy, true); - } - - b.loadLocal(target); // [target - b.loadThis(); // [target, this - b.loadField(property.getName(), type); // [target, this.propValue - mutateProperty(b, property, type); - - skipCopy.setLocation(); - } - - ordinal++; - if ((mask <<= 2) == 0) { - mask = 3; - stateBits = null; - } - } - - b.returnVoid(); - } - - private void addSkipIndependent(CodeBuilder b, - LocalVariable target, - StorableProperty property, - Label skipCopy) - { - TypeDesc storableTypeDesc = TypeDesc.forClass(Storable.class); - - if (target != null) { - b.loadLocal(target); - b.loadConstant(property.getName()); - b.invokeInterface(storableTypeDesc, - "isPropertySupported", - TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.STRING}); - b.ifZeroComparisonBranch(skipCopy, "=="); - } - - b.loadThis(); - b.loadConstant(property.getName()); - b.invokeInterface(storableTypeDesc, - "isPropertySupported", - TypeDesc.BOOLEAN, - new TypeDesc[] {TypeDesc.STRING}); - b.ifZeroComparisonBranch(skipCopy, "=="); - } - - /** - * Puts the value on the stack into the specified storable. If a write method is defined - * uses it, otherwise just shoves the value into the appropriate field. - * - * entry stack: [storable, value - * exit stack: [ - * - * @param b - {@link CodeBuilder} to which to add the mutation code - * @param property - property to mutate - * @param type - type of the property - */ - private void mutateProperty(CodeBuilder b, StorableProperty property, TypeDesc type) { - if (property.getWriteMethod() == null) { - b.storeField(property.getName(), type); - } else { - b.invoke(property.getWriteMethod()); - } - } - - /** - * Generates code that loads a property annotation to the stack. - */ - private void loadPropertyAnnotation(CodeBuilder b, - StorableProperty property, - StorablePropertyAnnotation annotation) { - /* Example - UserInfo.class.getMethod("setFirstName", new Class[] {String.class}) - .getAnnotation(LengthConstraint.class) - */ - - String methodName = annotation.getAnnotatedMethod().getName(); - boolean isAccessor = !methodName.startsWith("set"); - - b.loadConstant(TypeDesc.forClass(property.getEnclosingType())); - b.loadConstant(methodName); - if (isAccessor) { - // Accessor method has no parameters. - b.loadNull(); - } else { - // Mutator method has one parameter. - b.loadConstant(1); - b.newObject(TypeDesc.forClass(Class[].class)); - b.dup(); - b.loadConstant(0); - b.loadConstant(TypeDesc.forClass(property.getType())); - b.storeToArray(TypeDesc.forClass(Class[].class)); - } - b.invokeVirtual(Class.class.getName(), "getMethod", - TypeDesc.forClass(Method.class), new TypeDesc[] { - TypeDesc.STRING, TypeDesc.forClass(Class[].class) - }); - b.loadConstant(TypeDesc.forClass(annotation.getAnnotationType())); - b.invokeVirtual(Method.class.getName(), "getAnnotation", - TypeDesc.forClass(Annotation.class), new TypeDesc[] { - TypeDesc.forClass(Class.class) - }); - b.checkCast(TypeDesc.forClass(annotation.getAnnotationType())); - } - - /** - * Generates code that loads a Storage instance on the stack, throwing a - * FetchException if Storage request fails. - * - * @param type type of Storage to request - */ - private void loadStorageForFetch(CodeBuilder b, TypeDesc type) { - b.loadThis(); - b.loadField(SUPPORT_FIELD_NAME, mSupportType); - TypeDesc storageType = TypeDesc.forClass(Storage.class); - - TypeDesc repositoryType = TypeDesc.forClass(Repository.class); - b.invokeInterface - (mSupportType, "getRootRepository", repositoryType, null); - b.loadConstant(type); - - // This may throw a RepositoryException. - Label tryStart = b.createLabel().setLocation(); - b.invokeInterface(repositoryType, STORAGE_FOR_METHOD_NAME, storageType, - new TypeDesc[]{TypeDesc.forClass(Class.class)}); - Label tryEnd = b.createLabel().setLocation(); - Label noException = b.createLabel(); - b.branch(noException); - - b.exceptionHandler(tryStart, tryEnd, - RepositoryException.class.getName()); - b.invokeVirtual - (RepositoryException.class.getName(), "toFetchException", - TypeDesc.forClass(FetchException.class), null); - b.throwObject(); - - noException.setLocation(); - } - - /** - * For the given join property, marks all of its dependent internal join - * element properties as dirty. - */ - /* - private void markInternalJoinElementsDirty(CodeBuilder b, StorableProperty joinProperty) { - int count = mAllProperties.size(); - - int ordinal = 0; - int mask = 0; - for (StorableProperty property : mAllProperties.values()) { - if (property != joinProperty && !property.isJoin()) { - // Check to see if property is an internal member of joinProperty. - for (int i=joinProperty.getJoinElementCount(); --i>=0; ) { - if (property == joinProperty.getInternalJoinElement(i)) { - mask |= PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2); - } - } - } - ordinal++; - if (((ordinal & 0xf) == 0 || ordinal >= count) && mask != 0) { - String stateFieldName = PROPERTY_STATE_FIELD_NAME + ((ordinal - 1) >> 4); - b.loadThis(); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(mask); - b.math(Opcode.IOR); - b.storeField(stateFieldName, TypeDesc.INT); - mask = 0; - } - } - } - */ - - /** - * Generates code to set all state properties to zero. - */ - private void clearState(CodeBuilder b) { - int ordinal = -1; - int maxOrdinal = mAllProperties.size() - 1; - boolean requireStateField = false; - - for (StorableProperty property : mAllProperties.values()) { - ordinal++; - - if (property.isJoin() || mGenMode == GEN_ABSTRACT) { - requireStateField = true; - } - - if (ordinal == maxOrdinal || ((ordinal & 0xf) == 0xf)) { - if (requireStateField) { - String stateFieldName = PROPERTY_STATE_FIELD_NAME + (ordinal >> 4); - - b.loadThis(); - b.loadConstant(0); - b.storeField(stateFieldName, TypeDesc.INT); - } - requireStateField = false; - } - } - } - - private void addMarkCleanMethod(String name) { - MethodInfo mi = - addMethodIfNotFinal (Modifiers.PUBLIC.toSynchronized(true), name, null, null); - - if (mi == null) { - return; - } - - CodeBuilder b = new CodeBuilder(mi); - - if (mGenMode == GEN_WRAPPED) { - clearState(b); - callWrappedStorable(mi, b); - return; - } - - final int count = mAllProperties.size(); - int ordinal = 0; - int andMask = 0; - int orMask = 0; - - for (StorableProperty property : mAllProperties.values()) { - if (property.isQuery()) { - // Don't erase cached query. - andMask |= PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2); - } else if (!property.isJoin()) { - if (name == MARK_ALL_PROPERTIES_CLEAN) { - // Force clean state (1) always. - orMask |= PROPERTY_STATE_CLEAN << ((ordinal & 0xf) * 2); - } else if (name == MARK_PROPERTIES_CLEAN) { - // Mask will convert dirty (3) to clean (1). State 2, which - // is illegal, is converted to 0. - andMask |= PROPERTY_STATE_CLEAN << ((ordinal & 0xf) * 2); - } - } - - ordinal++; - if ((ordinal & 0xf) == 0 || ordinal >= count) { - String stateFieldName = PROPERTY_STATE_FIELD_NAME + ((ordinal - 1) >> 4); - b.loadThis(); - if (andMask == 0) { - b.loadConstant(orMask); - } else { - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(andMask); - b.math(Opcode.IAND); - if (orMask != 0) { - b.loadConstant(orMask); - b.math(Opcode.IOR); - } - } - b.storeField(stateFieldName, TypeDesc.INT); - andMask = 0; - orMask = 0; - } - } - - b.returnVoid(); - } - - private void addMarkDirtyMethod(String name) { - MethodInfo mi = - addMethodIfNotFinal(Modifiers.PUBLIC.toSynchronized(true), name, null, null); - - if (mi == null) { - return; - } - - CodeBuilder b = new CodeBuilder(mi); - - if (mGenMode == GEN_WRAPPED) { - clearState(b); - callWrappedStorable(mi, b); - return; - } - - final int count = mAllProperties.size(); - int ordinal = 0; - int andMask = 0; - int orMask = 0; - - for (StorableProperty property : mAllProperties.values()) { - if (property.isJoin()) { - // Erase cached join properties, but don't erase cached query. - if (!property.isQuery()) { - andMask |= PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2); - } - } else if (name == MARK_ALL_PROPERTIES_DIRTY) { - // Force dirty state (3). - orMask |= PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2); - } - - ordinal++; - if ((ordinal & 0xf) == 0 || ordinal >= count) { - String stateFieldName = PROPERTY_STATE_FIELD_NAME + ((ordinal - 1) >> 4); - if (name == MARK_ALL_PROPERTIES_DIRTY) { - if (orMask != 0 || andMask != 0) { - b.loadThis(); // [this - b.loadThis(); // [this, this - b.loadField(stateFieldName, TypeDesc.INT); // [this, this.stateField - if (andMask != 0) { - b.loadConstant(~andMask); - b.math(Opcode.IAND); - } - if (orMask != 0) { - b.loadConstant(orMask); - b.math(Opcode.IOR); - } - b.storeField(stateFieldName, TypeDesc.INT); - } - } else { - // This is a great trick to convert all states of value 1 - // (clean) into value 3 (dirty). States 0, 2, and 3 stay the - // same. Since joins cannot have state 1, they aren't affected. - // stateField |= ((stateField & 0x55555555) << 1); - - b.loadThis(); // [this - b.loadThis(); // [this, this - b.loadField(stateFieldName, TypeDesc.INT); // [this, this.stateField - if (andMask != 0) { - b.loadConstant(~andMask); - b.math(Opcode.IAND); - } - b.dup(); // [this, this.stateField, this.stateField - b.loadConstant(0x55555555); - b.math(Opcode.IAND); // [this, this.stateField, this.stateField & 0x55555555 - b.loadConstant(1); - b.math(Opcode.ISHL); // [this, this.stateField, orMaskValue - b.math(Opcode.IOR); // [this, newStateFieldValue - b.storeField(stateFieldName, TypeDesc.INT); - } - - andMask = 0; - orMask = 0; - } - } - - b.returnVoid(); - } - - /** - * For the given ordinary key property, marks all of its dependent join - * element properties as uninitialized, and marks given property as dirty. - */ - private void markOrdinaryPropertyDirty - (CodeBuilder b, StorableProperty ordinaryProperty) - { - int count = mAllProperties.size(); - - int ordinal = 0; - int andMask = 0xffffffff; - int orMask = 0; - for (StorableProperty property : mAllProperties.values()) { - if (property == ordinaryProperty) { - if (mGenMode == GEN_ABSTRACT) { - // Only GEN_ABSTRACT mode uses these state bits. - orMask |= PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2); - } - } else if (property.isJoin()) { - // Check to see if ordinary is an internal member of join property. - for (int i=property.getJoinElementCount(); --i>=0; ) { - if (ordinaryProperty == property.getInternalJoinElement(i)) { - andMask &= ~(PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2)); - } - } - } - ordinal++; - if ((ordinal & 0xf) == 0 || ordinal >= count) { - if (andMask != 0xffffffff || orMask != 0) { - String stateFieldName = PROPERTY_STATE_FIELD_NAME + ((ordinal - 1) >> 4); - b.loadThis(); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - if (andMask != 0xffffffff) { - b.loadConstant(andMask); - b.math(Opcode.IAND); - } - if (orMask != 0) { - b.loadConstant(orMask); - b.math(Opcode.IOR); - } - b.storeField(stateFieldName, TypeDesc.INT); - } - andMask = 0xffffffff; - orMask = 0; - } - } - } - - // Generates code that branches to the given label if any properties are dirty. - private void branchIfDirty(CodeBuilder b, boolean includePk, Label label) { - int count = mAllProperties.size(); - int ordinal = 0; - int andMask = 0; - for (StorableProperty property : mAllProperties.values()) { - if (!property.isJoin() && (!property.isPrimaryKeyMember() || includePk)) { - // Logical 'and' will convert state 1 (clean) to state 0, so - // that it will be ignored. State 3 (dirty) is what we're - // looking for, and it turns into 2. Essentially, we leave the - // high order bit on, since there is no state which has the - // high order bit on unless the low order bit is also on. - andMask |= 2 << ((ordinal & 0xf) * 2); - } - ordinal++; - if ((ordinal & 0xf) == 0 || ordinal >= count) { - String stateFieldName = PROPERTY_STATE_FIELD_NAME + ((ordinal - 1) >> 4); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(andMask); - b.math(Opcode.IAND); - // At least one property is dirty, so short circuit. - b.ifZeroComparisonBranch(label, "!="); - andMask = 0; - } - } - } - - private void addIsInitializedMethod - (String name, Map> properties) - { - // Don't check Automatic properties. - { - boolean cloned = false; - for (StorableProperty prop : properties.values()) { - if (prop.isAutomatic() || prop.isVersion()) { - if (!cloned) { - properties = new LinkedHashMap>(properties); - cloned = true; - } - // This isn't concurrent modification since the loop is - // still operating on the original properties map. - properties.remove(prop.getName()); - } - } - } - - MethodInfo mi = mClassFile.addMethod(Modifiers.PROTECTED, name, TypeDesc.BOOLEAN, null); - CodeBuilder b = new CodeBuilder(mi); - - if (properties.size() == 0) { - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - return; - } - - if (properties.size() == 1) { - int ordinal = findPropertyOrdinal(properties.values().iterator().next()); - b.loadThis(); - b.loadField(PROPERTY_STATE_FIELD_NAME + (ordinal >> 4), TypeDesc.INT); - b.loadConstant(PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2)); - b.math(Opcode.IAND); - // zero == false, not zero == true - b.returnValue(TypeDesc.BOOLEAN); - return; - } - - // Multiple properties is a bit more tricky. The goal here is to - // minimize the amount of work that needs to be done at runtime. - - int ordinal = 0; - int mask = 0; - for (StorableProperty property : mAllProperties.values()) { - if (properties.containsKey(property.getName())) { - mask |= PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2); - } - ordinal++; - if (((ordinal & 0xf) == 0 || ordinal >= mAllProperties.size()) && mask != 0) { - // This is a great trick to convert all states of value 1 - // (clean) into value 3 (dirty). States 0, 2, and 3 stay the - // same. Since joins cannot have state 1, they aren't affected. - // stateField | ((stateField & 0x55555555) << 1); - - b.loadThis(); - b.loadField(PROPERTY_STATE_FIELD_NAME + ((ordinal - 1) >> 4), TypeDesc.INT); - b.dup(); // [this.stateField, this.stateField - b.loadConstant(0x55555555); - b.math(Opcode.IAND); // [this.stateField, this.stateField & 0x55555555 - b.loadConstant(1); - b.math(Opcode.ISHL); // [this.stateField, orMaskValue - b.math(Opcode.IOR); // [newStateFieldValue - - // Flip all bits for property states. If final result is - // non-zero, then there were uninitialized properties. - - b.loadConstant(mask); - b.math(Opcode.IXOR); - if (mask != 0xffffffff) { - b.loadConstant(mask); - b.math(Opcode.IAND); - } - - Label cont = b.createLabel(); - b.ifZeroComparisonBranch(cont, "=="); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - cont.setLocation(); - - mask = 0; - } - } - - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - } - - private int findPropertyOrdinal(StorableProperty property) { - int ordinal = 0; - for (StorableProperty p : mAllProperties.values()) { - if (p == property) { - return ordinal; - } - ordinal++; - } - throw new IllegalArgumentException(); - } - - /** - * Generates code that verifies that all primary keys are initialized. - * - * @param b builder that will invoke generated method - * @param methodName name to give to generated method - */ - private void requirePkInitialized(CodeBuilder b, String methodName) { - // Add code to call method which we are about to define. - b.loadThis(); - b.invokeVirtual(methodName, null, null); - - // Now define new method, discarding original builder object. - b = new CodeBuilder(mClassFile.addMethod(Modifiers.PROTECTED, methodName, null, null)); - b.loadThis(); - b.invokeVirtual(IS_PK_INITIALIZED_METHOD_NAME, TypeDesc.BOOLEAN, null); - Label pkInitialized = b.createLabel(); - b.ifZeroComparisonBranch(pkInitialized, "!="); - CodeBuilderUtil.throwException - (b, IllegalStateException.class, "Primary key not fully specified"); - pkInitialized.setLocation(); - b.returnVoid(); - } - - /** - * Generates a private method which accepts a property name and returns - * PROPERTY_STATE_UNINITIALIZED, PROPERTY_STATE_DIRTY, or - * PROPERTY_STATE_CLEAN. - */ - private void addPropertyStateExtractMethod() { - if (mGenMode == GEN_WRAPPED) { - return; - } - - MethodInfo mi = mClassFile.addMethod(Modifiers.PRIVATE, PROPERTY_STATE_EXTRACT_METHOD_NAME, - TypeDesc.INT, new TypeDesc[] {TypeDesc.STRING}); - CodeBuilder b = new CodeBuilder(mi); - - // Generate big switch statement that operates on Strings. See also - // org.cojen.util.BeanPropertyAccessor, which also generates this kind of - // switch. - - // For switch case count, obtain a prime number, at least twice as - // large as needed. This should minimize hash collisions. Since all the - // hash keys are known up front, the capacity could be tweaked until - // there are no collisions, but this technique is easier and - // deterministic. - - int caseCount; - { - BigInteger capacity = BigInteger.valueOf(mAllProperties.size() * 2 + 1); - while (!capacity.isProbablePrime(100)) { - capacity = capacity.add(BigInteger.valueOf(2)); - } - caseCount = capacity.intValue(); - } - - int[] cases = new int[caseCount]; - for (int i=0; i>[] caseMatches = caseMatches(caseCount); - - for (int i=0; i matches = caseMatches[i]; - if (matches == null || matches.size() == 0) { - switchLabels[i] = noMatch; - } else { - switchLabels[i] = b.createLabel(); - } - } - - b.loadLocal(b.getParameter(0)); - b.invokeVirtual(String.class.getName(), "hashCode", TypeDesc.INT, null); - b.loadConstant(0x7fffffff); - b.math(Opcode.IAND); - b.loadConstant(caseCount); - b.math(Opcode.IREM); - - b.switchBranch(cases, switchLabels, noMatch); - - // Gather property ordinals. - Map, Integer> ordinalMap = new HashMap, Integer>(); - { - int ordinal = 0; - for (StorableProperty prop : mAllProperties.values()) { - ordinalMap.put(prop, ordinal++); - } - } - - // Params to invoke String.equals. - TypeDesc[] params = {TypeDesc.OBJECT}; - - Label joinMatch = null; - - for (int i=0; i> matches = caseMatches[i]; - if (matches == null || matches.size() == 0) { - continue; - } - - switchLabels[i].setLocation(); - - int matchCount = matches.size(); - for (int j=0; j prop = matches.get(j); - - // Test against name to find exact match. - - b.loadConstant(prop.getName()); - b.loadLocal(b.getParameter(0)); - b.invokeVirtual(String.class.getName(), "equals", TypeDesc.BOOLEAN, params); - - Label notEqual; - - if (j == matchCount - 1) { - notEqual = null; - b.ifZeroComparisonBranch(noMatch, "=="); - } else { - notEqual = b.createLabel(); - b.ifZeroComparisonBranch(notEqual, "=="); - } - - if (prop.isJoin()) { - if (joinMatch == null) { - joinMatch = b.createLabel(); - } - b.branch(joinMatch); - } else { - int ordinal = ordinalMap.get(prop); - - b.loadThis(); - b.loadField(PROPERTY_STATE_FIELD_NAME + (ordinal >> 4), TypeDesc.INT); - int shift = (ordinal & 0xf) * 2; - if (shift != 0) { - b.loadConstant(shift); - b.math(Opcode.ISHR); - } - b.loadConstant(PROPERTY_STATE_MASK); - b.math(Opcode.IAND); - b.returnValue(TypeDesc.INT); - } - - if (notEqual != null) { - notEqual.setLocation(); - } - } - } - - TypeDesc exceptionType = TypeDesc.forClass(IllegalArgumentException.class); - params = new TypeDesc[] {TypeDesc.STRING}; - - noMatch.setLocation(); - - b.newObject(exceptionType); - b.dup(); - b.loadConstant("Unknown property: "); - b.loadLocal(b.getParameter(0)); - b.invokeVirtual(TypeDesc.STRING, "concat", TypeDesc.STRING, params); - b.invokeConstructor(exceptionType, params); - b.throwObject(); - - if (joinMatch != null) { - joinMatch.setLocation(); - - b.newObject(exceptionType); - b.dup(); - b.loadConstant("Cannot get state for join property: "); - b.loadLocal(b.getParameter(0)); - b.invokeVirtual(TypeDesc.STRING, "concat", TypeDesc.STRING, params); - b.invokeConstructor(exceptionType, params); - b.throwObject(); - } - } - - /** - * Returns the properties that match on a given case. The array length is - * the same as the case count. Each list represents the matches. The lists - * themselves may be null if no matches for that case. - */ - private List>[] caseMatches(int caseCount) { - List>[] cases = new List[caseCount]; - - for (StorableProperty prop : mAllProperties.values()) { - int hashCode = prop.getName().hashCode(); - int caseValue = (hashCode & 0x7fffffff) % caseCount; - List matches = cases[caseValue]; - if (matches == null) { - matches = cases[caseValue] = new ArrayList>(); - } - matches.add(prop); - } - - return cases; - } - - /** - * Generates public method which accepts a property name and returns a - * boolean true, if the given state matches the property's actual state. - * - * @param name name of method - * @param state property state to check - */ - private void addPropertyStateCheckMethod(String name, int state) { - MethodInfo mi = addMethodIfNotFinal(Modifiers.PUBLIC, name, - TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING}); - - if (mi == null) { - return; - } - - CodeBuilder b = new CodeBuilder(mi); - - if (mGenMode == GEN_WRAPPED) { - callWrappedStorable(mi, b); - return; - } - - // Call private method to extract state and compare. - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokePrivate(PROPERTY_STATE_EXTRACT_METHOD_NAME, - TypeDesc.INT, new TypeDesc[] {TypeDesc.STRING}); - Label isFalse = b.createLabel(); - if (state == 0) { - b.ifZeroComparisonBranch(isFalse, "!="); - } else { - b.loadConstant(state); - b.ifComparisonBranch(isFalse, "!="); - } - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - isFalse.setLocation(); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - } - - /** - * Defines a hashCode method. - */ - private void addHashCodeMethod() { - Modifiers modifiers = Modifiers.PUBLIC.toSynchronized(mGenMode == GEN_ABSTRACT); - MethodInfo mi = addMethodIfNotFinal(modifiers, "hashCode", TypeDesc.INT, null); - - if (mi == null) { - return; - } - - if (mGenMode == GEN_WRAPPED) { - callWrappedStorable(mi); - return; - } - - CodeBuilder b = new CodeBuilder(mi); - - boolean mixIn = false; - for (StorableProperty property : mAllProperties.values()) { - if (property.isJoin()) { - continue; - } - addHashCodeCall(b, property.getName(), - TypeDesc.forClass(property.getType()), true, mixIn); - mixIn = true; - } - - b.returnValue(TypeDesc.INT); - } - - private void addHashCodeCall(CodeBuilder b, String fieldName, - TypeDesc fieldType, boolean testForNull, - boolean mixIn) - { - if (mixIn) { - // Multiply current hashcode by 31 before adding more to it. - b.loadConstant(5); - b.math(Opcode.ISHL); - b.loadConstant(1); - b.math(Opcode.ISUB); - } - - b.loadThis(); - b.loadField(fieldName, fieldType); - - switch (fieldType.getTypeCode()) { - case TypeDesc.FLOAT_CODE: - b.invokeStatic(TypeDesc.FLOAT.toObjectType(), "floatToIntBits", - TypeDesc.INT, new TypeDesc[]{TypeDesc.FLOAT}); - // Fall through - case TypeDesc.INT_CODE: - case TypeDesc.CHAR_CODE: - case TypeDesc.SHORT_CODE: - case TypeDesc.BYTE_CODE: - case TypeDesc.BOOLEAN_CODE: - if (mixIn) { - b.math(Opcode.IADD); - } - break; - - case TypeDesc.DOUBLE_CODE: - b.invokeStatic(TypeDesc.DOUBLE.toObjectType(), "doubleToLongBits", - TypeDesc.LONG, new TypeDesc[]{TypeDesc.DOUBLE}); - // Fall through - case TypeDesc.LONG_CODE: - b.dup2(); - b.loadConstant(32); - b.math(Opcode.LUSHR); - b.math(Opcode.LXOR); - b.convert(TypeDesc.LONG, TypeDesc.INT); - if (mixIn) { - b.math(Opcode.IADD); - } - break; - - case TypeDesc.OBJECT_CODE: - default: - LocalVariable value = null; - if (testForNull) { - value = b.createLocalVariable(null, fieldType); - b.storeLocal(value); - b.loadLocal(value); - } - if (mixIn) { - Label isNull = b.createLabel(); - if (testForNull) { - b.ifNullBranch(isNull, true); - b.loadLocal(value); - } - addHashCodeCallTo(b, fieldType); - b.math(Opcode.IADD); - if (testForNull) { - isNull.setLocation(); - } - } else { - Label cont = b.createLabel(); - if (testForNull) { - Label notNull = b.createLabel(); - b.ifNullBranch(notNull, false); - b.loadConstant(0); - b.branch(cont); - notNull.setLocation(); - b.loadLocal(value); - } - addHashCodeCallTo(b, fieldType); - if (testForNull) { - cont.setLocation(); - } - } - break; - } - } - - private void addHashCodeCallTo(CodeBuilder b, TypeDesc fieldType) { - if (fieldType.isArray()) { - if (!fieldType.getComponentType().isPrimitive()) { - b.invokeStatic("java.util.Arrays", "deepHashCode", - TypeDesc.INT, new TypeDesc[] {TypeDesc.forClass(Object[].class)}); - } else { - b.invokeStatic("java.util.Arrays", "hashCode", - TypeDesc.INT, new TypeDesc[] {fieldType}); - } - } else { - b.invokeVirtual(TypeDesc.OBJECT, "hashCode", TypeDesc.INT, null); - } - } - - /** - * Defines an equals method. - * - * @param equalityType Type of equality to define - {@link EQUAL_KEYS} for "equalKeys", - * {@link EQUAL_PROPERTIES} for "equalProperties", and {@link EQUAL_FULL} for "equals" - */ - private void addEqualsMethod(int equalityType) { - TypeDesc[] objectParam = {TypeDesc.OBJECT}; - - String equalsMethodName; - switch (equalityType) { - default: - throw new IllegalArgumentException(); - case EQUAL_KEYS: - equalsMethodName = EQUAL_PRIMARY_KEYS_METHOD_NAME; - break; - case EQUAL_PROPERTIES: - equalsMethodName = EQUAL_PROPERTIES_METHOD_NAME; - break; - case EQUAL_FULL: - equalsMethodName = EQUALS_METHOD_NAME; - } - - Modifiers modifiers = Modifiers.PUBLIC.toSynchronized(mGenMode == GEN_ABSTRACT); - MethodInfo mi = addMethodIfNotFinal - (modifiers, equalsMethodName, TypeDesc.BOOLEAN, objectParam); - - if (mi == null) { - return; - } - - if (mGenMode == GEN_WRAPPED && equalityType != EQUAL_FULL) { - callWrappedStorable(mi); - return; - } - - CodeBuilder b = new CodeBuilder(mi); - - // if (this == target) return true; - b.loadThis(); - b.loadLocal(b.getParameter(0)); - Label notEqual = b.createLabel(); - b.ifEqualBranch(notEqual, false); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - notEqual.setLocation(); - - // if (! target instanceof this) return false; - TypeDesc userStorableTypeDesc = TypeDesc.forClass(mStorableType); - b.loadLocal(b.getParameter(0)); - b.instanceOf(userStorableTypeDesc); - Label fail = b.createLabel(); - b.ifZeroComparisonBranch(fail, "=="); - - // this.class other = (this.class)target; - LocalVariable other = b.createLocalVariable(null, userStorableTypeDesc); - b.loadLocal(b.getParameter(0)); - b.checkCast(userStorableTypeDesc); - b.storeLocal(other); - - for (StorableProperty property : mAllProperties.values()) { - if (property.isJoin()) { - continue; - } - // If we're only comparing keys, and this isn't a key, skip it - if ((equalityType == EQUAL_KEYS) && !property.isPrimaryKeyMember()) { - continue; - } - - // Check if independent property is supported, and skip if not. - Label skipCheck = b.createLabel(); - if (equalityType != EQUAL_KEYS && property.isIndependent()) { - addSkipIndependent(b, other, property, skipCheck); - } - - TypeDesc fieldType = TypeDesc.forClass(property.getType()); - b.loadThis(); - if (mGenMode == GEN_ABSTRACT) { - b.loadField(property.getName(), fieldType); - } else { - b.loadField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - b.invoke(property.getReadMethod()); - } - - b.loadLocal(other); - b.invoke(property.getReadMethod()); - CodeBuilderUtil.addValuesEqualCall(b, fieldType, true, fail, false); - - skipCheck.setLocation(); - } - - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - - fail.setLocation(); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - } - - /** - * Defines a toString method, which assumes that the ClassFile is targeting - * version 1.5 of Java. - * - * @param keyOnly when true, generate a toStringKeyOnly method instead - */ - private void addToStringMethod(boolean keyOnly) { - TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class); - TypeDesc[] stringParam = {TypeDesc.STRING}; - TypeDesc[] charParam = {TypeDesc.CHAR}; - - Modifiers modifiers = Modifiers.PUBLIC.toSynchronized(mGenMode == GEN_ABSTRACT); - MethodInfo mi = addMethodIfNotFinal(modifiers, - keyOnly ? - TO_STRING_KEY_ONLY_METHOD_NAME : - TO_STRING_METHOD_NAME, - TypeDesc.STRING, null); - - if (mi == null) { - return; - } - - if (mGenMode == GEN_WRAPPED) { - callWrappedStorable(mi); - return; - } - - CodeBuilder b = new CodeBuilder(mi); - b.newObject(stringBuilder); - b.dup(); - b.invokeConstructor(stringBuilder, null); - b.loadConstant(mStorableType.getName()); - invokeAppend(b, stringParam); - - String detail; - if (keyOnly) { - detail = " (key only) {"; - } else { - detail = " {"; - } - - b.loadConstant(detail); - invokeAppend(b, stringParam); - - // First pass, just print primary keys. - int ordinal = 0; - for (StorableProperty property : mAllProperties.values()) { - if (property.isPrimaryKeyMember()) { - Label skipPrint = b.createLabel(); - - // Check if independent property is supported, and skip if not. - if (property.isIndependent()) { - addSkipIndependent(b, null, property, skipPrint); - } - - if (ordinal++ > 0) { - b.loadConstant(", "); - invokeAppend(b, stringParam); - } - addPropertyAppendCall(b, property, stringParam, charParam); - - skipPrint.setLocation(); - } - } - - // Second pass, print non-primary keys. - if (!keyOnly) { - for (StorableProperty property : mAllProperties.values()) { - // Don't print join properties if they may throw an exception. - if (!property.isPrimaryKeyMember() && (!property.isJoin())) { - Label skipPrint = b.createLabel(); - - // Check if independent property is supported, and skip if not. - if (property.isIndependent()) { - addSkipIndependent(b, null, property, skipPrint); - } - - b.loadConstant(", "); - invokeAppend(b, stringParam); - addPropertyAppendCall(b, property, stringParam, charParam); - - skipPrint.setLocation(); - } - } - } - - b.loadConstant('}'); - invokeAppend(b, charParam); - - // For key string, also show all the alternate keys. This makes the - // FetchNoneException message more helpful. - if (keyOnly) { - int altKeyCount = mInfo.getAlternateKeyCount(); - for (int i=0; i key = mInfo.getAlternateKey(i); - - ordinal = 0; - for (OrderedProperty op : key.getProperties()) { - StorableProperty property = op.getChainedProperty().getPrimeProperty(); - - Label skipPrint = b.createLabel(); - - // Check if independent property is supported, and skip if not. - if (property.isIndependent()) { - addSkipIndependent(b, null, property, skipPrint); - } - - if (ordinal++ > 0) { - b.loadConstant(", "); - invokeAppend(b, stringParam); - } - addPropertyAppendCall(b, property, stringParam, charParam); - - skipPrint.setLocation(); - } - - b.loadConstant('}'); - invokeAppend(b, charParam); - } - } - - b.invokeVirtual(stringBuilder, TO_STRING_METHOD_NAME, TypeDesc.STRING, null); - b.returnValue(TypeDesc.STRING); - } - - private void invokeAppend(CodeBuilder b, TypeDesc[] paramType) { - TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class); - b.invokeVirtual(stringBuilder, "append", stringBuilder, paramType); - } - - private void addPropertyAppendCall(CodeBuilder b, - StorableProperty property, - TypeDesc[] stringParam, - TypeDesc[] charParam) - { - b.loadConstant(property.getName()); - invokeAppend(b, stringParam); - b.loadConstant('='); - invokeAppend(b, charParam); - b.loadThis(); - TypeDesc type = TypeDesc.forClass(property.getType()); - b.loadField(property.getName(), type); - if (type.isPrimitive()) { - if (type == TypeDesc.BYTE || type == TypeDesc.SHORT) { - type = TypeDesc.INT; - } - } else { - if (type != TypeDesc.STRING) { - if (type.isArray()) { - if (!type.getComponentType().isPrimitive()) { - b.invokeStatic("java.util.Arrays", "deepToString", - TypeDesc.STRING, - new TypeDesc[] {TypeDesc.OBJECT.toArrayType()}); - } else { - b.invokeStatic("java.util.Arrays", TO_STRING_METHOD_NAME, - TypeDesc.STRING, new TypeDesc[] {type}); - } - } - type = TypeDesc.OBJECT; - } - } - invokeAppend(b, new TypeDesc[]{type}); - } - - /** - * Generates code to get a trigger, forcing a transaction if trigger is not - * null. Also, if there is a trigger, the "before" method is called. - * - * @param opType type of operation, Insert, Update, or Delete - * @param forTryVar optional boolean variable for selecting whether to call - * "before" or "beforeTry" method - * @param forTry used if forTryVar is null - * @param triggerVar required variable of type Trigger for storing trigger - * @param txnVar required variable of type Transaction for storing transaction - * @param stateVar variable of type Object for storing state - * @return try start label for transaction - */ - private Label addGetTriggerAndEnterTxn(CodeBuilder b, - String opType, - LocalVariable forTryVar, - boolean forTry, - LocalVariable triggerVar, - LocalVariable txnVar, - LocalVariable stateVar) - { - // trigger = support$.getXxxTrigger(); - b.loadThis(); - b.loadField(SUPPORT_FIELD_NAME, mSupportType); - Method m = lookupMethod(mSupportType.toClass(), "get" + opType + "Trigger", null); - b.invoke(m); - b.storeLocal(triggerVar); - // state = null; - b.loadNull(); - b.storeLocal(stateVar); - - // if (trigger == null) { - // txn = null; - // } else { - // txn = support.getRootRepository().enterTransaction(); - // tryStart: - // if (forTry) { - // state = trigger.beforeTryXxx(this); - // } else { - // state = trigger.beforeXxx(this); - // } - // } - b.loadLocal(triggerVar); - Label hasTrigger = b.createLabel(); - b.ifNullBranch(hasTrigger, false); - - // txn = null - b.loadNull(); - b.storeLocal(txnVar); - Label cont = b.createLabel(); - b.branch(cont); - - hasTrigger.setLocation(); - - // txn = support.getRootRepository().enterTransaction(); - TypeDesc repositoryType = TypeDesc.forClass(Repository.class); - TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - b.loadThis(); - b.loadField(SUPPORT_FIELD_NAME, mSupportType); - b.invokeInterface(mSupportType, "getRootRepository", repositoryType, null); - b.invokeInterface(repositoryType, ENTER_TRANSACTION_METHOD_NAME, transactionType, null); - b.storeLocal(txnVar); - - Label tryStart = b.createLabel().setLocation(); - - // if (forTry) { - // state = trigger.beforeTryXxx(this); - // } else { - // state = trigger.beforeXxx(this); - // } - b.loadLocal(triggerVar); - b.loadThis(); - - if (forTryVar == null) { - if (forTry) { - b.invokeVirtual(triggerVar.getType(), "beforeTry" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); - } else { - b.invokeVirtual(triggerVar.getType(), "before" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); - } - b.storeLocal(stateVar); - } else { - b.loadLocal(forTryVar); - Label isForTry = b.createLabel(); - - b.ifZeroComparisonBranch(isForTry, "!="); - b.invokeVirtual(triggerVar.getType(), "before" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); - b.storeLocal(stateVar); - b.branch(cont); - - isForTry.setLocation(); - b.invokeVirtual(triggerVar.getType(), "beforeTry" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); - b.storeLocal(stateVar); - } - - cont.setLocation(); - - return tryStart; - } - - /** - * Generates code to call a trigger after the persistence operation has - * been invoked. - * - * @param opType type of operation, Insert, Update, or Delete - * @param forTryVar optional boolean variable for selecting whether to call - * "after" or "afterTry" method - * @param forTry used if forTryVar is null - * @param triggerVar required variable of type Trigger for retrieving trigger - * @param txnVar required variable of type Transaction for storing transaction - * @param stateVar required variable of type Object for retrieving state - */ - private void addTriggerAfterAndExitTxn(CodeBuilder b, - String opType, - LocalVariable forTryVar, - boolean forTry, - LocalVariable triggerVar, - LocalVariable txnVar, - LocalVariable stateVar) - { - // if (trigger != null) { - b.loadLocal(triggerVar); - Label cont = b.createLabel(); - b.ifNullBranch(cont, true); - - // if (forTry) { - // trigger.afterTryXxx(this, state); - // } else { - // trigger.afterXxx(this, state); - // } - b.loadLocal(triggerVar); - b.loadThis(); - b.loadLocal(stateVar); - - if (forTryVar == null) { - if (forTry) { - b.invokeVirtual(TypeDesc.forClass(Trigger.class), "afterTry" + opType, null, - new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.OBJECT}); - } else { - b.invokeVirtual(TypeDesc.forClass(Trigger.class), "after" + opType, null, - new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.OBJECT}); - } - } else { - b.loadLocal(forTryVar); - Label isForTry = b.createLabel(); - - b.ifZeroComparisonBranch(isForTry, "!="); - b.invokeVirtual(TypeDesc.forClass(Trigger.class), "after" + opType, null, - new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.OBJECT}); - Label commitAndExit = b.createLabel(); - b.branch(commitAndExit); - - isForTry.setLocation(); - b.invokeVirtual(TypeDesc.forClass(Trigger.class), "afterTry" + opType, null, - new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.OBJECT}); - commitAndExit.setLocation(); - } - - // txn.commit(); - // txn.exit(); - TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - b.loadLocal(txnVar); - b.invokeInterface(transactionType, COMMIT_METHOD_NAME, null, null); - b.loadLocal(txnVar); - b.invokeInterface(transactionType, EXIT_METHOD_NAME, null, null); - - cont.setLocation(); - } - - /** - * Generates code to call a trigger after the persistence operation has - * failed. - * - * @param opType type of operation, Insert, Update, or Delete - * @param triggerVar required variable of type Trigger for retrieving trigger - * @param txnVar required variable of type Transaction for storing transaction - * @param stateVar required variable of type Object for retrieving state - */ - private void addTriggerFailedAndExitTxn(CodeBuilder b, - String opType, - LocalVariable triggerVar, - LocalVariable txnVar, - LocalVariable stateVar) - { - TypeDesc transactionType = TypeDesc.forClass(Transaction.class); - - // if (trigger != null) { - b.loadLocal(triggerVar); - Label isNull = b.createLabel(); - b.ifNullBranch(isNull, true); - - // try { - // trigger.failedXxx(this, state); - // } catch (Throwable e) { - // uncaught(e); - // } - Label tryStart = b.createLabel().setLocation(); - b.loadLocal(triggerVar); - b.loadThis(); - b.loadLocal(stateVar); - b.invokeVirtual(TypeDesc.forClass(Trigger.class), "failed" + opType, null, - new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.OBJECT}); - Label tryEnd = b.createLabel().setLocation(); - Label cont = b.createLabel(); - b.branch(cont); - b.exceptionHandler(tryStart, tryEnd, Throwable.class.getName()); - b.invokeStatic(UNCAUGHT_METHOD_NAME, null, - new TypeDesc[] {TypeDesc.forClass(Throwable.class)}); - cont.setLocation(); - - // txn.exit(); - b.loadLocal(txnVar); - b.invokeInterface(transactionType, EXIT_METHOD_NAME, null, null); - - isNull.setLocation(); - } - - /** - * Generates exception handler code to call a trigger after the persistence - * operation has failed. - * - * @param opType type of operation, Insert, Update, or Delete - * @param forTryVar optional boolean variable for selecting whether to - * throw or catch Trigger.Abort. - * @param forTry used if forTryVar is null - * @param triggerVar required variable of type Trigger for retrieving trigger - * @param txnVar required variable of type Transaction for storing transaction - * @param stateVar required variable of type Object for retrieving state - * @param tryStart start of exception handler around transaction - */ - private void addTriggerFailedAndExitTxn(CodeBuilder b, - String opType, - LocalVariable forTryVar, - boolean forTry, - LocalVariable triggerVar, - LocalVariable txnVar, - LocalVariable stateVar, - Label tryStart) - { - if (tryStart == null) { - addTriggerFailedAndExitTxn(b, opType, triggerVar, txnVar, stateVar); - return; - } - - // } catch (... e) { - // if (trigger != null) { - // try { - // trigger.failedXxx(this, state); - // } catch (Throwable e) { - // uncaught(e); - // } - // } - // txn.exit(); - // if (e instanceof Trigger.Abort) { - // if (forTryVar) { - // return false; - // } else { - // // Try to add some trace for context - // throw ((Trigger.Abort) e).withStackTrace(); - // } - // } - // if (e instanceof RepositoryException) { - // throw ((RepositoryException) e).toPersistException(); - // } - // throw e; - // } - - Label tryEnd = b.createLabel().setLocation(); - b.exceptionHandler(tryStart, tryEnd, null); - LocalVariable exceptionVar = b.createLocalVariable(null, TypeDesc.OBJECT); - b.storeLocal(exceptionVar); - - addTriggerFailedAndExitTxn(b, opType, triggerVar, txnVar, stateVar); - - b.loadLocal(exceptionVar); - TypeDesc abortException = TypeDesc.forClass(Trigger.Abort.class); - b.instanceOf(abortException); - Label nextCheck = b.createLabel(); - b.ifZeroComparisonBranch(nextCheck, "=="); - if (forTryVar == null) { - if (forTry) { - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - } else { - b.loadLocal(exceptionVar); - b.checkCast(abortException); - b.invokeVirtual(abortException, "withStackTrace", abortException, null); - b.throwObject(); - } - } else { - b.loadLocal(forTryVar); - Label isForTry = b.createLabel(); - b.ifZeroComparisonBranch(isForTry, "!="); - b.loadLocal(exceptionVar); - b.checkCast(abortException); - b.invokeVirtual(abortException, "withStackTrace", abortException, null); - b.throwObject(); - isForTry.setLocation(); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - } - - nextCheck.setLocation(); - b.loadLocal(exceptionVar); - TypeDesc repException = TypeDesc.forClass(RepositoryException.class); - b.instanceOf(repException); - Label throwAny = b.createLabel(); - b.ifZeroComparisonBranch(throwAny, "=="); - b.loadLocal(exceptionVar); - b.checkCast(repException); - b.invokeVirtual(repException, "toPersistException", - TypeDesc.forClass(PersistException.class), null); - b.throwObject(); - - throwAny.setLocation(); - b.loadLocal(exceptionVar); - b.throwObject(); - } - - /** - * Generates method which passes exception to uncaught exception handler. - */ - private void defineUncaughtExceptionHandler() { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PRIVATE.toStatic(true), UNCAUGHT_METHOD_NAME, null, - new TypeDesc[] {TypeDesc.forClass(Throwable.class)}); - CodeBuilder b = new CodeBuilder(mi); - - // Thread t = Thread.currentThread(); - // t.getUncaughtExceptionHandler().uncaughtException(t, e); - TypeDesc threadType = TypeDesc.forClass(Thread.class); - b.invokeStatic(Thread.class.getName(), "currentThread", threadType, null); - LocalVariable threadVar = b.createLocalVariable(null, threadType); - b.storeLocal(threadVar); - b.loadLocal(threadVar); - TypeDesc handlerType = TypeDesc.forClass(Thread.UncaughtExceptionHandler.class); - b.invokeVirtual(threadType, "getUncaughtExceptionHandler", handlerType, null); - b.loadLocal(threadVar); - b.loadLocal(b.getParameter(0)); - b.invokeInterface(handlerType, "uncaughtException", null, - new TypeDesc[] {threadType, TypeDesc.forClass(Throwable.class)}); - b.returnVoid(); - } - - /** - * @return MethodInfo for completing definition or null if superclass - * already implements method as final. - */ - private MethodInfo addMethodIfNotFinal(Modifiers modifiers, String name, - TypeDesc retType, TypeDesc[] params) - { - if (CodeBuilderUtil.isPublicMethodFinal(mStorableType, name, retType, params)) { - return null; - } - - return mClassFile.addMethod(modifiers, name, retType, params); - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/StorableSerializer.java b/src/main/java/com/amazon/carbonado/spi/StorableSerializer.java deleted file mode 100644 index 83621e9..0000000 --- a/src/main/java/com/amazon/carbonado/spi/StorableSerializer.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import java.lang.ref.Reference; -import java.lang.ref.SoftReference; - -import java.lang.reflect.UndeclaredThrowableException; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.cojen.classfile.ClassFile; -import org.cojen.classfile.CodeBuilder; -import org.cojen.classfile.LocalVariable; -import org.cojen.classfile.MethodInfo; -import org.cojen.classfile.Modifiers; -import org.cojen.classfile.TypeDesc; -import org.cojen.util.ClassInjector; -import org.cojen.util.WeakIdentityMap; - -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; -import com.amazon.carbonado.SupportException; - -import com.amazon.carbonado.info.StorableIntrospector; -import com.amazon.carbonado.info.StorableProperty; - -import static com.amazon.carbonado.spi.CommonMethodNames.*; - -import com.amazon.carbonado.raw.GenericEncodingStrategy; - -/** - * Support for general-purpose serialization of storables. - *

    - * TODO: This class is unable to determine state of properties, and so they are - * lost during serialization. Upon deserialization, all properties are assumed - * dirty. To fix this, serialization might need to be supported directly by - * Storables. When that happens, this class will be deprecated. - * - * @author Brian S O'Neill - */ -public abstract class StorableSerializer { - private static final String ENCODE_METHOD_NAME = "encode"; - private static final String DECODE_METHOD_NAME = "decode"; - private static final String WRITE_METHOD_NAME = "write"; - private static final String READ_METHOD_NAME = "read"; - - @SuppressWarnings("unchecked") - private static Map>> cCache = new WeakIdentityMap(); - - /** - * @param type type of storable to serialize - */ - @SuppressWarnings("unchecked") - public static StorableSerializer forType(Class type) - throws SupportException - { - synchronized (cCache) { - StorableSerializer serializer; - Reference> ref = cCache.get(type); - if (ref != null) { - serializer = (StorableSerializer) ref.get(); - if (serializer != null) { - return serializer; - } - } - serializer = generateSerializer(type); - cCache.put(type, new SoftReference>(serializer)); - return serializer; - } - } - - @SuppressWarnings("unchecked") - private static StorableSerializer generateSerializer(Class type) - throws SupportException - { - Class abstractClass = StorableGenerator.getAbstractClass(type); - - // Use abstract class ClassLoader in order to access adapter instances. - ClassInjector ci = ClassInjector.create - (type.getName(), abstractClass.getClassLoader()); - ClassFile cf = new ClassFile(ci.getClassName(), StorableSerializer.class); - cf.markSynthetic(); - cf.setSourceFile(StorableSerializer.class.getName()); - cf.setTarget("1.5"); - - cf.addDefaultConstructor(); - - Map> propertyMap = - StorableIntrospector.examine(type).getAllProperties(); - - StorableProperty[] properties; - { - // Exclude joins. - List> list = - new ArrayList>(propertyMap.size()); - for (StorableProperty property : propertyMap.values()) { - if (!property.isJoin()) { - list.add(property); - } - } - properties = new StorableProperty[list.size()]; - list.toArray(properties); - } - - GenericEncodingStrategy ges = new GenericEncodingStrategy(type, null); - - TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - TypeDesc storableType = TypeDesc.forClass(Storable.class); - TypeDesc userStorableType = TypeDesc.forClass(type); - TypeDesc storageType = TypeDesc.forClass(Storage.class); - - // Build method to encode storable into a byte array. - { - MethodInfo mi = cf.addMethod - (Modifiers.PRIVATE.toStatic(true), ENCODE_METHOD_NAME, byteArrayType, - new TypeDesc[] {userStorableType}); - CodeBuilder b = new CodeBuilder(mi); - LocalVariable encodedVar = - ges.buildDataEncoding(b, properties, b.getParameter(0), abstractClass, true, -1); - b.loadLocal(encodedVar); - b.returnValue(byteArrayType); - } - - // Build method to decode storable from a byte array. - { - MethodInfo mi = cf.addMethod - (Modifiers.PRIVATE.toStatic(true), DECODE_METHOD_NAME, userStorableType, - new TypeDesc[] {storageType, byteArrayType}); - CodeBuilder b = new CodeBuilder(mi); - LocalVariable instanceVar = b.createLocalVariable(null, userStorableType); - b.loadLocal(b.getParameter(0)); - b.invokeInterface(storageType, PREPARE_METHOD_NAME, - storableType, null); - b.checkCast(userStorableType); - b.storeLocal(instanceVar); - LocalVariable encodedVar = b.getParameter(1); - ges.buildDataDecoding - (b, properties, instanceVar, abstractClass, true, -1, null, encodedVar); - b.loadLocal(instanceVar); - b.returnValue(storableType); - } - - // Build write method for DataOutput. - { - TypeDesc dataOutputType = TypeDesc.forClass(DataOutput.class); - - MethodInfo mi = cf.addMethod(Modifiers.PUBLIC, WRITE_METHOD_NAME, null, - new TypeDesc[] {storableType, dataOutputType}); - - CodeBuilder b = new CodeBuilder(mi); - LocalVariable storableVar = b.getParameter(0); - LocalVariable doutVar = b.getParameter(1); - - b.loadLocal(storableVar); - b.checkCast(userStorableType); - b.invokeStatic(ENCODE_METHOD_NAME, byteArrayType, new TypeDesc[] {userStorableType}); - LocalVariable encodedVar = b.createLocalVariable(null, byteArrayType); - b.storeLocal(encodedVar); - - b.loadLocal(doutVar); - b.loadLocal(encodedVar); - b.arrayLength(); - b.invokeInterface(dataOutputType, "writeInt", null, new TypeDesc[] {TypeDesc.INT}); - - b.loadLocal(doutVar); - b.loadLocal(encodedVar); - b.invokeInterface(dataOutputType, "write", null, new TypeDesc[] {byteArrayType}); - b.returnVoid(); - } - - final TypeDesc storableSerializerType = TypeDesc.forClass(StorableSerializer.class); - - // Build write method for OutputStream. - { - TypeDesc outputStreamType = TypeDesc.forClass(OutputStream.class); - - MethodInfo mi = cf.addMethod(Modifiers.PUBLIC, WRITE_METHOD_NAME, null, - new TypeDesc[] {storableType, outputStreamType}); - - CodeBuilder b = new CodeBuilder(mi); - LocalVariable storableVar = b.getParameter(0); - LocalVariable outVar = b.getParameter(1); - - b.loadLocal(storableVar); - b.checkCast(userStorableType); - b.invokeStatic(ENCODE_METHOD_NAME, byteArrayType, new TypeDesc[] {userStorableType}); - LocalVariable encodedVar = b.createLocalVariable(null, byteArrayType); - b.storeLocal(encodedVar); - - b.loadLocal(outVar); - b.loadLocal(encodedVar); - b.arrayLength(); - b.invokeStatic(storableSerializerType, "writeInt", null, - new TypeDesc[] {outputStreamType, TypeDesc.INT}); - - b.loadLocal(outVar); - b.loadLocal(encodedVar); - b.invokeVirtual(outputStreamType, "write", null, new TypeDesc[] {byteArrayType}); - b.returnVoid(); - } - - // Build read method for DataInput. - { - TypeDesc dataInputType = TypeDesc.forClass(DataInput.class); - - MethodInfo mi = cf.addMethod(Modifiers.PUBLIC, READ_METHOD_NAME, storableType, - new TypeDesc[] {storageType, dataInputType}); - - CodeBuilder b = new CodeBuilder(mi); - LocalVariable storageVar = b.getParameter(0); - LocalVariable dinVar = b.getParameter(1); - - b.loadLocal(dinVar); - b.invokeInterface(dataInputType, "readInt", TypeDesc.INT, null); - b.newObject(byteArrayType); - LocalVariable byteArrayVar = b.createLocalVariable(null, byteArrayType); - b.storeLocal(byteArrayVar); - - b.loadLocal(dinVar); - b.loadLocal(byteArrayVar); - b.invokeInterface(dataInputType, "readFully", null, new TypeDesc[] {byteArrayType}); - - b.loadLocal(storageVar); - b.loadLocal(byteArrayVar); - b.invokeStatic(DECODE_METHOD_NAME, userStorableType, - new TypeDesc[] {storageType, byteArrayType}); - b.returnValue(storableType); - } - - // Build read method for InputStream. - { - TypeDesc inputStreamType = TypeDesc.forClass(InputStream.class); - - MethodInfo mi = cf.addMethod(Modifiers.PUBLIC, READ_METHOD_NAME, storableType, - new TypeDesc[] {storageType, inputStreamType}); - - CodeBuilder b = new CodeBuilder(mi); - LocalVariable storageVar = b.getParameter(0); - LocalVariable inVar = b.getParameter(1); - - b.loadLocal(inVar); - b.invokeStatic(storableSerializerType, "readInt", TypeDesc.INT, - new TypeDesc[] {inputStreamType}); - b.newObject(byteArrayType); - LocalVariable byteArrayVar = b.createLocalVariable(null, byteArrayType); - b.storeLocal(byteArrayVar); - - b.loadLocal(inVar); - b.loadLocal(byteArrayVar); - b.invokeStatic(storableSerializerType, "readFully", null, - new TypeDesc[] {inputStreamType, byteArrayType}); - - b.loadLocal(storageVar); - b.loadLocal(byteArrayVar); - b.invokeStatic(DECODE_METHOD_NAME, userStorableType, - new TypeDesc[] {storageType, byteArrayType}); - b.returnValue(storableType); - } - - Class clazz = (Class) ci.defineClass(cf); - - try { - return clazz.newInstance(); - } catch (InstantiationException e) { - throw new UndeclaredThrowableException(e); - } catch (IllegalAccessException e) { - throw new UndeclaredThrowableException(e); - } - } - - protected StorableSerializer() { - } - - public abstract void write(S storable, DataOutput out) throws IOException; - - public abstract void write(S storable, OutputStream out) throws IOException; - - public abstract S read(Storage storage, DataInput in) throws IOException, EOFException; - - public abstract S read(Storage storage, InputStream in) throws IOException, EOFException; - - public static void writeInt(OutputStream out, int v) throws IOException { - out.write((v >>> 24) & 0xff); - out.write((v >>> 16) & 0xff); - out.write((v >>> 8) & 0xff); - out.write(v & 0xff); - } - - public static int readInt(InputStream in) throws IOException { - int a = in.read(); - int b = in.read(); - int c = in.read(); - int d = in.read(); - if ((a | b | c | d) < 0) { - throw new EOFException(); - } - return (a << 24) | (b << 16) | (c << 8) | d; - } - - public static void readFully(InputStream in, byte[] b) throws IOException { - int length = b.length; - int n = 0; - while (n < length) { - int count = in.read(b, n, length - n); - if (count < 0) { - throw new EOFException(); - } - n += count; - } - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/StorableSupport.java b/src/main/java/com/amazon/carbonado/spi/StorableSupport.java deleted file mode 100644 index bf2609c..0000000 --- a/src/main/java/com/amazon/carbonado/spi/StorableSupport.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import com.amazon.carbonado.Repository; -import com.amazon.carbonado.Storable; - -/** - * - * - * @author Brian S O'Neill - */ -public interface StorableSupport { - /** - * Returns the root parent Repository that the Storable came from. - */ - Repository getRootRepository(); - - /** - * Returns true if the given property exists and is supported. - * - * @param propertyName name of property to check - */ - boolean isPropertySupported(String propertyName); -} diff --git a/src/main/java/com/amazon/carbonado/spi/TriggerSupport.java b/src/main/java/com/amazon/carbonado/spi/TriggerSupport.java deleted file mode 100644 index 0e40c38..0000000 --- a/src/main/java/com/amazon/carbonado/spi/TriggerSupport.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Trigger; - -/** - * - * - * @author Brian S O'Neill - */ -public interface TriggerSupport extends StorableSupport { - /** - * Returns a trigger which must be run for all insert operations. - * - * @return null if no trigger - */ - Trigger getInsertTrigger(); - - /** - * Returns a trigger which must be run for all update operations. - * - * @return null if no trigger - */ - Trigger getUpdateTrigger(); - - /** - * Returns a trigger which must be run for all delete operations. - * - * @return null if no trigger - */ - Trigger getDeleteTrigger(); -} diff --git a/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java b/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java index 71f6fbc..ea12223 100644 --- a/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java +++ b/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java @@ -31,6 +31,9 @@ import com.amazon.carbonado.TriggerFactory; import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.util.QuickConstructorGenerator; +import com.amazon.carbonado.gen.StorableGenerator; +import com.amazon.carbonado.gen.WrappedSupport; + /** * Abstract storage that wraps all returned Storables and Queries, including * those returned from joins. Property access methods (get and set) are diff --git a/src/main/java/com/amazon/carbonado/spi/WrappedSupport.java b/src/main/java/com/amazon/carbonado/spi/WrappedSupport.java deleted file mode 100644 index 24e2c02..0000000 --- a/src/main/java/com/amazon/carbonado/spi/WrappedSupport.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. 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. - */ - -package com.amazon.carbonado.spi; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.PersistException; -import com.amazon.carbonado.Storable; - -/** - * - * - * @author Brian S O'Neill - */ -public interface WrappedSupport extends TriggerSupport { - /** - * @see Storable#load - */ - void load() throws FetchException; - - /** - * @see Storable#tryLoad - */ - boolean tryLoad() throws FetchException; - - /** - * @see Storable#insert - */ - void insert() throws PersistException; - - /** - * @see Storable#tryInsert - */ - boolean tryInsert() throws PersistException; - - /** - * @see Storable#update - */ - void update() throws PersistException; - - /** - * @see Storable#tryUpdate - */ - boolean tryUpdate() throws PersistException; - - /** - * @see Storable#delete - */ - void delete() throws PersistException; - - /** - * @see Storable#tryDelete - */ - boolean tryDelete() throws PersistException; - - /** - * Return another Support instance for the given Storable. - */ - WrappedSupport createSupport(S storable); -} diff --git a/src/main/java/com/amazon/carbonado/spi/package-info.java b/src/main/java/com/amazon/carbonado/spi/package-info.java index 2a6abad..b50105b 100644 --- a/src/main/java/com/amazon/carbonado/spi/package-info.java +++ b/src/main/java/com/amazon/carbonado/spi/package-info.java @@ -17,8 +17,8 @@ */ /** - * Core Service Provider Interface for Carbonado. Repositories are free to use - * this package to aid in their implementation. User-level applications have no - * need to use this package. + * Service Provider Interface for Carbonado. Repositories are free to use this + * package to aid in their implementation. User-level applications have no need + * to use this package. */ package com.amazon.carbonado.spi; -- cgit v1.2.3