summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon/carbonado')
-rw-r--r--src/main/java/com/amazon/carbonado/Storable.java13
-rw-r--r--src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java1
-rw-r--r--src/main/java/com/amazon/carbonado/gen/StorableGenerator.java24
-rw-r--r--src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java326
4 files changed, 363 insertions, 1 deletions
diff --git a/src/main/java/com/amazon/carbonado/Storable.java b/src/main/java/com/amazon/carbonado/Storable.java
index 9f83dee..aa0fd4e 100644
--- a/src/main/java/com/amazon/carbonado/Storable.java
+++ b/src/main/java/com/amazon/carbonado/Storable.java
@@ -18,6 +18,8 @@
package com.amazon.carbonado;
+import java.util.Map;
+
/**
* A data access object in a {@link Repository}. User defined storables must
* either extend or implement this interface via an interface or abstract
@@ -413,6 +415,17 @@ public interface Storable<S extends Storable<S>> {
void setPropertyValue(String propertyName, Object value);
/**
+ * Returns a fixed-size map view of this Storable's properties. Properties
+ * which declare throwing any checked exceptions are excluded from the
+ * map. Removing and adding of map entries is unsupported.
+ *
+ * @return map of property name to property value; primitive property
+ * values are boxed
+ * @since 1.2
+ */
+ Map<String, Object> propertyMap();
+
+ /**
* 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 2ed5c8d..522edc6 100644
--- a/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java
+++ b/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java
@@ -54,6 +54,7 @@ public class CommonMethodNames {
IS_PROPERTY_SUPPORTED = "isPropertySupported",
GET_PROPERTY_VALUE = "getPropertyValue",
SET_PROPERTY_VALUE = "setPropertyValue",
+ PROPERTY_MAP = "propertyMap",
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 9db49ad..5c8c93d 100644
--- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
+++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
@@ -1618,9 +1618,10 @@ public final class StorableGenerator<S extends Storable> {
b.returnValue(TypeDesc.BOOLEAN);
}
- // Define reflection-like method for manipulating property by name.
+ // Define reflection-like methods for manipulating properties by name.
addGetPropertyValueMethod();
addSetPropertyValueMethod();
+ addPropertyMapMethod();
// Define standard object methods.
addHashCodeMethod();
@@ -2692,6 +2693,27 @@ public final class StorableGenerator<S extends Storable> {
addPropertySwitch(b, SWITCH_FOR_SET);
}
+ private void addPropertyMapMethod() {
+ TypeDesc mapType = TypeDesc.forClass(Map.class);
+
+ MethodInfo mi = addMethodIfNotFinal(Modifiers.PUBLIC, PROPERTY_MAP, mapType, null);
+
+ if (mi == null) {
+ return;
+ }
+
+ CodeBuilder b = new CodeBuilder(mi);
+
+ TypeDesc propertyMapType = TypeDesc.forClass(StorablePropertyMap.class);
+
+ b.loadConstant(TypeDesc.forClass(mStorableType));
+ b.loadThis();
+ b.invokeStatic(propertyMapType, "createMap", propertyMapType,
+ new TypeDesc[] {TypeDesc.forClass(Class.class),
+ TypeDesc.forClass(Storable.class)});
+ b.returnValue(mapType);
+ }
+
/**
* Defines a hashCode method.
*/
diff --git a/src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java b/src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java
new file mode 100644
index 0000000..cbf0d0b
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2008 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.gen;
+
+import java.lang.reflect.Method;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.cojen.util.SoftValuedHashMap;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.info.StorableInfo;
+import com.amazon.carbonado.info.StorableIntrospector;
+import com.amazon.carbonado.info.StorableProperty;
+
+/**
+ * Basic implementation for {@link Storable#propertyMap} method.
+ *
+ * @author Brian S O'Neill
+ */
+public class StorablePropertyMap<S extends Storable> extends AbstractMap<String, Object> {
+ private static final Map<Class, Set<String>> cPropertyNamesForType =
+ new SoftValuedHashMap();
+
+ public static <S extends Storable> StorablePropertyMap<S> createMap(Class<S> type, S storable)
+ {
+ Set<String> propertyNames;
+ synchronized (cPropertyNamesForType) {
+ propertyNames = cPropertyNamesForType.get(type);
+
+ if (propertyNames == null) {
+ Map<String, ? extends StorableProperty<S>> properties =
+ StorableIntrospector.examine(type).getAllProperties();
+
+ for (StorableProperty<S> property : properties.values()) {
+ if (shouldExclude(property)) {
+ if (propertyNames == null) {
+ propertyNames = new LinkedHashSet<String>(properties.keySet());
+ }
+ propertyNames.remove(property.getName());
+ continue;
+ }
+ }
+
+ if (propertyNames == null) {
+ propertyNames = properties.keySet();
+ } else {
+ propertyNames = Collections.unmodifiableSet(propertyNames);
+ }
+ }
+ }
+ return new StorablePropertyMap(propertyNames, storable);
+ }
+
+ private static boolean shouldExclude(StorableProperty<?> property) {
+ return throwsCheckedException(property.getReadMethod()) ||
+ throwsCheckedException(property.getWriteMethod());
+ }
+
+ private static boolean throwsCheckedException(Method method) {
+ if (method == null) {
+ return false;
+ }
+
+ Class<?>[] exceptionTypes = method.getExceptionTypes();
+ if (exceptionTypes == null) {
+ return false;
+ }
+
+ for (Class<?> exceptionType : exceptionTypes) {
+ if (RuntimeException.class.isAssignableFrom(exceptionType)) {
+ continue;
+ }
+ if (Error.class.isAssignableFrom(exceptionType)) {
+ continue;
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ private final Set<String> mPropertyNames;
+ private final S mStorable;
+
+ private StorablePropertyMap(Set<String> propertyNames, S storable) {
+ mPropertyNames = propertyNames;
+ mStorable = storable;
+ }
+
+ @Override
+ public int size() {
+ return mPropertyNames.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // Storables require at least a primary key.
+ return false;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return mPropertyNames.contains(key);
+ }
+
+ @Override
+ public Object get(Object key) {
+ try {
+ return mStorable.getPropertyValue((String) key);
+ } catch (IllegalArgumentException e) {
+ // Return null for unknown entries, as per Map specification.
+ return null;
+ }
+ }
+
+ @Override
+ public Object put(String key, Object value) {
+ Object old = mStorable.getPropertyValue(key);
+ mStorable.setPropertyValue(key, value);
+ return old;
+ }
+
+ @Override
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return mPropertyNames;
+ }
+
+ @Override
+ public Collection<Object> values() {
+ return new AbstractCollection<Object>() {
+ @Override
+ public Iterator<Object> iterator() {
+ return new Iterator<Object>() {
+ private final Iterator<String> mPropIterator = keySet().iterator();
+
+ public boolean hasNext() {
+ return mPropIterator.hasNext();
+ }
+
+ public Object next() {
+ return get(mPropIterator.next());
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return StorablePropertyMap.this.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // Storables require at least a primary key.
+ return false;
+ }
+
+ @Override
+ public boolean contains(Object v) {
+ return containsValue(v);
+ }
+
+ @Override
+ public boolean remove(Object e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public Set<Map.Entry<String, Object>> entrySet() {
+ return new AbstractSet<Map.Entry<String, Object>>() {
+ @Override
+ public Iterator<Map.Entry<String, Object>> iterator() {
+ return new Iterator<Map.Entry<String, Object>>() {
+ private final Iterator<String> mPropIterator = keySet().iterator();
+
+ public boolean hasNext() {
+ return mPropIterator.hasNext();
+ }
+
+ public Map.Entry<String, Object> next() {
+ final String property = mPropIterator.next();
+ final Object value = get(property);
+
+ return new Map.Entry<String, Object>() {
+ Object mutableValue = value;
+
+ public String getKey() {
+ return property;
+ }
+
+ public Object getValue() {
+ return mutableValue;
+ }
+
+ public Object setValue(Object value) {
+ Object old = StorablePropertyMap.this.put(property, value);
+ mutableValue = value;
+ return old;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof Map.Entry) {
+ Map.Entry other = (Map.Entry) obj;
+
+ return
+ (this.getKey() == null ?
+ other.getKey() == null
+ : this.getKey().equals(other.getKey()))
+ &&
+ (this.getValue() == null ?
+ other.getValue() == null
+ : this.getValue().equals(other.getValue()));
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (getKey() == null ? 0 : getKey().hashCode()) ^
+ (getValue() == null ? 0 : getValue().hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return property + "=" + mutableValue;
+ }
+ };
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return StorablePropertyMap.this.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // Storables require at least a primary key.
+ return false;
+ }
+
+ @Override
+ public boolean contains(Object e) {
+ Map.Entry<String, Object> entry = (Map.Entry<String, Object>) e;
+ String key = entry.getKey();
+ if (StorablePropertyMap.this.containsKey(key)) {
+ Object value = StorablePropertyMap.this.get(key);
+ return value == null ? entry.getValue() == null
+ : value.equals(entry.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ public boolean add(Map.Entry<String, Object> e) {
+ StorablePropertyMap.this.put(e.getKey(), e.getValue());
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}