David Caruana 19e3138e1c - Incorporate JCR project into Repository project
- Single configuration entry point for JCR and non-JCR clients (i.e. application-context.xml)
- Addition of build-war, incremental-war build targets (no deploy)
- Remove build of JCR TCK war file by default

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2777 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2006-05-05 16:33:22 +00:00

685 lines
22 KiB
Java

/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.jcr.item;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import javax.jcr.AccessDeniedException;
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.version.VersionException;
import org.alfresco.jcr.api.JCRNodeRef;
import org.alfresco.jcr.dictionary.DataTypeMap;
import org.alfresco.jcr.dictionary.PropertyDefinitionImpl;
import org.alfresco.jcr.util.JCRProxyFactory;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
import org.alfresco.service.namespace.QName;
/**
* Alfresco implementation of a Property
*
* @author David Caruana
*/
public class PropertyImpl extends ItemImpl implements Property
{
private NodeImpl node;
private QName name;
private Property proxy = null;
/**
* Constructor
*
* @param session
*/
public PropertyImpl(NodeImpl node, QName name)
{
super(node.session);
this.node = node;
this.name = name;
}
/**
* Create proxied JCR Property
*
* @return property
*/
@Override
public Property getProxy()
{
if (proxy == null)
{
proxy = (Property)JCRProxyFactory.create(this, Property.class, session);
}
return proxy;
}
/* (non-Javadoc)
* @see javax.jcr.Item#remove()
*/
public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException
{
setValue((Value)null);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(javax.jcr.Value)
*/
public void setValue(Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(value, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(javax.jcr.Value[])
*/
public void setValue(Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(values, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(java.lang.String)
*/
public void setValue(String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(value, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(java.lang.String[])
*/
public void setValue(String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(values, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(java.io.InputStream)
*/
public void setValue(InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(value, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(long)
*/
public void setValue(long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(value, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(double)
*/
public void setValue(double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(value, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(java.util.Calendar)
*/
public void setValue(Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue((value == null) ? null : value.getTime(), -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(boolean)
*/
public void setValue(boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue(value, -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#setValue(javax.jcr.Node)
*/
public void setValue(Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException
{
setPropertyValue((value == null) ? null : JCRNodeRef.getNodeRef(value), -1);
}
/* (non-Javadoc)
* @see javax.jcr.Property#getValue()
*/
public Value getValue() throws ValueFormatException, RepositoryException
{
checkSingleValued();
ValueImpl valueImpl = new ValueImpl(session, getType(), getPropertyValue());
// TODO: Could consider returning proxied value implementation (but i don't think is necessary)
return valueImpl;
}
/* (non-Javadoc)
* @see javax.jcr.Property#getValues()
*/
public Value[] getValues() throws ValueFormatException, RepositoryException
{
// get values from node property
checkMultiValued();
Collection values = (Collection)getPropertyValue();
int type = getType();
// construct JCR wrappers
List<Value> jcrValues = new ArrayList<Value>(values.size());
for (Object value : values)
{
// Note: In JCR all null values are stripped
if (value != null)
{
// TODO: Could consider returning proxied value implementation (but i don't think is necessary)
jcrValues.add(new ValueImpl(session, type, value));
}
}
return jcrValues.toArray(new Value[jcrValues.size()]);
}
/* (non-Javadoc)
* @see javax.jcr.Property#getString()
*/
public String getString() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return session.getTypeConverter().stringValue(getPropertyValue());
}
/* (non-Javadoc)
* @see javax.jcr.Property#getStream()
*/
public InputStream getStream() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return session.getTypeConverter().streamValue(getPropertyValue());
}
/* (non-Javadoc)
* @see javax.jcr.Property#getLong()
*/
public long getLong() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return session.getTypeConverter().longValue(getPropertyValue());
}
/* (non-Javadoc)
* @see javax.jcr.Property#getDouble()
*/
public double getDouble() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return session.getTypeConverter().doubleValue(getPropertyValue());
}
/* (non-Javadoc)
* @see javax.jcr.Property#getDate()
*/
public Calendar getDate() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return session.getTypeConverter().dateValue(getPropertyValue());
}
/* (non-Javadoc)
* @see javax.jcr.Property#getBoolean()
*/
public boolean getBoolean() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return session.getTypeConverter().booleanValue(getPropertyValue());
}
/* (non-Javadoc)
* @see javax.jcr.Property#getNode()
*/
public Node getNode() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return session.getTypeConverter().referenceValue(getPropertyValue()).getProxy();
}
/* (non-Javadoc)
* @see javax.jcr.Property#getLength()
*/
public long getLength() throws ValueFormatException, RepositoryException
{
checkSingleValued();
return getPropertyLength(getPropertyValue());
}
/* (non-Javadoc)
* @see javax.jcr.Property#getLengths()
*/
public long[] getLengths() throws ValueFormatException, RepositoryException
{
checkMultiValued();
Collection values = (Collection)getPropertyValue();
long[] lengths = new long[values.size()];
int i = 0;
for (Object value : values)
{
lengths[i++] = getPropertyLength(value);
}
return lengths;
}
/* (non-Javadoc)
* @see javax.jcr.Property#getDefinition()
*/
public PropertyDefinition getDefinition() throws RepositoryException
{
PropertyDefinitionImpl propDefImpl = new PropertyDefinitionImpl(session.getTypeManager(), getPropertyDefinition());
return propDefImpl;
}
/* (non-Javadoc)
* @see javax.jcr.Property#getType()
*/
public int getType() throws RepositoryException
{
// TODO: The type should be based on the property value (in the case of undefined required type)
return DataTypeMap.convertDataTypeToPropertyType(getPropertyDefinition().getDataType().getName());
}
/* (non-Javadoc)
* @see javax.jcr.Item#getName()
*/
public String getName() throws RepositoryException
{
return name.toPrefixString(session.getNamespaceResolver());
}
/* (non-Javadoc)
* @see javax.jcr.Item#isNode()
*/
public boolean isNode()
{
return false;
}
/* (non-Javadoc)
* @see javax.jcr.Item#getParent()
*/
public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException
{
return node.getProxy();
}
/* (non-Javadoc)
* @see javax.jcr.Item#getPath()
*/
public String getPath() throws RepositoryException
{
NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService();
Path path = nodeService.getPath(node.getNodeRef());
path.append(new JCRPath.SimpleElement(name));
return path.toPrefixString(session.getNamespaceResolver());
}
/* (non-Javadoc)
* @see javax.jcr.Item#getDepth()
*/
public int getDepth() throws RepositoryException
{
NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService();
Path path = nodeService.getPath(node.getNodeRef());
// Note: Property is one depth lower than its node
return path.size();
}
/* (non-Javadoc)
* @see javax.jcr.Item#getAncestor(int)
*/
public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException
{
int propertyDepth = getDepth();
if (depth < 0 || depth > propertyDepth)
{
throw new ItemNotFoundException("Ancestor at depth " + depth + " not found for property " + name);
}
if (depth == propertyDepth)
{
return this.getProxy();
}
else
{
return node.getAncestor(depth -1);
}
}
/* (non-Javadoc)
* @see javax.jcr.Item#isSame(javax.jcr.Item)
*/
public boolean isSame(Item otherItem) throws RepositoryException
{
return getProxy().equals(otherItem);
}
/* (non-Javadoc)
* @see javax.jcr.Item#accept(javax.jcr.ItemVisitor)
*/
public void accept(ItemVisitor visitor) throws RepositoryException
{
visitor.visit(getProxy());
}
/**
* Gets the Node Implementation that contains this property
*
* @return the node implementation
*/
protected NodeImpl getNodeImpl()
{
return node;
}
/**
* Gets the Property Name
*
* @return the property name
*/
protected QName getPropertyName()
{
return name;
}
/**
* Gets the property value
*
* @return the property value
*/
protected Object getPropertyValue()
throws RepositoryException
{
Object value = null;
if (getPropertyDefinition().getDataType().getName().equals(DataTypeDefinition.CONTENT))
{
// Retrieve content reader as value
ContentService contentService = node.session.getRepositoryImpl().getServiceRegistry().getContentService();
value = contentService.getReader(node.getNodeRef(), name);
if (value == null)
{
// TODO: Check - If value is now null, then effectively the property has been removed
throw new RepositoryException("Property " + name + " has been removed.");
}
}
else
{
// TODO: We may need to copy value here...
NodeService nodeService = node.session.getRepositoryImpl().getServiceRegistry().getNodeService();
value = nodeService.getProperty(node.getNodeRef(), name);
if (value == null)
{
// TODO: Check - If value is now null, then effectively the property has been removed
throw new RepositoryException("Property " + name + " has been removed.");
}
// Note: Internal check to ensure that value is single or multi-valued as expected
boolean multiValued = getPropertyDefinition().isMultiValued();
if (multiValued != (value instanceof Collection))
{
throw new RepositoryException("Alfresco value does not match multi-valued definition of " + multiValued);
}
}
return value;
}
/**
* Get Length of a Value
*
* @param value
* @return
* @throws ValueFormatException
* @throws RepositoryException
*/
private long getPropertyLength(Object value) throws ValueFormatException, RepositoryException
{
// Handle streams
if (value instanceof ContentReader)
{
return ((ContentReader)value).getSize();
}
if (value instanceof InputStream)
{
return -1;
}
// Handle all other data types by converting to string
String strValue = (String)DefaultTypeConverter.INSTANCE.convert(String.class, value);
return strValue.length();
}
/**
* Sets a property value
*
* @param value the value to set
* @param type type to explicitly convert to or -1 to convert to property type
* @throws RepositoryException
*/
protected void setPropertyValue(Object value, int type)
throws RepositoryException
{
checkSingleValued();
Object castValue = castValue(value, type);
writeValue(castValue);
}
/**
* Sets a property value
*
* @param values the values to set
* @param type type to explicitly convert to or -1 to convert to property type
* @throws RepositoryException
*/
protected void setPropertyValue(Object[] values, int type)
throws RepositoryException
{
checkMultiValued();
// create collection for multi-valued property
List<Object> castValues = null;
if (values != null)
{
castValues = new ArrayList<Object>(values.length);
for (Object value : values)
{
Object castValue = castValue(value, type);
castValues.add(castValue);
}
}
writeValue(castValues);
}
/**
* Cast value to appropriate type for this property
*
* @param value value to cast
* @param type -1 => cast to property type; otherwise cast to type explicitly provided
* @return the cast value
* @throws RepositoryException
*/
private Object castValue(Object value, int type)
throws RepositoryException
{
// extract raw value if JCR value provided
if (value instanceof Value)
{
value = ValueImpl.getValue((Value)value);
}
// cast value to appropriate type
DataTypeDefinition dataTypeDef = getPropertyDefinition().getDataType();
if (type != -1 && dataTypeDef.getName().equals(DataTypeDefinition.ANY))
{
// attempt cast to explicitly specified type, but only in case where property type can be ANY
QName dataTypeName = DataTypeMap.convertPropertyTypeToDataType(type);
DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService();
dataTypeDef = dictionaryService.getDataType(dataTypeName);
if (!dataTypeName.equals(DataTypeDefinition.CONTENT))
{
value = session.getTypeConverter().convert(dataTypeDef, value);
}
}
// special case where binary is converted to inputStream ready for writing via a ContentWriter
if (dataTypeDef.getName().equals(DataTypeDefinition.CONTENT))
{
value = session.getTypeConverter().streamValue(value);
}
return value;
}
/**
* Write the passed value to the property
*
* @param value value to write
* @throws ValueFormatException
*/
private void writeValue(Object value)
throws ValueFormatException
{
// set the property value
if (value instanceof InputStream)
{
// write content
try
{
ContentService contentService = session.getRepositoryImpl().getServiceRegistry().getContentService();
ContentWriter writer = contentService.getWriter(node.getNodeRef(), name, true);
writer.setMimetype(MimetypeMap.MIMETYPE_BINARY);
writer.putContent((InputStream)value);
}
catch(InvalidTypeException e)
{
throw new ValueFormatException(e);
}
}
else
{
// write property value
// Note: In the case of Content properties, this effectively "deletes" the content when the value is null
try
{
NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService();
nodeService.setProperty(node.getNodeRef(), name, (Serializable)value);
}
catch(TypeConversionException e)
{
throw new ValueFormatException(e);
}
}
}
/**
* Checks that this property is single valued.
*
* @throws ValueFormatException if value is multi-valued
*/
private void checkSingleValued()
throws ValueFormatException
{
if (getPropertyDefinition().isMultiValued())
{
// Expected exception for JSR-170
throw new ValueFormatException("Property " + name + " is multi-valued.");
}
}
/**
* Checks that this property is single valued.
*
* @throws ValueFormatException if value is multi-valued
*/
private void checkMultiValued()
throws ValueFormatException
{
if (!getPropertyDefinition().isMultiValued())
{
// Expected exception for JSR-170
throw new ValueFormatException("Property " + name + " is single-valued.");
}
}
/**
* Gets the Property Data Type
*
* @return the (JCR) data type
*/
private org.alfresco.service.cmr.dictionary.PropertyDefinition getPropertyDefinition()
{
DictionaryService dictionary = session.getRepositoryImpl().getServiceRegistry().getDictionaryService();
return dictionary.getProperty(name);
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (!(obj instanceof PropertyImpl))
{
return false;
}
PropertyImpl other = (PropertyImpl)obj;
return this.name.equals(other.name);
}
@Override
public int hashCode()
{
return name.hashCode();
}
}