, PersistedType> persistenceMapping;
@@ -89,6 +104,72 @@ public class DefaultPropertyTypeConverter implements PropertyTypeConverter
this.persistenceMapping.put(clazz, targetType);
}
+ /**
+ * Determines if the value can be adequately recreated (to equality) by creating
+ * a new instance. For example, a java.util.HashMap is constructable provided
+ * that the map is empty.
+ *
+ * Subclasses can override this to handle any well-known types, and in conjunction with
+ * {@link #constructInstance(String)}, even choose to return true if it needs a
+ * non-default constructor.
+ *
+ * @param value the value to check
+ * @return Returns true if the value can be reconstructed by
+ * instantiation using a default constructor
+ */
+ protected boolean isConstructable(Serializable value)
+ {
+ // Is it in the set directly
+ Class> valueClazz = value.getClass();
+ // Check for default constructor
+ try
+ {
+ valueClazz.getConstructor();
+ }
+ catch (NoSuchMethodException e)
+ {
+ // Can't reconstruct using just a type name
+ return false;
+ }
+ // Maps and Collections
+ if (value instanceof Map, ?>)
+ {
+ Map, ?> mapValue = (Map, ?>) value;
+ return mapValue.isEmpty();
+ }
+ else if (value instanceof Collection>)
+ {
+ Collection> collectionValue = (Collection>) value;
+ return collectionValue.isEmpty();
+ }
+ else
+ {
+ // We don't recognise it
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Serializable constructInstance(String clazzName)
+ {
+ try
+ {
+ Class> clazz = Class.forName(clazzName);
+ Constructor> constructor = clazz.getConstructor();
+ return (Serializable) constructor.newInstance();
+ }
+ catch (ClassCastException e)
+ {
+ throw new AlfrescoRuntimeException("The constructed property is not serializable: " + clazzName);
+ }
+ catch (Throwable e)
+ {
+ throw new AlfrescoRuntimeException("Unable to construct property for class: " + clazzName);
+ }
+ }
+
/**
* {@inheritDoc}
*/
@@ -98,20 +179,38 @@ public class DefaultPropertyTypeConverter implements PropertyTypeConverter
Class> clazz = value.getClass();
PersistedType type = persistenceMapping.get(clazz);
- if (type == null)
+ if (type != null)
{
- return PersistedType.SERIALIZABLE;
+ return type;
+ }
+ // Before we give up, check if it is constructable
+ if (isConstructable(value))
+ {
+ // It'll just be given back as a class name i.e. a CONSTRUCTABLE
+ return PersistedType.CONSTRUCTABLE;
}
else
{
- return type;
+ // Check if there are converters to and from well-known types, just in case
+ if (DefaultTypeConverter.INSTANCE.getConverter(clazz, Long.class) != null &&
+ DefaultTypeConverter.INSTANCE.getConverter(Long.class, clazz) != null)
+ {
+ return PersistedType.LONG;
+ }
+ else if (DefaultTypeConverter.INSTANCE.getConverter(clazz, String.class) != null &&
+ DefaultTypeConverter.INSTANCE.getConverter(String.class, clazz) != null)
+ {
+ return PersistedType.STRING;
+ }
+ // No hope of doing anything useful other than storing it
+ return PersistedType.SERIALIZABLE;
}
}
/**
* Performs the conversion
*/
- public T convert(Class targetClass, Object value)
+ public T convert(Class targetClass, Serializable value)
{
return DefaultTypeConverter.INSTANCE.convert(targetClass, value);
}
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java b/source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java
index cb7b76ed8a..d8c2dc517c 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyIdSearchRow.java
@@ -64,25 +64,30 @@ public class PropertyIdSearchRow
return valueEntity;
}
- public void setRootPropId(long rootPropId)
+ public void setRootPropId(Long rootPropId)
{
linkEntity.setRootPropId(rootPropId);
}
- public void setCurrentPropId(long currentPropId)
+ public void setPropIndex(Long propIndex)
{
- linkEntity.setCurrentPropId(currentPropId);
+ linkEntity.setPropIndex(propIndex);
}
- public void setValuePropId(long valuePropId)
+ public void setContainedIn(Long containedIn)
{
- linkEntity.setValuePropId(valuePropId);
+ linkEntity.setContainedIn(containedIn);
}
- public void setKeyPropId(long keyPropId)
+ public void setKeyPropId(Long keyPropId)
{
linkEntity.setKeyPropId(keyPropId);
}
+
+ public void setValuePropId(Long valuePropId)
+ {
+ linkEntity.setValuePropId(valuePropId);
+ }
public void setActualTypeId(Long actualTypeId)
{
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyLinkEntity.java b/source/java/org/alfresco/repo/domain/propval/PropertyLinkEntity.java
index 542655f009..e2b9020315 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyLinkEntity.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyLinkEntity.java
@@ -32,93 +32,77 @@ package org.alfresco.repo.domain.propval;
*/
public class PropertyLinkEntity
{
- private long rootPropId;
- private long currentPropId;
- private long valuePropId;
- private long keyPropId;
+ private Long rootPropId;
+ private Long propIndex;
+ private Long containedIn;
+ private Long keyPropId;
+ private Long valuePropId;
public PropertyLinkEntity()
{
}
- @Override
- public int hashCode()
- {
- return (int) rootPropId + (int) valuePropId;
- }
-
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- {
- return true;
- }
- else if (obj instanceof PropertyLinkEntity)
- {
- PropertyLinkEntity that = (PropertyLinkEntity) obj;
- return
- this.rootPropId == that.rootPropId &&
- this.currentPropId == that.currentPropId &&
- this.valuePropId == that.valuePropId &&
- this.keyPropId == that.keyPropId;
- }
- else
- {
- return false;
- }
- }
-
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(512);
sb.append("PropertyLinkEntity")
.append("[ rootPropId=").append(rootPropId)
- .append(", currentPropId=").append(currentPropId)
- .append(", valuePropId=").append(valuePropId)
+ .append(", propIndex=").append(propIndex)
+ .append(", containedIn=").append(containedIn)
.append(", keyPropId=").append(keyPropId)
+ .append(", valuePropId=").append(valuePropId)
.append("]");
return sb.toString();
}
- public long getRootPropId()
+ public Long getRootPropId()
{
return rootPropId;
}
- public void setRootPropId(long rootPropId)
+ public void setRootPropId(Long rootPropId)
{
this.rootPropId = rootPropId;
}
- public long getCurrentPropId()
+ public Long getPropIndex()
{
- return currentPropId;
+ return propIndex;
}
- public void setCurrentPropId(long currentPropId)
+ public void setPropIndex(Long propIndex)
{
- this.currentPropId = currentPropId;
+ this.propIndex = propIndex;
}
- public long getValuePropId()
+ public Long getContainedIn()
{
- return valuePropId;
+ return containedIn;
}
- public void setValuePropId(long valuePropId)
+ public void setContainedIn(Long containedIn)
{
- this.valuePropId = valuePropId;
+ this.containedIn = containedIn;
}
- public long getKeyPropId()
+ public Long getKeyPropId()
{
return keyPropId;
}
- public void setKeyPropId(long keyPropId)
+ public void setKeyPropId(Long keyPropId)
{
this.keyPropId = keyPropId;
}
+
+ public Long getValuePropId()
+ {
+ return valuePropId;
+ }
+
+ public void setValuePropId(Long valuePropId)
+ {
+ this.valuePropId = valuePropId;
+ }
}
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyRootEntity.java b/source/java/org/alfresco/repo/domain/propval/PropertyRootEntity.java
new file mode 100644
index 0000000000..489cec80d5
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyRootEntity.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2005-2009 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have recieved a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+package org.alfresco.repo.domain.propval;
+
+/**
+ * Entity bean for alf_prop_root table.
+ *
+ * @author Derek Hulley
+ * @since 3.2
+ */
+public class PropertyRootEntity
+{
+ private Long id;
+ private short version;
+
+ public PropertyRootEntity()
+ {
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(512);
+ sb.append("PropertyRootEntity")
+ .append("[ ID=").append(id)
+ .append(", version=").append(version)
+ .append("]");
+ return sb.toString();
+ }
+
+ public void incrementVersion()
+ {
+ if (version >= Short.MAX_VALUE)
+ {
+ this.version = 0;
+ }
+ else
+ {
+ this.version++;
+ }
+ }
+
+ public Long getId()
+ {
+ return id;
+ }
+
+ public void setId(Long id)
+ {
+ this.id = id;
+ }
+
+ public short getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(short version)
+ {
+ this.version = version;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyTypeConverter.java b/source/java/org/alfresco/repo/domain/propval/PropertyTypeConverter.java
index f6a151cc8a..0a5f436b1b 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyTypeConverter.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyTypeConverter.java
@@ -48,10 +48,14 @@ public interface PropertyTypeConverter
* {@link PersistedType#DOUBLE}
* {@link PersistedType#STRING}
* {@link PersistedType#SERIALIZABLE}
+ * {@link PersistedType#CONSTRUCTABLE}
*
* The converter should return {@link PersistedType#SERIALIZABLE} if no further conversions
* are possible. Implicit in the return value is the converter's ability to do the
* conversion when required.
+ *
+ * If the converter can fully reconstruct an equal instance using just the name of the value's
+ * class, then {@link PersistedType#CONSTRUCTABLE} can be used.
*
* @param value the value that does not have an obvious persistence slot
* @return Returns the type of persistence to use
@@ -64,5 +68,13 @@ public interface PropertyTypeConverter
* @param value the value to convert
* @return Returns the persisted type and value to persist
*/
- T convert(Class targetClass, Object value);
+ T convert(Class targetClass, Serializable value);
+
+ /**
+ * Construct an instance of an object that was deemed to be {@link PersistedType#CONSTRUCTABLE}.
+ *
+ * @param clazzName the name of the class
+ * @return Returns the new instance
+ */
+ Serializable constructInstance(String clazzName);
}
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
index 831b8f325e..80d06dff76 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
@@ -175,27 +175,41 @@ public interface PropertyValueDAO
* @param value the value to find the ID for (may be null)
*/
Pair getOrCreatePropertyValue(Serializable value);
+
+ //================================
+ // 'alf_prop_root' accessors
+ //================================
/**
- * alf_prop_value accessor: find or create a property based on the value.
- * Note: This method will not recurse into maps or collections. Use the
- * dedicated methods if you want recursion; otherwise maps and collections will
- * be serialized and probably stored as BLOB values.
- *
- * Max depth examples (there is no upper limit):
- *
- * - 0: don't expand the value if it's a map or collection
- * - 1: open the value up if it is a map or collection but don't do any more
- * - ...
- * - 10: open up 10 levels of maps or collections
- *
- * All collections that are not opened up will be serialized unless there is a
- * custom {@link PropertyTypeConverter converter} which can serialize it in an
- * alternative format.
+ * alf_prop_root accessor: get a property based on the database ID
*
- * @param value the value to find the ID for (may be null)
- * @param maxDepth the maximum depth of collections and maps to iterate into
+ * @param id the ID (may not be null)
+ * @return Returns the value of the property (never null)
*/
- Pair getOrCreatePropertyValue(Serializable value, int maxDepth);
+ Serializable getPropertyById(Long id);
+ /**
+ * alf_prop_root accessor: find or create a property based on the value.
+ *
+ * All collections and maps will be opened up to any depth.
+ *
+ * @param value the value to create (may be null)
+ * @return Returns the new property's ID
+ */
+ Long createProperty(Serializable value);
+
+ /**
+ * alf_prop_root accessor: update the property root to contain a new value.
+ *
+ * @param id the ID of the root property to change
+ * @param value the new property value
+ */
+ void updateProperty(Long id, Serializable value);
+
+ /**
+ * alf_prop_root accessor: delete a property root completely
+ *
+ * @param id the ID of the root property to delete
+ */
+ void deleteProperty(Long id);
/**
* Utility method to convert property query results into the original value. Note
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
index 2901b28deb..e122088c43 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
@@ -28,6 +28,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Random;
@@ -65,6 +66,7 @@ public class PropertyValueDAOTest extends TestCase
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
transactionService = serviceRegistry.getTransactionService();
txnHelper = transactionService.getRetryingTransactionHelper();
+ txnHelper.setMaxRetries(0);
propertyValueDAO = (PropertyValueDAO) ctx.getBean("propertyValueDAO");
@@ -434,6 +436,24 @@ public class PropertyValueDAOTest extends TestCase
}
}
+ public void testPropertyValue_EmptyHashMap() throws Exception
+ {
+ final HashMap map = new HashMap(15);
+ runPropertyValueTest(map, true);
+ }
+
+ public void testPropertyValue_EmptyArrayList() throws Exception
+ {
+ final ArrayList list = new ArrayList(20);
+ runPropertyValueTest(list, true);
+ }
+
+ public void testPropertyValue_EmptyHashSet() throws Exception
+ {
+ final HashSet set = new HashSet(20);
+ runPropertyValueTest(set, true);
+ }
+
public void testPropertyValue_MapOfStrings() throws Exception
{
final HashMap map = new HashMap(15);
@@ -446,7 +466,53 @@ public class PropertyValueDAOTest extends TestCase
runPropertyValueTest(map, false);
}
- public void testPropertyValue_MapOfMapOfSerializables() throws Exception
+ /**
+ * Tests that the given value can be persisted and retrieved with the same resulting ID
+ */
+ private Long runPropertyTest(final Serializable value) throws Exception
+ {
+ // Create it
+ RetryingTransactionCallback createValueCallback = new RetryingTransactionCallback()
+ {
+ public Long execute() throws Throwable
+ {
+ // Get the classes
+ return propertyValueDAO.createProperty(value);
+ }
+ };
+ final Long entityId = txnHelper.doInTransaction(createValueCallback, false);
+ assertNotNull(entityId);
+
+ // Retrieve it by ID
+ RetryingTransactionCallback getByIdCallback = new RetryingTransactionCallback()
+ {
+ public Serializable execute() throws Throwable
+ {
+ // Get the classes
+ return propertyValueDAO.getPropertyById(entityId);
+ }
+ };
+ final Serializable entityValueCheck = txnHelper.doInTransaction(getByIdCallback, false);
+ assertNotNull(entityValueCheck);
+ assertEquals(value, entityValueCheck);
+
+ // Done
+ return entityId;
+ }
+
+ public void testProperty_MapOfStrings() throws Exception
+ {
+ final HashMap map = new HashMap(15);
+ for (int i = 0; i < 20; i++)
+ {
+ String key = "MAP-KEY-" + i;
+ String value = "MAP-VALUE-" + i;
+ map.put(key, value);
+ }
+ runPropertyTest(map);
+ }
+
+ public void testProperty_MapOfMapOfSerializables() throws Exception
{
final HashMap mapInner = new HashMap(15);
for (int i = 0; i < 20; i++)
@@ -461,10 +527,10 @@ public class PropertyValueDAOTest extends TestCase
String key = "OUTERMAP-KEY-" + i;
mapOuter.put(key, mapInner);
}
- runPropertyValueTest(mapOuter, false);
+ runPropertyTest(mapOuter);
}
- public void testPropertyValue_MapOfMapOfStrings() throws Exception
+ public void testProperty_MapOfMapOfStrings() throws Exception
{
final HashMap mapInner = new HashMap(15);
for (int i = 0; i < 20; i++)
@@ -479,10 +545,10 @@ public class PropertyValueDAOTest extends TestCase
String key = "OUTERMAP-KEY-" + i;
mapOuter.put(key, mapInner);
}
- runPropertyValueTest(mapOuter, false);
+ runPropertyTest(mapOuter);
}
- public void testPropertyValue_CollectionOfStrings() throws Exception
+ public void testProperty_CollectionOfStrings() throws Exception
{
final ArrayList list = new ArrayList(20);
for (int i = 0; i < 20; i++)
@@ -490,9 +556,77 @@ public class PropertyValueDAOTest extends TestCase
String value = "COLL-VALUE-" + i;
list.add(value);
}
- runPropertyValueTest(list, false);
+ runPropertyTest(list);
}
+ public void testProperty_UpdateCollection() throws Exception
+ {
+ final ArrayList list = new ArrayList(20);
+ for (int i = 0; i < 20; i++)
+ {
+ String value = "COLL-VALUE-" + i;
+ list.add(value);
+ }
+ final Long propId = runPropertyTest(list);
+
+ // Now update it
+ list.add("Additional value");
+
+ RetryingTransactionCallback updateAndGetCallback = new RetryingTransactionCallback()
+ {
+ public Serializable execute() throws Throwable
+ {
+ // Get the classes
+ propertyValueDAO.updateProperty(propId, list);
+ // Get it by the ID again
+ return propertyValueDAO.getPropertyById(propId);
+ }
+ };
+ final Serializable entityValueCheck = txnHelper.doInTransaction(updateAndGetCallback, false);
+ assertNotNull(entityValueCheck);
+ assertEquals(list, entityValueCheck);
+ }
+
+ public void testProperty_Delete() throws Exception
+ {
+ final ArrayList list = new ArrayList(20);
+ final Long propId = runPropertyTest(list);
+
+ // Now delete it
+ RetryingTransactionCallback deleteCallback = new RetryingTransactionCallback()
+ {
+ public Serializable execute() throws Throwable
+ {
+ // Get the classes
+ propertyValueDAO.deleteProperty(propId);
+ return null;
+ }
+ };
+ txnHelper.doInTransaction(deleteCallback, false);
+
+ RetryingTransactionCallback failedGetCallback = new RetryingTransactionCallback()
+ {
+ public Serializable execute() throws Throwable
+ {
+ // Get it by the ID again
+ return propertyValueDAO.getPropertyById(propId);
+ }
+ };
+ try
+ {
+ final Serializable entityValueCheck = txnHelper.doInTransaction(failedGetCallback, false);
+ fail("Deleted property should not be gettable. Got: " + entityValueCheck);
+ }
+ catch(Throwable e)
+ {
+ // Expected
+ }
+ }
+
+ /*
+ * Switch off caches and rerun some of the tests
+ */
+
public void testPropertyClass_NoCache() throws Exception
{
removeCaches();
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java
index bf269a3d59..f5d5bbb0cc 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueEntity.java
@@ -25,9 +25,7 @@
package org.alfresco.repo.domain.propval;
import java.io.Serializable;
-import java.util.Collection;
import java.util.Collections;
-import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -171,11 +169,6 @@ public class PropertyValueEntity
*/
public static final Map persistedTypesByOrdinal;
- /**
- * An unmodifiable map of persisted type enums keyed by the classes they store
- */
- public static final Map, PersistedType> persistedTypesByClass;
-
static
{
// Create a pair for null values
@@ -187,20 +180,6 @@ public class PropertyValueEntity
mapOrdinal.put(persistedType.getOrdinalNumber(), persistedType);
}
persistedTypesByOrdinal = Collections.unmodifiableMap(mapOrdinal);
- // Create the map of class-type
- Map, PersistedType> mapClass = new HashMap, PersistedType>(29);
- mapClass.put(Boolean.class, PersistedType.LONG);
- mapClass.put(Short.class, PersistedType.LONG);
- mapClass.put(Integer.class, PersistedType.LONG);
- mapClass.put(Long.class, PersistedType.LONG);
- mapClass.put(Float.class, PersistedType.DOUBLE);
- mapClass.put(Double.class, PersistedType.DOUBLE);
- mapClass.put(String.class, PersistedType.STRING);
- mapClass.put(Date.class, PersistedType.LONG);
- mapClass.put(Map.class, PersistedType.SERIALIZABLE); // Will be serialized if encountered
- mapClass.put(Collection.class, PersistedType.SERIALIZABLE); // Will be serialized if encountered
- mapClass.put(Class.class, PersistedType.CONSTRUCTABLE); // Will construct a new instance
- persistedTypesByClass = Collections.unmodifiableMap(mapClass);
}
private static final Log logger = LogFactory.getLog(PropertyValueEntity.class);
@@ -259,40 +238,29 @@ public class PropertyValueEntity
}
/**
- * Gets the value based on the persisted type.
- * Note that this is the value as persisted and not the original, client-required
- * value.
- * @return Returns the persisted value
+ * Helper method to get the value based on the persisted type.
+ *
+ * @param actualType the type to convert to
+ * @param converter the data converter to use
+ * @return Returns the converted value
*/
- public Serializable getPersistedValue()
+ public Serializable getValue(Class actualType, PropertyTypeConverter converter)
{
switch (persistedTypeEnum)
{
case NULL:
return null;
case LONG:
- return longValue;
+ return converter.convert(actualType, Long.valueOf(longValue));
case DOUBLE:
- return doubleValue;
+ return converter.convert(actualType, Double.valueOf(doubleValue));
case STRING:
- return stringValue;
+ return converter.convert(actualType, stringValue);
case SERIALIZABLE:
- return serializableValue;
+ return converter.convert(actualType, serializableValue);
case CONSTRUCTABLE:
- // Construct an instance
- try
- {
- Class> clazz = Class.forName(stringValue);
- return (Serializable) clazz.newInstance();
- }
- catch (ClassNotFoundException e)
- {
- throw new RuntimeException("Unable to construct instance of class " + stringValue, e);
- }
- catch (Throwable e)
- {
- throw new RuntimeException("Unable to create new instance of " + stringValue, e);
- }
+ // Construct an instance using the converter (it knows best!)
+ return converter.constructInstance(stringValue);
default:
throw new IllegalStateException("Should not be able to get through switch");
}
@@ -313,22 +281,11 @@ public class PropertyValueEntity
this.persistedTypeEnum = PersistedType.NULL;
this.longValue = LONG_ZERO;
}
- else if (value instanceof Class>)
- {
- Class> clazz = (Class>) value;
- stringValue = clazz.getName();
- persistedTypeEnum = PersistedType.CONSTRUCTABLE;
- persistedType = persistedTypeEnum.getOrdinalNumber();
- }
else
{
- Class> valueClazz = value.getClass();
- persistedTypeEnum = persistedTypesByClass.get(valueClazz);
- if (persistedTypeEnum == null)
- {
- // Give the converter a chance to change the type it must be persisted as
- persistedTypeEnum = converter.getPersistentType(value);
- }
+ // The converter will be responsible for deserializing, so let it choose
+ // how the data is to be stored.
+ persistedTypeEnum = converter.getPersistentType(value);
persistedType = persistedTypeEnum.getOrdinalNumber();
// Get the class to persist as
switch (persistedTypeEnum)
@@ -342,6 +299,10 @@ public class PropertyValueEntity
case STRING:
stringValue = converter.convert(String.class, value);
break;
+ case CONSTRUCTABLE:
+ // A special case. There is no conversion, so just Store the name of the class.
+ stringValue = value.getClass().getName();
+ break;
case SERIALIZABLE:
serializableValue = value;
break;
@@ -359,9 +320,12 @@ public class PropertyValueEntity
* Helper method to determine how the given value will be stored.
*
* @param value the value to check
+ * @param converter the type converter
* @return Returns the persisted type
+ *
+ * @see PropertyTypeConverter#getPersistentType(Serializable)
*/
- public static PersistedType getPersistedTypeEnum(Serializable value)
+ public static PersistedType getPersistedTypeEnum(Serializable value, PropertyTypeConverter converter)
{
PersistedType persistedTypeEnum;
if (value == null)
@@ -370,12 +334,7 @@ public class PropertyValueEntity
}
else
{
- Class> valueClazz = value.getClass();
- persistedTypeEnum = persistedTypesByClass.get(valueClazz);
- if (persistedTypeEnum == null)
- {
- persistedTypeEnum = PersistedType.SERIALIZABLE;
- }
+ persistedTypeEnum = converter.getPersistentType(value);
}
return persistedTypeEnum;
}
diff --git a/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java b/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java
index 5bfe50f796..30d2c06a46 100644
--- a/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java
@@ -34,6 +34,7 @@ import org.alfresco.repo.domain.propval.PropertyDateValueEntity;
import org.alfresco.repo.domain.propval.PropertyDoubleValueEntity;
import org.alfresco.repo.domain.propval.PropertyIdSearchRow;
import org.alfresco.repo.domain.propval.PropertyLinkEntity;
+import org.alfresco.repo.domain.propval.PropertyRootEntity;
import org.alfresco.repo.domain.propval.PropertySerializableValueEntity;
import org.alfresco.repo.domain.propval.PropertyStringQueryEntity;
import org.alfresco.repo.domain.propval.PropertyStringValueEntity;
@@ -75,7 +76,13 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
private static final String SELECT_PROPERTY_VALUE_BY_STRING_VALUE = "alfresco.propval.select_PropertyValueByStringValue";
private static final String INSERT_PROPERTY_VALUE = "alfresco.propval.insert_PropertyValue";
+ private static final String SELECT_PROPERTY_BY_ID = "alfresco.propval.select_PropertyById";
+ private static final String SELECT_PROPERTY_ROOT_BY_ID = "alfresco.propval.select_PropertyRootById";
+ private static final String INSERT_PROPERTY_ROOT = "alfresco.propval.insert_PropertyRoot";
+ private static final String UPDATE_PROPERTY_ROOT = "alfresco.propval.update_PropertyRoot";
+ private static final String DELETE_PROPERTY_ROOT_BY_ID = "alfresco.propval.delete_PropertyRootById";
private static final String INSERT_PROPERTY_LINK = "alfresco.propval.insert_PropertyLink";
+ private static final String DELETE_PROPERTY_LINKS_BY_ROOT_ID = "alfresco.propval.delete_PropertyLinksByRootId";
private SqlMapClientTemplate template;
@@ -287,18 +294,30 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
@SuppressWarnings("unchecked")
@Override
- protected List findPropertyValueById(Long id)
+ protected PropertyValueEntity findPropertyValueById(Long id)
{
PropertyValueEntity entity = new PropertyValueEntity();
entity.setId(id);
- List results = (List) template.queryForList(
+ List results = (List) template.queryForList(
SELECT_PROPERTY_VALUE_BY_ID,
entity);
- // Done
- return results;
+ // At most one of the results represents a real value
+ int size = results.size();
+ if (size == 0)
+ {
+ return null;
+ }
+ else if (size == 1)
+ {
+ return results.get(0);
+ }
+ else
+ {
+ logger.error("Found property value linked to multiple raw types: " + results);
+ return results.get(0);
+ }
}
- @SuppressWarnings("unchecked")
@Override
protected PropertyValueEntity findPropertyValueByValue(Serializable value)
{
@@ -336,6 +355,8 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
case DOUBLE:
query = SELECT_PROPERTY_VALUE_BY_DOUBLE_VALUE;
break;
+ case CONSTRUCTABLE:
+ // The string value is the name of the class (e.g. 'java.util.HashMap')
case STRING:
// It's best to query using the CRC and short end-value
query = SELECT_PROPERTY_VALUE_BY_STRING_VALUE;
@@ -355,15 +376,8 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
PropertyValueEntity result = null;
if (query != null)
{
- List results = (List) template.queryForList(
- query,
- queryObject,
- 0, 1); // Only want one result
- for (PropertyValueEntity row : results)
- {
- result = row;
- break;
- }
+ // Uniqueness is guaranteed by the tables, so we get one value only
+ result = (PropertyValueEntity) template.queryForObject(query, queryObject);
}
// Done
@@ -416,19 +430,77 @@ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
return insertEntity;
}
+ //================================
+ // 'alf_prop_root' accessors
+ //================================
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected List findPropertyById(Long id)
+ {
+ PropertyValueEntity entity = new PropertyValueEntity();
+ entity.setId(id);
+ List results = (List) template.queryForList(
+ SELECT_PROPERTY_BY_ID,
+ entity);
+ return results;
+ }
+
+ @Override
+ protected Long createPropertyRoot()
+ {
+ PropertyRootEntity rootEntity = new PropertyRootEntity();
+ rootEntity.setVersion((short)0);
+ return (Long) template.insert(INSERT_PROPERTY_ROOT, rootEntity);
+ }
+
+ @Override
+ protected PropertyRootEntity getPropertyRoot(Long id)
+ {
+ PropertyRootEntity entity = new PropertyRootEntity();
+ entity.setId(id);
+ return (PropertyRootEntity) template.queryForObject(SELECT_PROPERTY_ROOT_BY_ID, entity);
+ }
+
+ @Override
+ protected PropertyRootEntity updatePropertyRoot(PropertyRootEntity entity)
+ {
+ entity.incrementVersion();
+ template.update(UPDATE_PROPERTY_ROOT, entity, 1);
+ return entity;
+ }
+
+ @Override
+ protected void deletePropertyRoot(Long id)
+ {
+ PropertyRootEntity entity = new PropertyRootEntity();
+ entity.setId(id);
+ template.delete(DELETE_PROPERTY_ROOT_BY_ID, entity);
+ }
+
@Override
protected void createPropertyLink(
Long rootPropId,
- Long currentPropId,
- Long valueId,
- Long keyId)
+ Long propIndex,
+ Long containedIn,
+ Long keyPropId,
+ Long valuePropId)
{
PropertyLinkEntity insertEntity = new PropertyLinkEntity();
insertEntity.setRootPropId(rootPropId);
- insertEntity.setCurrentPropId(currentPropId);
- insertEntity.setValuePropId(valueId);
- insertEntity.setKeyPropId(keyId);
+ insertEntity.setPropIndex(propIndex);
+ insertEntity.setContainedIn(containedIn);
+ insertEntity.setKeyPropId(keyPropId);
+ insertEntity.setValuePropId(valuePropId);
template.insert(INSERT_PROPERTY_LINK, insertEntity);
// Done
}
+
+ @Override
+ protected int deletePropertyLinks(Long rootPropId)
+ {
+ PropertyRootEntity entity = new PropertyRootEntity();
+ entity.setId(rootPropId);
+ return template.delete(DELETE_PROPERTY_LINKS_BY_ROOT_ID, entity);
+ }
}