/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl.core;
import org.exoplatform.services.jcr.core.nodetype.ExtendedNodeTypeManager;
import org.exoplatform.services.jcr.core.nodetype.ItemDefinitionData;
import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionData;
import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionDatas;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.impl.core.nodetype.PropertyDefinitionImpl;
import org.exoplatform.services.jcr.impl.core.value.BaseValue;
import org.exoplatform.services.jcr.impl.core.value.ReferenceValue;
import org.exoplatform.services.jcr.impl.dataflow.ValueDataUtil;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.io.InputStream;
import java.util.Calendar;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
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;
/**
* Created by The eXo Platform SAS.
*
* @author Gennady Azarenkov
* @version $Id: PropertyImpl.java 11907 2008-03-13 15:36:21Z ksm $
*/
public class PropertyImpl extends ItemImpl implements Property
{
/**
* Logger.
*/
protected static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.PropertyImpl");
/**
* Value type.
*/
protected int type;
private PropertyDefinitionData propertyDef;
private PropertyData propertyData;
/**
* PropertyImpl constructor.
*
* @param data
* ItemData object
* @param session
* Session object
* @throws RepositoryException
* @throws ConstraintViolationException
*/
PropertyImpl(ItemData data, SessionImpl session) throws RepositoryException, ConstraintViolationException
{
super(data, session);
loadData(data);
}
/**
* PropertyImpl constructor.
*
* @param data
* ItemData object
* @param parent NodeData Property's parent
* @param session
* Session object
* @throws RepositoryException
* @throws ConstraintViolationException
*/
PropertyImpl(ItemData data, NodeData parent, SessionImpl session) throws RepositoryException,
ConstraintViolationException
{
super(data, session);
loadData(data, parent);
}
/**
* {@inheritDoc}
*/
void loadData(ItemData data) throws RepositoryException, ConstraintViolationException
{
loadData(data, (NodeData)null);
}
/**
* {@inheritDoc}
*/
void loadData(ItemData data, NodeData parent) throws RepositoryException, ConstraintViolationException
{
if (data.isNode())
{
throw new RepositoryException("Load data failed: Property expected");
}
this.data = data;
this.propertyData = (PropertyData)data;
this.type = propertyData.getType();
this.qpath = data.getQPath();
this.location = null;
initDefinitions(this.propertyData.isMultiValued(), parent);
}
/**
* {@inheritDoc}
*/
public ItemDefinitionData getItemDefinitionData()
{
return propertyDef;
}
/**
* {@inheritDoc}
*/
public Value getValue() throws ValueFormatException, RepositoryException
{
checkValid();
if (isMultiValued())
{
throw new ValueFormatException("The property " + getPath() + " is multi-valued (6.2.4)");
}
if (propertyData.getValues() != null && propertyData.getValues().size() == 0)
{
throw new ValueFormatException("The single valued property " + getPath() + " is empty");
}
return valueFactory.loadValue(propertyData.getValues().get(0), propertyData.getType());
}
/**
* {@inheritDoc}
*/
public Value[] getValues() throws ValueFormatException, RepositoryException
{
checkValid();
// Check property definition and life-state flag both
if (!isMultiValued())
{
throw new ValueFormatException("The property " + getPath() + " is single-valued (6.2.4)");
}
// The array returned is a copy of the stored values
return getValueArray();
}
/**
* {@inheritDoc}
*/
public String getString() throws ValueFormatException, RepositoryException
{
try
{
return getValue().getString();
}
catch (ValueFormatException e)
{
throw new ValueFormatException("PropertyImpl.getString() for " + getPath() + " failed: " + e);
}
catch (IllegalStateException e)
{
throw new ValueFormatException("PropertyImpl.getString() for " + getPath() + " failed: " + e);
}
}
/**
* {@inheritDoc}
*/
public double getDouble() throws ValueFormatException, RepositoryException
{
try
{
return getValue().getDouble();
}
catch (IllegalStateException e)
{
throw new ValueFormatException("PropertyImpl.getDouble() failed: " + e);
}
}
/**
* {@inheritDoc}
*/
public long getLong() throws ValueFormatException, RepositoryException
{
try
{
return getValue().getLong();
}
catch (IllegalStateException e)
{
throw new ValueFormatException("PropertyImpl.getLong() failed: " + e);
}
}
/**
* {@inheritDoc}
*/
public InputStream getStream() throws ValueFormatException, RepositoryException
{
try
{
return getValue().getStream();
}
catch (IllegalStateException e)
{
throw new ValueFormatException("PropertyImpl.getStream() failed: " + e);
}
}
/**
* {@inheritDoc}
*/
public Calendar getDate() throws ValueFormatException, RepositoryException
{
try
{
return getValue().getDate();
}
catch (IllegalStateException e)
{
throw new ValueFormatException("PropertyImpl.getDate() failed: " + e);
}
}
/**
* {@inheritDoc}
*/
public boolean getBoolean() throws ValueFormatException, RepositoryException
{
try
{
return getValue().getBoolean();
}
catch (IllegalStateException e)
{
throw new ValueFormatException("PropertyImpl.getBoolean() failed: " + e);
}
}
/**
* {@inheritDoc}
*/
public Node getNode() throws ValueFormatException, RepositoryException
{
Value value = getValue();
if (value instanceof ReferenceValue)
{
String identifier = ((BaseValue)value).getReference();
return session.getNodeByUUID(identifier);
}
else
{
throw new ValueFormatException("Property cannot be converted to a reference");
}
}
/**
* {@inheritDoc}
*/
public long getLength() throws ValueFormatException, RepositoryException
{
return ((BaseValue)getValue()).getLength();
}
/**
* {@inheritDoc}
*/
public long[] getLengths() throws ValueFormatException, RepositoryException
{
Value[] thisValues = getValues();
long[] lengths = new long[thisValues.length];
for (int i = 0; i < lengths.length; i++)
{
lengths[i] = ((BaseValue)thisValues[i]).getLength();
}
return lengths;
}
/**
* {@inheritDoc}
*/
public PropertyDefinition getDefinition() throws RepositoryException
{
checkValid();
if (propertyDef == null)
{
throw new RepositoryException("FATAL: property definition is NULL " + getPath() + " "
+ propertyData.getValues());
}
Value[] defaultValues = new Value[propertyDef.getDefaultValues().length];
String[] propVal = propertyDef.getDefaultValues();
// there can be null in definition but should not be null value
if (propVal != null)
{
for (int i = 0; i < propVal.length; i++)
{
if (propertyDef.getRequiredType() == PropertyType.UNDEFINED)
defaultValues[i] = valueFactory.createValue(propVal[i]);
else
defaultValues[i] = valueFactory.createValue(propVal[i], propertyDef.getRequiredType());
}
}
return new PropertyDefinitionImpl(propertyDef, session.getWorkspace().getNodeTypesHolder(),
(ExtendedNodeTypeManager)session.getWorkspace().getNodeTypeManager(), session.getSystemLocationFactory(),
session.getValueFactory(), session.getTransientNodesManager());
}
/**
* @param multiple
* @param parent
* @throws RepositoryException
* @throws ConstraintViolationException
*/
private void initDefinitions(boolean multiple, NodeData parent) throws RepositoryException,
ConstraintViolationException
{
InternalQName pname = getData().getQPath().getName();
if (parent == null)
{
parent = parentData();
}
PropertyDefinitionDatas definitions =
session.getWorkspace().getNodeTypesHolder().getPropertyDefinitions(pname, parent.getPrimaryTypeName(),
parent.getMixinTypeNames());
if (definitions == null)
{
throw new ConstraintViolationException("Definition for property " + getPath() + " not found.");
}
propertyDef = definitions.getDefinition(multiple);
}
/**
* {@inheritDoc}
*/
public int getType()
{
return type;
}
/**
* {@inheritDoc}
*/
public void setValue(Value value) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
checkValid();
doUpdateProperty(parent(), getInternalName(), value, false, PropertyType.UNDEFINED);
}
/**
* {@inheritDoc}
*/
public void setValue(Value[] values) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
checkValid();
doUpdateProperty(parent(), getInternalName(), values, true, PropertyType.UNDEFINED);
}
/**
* Check if property is multi valued.
*
* @return multiValued property of data field (PropertyData) it's a life-state property field
* which contains multiple-valued flag for value(s) data. Can be set in property creation
* time or from persistent storage.
*/
public boolean isMultiValued()
{
return ((PropertyData)data).isMultiValued();
}
/**
* {@inheritDoc}
*/
public void setValue(String value) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
setValue(valueFactory.createValue(value));
}
/**
* {@inheritDoc}
*/
public void setValue(InputStream stream) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
setValue(valueFactory.createValue(stream));
}
/**
* {@inheritDoc}
*/
public void setValue(double number) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
setValue(valueFactory.createValue(number));
}
/**
* {@inheritDoc}
*/
public void setValue(long number) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
setValue(valueFactory.createValue(number));
}
/**
* {@inheritDoc}
*/
public void setValue(Calendar date) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
setValue(valueFactory.createValue(date));
}
/**
* {@inheritDoc}
*/
public void setValue(boolean b) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
setValue(valueFactory.createValue(b));
}
/**
* {@inheritDoc}
*/
public void setValue(Node value) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
setValue(valueFactory.createValue(value));
}
/**
* {@inheritDoc}
*/
public void setValue(String[] values) throws ValueFormatException, VersionException, LockException,
ConstraintViolationException, RepositoryException
{
Value[] strValues = null;
if (values != null)
{
strValues = new Value[values.length];
for (int i = 0; i < values.length; i++)
strValues[i] = valueFactory.createValue(values[i]);
}
setValue(strValues);
}
// ////////////////// Item implementation ////////////////////
/**
* {@inheritDoc}
*/
public void accept(ItemVisitor visitor) throws RepositoryException
{
checkValid();
visitor.visit(this);
}
/**
* {@inheritDoc}
*/
public boolean isNode()
{
return false;
}
// // ----------------------- ExtendedProperty -----------------------
//
// public void updateValue(int index, InputStream value, long length,
// long position) throws ValueFormatException, VersionException,
// LockException, ConstraintViolationException, RepositoryException {
//
// checkValid();
//
// PropertyData pdata = (PropertyData) getData();
// TransientValueData vdata = (TransientValueData)
// pdata.getValues().get(index);
//
// doUpdateProperty(parent(), getInternalName(),
// valueFactory.loadValue(vdata, PropertyType.BINARY), false,
// PropertyType.BINARY);
//
// // get new data
// vdata = (TransientValueData) ((PropertyData)
// getData()).getValues().get(index);
//
// try {
// vdata.update(value, length, position);
// } catch (IOException e) {
// throw new RepositoryException(e);
// }
//
// //setValue(valueFactory.loadValue(vdata, PropertyType.BINARY));
// }
// ////////////////////////////////
/**
* Copies property values into array.
*
* @return array of property values
* @throws RepositoryException
* if any Exception is occurred
*/
public Value[] getValueArray() throws RepositoryException
{
Value[] values = new Value[propertyData.getValues().size()];
for (int i = 0; i < values.length; i++)
{
values[i] = valueFactory.loadValue(propertyData.getValues().get(i), propertyData.getType());
}
return values;
}
/**
* Get info about property values.
*
* @return string with property values
*/
public String dump()
{
StringBuilder vals = new StringBuilder("Property ");
try
{
vals = new StringBuilder(getPath()).append(" values: ");
for (int i = 0; i < getValueArray().length; i++)
{
vals.append(ValueDataUtil.getString(((BaseValue)getValueArray()[i]).getInternalData())).append(";");
}
}
catch (Exception e)
{
LOG.error(e.getLocalizedMessage(), e);
}
return vals.toString();
}
// ----------------------- Object -----------------------
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj)
{
if (obj instanceof PropertyImpl)
{
try
{
// by path
return getLocation().equals(((PropertyImpl)obj).getLocation());
}
catch (Exception e)
{
return false;
}
}
return false;
}
@Override
public String toString()
{
String typeName;
try
{
typeName = PropertyType.nameFromValue(type);
}
catch (IllegalArgumentException e)
{
// Value has abnormal type
typeName = String.valueOf(type);
}
return String.format("Property {\n id: %s;\n path: %s;\n type: %s;\n multi-valued: %b\n}", data == null
? "not valid property" : data.getIdentifier(), qpath == null ? "undefined" : qpath.getAsString(), typeName,
data == null ? false : isMultiValued());
}
}