summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian S. O'Neill <bronee@gmail.com>2007-06-10 04:22:34 +0000
committerBrian S. O'Neill <bronee@gmail.com>2007-06-10 04:22:34 +0000
commit7977fcd381e17c4e3bdfebfbf7ca4c0d90cdba1f (patch)
treea1b8e28aa59b01cfda331c6bc27809fb21296705
parente69fc3804d93419b8f44035bb8d7a35bf052246a (diff)
Added methods to access Storable properties by name.
-rw-r--r--RELEASE-NOTES.txt1
-rw-r--r--src/main/java/com/amazon/carbonado/Storable.java34
-rw-r--r--src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java2
-rw-r--r--src/main/java/com/amazon/carbonado/gen/StorableGenerator.java174
-rw-r--r--src/main/java/com/amazon/carbonado/repo/indexed/DependentStorableFetcher.java6
-rw-r--r--src/main/java/com/amazon/carbonado/repo/indexed/IndexEntryAccessor.java3
-rw-r--r--src/main/java/com/amazon/carbonado/spi/LobEngineTrigger.java20
-rw-r--r--src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableBuilder.java9
8 files changed, 171 insertions, 78 deletions
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 31630f1..cf2086d 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -23,6 +23,7 @@ Carbonado change history
- BDBRepository detects if changes are made to primary key and throws exception.
- Added support for derived properies.
- Enhanced query engine to optimize for covering indexes.
+- Added methods to access Storable properties by name.
1.1 to 1.1.1
-------------------------------
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