summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado/gen
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon/carbonado/gen')
-rw-r--r--src/main/java/com/amazon/carbonado/gen/CodeBuilderUtil.java50
-rw-r--r--src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java178
-rw-r--r--src/main/java/com/amazon/carbonado/gen/StorableGenerator.java181
-rw-r--r--src/main/java/com/amazon/carbonado/gen/StorableSerializer.java4
4 files changed, 290 insertions, 123 deletions
diff --git a/src/main/java/com/amazon/carbonado/gen/CodeBuilderUtil.java b/src/main/java/com/amazon/carbonado/gen/CodeBuilderUtil.java
index c1f26a0..1b754fc 100644
--- a/src/main/java/com/amazon/carbonado/gen/CodeBuilderUtil.java
+++ b/src/main/java/com/amazon/carbonado/gen/CodeBuilderUtil.java
@@ -267,7 +267,9 @@ public class CodeBuilderUtil {
/**
* 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.
+ * provided Label if they are not equal. Both values must be of the same
+ * type. If they are floating point values, NaN is considered equal to NaN,
+ * which is inconsistent with the usual treatment for NaN.
*
* <P>The generated instruction consumes both values on the stack.
*
@@ -286,18 +288,25 @@ public class CodeBuilderUtil {
final boolean choice)
{
if (valueType.getTypeCode() != TypeDesc.OBJECT_CODE) {
- b.ifComparisonBranch(label, choice ? "==" : "!=", valueType);
+ if (valueType.getTypeCode() == TypeDesc.FLOAT_CODE) {
+ // Special treatment to handle NaN.
+ b.invokeStatic(TypeDesc.FLOAT.toObjectType(), "compare", TypeDesc.INT,
+ new TypeDesc[] {TypeDesc.FLOAT, TypeDesc.FLOAT});
+ b.ifZeroComparisonBranch(label, choice ? "==" : "!=");
+ } else if (valueType.getTypeCode() == TypeDesc.DOUBLE_CODE) {
+ // Special treatment to handle NaN.
+ b.invokeStatic(TypeDesc.DOUBLE.toObjectType(), "compare", TypeDesc.INT,
+ new TypeDesc[] {TypeDesc.DOUBLE, TypeDesc.DOUBLE});
+ b.ifZeroComparisonBranch(label, choice ? "==" : "!=");
+ } else {
+ 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);
+ String op = addEqualsCallTo(b, valueType, choice);
+ b.ifZeroComparisonBranch(label, op);
return;
}
@@ -318,14 +327,19 @@ public class CodeBuilderUtil {
isNotNull.setLocation();
b.loadLocal(value);
b.swap();
- addEqualsCallTo(b, valueType);
- b.ifZeroComparisonBranch(label, equalsBranchOp);
+ String op = addEqualsCallTo(b, valueType, choice);
+ b.ifZeroComparisonBranch(label, op);
cont.setLocation();
}
- public static void addEqualsCallTo(CodeBuilder b, TypeDesc fieldType) {
+ /**
+ * @param fieldType must be an object type
+ * @return zero comparison branch operator
+ */
+ private static String addEqualsCallTo(CodeBuilder b, TypeDesc fieldType, boolean choice) {
if (fieldType.isArray()) {
+ // FIXME: Array comparisons don't handle desired comparison of NaN.
if (!fieldType.getComponentType().isPrimitive()) {
TypeDesc type = TypeDesc.forClass(Object[].class);
b.invokeStatic("java.util.Arrays", "deepEquals",
@@ -334,6 +348,17 @@ public class CodeBuilderUtil {
b.invokeStatic("java.util.Arrays", "equals",
TypeDesc.BOOLEAN, new TypeDesc[] {fieldType, fieldType});
}
+ return choice ? "!=" : "==";
+ } else if (fieldType.toPrimitiveType() == TypeDesc.FLOAT) {
+ // Special treatment to handle NaN.
+ b.invokeVirtual(TypeDesc.FLOAT.toObjectType(), "compareTo", TypeDesc.INT,
+ new TypeDesc[] {TypeDesc.FLOAT.toObjectType()});
+ return choice ? "==" : "!=";
+ } else if (fieldType.toPrimitiveType() == TypeDesc.DOUBLE) {
+ // Special treatment to handle NaN.
+ b.invokeVirtual(TypeDesc.DOUBLE.toObjectType(), "compareTo", TypeDesc.INT,
+ new TypeDesc[] {TypeDesc.DOUBLE.toObjectType()});
+ return choice ? "==" : "!=";
} else {
TypeDesc[] params = {TypeDesc.OBJECT};
if (fieldType.toClass() != null) {
@@ -345,6 +370,7 @@ public class CodeBuilderUtil {
} else {
b.invokeVirtual(TypeDesc.OBJECT, "equals", TypeDesc.BOOLEAN, params);
}
+ return choice ? "!=" : "==";
}
}
diff --git a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java
index 17efb70..581a7d6 100644
--- a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java
+++ b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java
@@ -126,7 +126,7 @@ public final class MasterStorableGenerator<S extends Storable> {
anySequences:
if (features.contains(MasterFeature.INSERT_SEQUENCES)) {
for (StorableProperty<S> property : info.getAllProperties().values()) {
- if (property.getSequenceName() != null) {
+ if (!property.isDerived() && property.getSequenceName() != null) {
break anySequences;
}
}
@@ -266,7 +266,7 @@ public final class MasterStorableGenerator<S extends Storable> {
int ordinal = 0;
for (StorableProperty<S> property : mAllProperties.values()) {
- if (property.getSequenceName() != null) {
+ if (!property.isDerived() && property.getSequenceName() != null) {
// Check the state of this property, to see if it is
// uninitialized. Uninitialized state has value zero.
@@ -362,14 +362,16 @@ public final class MasterStorableGenerator<S extends Storable> {
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 (!mInfo.getVersionProperty().isDerived()) {
+ // 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)) {
@@ -410,7 +412,8 @@ public final class MasterStorableGenerator<S extends Storable> {
for (StorableProperty<S> property : mAllProperties.values()) {
ordinal++;
- if (property.isJoin() || property.isPrimaryKeyMember()
+ if (property.isDerived()
+ || property.isJoin() || property.isPrimaryKeyMember()
|| property.isNullable()
|| property.isAutomatic() || property.isVersion())
{
@@ -548,33 +551,109 @@ public final class MasterStorableGenerator<S extends Storable> {
b.ifZeroComparisonBranch(failed, "==");
// if (version support enabled) {
- // if (this.getVersionNumber() != saved.getVersionNumber()) {
- // throw new OptimisticLockException
- // (this.getVersionNumber(), saved.getVersionNumber(), this);
+ // if (!derived version) {
+ // if (this.getVersionNumber() != saved.getVersionNumber()) {
+ // throw new OptimisticLockException
+ // (this.getVersionNumber(), saved.getVersionNumber(), this);
+ // }
+ // } else {
+ // if (this.getVersionNumber() <= saved.getVersionNumber()) {
+ // throw new OptimisticLockException
+ // (saved.getVersionNumber(), this, this.getVersionNumber());
+ // }
// }
// }
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();
+ StorableProperty<S> versionProperty = mInfo.getVersionProperty();
+ TypeDesc versionType = TypeDesc.forClass(versionProperty.getType());
+
+ Label allowedVersion = b.createLabel();
+
+ if (!versionProperty.isDerived()) {
+ b.loadThis();
+ b.invoke(versionProperty.getReadMethod());
+ b.loadLocal(savedVar);
+ b.invoke(versionProperty.getReadMethod());
+ CodeBuilderUtil.addValuesEqualCall
+ (b, versionType, true, allowedVersion, true);
+
+ b.newObject(optimisticLockType);
+ b.dup();
+ b.loadThis();
+ b.invoke(versionProperty.getReadMethod());
+ b.convert(versionType, TypeDesc.OBJECT);
+ b.loadLocal(savedVar);
+ b.invoke(versionProperty.getReadMethod());
+ b.convert(versionType, TypeDesc.OBJECT);
+ b.loadThis();
+ b.invokeConstructor
+ (optimisticLockType,
+ new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.OBJECT, storableType});
+ b.throwObject();
+ } else {
+ b.loadThis();
+ b.invoke(versionProperty.getReadMethod());
+ LocalVariable newVersion = b.createLocalVariable(null, versionType);
+ b.storeLocal(newVersion);
+
+ b.loadLocal(savedVar);
+ b.invoke(versionProperty.getReadMethod());
+ LocalVariable savedVersion = b.createLocalVariable(null, versionType);
+ b.storeLocal(savedVersion);
+
+ // Skip check if new or saved version is null.
+ branchIfNull(b, newVersion, allowedVersion);
+ branchIfNull(b, savedVersion, allowedVersion);
+
+ TypeDesc primVersionType = versionType.toPrimitiveType();
+ if (primVersionType != null) {
+ if (versionType != primVersionType) {
+ b.loadLocal(newVersion);
+ b.convert(versionType, primVersionType);
+ newVersion = b.createLocalVariable(null, primVersionType);
+ b.storeLocal(newVersion);
+
+ b.loadLocal(savedVersion);
+ b.convert(versionType, primVersionType);
+ savedVersion = b.createLocalVariable(null, primVersionType);
+ b.storeLocal(savedVersion);
+ }
+
+ // Skip check if new or saved version is NaN.
+ branchIfNaN(b, newVersion, allowedVersion);
+ branchIfNaN(b, savedVersion, allowedVersion);
+
+ b.loadLocal(newVersion);
+ b.loadLocal(savedVersion);
+ b.ifComparisonBranch(allowedVersion, ">", primVersionType);
+ } else if (Comparable.class.isAssignableFrom(versionProperty.getType())) {
+ b.loadLocal(newVersion);
+ b.loadLocal(savedVersion);
+ b.invokeInterface(TypeDesc.forClass(Comparable.class), "compareTo",
+ TypeDesc.INT, new TypeDesc[] {TypeDesc.OBJECT});
+ b.ifZeroComparisonBranch(allowedVersion, ">");
+ } else {
+ throw new SupportException
+ ("Derived version property must be Comparable: " +
+ versionProperty);
+ }
+
+ b.newObject(optimisticLockType);
+ b.dup();
+ b.loadLocal(savedVar);
+ b.invoke(versionProperty.getReadMethod());
+ b.convert(versionType, TypeDesc.OBJECT);
+ b.loadThis();
+ b.loadThis();
+ b.invoke(versionProperty.getReadMethod());
+ b.convert(versionType, TypeDesc.OBJECT);
+ b.invokeConstructor
+ (optimisticLockType,
+ new TypeDesc[] {TypeDesc.OBJECT, storableType, TypeDesc.OBJECT});
+ b.throwObject();
+ }
+
+ allowedVersion.setLocation();
}
// this.copyDirtyProperties(saved);
@@ -585,7 +664,9 @@ public final class MasterStorableGenerator<S extends Storable> {
b.loadLocal(savedVar);
b.invokeVirtual(COPY_DIRTY_PROPERTIES, null, new TypeDesc[] {storableType});
if (mFeatures.contains(MasterFeature.VERSIONING)) {
- addAdjustVersionProperty(b, savedVar, -1);
+ if (!mInfo.getVersionProperty().isDerived()) {
+ addAdjustVersionProperty(b, savedVar, -1);
+ }
}
// if (!saved.doTryUpdateMaster()) {
@@ -661,6 +742,31 @@ public final class MasterStorableGenerator<S extends Storable> {
}
}
+ private void branchIfNull(CodeBuilder b, LocalVariable value, Label isNull) {
+ if (!value.getType().isPrimitive()) {
+ b.loadLocal(value);
+ b.ifNullBranch(isNull, true);
+ }
+ }
+
+ private void branchIfNaN(CodeBuilder b, LocalVariable value, Label isNaN) {
+ TypeDesc type = value.getType();
+ if (type == TypeDesc.FLOAT || type == TypeDesc.DOUBLE) {
+ b.loadLocal(value);
+ if (type == TypeDesc.FLOAT) {
+ b.invokeStatic(TypeDesc.FLOAT.toObjectType(),
+ "isNaN", TypeDesc.BOOLEAN,
+ new TypeDesc[] {TypeDesc.FLOAT});
+ b.ifZeroComparisonBranch(isNaN, "!=");
+ } else {
+ b.invokeStatic(TypeDesc.DOUBLE.toObjectType(),
+ "isNaN", TypeDesc.BOOLEAN,
+ new TypeDesc[] {TypeDesc.DOUBLE});
+ b.ifZeroComparisonBranch(isNaN, "!=");
+ }
+ }
+ }
+
/**
* Generates code to enter a transaction, if required.
*
diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
index 4934db3..a28f7e6 100644
--- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
+++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
@@ -193,7 +193,7 @@ public final class StorableGenerator<S extends Storable> {
* 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:
+ * abstract class is defined as follows:
*
* <pre>
* /**
@@ -569,27 +569,29 @@ public final class StorableGenerator<S extends Storable> {
for (StorableProperty<S> property : mAllProperties.values()) {
ordinal++;
- if (property.isVersion()) {
+ if (!property.isDerived() && 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;
+ if (!property.isDerived()) {
+ 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);
@@ -605,7 +607,7 @@ public final class StorableGenerator<S extends Storable> {
}
// Add read method.
- buildReadMethod: {
+ buildReadMethod: if (!property.isDerived()) {
Method readMethod = property.getReadMethod();
MethodInfo mi;
@@ -849,7 +851,7 @@ public final class StorableGenerator<S extends Storable> {
}
// Add write method.
- if (!property.isQuery()) {
+ buildWriteMethod: if (!property.isDerived() && !property.isQuery()) {
Method writeMethod = property.getWriteMethod();
MethodInfo mi;
@@ -1901,7 +1903,8 @@ public final class StorableGenerator<S extends Storable> {
new HashMap<String, StorableProperty<S>>();
for (StorableProperty property : mAllProperties.values()) {
- if (!property.isPrimaryKeyMember() &&
+ if (!property.isDerived() &&
+ !property.isPrimaryKeyMember() &&
!property.isJoin() &&
!property.isNullable()) {
@@ -2111,7 +2114,7 @@ public final class StorableGenerator<S extends Storable> {
for (StorableProperty property : mAllProperties.values()) {
// Decide if property should be part of the copy.
- boolean shouldCopy = !property.isJoin() &&
+ boolean shouldCopy = !property.isDerived() && !property.isJoin() &&
(property.isPrimaryKeyMember() && pkProperties ||
property.isVersion() && versionProperty ||
!property.isPrimaryKeyMember() && dataProperties);
@@ -2309,7 +2312,7 @@ public final class StorableGenerator<S extends Storable> {
int ordinal = 0;
int mask = 0;
for (StorableProperty property : mAllProperties.values()) {
- if (property != joinProperty && !property.isJoin()) {
+ if (property != joinProperty && !property.isDerived() && !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)) {
@@ -2343,19 +2346,21 @@ public final class StorableGenerator<S extends Storable> {
for (StorableProperty property : mAllProperties.values()) {
ordinal++;
- if (property.isJoin() || mGenMode == GEN_ABSTRACT) {
- requireStateField = true;
- }
+ if (!property.isDerived()) {
+ if (property.isJoin() || mGenMode == GEN_ABSTRACT) {
+ requireStateField = true;
+ }
- if (ordinal == maxOrdinal || ((ordinal & 0xf) == 0xf)) {
- if (requireStateField) {
- String stateFieldName = PROPERTY_STATE_FIELD_NAME + (ordinal >> 4);
+ 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);
+ b.loadThis();
+ b.loadConstant(0);
+ b.storeField(stateFieldName, TypeDesc.INT);
+ }
+ requireStateField = false;
}
- requireStateField = false;
}
}
}
@@ -2382,17 +2387,19 @@ public final class StorableGenerator<S extends Storable> {
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);
+ if (!property.isDerived()) {
+ 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);
+ }
}
}
@@ -2443,14 +2450,16 @@ public final class StorableGenerator<S extends Storable> {
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);
+ if (!property.isDerived()) {
+ 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);
}
- } else if (name == MARK_ALL_PROPERTIES_DIRTY) {
- // Force dirty state (3).
- orMask |= PROPERTY_STATE_DIRTY << ((ordinal & 0xf) * 2);
}
ordinal++;
@@ -2514,16 +2523,18 @@ public final class StorableGenerator<S extends Storable> {
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));
+ if (!property.isDerived()) {
+ 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));
+ }
}
}
}
@@ -2556,13 +2567,15 @@ public final class StorableGenerator<S extends Storable> {
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);
+ if (!property.isDerived()) {
+ 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) {
@@ -2623,8 +2636,10 @@ public final class StorableGenerator<S extends Storable> {
int ordinal = 0;
int mask = 0;
for (StorableProperty property : mAllProperties.values()) {
- if (properties.containsKey(property.getName())) {
- mask |= PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2);
+ if (!property.isDerived()) {
+ if (properties.containsKey(property.getName())) {
+ mask |= PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2);
+ }
}
ordinal++;
if (((ordinal & 0xf) == 0 || ordinal >= mAllProperties.size()) && mask != 0) {
@@ -2772,6 +2787,7 @@ public final class StorableGenerator<S extends Storable> {
// Params to invoke String.equals.
TypeDesc[] params = {TypeDesc.OBJECT};
+ Label derivedMatch = null;
Label joinMatch = null;
for (int i=0; i<caseCount; i++) {
@@ -2802,7 +2818,12 @@ public final class StorableGenerator<S extends Storable> {
b.ifZeroComparisonBranch(notEqual, "==");
}
- if (prop.isJoin()) {
+ if (prop.isDerived()) {
+ if (derivedMatch == null) {
+ derivedMatch = b.createLabel();
+ }
+ b.branch(derivedMatch);
+ } else if (prop.isJoin()) {
if (joinMatch == null) {
joinMatch = b.createLabel();
}
@@ -2841,6 +2862,18 @@ public final class StorableGenerator<S extends Storable> {
b.invokeConstructor(exceptionType, params);
b.throwObject();
+ if (derivedMatch != null) {
+ derivedMatch.setLocation();
+
+ b.newObject(exceptionType);
+ b.dup();
+ b.loadConstant("Cannot get state for derived 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();
@@ -2936,7 +2969,7 @@ public final class StorableGenerator<S extends Storable> {
boolean mixIn = false;
for (StorableProperty property : mAllProperties.values()) {
- if (property.isJoin()) {
+ if (property.isDerived() || property.isJoin()) {
continue;
}
addHashCodeCall(b, property.getName(),
@@ -3105,7 +3138,7 @@ public final class StorableGenerator<S extends Storable> {
b.storeLocal(other);
for (StorableProperty property : mAllProperties.values()) {
- if (property.isJoin()) {
+ if (property.isDerived() || property.isJoin()) {
continue;
}
// If we're only comparing keys, and this isn't a key, skip it
@@ -3211,8 +3244,10 @@ public final class StorableGenerator<S extends Storable> {
// 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())) {
+ // Don't print any derived or join properties since they may throw an exception.
+ if (!property.isPrimaryKeyMember() &&
+ (!property.isDerived()) && (!property.isJoin()))
+ {
Label skipPrint = b.createLabel();
// Check if independent property is supported, and skip if not.
diff --git a/src/main/java/com/amazon/carbonado/gen/StorableSerializer.java b/src/main/java/com/amazon/carbonado/gen/StorableSerializer.java
index 5115854..09eff96 100644
--- a/src/main/java/com/amazon/carbonado/gen/StorableSerializer.java
+++ b/src/main/java/com/amazon/carbonado/gen/StorableSerializer.java
@@ -116,11 +116,11 @@ public abstract class StorableSerializer<S extends Storable> {
StorableProperty<S>[] properties;
{
- // Exclude joins.
+ // Exclude derived properties and joins.
List<StorableProperty<S>> list =
new ArrayList<StorableProperty<S>>(propertyMap.size());
for (StorableProperty<S> property : propertyMap.values()) {
- if (!property.isJoin()) {
+ if (!property.isDerived() && !property.isJoin()) {
list.add(property);
}
}