/* * 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.util.Calendar; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Value; import javax.jcr.ValueFormatException; import org.alfresco.jcr.session.SessionImpl; import org.alfresco.jcr.util.JCRProxyFactory; import org.alfresco.service.cmr.repository.ContentReader; /** * Alfresco implementation of JCR Value * * @author David Caruana */ public class ValueImpl implements Value { private enum ValueState {Stream, Value, None}; private ValueState state = ValueState.None; private SessionImpl session; private int datatype; private Object value; private InputStream stream = null; private Value proxy; /** * Constuct * * @param value value to wrap */ public ValueImpl(SessionImpl session, int datatype, Object value) { this.session = session; this.datatype = datatype; this.value = value; } /** * Create a proxied JCR Value * * @return the proxied value */ public Value getProxy() { if (proxy == null) { proxy = (Value)JCRProxyFactory.create(this, Value.class, session); } return proxy; } /* (non-Javadoc) * @see javax.jcr.Value#getString() */ public String getString() throws ValueFormatException, IllegalStateException, RepositoryException { isValidState(ValueState.Value); String typedValue = session.getTypeConverter().stringValue(getInternalValue()); value = typedValue; enterState(ValueState.Value); return typedValue; } /* (non-Javadoc) * @see javax.jcr.Value#getStream() */ public InputStream getStream() throws IllegalStateException, RepositoryException { isValidState(ValueState.Stream); if (stream == null) { stream = session.getTypeConverter().streamValue(value); } enterState(ValueState.Stream); return stream; } /* (non-Javadoc) * @see javax.jcr.Value#getLong() */ public long getLong() throws ValueFormatException, IllegalStateException, RepositoryException { isValidState(ValueState.Value); long typedValue = session.getTypeConverter().longValue(getInternalValue()); value = typedValue; enterState(ValueState.Value); return typedValue; } /* (non-Javadoc) * @see javax.jcr.Value#getDouble() */ public double getDouble() throws ValueFormatException, IllegalStateException, RepositoryException { isValidState(ValueState.Value); double typedValue = session.getTypeConverter().doubleValue(getInternalValue()); value = typedValue; enterState(ValueState.Value); return typedValue; } /* (non-Javadoc) * @see javax.jcr.Value#getDate() */ public Calendar getDate() throws ValueFormatException, IllegalStateException, RepositoryException { isValidState(ValueState.Value); Calendar typedValue = session.getTypeConverter().dateValue(getInternalValue()); value = typedValue.getTime(); enterState(ValueState.Value); return typedValue; } /* (non-Javadoc) * @see javax.jcr.Value#getBoolean() */ public boolean getBoolean() throws ValueFormatException, IllegalStateException, RepositoryException { isValidState(ValueState.Value); boolean typedValue = session.getTypeConverter().booleanValue(getInternalValue()); value = typedValue; enterState(ValueState.Value); return typedValue; } /* (non-Javadoc) * @see javax.jcr.Value#getType() */ public int getType() { return datatype; } /** * Get value * * @param value the value wrapper to extract from * @return the value */ public static Object getValue(Value value) throws RepositoryException { Object objValue = null; int valueType = value.getType(); switch(valueType) { case PropertyType.STRING: case PropertyType.NAME: case PropertyType.PATH: objValue = value.getString(); break; case PropertyType.LONG: objValue = value.getLong(); break; case PropertyType.DOUBLE: objValue = value.getDouble(); break; case PropertyType.BOOLEAN: objValue = value.getBoolean(); break; case PropertyType.DATE: objValue = value.getDate(); break; case PropertyType.BINARY: objValue = value.getStream(); break; default: // Note: just take the internal value objValue = ((ValueImpl)value).value; break; } return objValue; } /** * Get typed value * * @param value the value to extract from * @return the wrapped object */ public static Object getValue(JCRTypeConverter typeConverter, int requiredType, Value value) throws RepositoryException { Object objValue = null; switch(requiredType) { case PropertyType.STRING: objValue = value.getString(); break; case PropertyType.LONG: objValue = value.getLong(); break; case PropertyType.DOUBLE: objValue = value.getDouble(); break; case PropertyType.BOOLEAN: objValue = value.getBoolean(); break; case PropertyType.DATE: objValue = value.getDate(); break; case PropertyType.BINARY: objValue = value.getStream(); break; case PropertyType.NAME: objValue = typeConverter.nameValue(ValueImpl.getValue(value)); break; case PropertyType.PATH: objValue = typeConverter.pathValue(ValueImpl.getValue(value)); break; default: throw new ValueFormatException("Unsupported Value Type " + requiredType); } return objValue; } /** * Retrieve Value * * Note: When retrieving non stream values against a backed stream, the content reader * has to be re-created. * * @return the value */ private Object getInternalValue() { if (value instanceof ContentReader && state == ValueState.Value) { value = ((ContentReader)value).getReader(); } return value; } /** * Check for valid state * * @param state the state to check * @throws IllegalStateException state is not valid */ private void isValidState(ValueState state) { if (this.state != ValueState.None && this.state != state) { throw new IllegalStateException("This value has already been retrieved as a " + state + " and cannot be retrieved as a " + ValueState.Stream + "."); } } /** * Enter state * * @param state the state to enter */ private void enterState(ValueState state) { this.state = state; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ValueImpl)) { return false; } ValueImpl other = (ValueImpl)obj; // check data type first if (datatype != other.datatype) { return false; } // handle case where values are content streams if (value instanceof ContentReader) { String thisUrl = ((ContentReader)value).getContentUrl(); String otherUrl = ((ContentReader)other).getContentUrl(); return thisUrl.equals(otherUrl); } // handle other value types return value.equals(other.value); } @Override public int hashCode() { return value.hashCode() * 32 + datatype; } }