diff options
Diffstat (limited to 'src')
7 files changed, 170 insertions, 78 deletions
diff --git a/src/main/java/com/amazon/carbonado/Storable.java b/src/main/java/com/amazon/carbonado/Storable.java index 72ff1c7..b3372c3 100644 --- a/src/main/java/com/amazon/carbonado/Storable.java +++ b/src/main/java/com/amazon/carbonado/Storable.java @@ -351,7 +351,7 @@ public interface Storable<S extends Storable<S>> { * loaded or set.
*
* @param propertyName name of property to interrogate
- * @throws IllegalArgumentException if property is unknown or is a join
+ * @throws IllegalArgumentException if property is unknown, is a join or is derived
*/
boolean isPropertyUninitialized(String propertyName);
@@ -360,7 +360,7 @@ public interface Storable<S extends Storable<S>> { * load or store operation has been performed yet.
*
* @param propertyName name of property to interrogate
- * @throws IllegalArgumentException if property is unknown or is a join
+ * @throws IllegalArgumentException if property is unknown, is a join or is derived
*/
boolean isPropertyDirty(String propertyName);
@@ -369,7 +369,7 @@ public interface Storable<S extends Storable<S>> { * properties are clean after a successful load or store operation.
*
* @param propertyName name of property to interrogate
- * @throws IllegalArgumentException if property is unknown or is a join
+ * @throws IllegalArgumentException if property is unknown, is a join or is derived
*/
boolean isPropertyClean(String propertyName);
@@ -383,6 +383,34 @@ public interface Storable<S extends Storable<S>> { boolean isPropertySupported(String propertyName);
/**
+ * Returns a Storable property value by name.
+ *
+ * @param propertyName name of property to get value of
+ * @return property value, which is boxed if property type is primitive
+ * @throws IllegalArgumentException if property is unknown
+ * @throws UnsupportedOperationException if property is independent but unsupported
+ * @throws NullPointerException if property name is null
+ * @since 1.2
+ */
+ Object getPropertyValue(String propertyName);
+
+ /**
+ * Sets a Storable property value by name. Call insert or update to persist
+ * the change.
+ *
+ * @param propertyName name of property to set value to
+ * @param value new value for property
+ * @throws IllegalArgumentException if property is unknown or if value is
+ * unsupported due to a constraint
+ * @throws UnsupportedOperationException if property is independent but unsupported
+ * @throws ClassCastException if value is of wrong type
+ * @throws NullPointerException if property name is null or if primitive
+ * value is required but value is null
+ * @since 1.2
+ */
+ void setPropertyValue(String propertyName, Object value);
+
+ /**
* Returns an exact shallow copy of this object, including the state.
*/
S copy();
diff --git a/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java b/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java index d9db47c..f0262ea 100644 --- a/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java +++ b/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java @@ -52,6 +52,8 @@ public class CommonMethodNames { IS_PROPERTY_DIRTY = "isPropertyDirty",
IS_PROPERTY_CLEAN = "isPropertyClean",
IS_PROPERTY_SUPPORTED = "isPropertySupported",
+ GET_PROPERTY_VALUE = "getPropertyValue",
+ SET_PROPERTY_VALUE = "setPropertyValue",
TO_STRING_KEY_ONLY_METHOD_NAME = "toStringKeyOnly",
TO_STRING_METHOD_NAME = "toString",
HASHCODE_METHOD_NAME = "hashCode",
diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java index 5f473cb..28dbd9e 100644 --- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java @@ -189,6 +189,9 @@ public final class StorableGenerator<S extends Storable> { private static final String UPDATE_OP = "Update";
private static final String DELETE_OP = "Delete";
+ // Different uses for generated property switch statements.
+ private static final int SWITCH_FOR_STATE = 1, SWITCH_FOR_GET = 2, SWITCH_FOR_SET = 3;
+
/**
* Returns an abstract implementation of the given Storable type, which is
* fully thread-safe. The Storable type itself may be an interface or a
@@ -1863,6 +1866,10 @@ public final class StorableGenerator<S extends Storable> { b.returnValue(TypeDesc.BOOLEAN);
}
+ // Define reflection-like method for manipulating property by name.
+ addGetPropertyValueMethod();
+ addSetPropertyValueMethod();
+
// Define standard object methods.
addHashCodeMethod();
addEqualsMethod(EQUAL_FULL);
@@ -2733,8 +2740,11 @@ public final class StorableGenerator<S extends Storable> { MethodInfo mi = mClassFile.addMethod(Modifiers.PRIVATE, PROPERTY_STATE_EXTRACT_METHOD_NAME,
TypeDesc.INT, new TypeDesc[] {TypeDesc.STRING});
- CodeBuilder b = new CodeBuilder(mi);
+
+ addPropertySwitch(new CodeBuilder(mi), SWITCH_FOR_STATE);
+ }
+ private void addPropertySwitch(CodeBuilder b, int switchFor) {
// Generate big switch statement that operates on Strings. See also
// org.cojen.util.BeanPropertyAccessor, which also generates this kind of
// switch.
@@ -2795,6 +2805,8 @@ public final class StorableGenerator<S extends Storable> { Label derivedMatch = null;
Label joinMatch = null;
+ Label unreadable = null;
+ Label unwritable = null;
for (int i=0; i<caseCount; i++) {
List<StorableProperty<?>> matches = caseMatches[i];
@@ -2824,29 +2836,59 @@ public final class StorableGenerator<S extends Storable> { b.ifZeroComparisonBranch(notEqual, "==");
}
- if (prop.isDerived()) {
- if (derivedMatch == null) {
- derivedMatch = b.createLabel();
+ if (switchFor == SWITCH_FOR_STATE) {
+ if (prop.isDerived()) {
+ if (derivedMatch == null) {
+ derivedMatch = b.createLabel();
+ }
+ b.branch(derivedMatch);
+ } else 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);
}
- b.branch(derivedMatch);
- } else if (prop.isJoin()) {
- if (joinMatch == null) {
- joinMatch = b.createLabel();
+ } else if (switchFor == SWITCH_FOR_GET) {
+ if (prop.getReadMethod() == null) {
+ if (unreadable == null) {
+ unreadable = b.createLabel();
+ }
+ b.branch(unreadable);
+ } else {
+ b.loadThis();
+ b.invoke(prop.getReadMethod());
+ TypeDesc type = TypeDesc.forClass(prop.getType());
+ b.convert(type, type.toObjectType());
+ b.returnValue(TypeDesc.OBJECT);
}
- 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);
+ } else if (switchFor == SWITCH_FOR_SET) {
+ if (prop.getWriteMethod() == null) {
+ if (unwritable == null) {
+ unwritable = b.createLabel();
+ }
+ b.branch(unwritable);
+ } else {
+ b.loadThis();
+ b.loadLocal(b.getParameter(1));
+ TypeDesc type = TypeDesc.forClass(prop.getType());
+ b.checkCast(type.toObjectType());
+ b.convert(type.toObjectType(), type);
+ b.invoke(prop.getWriteMethod());
+ b.returnVoid();
}
- b.loadConstant(PROPERTY_STATE_MASK);
- b.math(Opcode.IAND);
- b.returnValue(TypeDesc.INT);
}
if (notEqual != null) {
@@ -2855,42 +2897,44 @@ public final class StorableGenerator<S extends Storable> { }
}
- 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();
+ throwIllegalArgException(b, "Unknown property: ", b.getParameter(0));
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();
+ throwIllegalArgException
+ (b, "Cannot get state for derived property: ", b.getParameter(0));
}
if (joinMatch != null) {
joinMatch.setLocation();
+ throwIllegalArgException(b, "Cannot get state for join property: ", b.getParameter(0));
+ }
- 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();
+ if (unreadable != null) {
+ unreadable.setLocation();
+ throwIllegalArgException(b, "Property cannot be read: ", b.getParameter(0));
}
+
+ if (unwritable != null) {
+ unwritable.setLocation();
+ throwIllegalArgException(b, "Property cannot be written: ", b.getParameter(0));
+ }
+ }
+
+ private static void throwIllegalArgException(CodeBuilder b, String message,
+ LocalVariable concatStr)
+ {
+ TypeDesc exceptionType = TypeDesc.forClass(IllegalArgumentException.class);
+ TypeDesc[] params = {TypeDesc.STRING};
+
+ b.newObject(exceptionType);
+ b.dup();
+ b.loadConstant(message);
+ b.loadLocal(concatStr);
+ b.invokeVirtual(TypeDesc.STRING, "concat", TypeDesc.STRING, params);
+ b.invokeConstructor(exceptionType, params);
+ b.throwObject();
}
/**
@@ -2955,6 +2999,42 @@ public final class StorableGenerator<S extends Storable> { b.returnValue(TypeDesc.BOOLEAN);
}
+ private void addGetPropertyValueMethod() {
+ MethodInfo mi = addMethodIfNotFinal(Modifiers.PUBLIC, GET_PROPERTY_VALUE,
+ TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.STRING});
+
+ if (mi == null) {
+ return;
+ }
+
+ CodeBuilder b = new CodeBuilder(mi);
+
+ if (mGenMode == GEN_WRAPPED) {
+ callWrappedStorable(mi, b);
+ return;
+ }
+
+ addPropertySwitch(b, SWITCH_FOR_GET);
+ }
+
+ private void addSetPropertyValueMethod() {
+ MethodInfo mi = addMethodIfNotFinal(Modifiers.PUBLIC, SET_PROPERTY_VALUE, null,
+ new TypeDesc[] {TypeDesc.STRING, TypeDesc.OBJECT});
+
+ if (mi == null) {
+ return;
+ }
+
+ CodeBuilder b = new CodeBuilder(mi);
+
+ if (mGenMode == GEN_WRAPPED) {
+ callWrappedStorable(mi, b);
+ return;
+ }
+
+ addPropertySwitch(b, SWITCH_FOR_SET);
+ }
+
/**
* Defines a hashCode method.
*/
diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/DependentStorableFetcher.java b/src/main/java/com/amazon/carbonado/repo/indexed/DependentStorableFetcher.java index ce37f5f..98454f1 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/DependentStorableFetcher.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/DependentStorableFetcher.java @@ -22,8 +22,6 @@ import java.util.Arrays; import java.util.ArrayList;
import java.util.List;
-import org.cojen.util.BeanPropertyAccessor;
-
import com.amazon.carbonado.Cursor;
import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.Query;
@@ -49,7 +47,6 @@ class DependentStorableFetcher<S extends Storable, D extends Storable> { private final IndexEntryAccessor<D>[] mIndexEntryAccessors;
private final Query<D> mQuery;
private final String[] mJoinProperties;
- private final BeanPropertyAccessor mPropertyAccessor;
/**
* @param derivedTo special chained property from StorableProperty.getDerivedToProperties
@@ -116,7 +113,6 @@ class DependentStorableFetcher<S extends Storable, D extends Storable> { mIndexEntryAccessors = accessorList.toArray(new IndexEntryAccessor[accessorList.size()]);
mQuery = repository.storageFor(dType).query(dFilter);
mJoinProperties = joinProperties;
- mPropertyAccessor = BeanPropertyAccessor.forClass(sType);
}
public Transaction enterTransaction() {
@@ -126,7 +122,7 @@ class DependentStorableFetcher<S extends Storable, D extends Storable> { public Cursor<D> fetchDependenentStorables(S storable) throws FetchException {
Query<D> query = mQuery;
for (String property : mJoinProperties) {
- query = query.with(mPropertyAccessor.getPropertyValue(storable, property));
+ query = query.with(storable.getPropertyValue(property));
}
return query.fetch();
}
diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexEntryAccessor.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexEntryAccessor.java index 5f27aa8..42ce214 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexEntryAccessor.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexEntryAccessor.java @@ -33,8 +33,7 @@ import com.amazon.carbonado.capability.IndexInfo; */
public interface IndexEntryAccessor<S extends Storable> extends IndexInfo {
/**
- * Returns the index entry storage. Index entry properties can only be
- * accessed via reflection.
+ * Returns the index entry storage.
*/
Storage<?> getIndexEntryStorage();
diff --git a/src/main/java/com/amazon/carbonado/spi/LobEngineTrigger.java b/src/main/java/com/amazon/carbonado/spi/LobEngineTrigger.java index 4b5ac5f..d278556 100644 --- a/src/main/java/com/amazon/carbonado/spi/LobEngineTrigger.java +++ b/src/main/java/com/amazon/carbonado/spi/LobEngineTrigger.java @@ -20,8 +20,6 @@ package com.amazon.carbonado.spi; import java.util.List;
-import org.cojen.util.BeanPropertyAccessor;
-
import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.PersistException;
import com.amazon.carbonado.Storable;
@@ -38,14 +36,12 @@ import com.amazon.carbonado.lob.Lob; class LobEngineTrigger<S extends Storable> extends Trigger<S> {
final LobEngine mEngine;
private final int mBlockSize;
- private final BeanPropertyAccessor mAccessor;
private final LobProperty<Lob>[] mLobProperties;
LobEngineTrigger(LobEngine engine, Class<S> type, int blockSize,
List<LobProperty<?>> lobProperties)
{
mEngine = engine;
- mAccessor = BeanPropertyAccessor.forClass(type);
mBlockSize = blockSize;
mLobProperties = new LobProperty[lobProperties.size()];
@@ -59,11 +55,11 @@ class LobEngineTrigger<S extends Storable> extends Trigger<S> { Object[] userLobs = new Object[length];
for (int i=0; i<length; i++) {
LobProperty<Lob> prop = mLobProperties[i];
- Object userLob = mAccessor.getPropertyValue(storable, prop.mName);
+ Object userLob = storable.getPropertyValue(prop.mName);
userLobs[i] = userLob;
if (userLob != null) {
Object lob = prop.createNewLob(mBlockSize);
- mAccessor.setPropertyValue(storable, prop.mName, lob);
+ storable.setPropertyValue(prop.mName, lob);
}
}
return userLobs;
@@ -79,7 +75,7 @@ class LobEngineTrigger<S extends Storable> extends Trigger<S> { Object userLob = userLobs[i];
if (userLob != null) {
LobProperty<Lob> prop = mLobProperties[i];
- Lob lob = (Lob) mAccessor.getPropertyValue(storable, prop.mName);
+ Lob lob = (Lob) storable.getPropertyValue(prop.mName);
prop.setLobValue(mEngine.getLocator(lob), (Lob) userLob);
}
}
@@ -112,9 +108,9 @@ class LobEngineTrigger<S extends Storable> extends Trigger<S> { throw e.toPersistException();
}
- Object userLob = mAccessor.getPropertyValue(storable, prop.mName);
+ Object userLob = storable.getPropertyValue(prop.mName);
userLobs[i] = userLob;
- Lob existingLob = (Lob) mAccessor.getPropertyValue(existing, prop.mName);
+ Lob existingLob = (Lob) existing.getPropertyValue(prop.mName);
if (userLob == null) {
if (existingLob != null) {
// User is setting existing lob to null, so delete it.
@@ -126,7 +122,7 @@ class LobEngineTrigger<S extends Storable> extends Trigger<S> { existingLob = prop.createNewLob(mBlockSize);
}
prop.setLobValue(mEngine.getLocator(existingLob), (Lob) userLob);
- mAccessor.setPropertyValue(storable, prop.mName, existingLob);
+ storable.setPropertyValue(prop.mName, existingLob);
}
}
@@ -154,7 +150,7 @@ class LobEngineTrigger<S extends Storable> extends Trigger<S> { if (existing != null) {
// After successful delete of master storable, delete all the lobs.
for (LobProperty<Lob> prop : mLobProperties) {
- Lob lob = (Lob) mAccessor.getPropertyValue(existing, prop.mName);
+ Lob lob = (Lob) ((S) existing).getPropertyValue(prop.mName);
mEngine.deleteLob(lob);
}
}
@@ -175,7 +171,7 @@ class LobEngineTrigger<S extends Storable> extends Trigger<S> { for (int i=0; i<length; i++) {
Object userLob = userLobs[i];
if (userLob != null) {
- mAccessor.setPropertyValue(storable, mLobProperties[i].mName, userLob);
+ storable.setPropertyValue(mLobProperties[i].mName, userLob);
}
}
}
diff --git a/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableBuilder.java b/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableBuilder.java index af44f45..dcbdeb7 100644 --- a/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableBuilder.java +++ b/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableBuilder.java @@ -29,7 +29,6 @@ import org.cojen.classfile.Modifiers; import org.cojen.classfile.TypeDesc;
import org.cojen.classfile.attribute.Annotation;
import org.cojen.util.ClassInjector;
-import org.cojen.util.BeanPropertyAccessor;
import com.amazon.carbonado.Index;
import com.amazon.carbonado.Indexes;
@@ -313,14 +312,6 @@ public class SyntheticStorableBuilder }
/**
- * Utility accessor to ease getting access to the properties of the
- * generated class
- */
- public BeanPropertyAccessor getAccessor() throws SupportException {
- return BeanPropertyAccessor.forClass(getStorableClass());
- }
-
- /**
* Decorate a classfile with the @PrimaryKey for this synthetic storable.
*
* @param cf ClassFile to decorate
|