summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado/spi
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon/carbonado/spi')
-rw-r--r--src/main/java/com/amazon/carbonado/spi/CodeBuilderUtil.java574
-rw-r--r--src/main/java/com/amazon/carbonado/spi/CommonMethodNames.java87
-rw-r--r--src/main/java/com/amazon/carbonado/spi/ConversionComparator.java212
-rw-r--r--src/main/java/com/amazon/carbonado/spi/MasterFeature.java59
-rw-r--r--src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java835
-rw-r--r--src/main/java/com/amazon/carbonado/spi/MasterSupport.java39
-rw-r--r--src/main/java/com/amazon/carbonado/spi/StorableGenerator.java3671
-rw-r--r--src/main/java/com/amazon/carbonado/spi/StorableSerializer.java337
-rw-r--r--src/main/java/com/amazon/carbonado/spi/StorableSupport.java41
-rw-r--r--src/main/java/com/amazon/carbonado/spi/TriggerSupport.java50
-rw-r--r--src/main/java/com/amazon/carbonado/spi/WrappedStorage.java3
-rw-r--r--src/main/java/com/amazon/carbonado/spi/WrappedSupport.java75
-rw-r--r--src/main/java/com/amazon/carbonado/spi/package-info.java6
13 files changed, 6 insertions, 5983 deletions
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<foo>, all
- * classes and implemented interfaces for every superclass between foo (the leaf) and
- * Object (the base).
- * <P>A copy must be coercible into any of these types, and copy bridge methods must be
- * provided to do so.
- *
- * <P>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<Class> gatherAllBridgeTypes(Set<Class> 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<Class>(), 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<String, Method> gatherAllDeclaredMethods(Class clazz) {
- Map<String, Method> methods = new HashMap<String, Method>();
- gatherAllDeclaredMethods(methods, clazz);
- return methods;
- }
-
- private static void gatherAllDeclaredMethods(Map<String, Method> 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<params.length; i++) {
- paramClasses[i] = params[i].toClass();
- }
- }
- try {
- Method existing = clazz.getMethod(name, paramClasses);
- if (Modifier.isFinal(existing.getModifiers())) {
- if (TypeDesc.forClass(existing.getReturnType()) == retType) {
- // Method is already implemented and is final.
- return true;
- }
- }
- } catch (NoSuchMethodException e) {
- }
- }
-
- return false;
- }
-
- /**
- * Define a classfile appropriate for most Storables. Specifically:
- * <ul>
- * <li>implements Storable</li>
- * <li>implements Cloneable
- * <li>abstract if appropriate
- * <li>marked synthetic
- * <li>targetted for java version 1.5
- * </ul>
- * @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 <S extends Storable> ClassFile createStorableClassFile(
- ClassInjector ci, Class<S> 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.
- *
- * <P>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<Class> {
- 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 &lt;0 if "a" is nearest, 0 if both are equally good, &gt;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<S extends Storable> {
- // 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<Object, Class<? extends Storable>> 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:
- *
- * <pre>
- * public &lt;init&gt;(MasterSupport);
- * </pre>
- *
- * Subclasses must implement the following abstract protected methods,
- * whose exact names are defined by constants in this class:
- *
- * <pre>
- * // 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;
- * </pre>
- *
- * 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 <S extends Storable> Class<? extends S>
- getAbstractClass(Class<S> type, EnumSet<MasterFeature> features)
- throws SupportException, IllegalArgumentException
- {
- StorableInfo<S> info = StorableIntrospector.examine(type);
-
- anySequences:
- if (features.contains(MasterFeature.INSERT_SEQUENCES)) {
- for (StorableProperty<S> 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<? extends S> abstractClass = (Class<? extends S>) cCache.get(key);
- if (abstractClass != null) {
- return abstractClass;
- }
- abstractClass =
- new MasterStorableGenerator<S>(type, features).generateAndInjectClass();
- cCache.put(key, abstractClass);
- return abstractClass;
- }
- }
-
- private final EnumSet<MasterFeature> mFeatures;
- private final StorableInfo<S> mInfo;
- private final Map<String, ? extends StorableProperty<S>> mAllProperties;
-
- private final ClassInjector mClassInjector;
- private final ClassFile mClassFile;
-
- private MasterStorableGenerator(Class<S> storableType, EnumSet<MasterFeature> features) {
- mFeatures = features;
- mInfo = StorableIntrospector.examine(storableType);
- mAllProperties = mInfo.getAllProperties();
-
- final Class<? extends S> 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<? extends S> generateAndInjectClass() throws SupportException {
- generateClass();
- Class abstractClass = mClassInjector.defineClass(mClassFile);
- return (Class<? extends S>) 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<S> 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<Integer> stateAppendMethods = new HashSet<Integer>();
-
- // Parameters are: StringBuilder, count, mask, property name
- TypeDesc[] appendParams = {sbType, TypeDesc.INT, TypeDesc.INT, TypeDesc.STRING};
-
- for (StorableProperty<S> 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<MasterFeature> 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<MasterFeature> 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<S extends Storable> extends TriggerSupport<S> {
- /**
- * Returns a sequence value producer by name, or throw PersistException if not found.
- *
- * <p>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<S extends Storable> {
-
- // 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<Class, Reference<Class<? extends Storable>>> cAbstractCache;
- // Cache of generated wrapped classes.
- private static Map<Class, Reference<Class<? extends Storable>>> 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:
- *
- * <pre>
- * /**
- * * @param support Access to triggers
- * *&#047;
- * public &lt;init&gt;(TriggerSupport support);
- * </pre>
- *
- * <p>Subclasses must implement the following abstract protected methods,
- * whose exact names are defined by constants in this class:
- *
- * <pre>
- * // 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;
- * </pre>
- *
- * 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.
- *
- * <pre>
- * // 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;
- * </pre>
- *
- * 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <pre>
- * // 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();
- * </pre>
- *
- * 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 <S extends Storable> Class<? extends S> getAbstractClass(Class<S> type)
- throws IllegalArgumentException
- {
- synchronized (cAbstractCache) {
- Class<? extends S> abstractClass;
- Reference<Class<? extends Storable>> ref = cAbstractCache.get(type);
- if (ref != null) {
- abstractClass = (Class<? extends S>) ref.get();
- if (abstractClass != null) {
- return abstractClass;
- }
- }
- abstractClass = new StorableGenerator<S>(type, GEN_ABSTRACT).generateAndInjectClass();
- cAbstractCache.put(type, new SoftReference<Class<? extends Storable>>(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:
- *
- * <pre>
- * /**
- * * @param support Custom implementation for Storable CRUD operations
- * * @param storable Storable being wrapped
- * *&#047;
- * public &lt;init&gt;(WrappedSupport support, Storable storable);
- * </pre>
- *
- * <p>Instances of the wrapped Storable delegate to the WrappedSupport for
- * all CRUD operations:
- *
- * <ul>
- * <li>load and tryLoad
- * <li>insert and tryInsert
- * <li>update and tryUpdate
- * <li>delete and tryDelete
- * </ul>
- *
- * <p>Methods which delegate to wrapped Storable:
- *
- * <ul>
- * <li>all ordinary user-defined properties
- * <li>copyAllProperties
- * <li>copyPrimaryKeyProperties
- * <li>copyVersionProperty
- * <li>copyUnequalProperties
- * <li>copyDirtyProperties
- * <li>hasDirtyProperties
- * <li>markPropertiesClean
- * <li>markAllPropertiesClean
- * <li>markPropertiesDirty
- * <li>markAllPropertiesDirty
- * <li>hashCode
- * <li>equalPrimaryKeys
- * <li>equalProperties
- * <li>toString
- * <li>toStringKeyOnly
- * </ul>
- *
- * <p>Methods with special implementation:
- *
- * <ul>
- * <li>all user-defined join properties (join properties query using wrapper's Storage)
- * <li>storage (returns Storage used by wrapper)
- * <li>storableType (returns literal class)
- * <li>copy (delegates to wrapped storable, but bridge methods must be defined as well)
- * <li>equals (compares Storage instance and properties)
- * </ul>
- *
- * @throws com.amazon.carbonado.MalformedTypeException if Storable type is not well-formed
- * @throws IllegalArgumentException if type is null
- */
- @SuppressWarnings("unchecked")
- public static <S extends Storable> Class<? extends S> getWrappedClass(Class<S> type)
- throws IllegalArgumentException
- {
- synchronized (cWrappedCache) {
- Class<? extends S> wrappedClass;
- Reference<Class<? extends Storable>> ref = cWrappedCache.get(type);
- if (ref != null) {
- wrappedClass = (Class<? extends S>) ref.get();
- if (wrappedClass != null) {
- return wrappedClass;
- }
- }
- wrappedClass = new StorableGenerator<S>(type, GEN_WRAPPED).generateAndInjectClass();
- cWrappedCache.put(type, new SoftReference<Class<? extends Storable>>(wrappedClass));
- return wrappedClass;
- }
- }
-
- private final Class<S> mStorableType;
- private final int mGenMode;
- private final TypeDesc mSupportType;
- private final StorableInfo<S> mInfo;
- private final Map<String, ? extends StorableProperty<S>> mAllProperties;
- private final boolean mHasJoins;
-
- private final ClassInjector mClassInjector;
- private final ClassFile mClassFile;
-
- private StorableGenerator(Class<S> 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<? extends S> generateAndInjectClass() {
- generateClass();
- Class abstractClass = mClassInjector.defineClass(mClassFile);
- return (Class<? extends S>) 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 <user storable> 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<count; i++) {
- StorablePropertyConstraint spc = property.getConstraint(i);
- String fieldName = property.getName() + CONSTRAINT_FIELD_ELEMENT + i;
- TypeDesc constraintType = TypeDesc.forClass
- (spc.getConstraintConstructor().getDeclaringClass());
- mClassFile.addField(fieldModifiers, fieldName, constraintType);
-
- if (clinit == null) {
- clinit = new CodeBuilder(mClassFile.addInitializer());
- }
-
- // Assign value to new field.
- // admin$constraint$0 = new LengthConstraint.Constraint
- // (UserInfo.class, "firstName", annotation);
-
- clinit.newObject(constraintType);
- clinit.dup();
- clinit.loadConstant(TypeDesc.forClass(mStorableType));
- clinit.loadConstant(property.getName());
-
- // Generate code to load property annotation third parameter.
- loadPropertyAnnotation(clinit, property, spc.getAnnotation());
-
- clinit.invoke(spc.getConstraintConstructor());
- clinit.storeStaticField(fieldName, constraintType);
- }
- }
-
- if (clinit != null) {
- // Must return else verifier complains.
- clinit.returnVoid();
- }
- }
-
- // Add property fields and methods.
- // Also remember ordinal of optional version property for use later.
- int versionOrdinal = -1;
- {
- int ordinal = -1;
- int maxOrdinal = mAllProperties.size() - 1;
- boolean requireStateField = false;
-
- for (StorableProperty<S> 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<count; i++) {
- StorableProperty internal = property.getInternalJoinElement(i);
- StorableProperty external = property.getExternalJoinElement(i);
- if (internal.isNullable() && !external.isNullable()) {
- break nullPossible;
- }
- }
- break buildShortCircuit;
- }
-
- for (int i=0; i<count; i++) {
- StorableProperty internal = property.getInternalJoinElement(i);
- StorableProperty external = property.getExternalJoinElement(i);
- if (internal.isNullable() && !external.isNullable()) {
- 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());
- }
-
- Label notNull = b.createLabel();
- b.ifNullBranch(notNull, false);
- b.loadNull();
- b.storeLocal(join);
- b.branch(shortCircuit);
- notNull.setLocation();
- }
- }
- }
-
- // Get the storage for the join type.
- loadStorageForFetch(b, TypeDesc.forClass(property.getJoinedType()));
- TypeDesc storageType = TypeDesc.forClass(Storage.class);
-
- // There are two ways that property can be loaded. The
- // general form is to use a Query. Calling load on the
- // property itself is preferred, but it is only
- // possible if the join is against a key and all
- // external properties have a write method.
-
- boolean canUseDirectForm = !property.isQuery();
-
- if (canUseDirectForm) {
- int joinCount = property.getJoinElementCount();
- for (int i=0; i<joinCount; i++) {
- StorableProperty external = property.getExternalJoinElement(i);
- if (external.getWriteMethod() == null) {
- canUseDirectForm = false;
- }
- }
- }
-
- final TypeDesc storableDesc = TypeDesc.forClass(Storable.class);
-
- if (canUseDirectForm) {
- // Generate direct load form.
-
- // Storage instance is already on the stack... replace it
- // with an instance of the joined type.
- b.invokeInterface
- (storageType, PREPARE_METHOD_NAME, storableDesc, null);
- b.checkCast(type);
- b.storeLocal(join);
-
- // Set the keys on the joined type.
- int count = property.getJoinElementCount();
- for (int i=0; i<count; i++) {
- b.loadLocal(join);
- StorableProperty internal = property.getInternalJoinElement(i);
- StorableProperty external = property.getExternalJoinElement(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());
- }
- CodeBuilderUtil.convertValue
- (b, internal.getType(), external.getType());
- b.invoke(external.getWriteMethod());
- }
-
- // Now load the object.
- b.loadLocal(join);
- if (!property.isNullable()) {
- b.invokeInterface(storableDesc, LOAD_METHOD_NAME, null, null);
- } else {
- b.invokeInterface
- (storableDesc, TRY_LOAD_METHOD_NAME, TypeDesc.BOOLEAN, null);
- Label wasLoaded = b.createLabel();
- b.ifZeroComparisonBranch(wasLoaded, "!=");
- // Not loaded, so replace joined object with null.
- b.loadNull();
- b.storeLocal(join);
- wasLoaded.setLocation();
- }
- } else {
- // Generate query load form.
-
- // Storage instance is already on the stack... replace it
- // with a Query. First, we need to define the query string.
-
- StringBuilder queryBuilder = new StringBuilder();
-
- // Set the keys on the joined type.
- int count = property.getJoinElementCount();
- for (int i=0; i<count; i++) {
- if (i > 0) {
- queryBuilder.append(" & ");
- }
- queryBuilder.append(property.getExternalJoinElement(i).getName());
- queryBuilder.append(" = ?");
- }
-
- b.loadConstant(queryBuilder.toString());
- TypeDesc queryType = TypeDesc.forClass(Query.class);
- b.invokeInterface(storageType, QUERY_METHOD_NAME, queryType,
- new TypeDesc[]{TypeDesc.STRING});
-
- // Now fill in the parameters of the query.
- for (int i=0; i<count; i++) {
- StorableProperty<S> 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<count; i++) {
- StorableProperty internal = property.getInternalJoinElement(i);
- StorableProperty external = property.getExternalJoinElement(i);
-
- b.loadLocal(b.getParameter(0));
- b.invoke(external.getReadMethod());
- CodeBuilderUtil.convertValue
- (b, external.getType(), internal.getType());
-
- LocalVariable newInternalPropVar =
- b.createLocalVariable(null, TypeDesc.forClass(internal.getType()));
- b.storeLocal(newInternalPropVar);
-
- // Since join properties may be pre-loaded, they
- // are set via the public write method. If internal
- // property is clean and equal to new value, then
- // don't set internal property. Doing so would mark
- // it as dirty, which is not the right behavior
- // when pre-loading join properties. The internal
- // properties should remain clean.
-
- Label setInternalProp = b.createLabel();
-
- if (mGenMode == GEN_ABSTRACT) {
- // Access state of internal property directly.
- int ord = findPropertyOrdinal(internal);
- b.loadThis();
- b.loadField(PROPERTY_STATE_FIELD_NAME + (ord >> 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<mInfo.getAlternateKeyCount(); i++) {
- b.loadThis();
- b.invokeVirtual(IS_ALT_KEY_INITIALIZED_PREFIX + i, TypeDesc.BOOLEAN, null);
- Label noAltKey = b.createLabel();
- b.ifZeroComparisonBranch(noAltKey, "==");
-
- StorableKey<S> altKey = mInfo.getAlternateKey(i);
-
- // Form query filter.
- StringBuilder queryBuilder = new StringBuilder();
- for (OrderedProperty<S> 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<S> op : altKey.getProperties()) {
- StorableProperty<S> 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<mInfo.getAlternateKeyCount(); i++) {
- Map<String, StorableProperty<S>> altProps =
- new HashMap<String, StorableProperty<S>>();
-
- StorableKey<S> altKey = mInfo.getAlternateKey(i);
-
- for (OrderedProperty<S> op : altKey.getProperties()) {
- ChainedProperty<S> cp = op.getChainedProperty();
- if (cp.getChainCount() > 0) {
- // This should not be possible.
- continue addAltKeyMethods;
- }
- StorableProperty<S> property = cp.getPrimeProperty();
- altProps.put(property.getName(), property);
- }
-
- addIsInitializedMethod(IS_ALT_KEY_INITIALIZED_PREFIX + i, altProps);
- }
-
- // Define protected isRequiredDataInitialized method.
- defineIsRequiredDataInitialized: {
- Map<String, StorableProperty<S>> requiredProperties =
- new HashMap<String, StorableProperty<S>>();
-
- 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<count; j++) {
- b.loadLocal(b.getParameter(j));
- }
-
- Method method = lookupMethod(mStorableType, mi);
- b.invoke(method);
- if (method.getReturnType() == void.class) {
- b.returnVoid();
- } else {
- b.returnValue(TypeDesc.forClass(method.getReturnType()));
- }
- }
-
- private static Method lookupMethod(Class type, MethodInfo mi) {
- MethodDesc desc = mi.getMethodDescriptor();
- TypeDesc[] params = desc.getParameterTypes();
- Class[] args;
-
- if (params == null || params.length == 0) {
- args = null;
- } else {
- args = new Class[params.length];
- for (int i=0; i<args.length; i++) {
- args[i] = params[i].toClass();
- }
- }
-
- return lookupMethod(type, mi.getName(), args);
- }
-
- private static Method lookupMethod(Class type, String name, Class[] args) {
- try {
- return type.getMethod(name, args);
- } catch (NoSuchMethodException e) {
- Error error = new NoSuchMethodError();
- error.initCause(e);
- throw error;
- }
- }
-
- /**
- * Generates a copy properties method with several options to control its
- * behavior. Although eight combinations can be defined, only four are
- * required by Storable interface. Uninitialized properties are never
- * copied.
- *
- * @param pkProperties when true, copy primary key properties
- * @param dataProperties when true, copy data properties
- * @param unequalOnly when true, only copy unequal properties
- * @param dirtyOnly when true, only copy dirty properties
- */
- private void addCopyPropertiesMethod
- (String methodName,
- boolean pkProperties,
- boolean versionProperty,
- boolean dataProperties,
- boolean unequalOnly,
- boolean dirtyOnly)
- {
- TypeDesc[] param = { TypeDesc.forClass(Storable.class) };
- TypeDesc storableTypeDesc = TypeDesc.forClass(mStorableType);
-
- MethodInfo mi= addMethodIfNotFinal
- (Modifiers.PUBLIC.toSynchronized(mGenMode == GEN_ABSTRACT),
- methodName,
- null,
- param);
-
- if (mi == null) {
- return;
- }
-
- if (mGenMode == GEN_WRAPPED) {
- callWrappedStorable(mi);
- return;
- }
-
- CodeBuilder b = new CodeBuilder(mi);
-
- LocalVariable target = CodeBuilderUtil.uneraseGenericParameter(b, storableTypeDesc, 0);
-
- LocalVariable stateBits = null;
- int ordinal = 0;
- int mask = PROPERTY_STATE_DIRTY;
-
- for (StorableProperty property : mAllProperties.values()) {
- // Decide if property should be part of the copy.
- boolean shouldCopy = !property.isJoin() &&
- (property.isPrimaryKeyMember() && pkProperties ||
- property.isVersion() && versionProperty ||
- !property.isPrimaryKeyMember() && dataProperties);
-
- if (shouldCopy) {
- if (stateBits == null) {
- // Load state bits into local for quick retrieval.
- stateBits = b.createLocalVariable(null, TypeDesc.INT);
- String stateFieldName =
- StorableGenerator.PROPERTY_STATE_FIELD_NAME + (ordinal >> 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<String, ? extends StorableProperty<S>> properties)
- {
- // Don't check Automatic properties.
- {
- boolean cloned = false;
- for (StorableProperty<S> prop : properties.values()) {
- if (prop.isAutomatic() || prop.isVersion()) {
- if (!cloned) {
- properties = new LinkedHashMap<String, StorableProperty<S>>(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<S> 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<caseCount; i++) {
- cases[i] = i;
- }
-
- Label[] switchLabels = new Label[caseCount];
- Label noMatch = b.createLabel();
- List<StorableProperty<?>>[] caseMatches = caseMatches(caseCount);
-
- for (int i=0; i<caseCount; i++) {
- List<?> 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<StorableProperty<?>, Integer> ordinalMap = new HashMap<StorableProperty<?>, 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<caseCount; i++) {
- List<StorableProperty<?>> matches = caseMatches[i];
- if (matches == null || matches.size() == 0) {
- continue;
- }
-
- switchLabels[i].setLocation();
-
- int matchCount = matches.size();
- for (int j=0; j<matchCount; j++) {
- StorableProperty<?> 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<StorableProperty<?>>[] caseMatches(int caseCount) {
- List<StorableProperty<?>>[] 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<StorableProperty<?>>();
- }
- 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<altKeyCount; i++) {
- b.loadConstant(", {");
- invokeAppend(b, stringParam);
-
- StorableKey<S> key = mInfo.getAlternateKey(i);
-
- ordinal = 0;
- for (OrderedProperty<S> op : key.getProperties()) {
- StorableProperty<S> 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.
- * <p>
- * 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<S extends Storable> {
- 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<Class, Reference<StorableSerializer<?>>> cCache = new WeakIdentityMap();
-
- /**
- * @param type type of storable to serialize
- */
- @SuppressWarnings("unchecked")
- public static <S extends Storable> StorableSerializer<S> forType(Class<S> type)
- throws SupportException
- {
- synchronized (cCache) {
- StorableSerializer<S> serializer;
- Reference<StorableSerializer<?>> ref = cCache.get(type);
- if (ref != null) {
- serializer = (StorableSerializer<S>) ref.get();
- if (serializer != null) {
- return serializer;
- }
- }
- serializer = generateSerializer(type);
- cCache.put(type, new SoftReference<StorableSerializer<?>>(serializer));
- return serializer;
- }
- }
-
- @SuppressWarnings("unchecked")
- private static <S extends Storable> StorableSerializer<S> generateSerializer(Class<S> type)
- throws SupportException
- {
- Class<? extends S> 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<String, ? extends StorableProperty<S>> propertyMap =
- StorableIntrospector.examine(type).getAllProperties();
-
- StorableProperty<S>[] properties;
- {
- // Exclude joins.
- List<StorableProperty<S>> list =
- new ArrayList<StorableProperty<S>>(propertyMap.size());
- for (StorableProperty<S> property : propertyMap.values()) {
- if (!property.isJoin()) {
- list.add(property);
- }
- }
- properties = new StorableProperty[list.size()];
- list.toArray(properties);
- }
-
- GenericEncodingStrategy<S> ges = new GenericEncodingStrategy<S>(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<StorableSerializer> clazz = (Class<StorableSerializer>) 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<S> storage, DataInput in) throws IOException, EOFException;
-
- public abstract S read(Storage<S> 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<S extends Storable> {
- /**
- * 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<S extends Storable> extends StorableSupport<S> {
- /**
- * Returns a trigger which must be run for all insert operations.
- *
- * @return null if no trigger
- */
- Trigger<? super S> getInsertTrigger();
-
- /**
- * Returns a trigger which must be run for all update operations.
- *
- * @return null if no trigger
- */
- Trigger<? super S> getUpdateTrigger();
-
- /**
- * Returns a trigger which must be run for all delete operations.
- *
- * @return null if no trigger
- */
- Trigger<? super S> 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<S extends Storable> extends TriggerSupport<S> {
- /**
- * @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<S> 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;