Moving to root below branch label

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2005-12-08 07:13:07 +00:00
commit e1e6508fec
1095 changed files with 230566 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
/*
* 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.service.cmr.repository;
/**
* Store-related exception that keeps a handle to the store reference
*
* @author Derek Hulley
*/
public abstract class AbstractStoreException extends RuntimeException
{
private StoreRef storeRef;
public AbstractStoreException(StoreRef storeRef)
{
this(null, storeRef);
}
public AbstractStoreException(String msg, StoreRef storeRef)
{
super(msg);
this.storeRef = storeRef;
}
/**
* @return Returns the offending store reference
*/
public StoreRef getStoreRef()
{
return storeRef;
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.service.cmr.repository;
import java.text.MessageFormat;
import org.alfresco.service.namespace.QName;
/**
* Used to indicate that an aspect is missing from a node.
*
* @author Roy Wetherall
*/
public class AspectMissingException extends RuntimeException
{
private static final long serialVersionUID = 3257852099244210228L;
private QName missingAspect;
private NodeRef nodeRef;
/**
* Error message
*/
private static final String ERROR_MESSAGE = "The {0} aspect is missing from this node (id: {1}). " +
"It is required for this operation.";
/**
* Constructor
*/
public AspectMissingException(QName missingAspect, NodeRef nodeRef)
{
super(MessageFormat.format(ERROR_MESSAGE, new Object[]{missingAspect.toString(), nodeRef.getId()}));
this.missingAspect = missingAspect;
this.nodeRef = nodeRef;
}
public QName getMissingAspect()
{
return missingAspect;
}
public NodeRef getNodeRef()
{
return nodeRef;
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.service.cmr.repository;
import org.alfresco.service.namespace.QName;
/**
* Thrown when an operation could not be performed because a named association already
* exists between two nodes
*
* @author Derek Hulley
*/
public class AssociationExistsException extends RuntimeException
{
private static final long serialVersionUID = 3256440317824874800L;
private NodeRef sourceRef;
private NodeRef targetRef;
private QName qname;
/**
* @param sourceRef the source of the association
* @param targetRef the target of the association
* @param qname the qualified name of the association
*/
public AssociationExistsException(NodeRef sourceRef, NodeRef targetRef, QName qname)
{
super();
this.sourceRef = sourceRef;
this.targetRef = targetRef;
this.qname = qname;
}
public NodeRef getSourceRef()
{
return sourceRef;
}
public NodeRef getTargetRef()
{
return targetRef;
}
public QName getQName()
{
return qname;
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
/**
* This class represents a regular, named node relationship between two nodes.
*
* @author Derek Hulley
*/
public class AssociationRef implements EntityRef, Serializable
{
private static final long serialVersionUID = 3977867284482439475L;
private NodeRef sourceRef;
private QName assocTypeQName;
private NodeRef targetRef;
/**
* Construct a representation of a source --- name ----> target
* relationship.
*
* @param sourceRef
* the source reference - never null
* @param assocTypeQName
* the qualified name of the association type - never null
* @param targetRef
* the target node reference - never null.
*/
public AssociationRef(NodeRef sourceRef, QName assocTypeQName, NodeRef targetRef)
{
this.sourceRef = sourceRef;
this.assocTypeQName = assocTypeQName;
this.targetRef = targetRef;
// check
if (sourceRef == null)
{
throw new IllegalArgumentException("Source reference may not be null");
}
if (assocTypeQName == null)
{
throw new IllegalArgumentException("QName may not be null");
}
if (targetRef == null)
{
throw new IllegalArgumentException("Target reference may not be null");
}
}
/**
* Get the qualified name of the source-target association
*
* @return Returns the qualified name of the source-target association.
*/
public QName getTypeQName()
{
return assocTypeQName;
}
/**
* @return Returns the child node reference - never null
*/
public NodeRef getTargetRef()
{
return targetRef;
}
/**
* @return Returns the parent node reference, which may be null if this
* represents the imaginary reference to the root node
*/
public NodeRef getSourceRef()
{
return sourceRef;
}
/**
* Compares:
* <ul>
* <li>{@link #sourceRef}</li>
* <li>{@link #targetRef}</li>
* <li>{@link #assocTypeQName}</li>
* </ul>
*/
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof ChildAssociationRef))
{
return false;
}
AssociationRef other = (AssociationRef) o;
return (EqualsHelper.nullSafeEquals(this.sourceRef, other.sourceRef)
&& EqualsHelper.nullSafeEquals(this.assocTypeQName, other.assocTypeQName)
&& EqualsHelper.nullSafeEquals(this.targetRef, other.targetRef));
}
public int hashCode()
{
int hashCode = (getSourceRef() == null) ? 0 : getSourceRef().hashCode();
hashCode = 37 * hashCode + ((getTypeQName() == null) ? 0 : getTypeQName().hashCode());
hashCode = 37 * hashCode + getTargetRef().hashCode();
return hashCode;
}
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append(getSourceRef());
buffer.append(" --- ").append(getTypeQName()).append(" ---> ");
buffer.append(getTargetRef());
return buffer.toString();
}
}

View File

@@ -0,0 +1,228 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
/**
* This class represents a child relationship between two nodes. This
* relationship is named.
* <p>
* So it requires the parent node ref, the child node ref and the name of the
* child within the particular parent.
* <p>
* This combination is not a unique identifier for the relationship with regard
* to structure. In use this does not matter as we have no concept of order,
* particularly in the index.
*
* @author andyh
*
*/
public class ChildAssociationRef
implements EntityRef, Comparable<ChildAssociationRef>, Serializable
{
private static final long serialVersionUID = 4051322336257127729L;
private QName assocTypeQName;
private NodeRef parentRef;
private QName childQName;
private NodeRef childRef;
private boolean isPrimary;
private int nthSibling;
/**
* Construct a representation of a parent --- name ----> child relationship.
*
* @param assocTypeQName
* the type of the association
* @param parentRef
* the parent reference - may be null
* @param childQName
* the qualified name of the association - may be null
* @param childRef
* the child node reference. This must not be null.
* @param isPrimary
* true if this represents the primary parent-child relationship
* @param nthSibling
* the nth association with the same properties. Usually -1 to be
* ignored.
*/
public ChildAssociationRef(
QName assocTypeQName,
NodeRef parentRef,
QName childQName,
NodeRef childRef,
boolean isPrimary,
int nthSibling)
{
this.assocTypeQName = assocTypeQName;
this.parentRef = parentRef;
this.childQName = childQName;
this.childRef = childRef;
this.isPrimary = isPrimary;
this.nthSibling = nthSibling;
// check
if (childRef == null)
{
throw new IllegalArgumentException("Child reference may not be null");
}
}
/**
* Constructs a <b>non-primary</b>, -1th sibling parent-child association
* reference.
*
* @see ChildAssociationRef#ChildAssocRef(QName, NodeRef, QName, NodeRef, boolean, int)
*/
public ChildAssociationRef(QName assocTypeQName, NodeRef parentRef, QName childQName, NodeRef childRef)
{
this(assocTypeQName, parentRef, childQName, childRef, false, -1);
}
/**
* Compares:
* <ul>
* <li>{@link #assocTypeQName}</li>
* <li>{@link #parentRef}</li>
* <li>{@link #childRef}</li>
* <li>{@link #childQName}</li>
* </ul>
*/
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof ChildAssociationRef))
{
return false;
}
ChildAssociationRef other = (ChildAssociationRef) o;
return (EqualsHelper.nullSafeEquals(this.assocTypeQName, other.assocTypeQName)
&& EqualsHelper.nullSafeEquals(this.parentRef, other.parentRef)
&& EqualsHelper.nullSafeEquals(this.childQName, other.childQName)
&& EqualsHelper.nullSafeEquals(this.childRef, other.childRef));
}
public int hashCode()
{
int hashCode = ((getTypeQName() == null) ? 0 : getTypeQName().hashCode());
hashCode = 37 * hashCode + ((getParentRef() == null) ? 0 : getParentRef().hashCode());
hashCode = 37 * hashCode + ((getQName() == null) ? 0 : getQName().hashCode());
hashCode = 37 * hashCode + getChildRef().hashCode();
return hashCode;
}
/**
* @see #setNthSibling(int)
*/
public int compareTo(ChildAssociationRef another)
{
int thisVal = this.nthSibling;
int anotherVal = another.nthSibling;
return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
}
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("[").append(getTypeQName()).append("]");
sb.append(getParentRef());
sb.append(" --- ").append(getQName()).append(" ---> ");
sb.append(getChildRef());
return sb.toString();
}
/**
* Get the qualified name of the association type
*
* @return Returns the qualified name of the parent-child association type
* as defined in the data dictionary. It may be null if this is the
* imaginary association to the root node.
*/
public QName getTypeQName()
{
return assocTypeQName;
}
/**
* Get the qualified name of the parent-child association
*
* @return Returns the qualified name of the parent-child association. It
* may be null if this is the imaginary association to a root node.
*/
public QName getQName()
{
return childQName;
}
/**
* @return Returns the child node reference - never null
*/
public NodeRef getChildRef()
{
return childRef;
}
/**
* @return Returns the parent node reference, which may be null if this
* represents the imaginary reference to the root node
*/
public NodeRef getParentRef()
{
return parentRef;
}
/**
* @return Returns true if this represents a primary association
*/
public boolean isPrimary()
{
return isPrimary;
}
/**
* @return Returns the nth sibling required
*/
public int getNthSibling()
{
return nthSibling;
}
/**
* Allows post-creation setting of the ordering index. This is a helper
* so that sorted sets and lists can be easily sorted.
* <p>
* This index is <b>in no way absolute</b> and should change depending on
* the results that appear around this instance. Therefore, the sibling
* number cannot be used to construct, say, sibling number 5. Sibling
* number 5 will exist only in results where there are siblings 1 - 4.
*
* @param nthSibling the sibling index
*/
public void setNthSibling(int nthSibling)
{
this.nthSibling = nthSibling;
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.service.cmr.repository;
import org.alfresco.service.transaction.TransactionService;
/**
* Interface for instances that provide read and write access to content.
*
* @author Derek Hulley
*/
public interface ContentAccessor
{
/**
* Use this method to register any interest in events against underlying
* content streams.
* {@link #getContentOutputStream() output stream}.
* <p>
* This method can only be used before the content stream has been retrieved.
* <p>
* When the stream has been closed, all listeners will be called
* within a {@link #setTransactionService(TransactionService) transaction} -
* to this end, a {@link TransactionService} must have been set as well.
*
* @param listener a listener that will be called for output stream
* event notification
*
* @see #setTransactionService(TransactionService)
*/
public void addListener(ContentStreamListener listener);
/**
* Set the transaction provider that will be used when stream listeners are called.
*
* @param transactionService a transaction provider
*/
public void setTransactionService(TransactionService transactionService);
/**
* Gets the size of the content that this reader references.
*
* @return Returns the document byte length, or <code>OL</code> if the
* content doesn't {@link #exists() exist}.
*/
public long getSize();
/**
* Get the data representation of the content being accessed.
* <p>
* The content {@link #setMimetype(String) mimetype } must be set before this
* method is called as the content data requires a mimetype whenever the
* content URL is specified.
*
* @return Returns the content data
*
* @see ContentData#ContentData(String, String, long, String)
*/
public ContentData getContentData();
/**
* Retrieve the URL that this accessor references
*
* @return the content URL
*/
public String getContentUrl();
/**
* Get the content mimetype
*
* @return Returns a content mimetype
*/
public String getMimetype();
/**
* Set the mimetype that must be used for accessing the content
*
* @param mimetype the content mimetype
*/
public void setMimetype(String mimetype);
/**
* Get the encoding of the content being accessed
*
* @return Returns a valid java String encoding
*/
public String getEncoding();
/**
* Set the <code>String</code> encoding for this accessor
*
* @param encoding a java-recognised encoding format
*/
public void setEncoding(String encoding);
}

View File

@@ -0,0 +1,246 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.util.EqualsHelper;
/**
* The compound property representing content
*
* @author Derek Hulley
*/
public class ContentData implements Serializable
{
private static final long serialVersionUID = 8979634213050121462L;
private static char[] INVALID_CONTENT_URL_CHARS = new char[] {'|'};
private final String contentUrl;
private final String mimetype;
private final long size;
private final String encoding;
/**
* Construct a content property from a string
*
* @param contentPropertyStr the string representing the content details
* @return Returns a bean version of the string
*/
public static ContentData createContentProperty(String contentPropertyStr)
{
// get the content url
int contentUrlIndex = contentPropertyStr.indexOf("contentUrl=");
if (contentUrlIndex == -1)
{
throw new AlfrescoRuntimeException(
"ContentData string does not have a content URL: " +
contentPropertyStr);
}
int mimetypeIndex = contentPropertyStr.indexOf("|mimetype=", contentUrlIndex + 11);
if (mimetypeIndex == -1)
{
throw new AlfrescoRuntimeException(
"ContentData string does not have a mimetype: " +
contentPropertyStr);
}
int sizeIndex = contentPropertyStr.indexOf("|size=", mimetypeIndex + 10);
if (sizeIndex == -1)
{
throw new AlfrescoRuntimeException(
"ContentData string does not have a size: " +
contentPropertyStr);
}
int encodingIndex = contentPropertyStr.indexOf("|encoding=", sizeIndex + 6);
if (encodingIndex == -1)
{
throw new AlfrescoRuntimeException(
"ContentData string does not have an encoding: " +
contentPropertyStr);
}
String contentUrl = contentPropertyStr.substring(contentUrlIndex + 11, mimetypeIndex);
if (contentUrl.length() == 0)
contentUrl = null;
String mimetype = contentPropertyStr.substring(mimetypeIndex + 10, sizeIndex);
if (mimetype.length() == 0)
mimetype = null;
String sizeStr = contentPropertyStr.substring(sizeIndex + 6, encodingIndex);
if (sizeStr.length() == 0)
sizeStr = "0";
String encoding = contentPropertyStr.substring(encodingIndex + 10);
if (encoding.length() == 0)
encoding = null;
long size = Long.valueOf(sizeStr);
ContentData property = new ContentData(contentUrl, mimetype, size, encoding);
// done
return property;
}
/**
* Constructs a new instance using the existing one as a template, but replacing the
* mimetype
*
* @param existing an existing set of content data, null to use default values
* @param mimetype the mimetype to set
* @return Returns a new, immutable instance of the data
*/
public static ContentData setMimetype(ContentData existing, String mimetype)
{
ContentData ret = new ContentData(
existing == null ? null : existing.contentUrl,
mimetype,
existing == null ? 0L : existing.size,
existing == null ? "UTF-8" : existing.encoding);
// done
return ret;
}
/**
* Create a compound set of data representing a single instance of <i>content</i>.
* <p>
* In order to ensure data integrity, the {@link #getMimetype() mimetype}
* must be set if the {@link #getContentUrl() content URL} is set.
*
* @param contentUrl the content URL. If this value is non-null, then the
* <b>mimetype</b> must be supplied.
* @param mimetype the content mimetype. This is mandatory if the <b>contentUrl</b> is specified.
* @param size the content size.
* @param encoding the content encoding.
*/
public ContentData(String contentUrl, String mimetype, long size, String encoding)
{
checkContentUrl(contentUrl, mimetype);
this.contentUrl = contentUrl;
this.mimetype = mimetype;
this.size = size;
this.encoding = encoding;
}
public boolean equals(Object obj)
{
if (obj == this)
return true;
else if (obj == null)
return false;
else if (!(obj instanceof ContentData))
return false;
ContentData that = (ContentData) obj;
return (EqualsHelper.nullSafeEquals(this.contentUrl, that.contentUrl) &&
EqualsHelper.nullSafeEquals(this.mimetype, that.mimetype) &&
this.size == that.size &&
EqualsHelper.nullSafeEquals(this.encoding, that.encoding));
}
/**
* @return Returns a string of form: <code>contentUrl=xxx;mimetype=xxx;size=xxx;encoding=xxx</code>
*/
public String toString()
{
StringBuilder sb = new StringBuilder(80);
sb.append("contentUrl=").append(contentUrl == null ? "" : contentUrl)
.append("|mimetype=").append(mimetype == null ? "" : mimetype)
.append("|size=").append(size)
.append("|encoding=").append(encoding == null ? "" : encoding);
return sb.toString();
}
/**
* @return Returns a URL identifying the specific location of the content.
* The URL must identify, within the context of the originating content
* store, the exact location of the content.
* @throws ContentIOException
*/
public String getContentUrl()
{
return contentUrl;
}
/**
* Checks that the content URL is correct, and also that the mimetype is
* non-null if the URL is present.
*
* @param contentUrl the content URL to check
* @param mimetype
*/
private void checkContentUrl(String contentUrl, String mimetype)
{
// check the URL
if (contentUrl != null && contentUrl.length() > 0)
{
for (int i = 0; i < INVALID_CONTENT_URL_CHARS.length; i++)
{
for (int j = contentUrl.length() - 1; j > -1; j--)
{
if (contentUrl.charAt(j) == INVALID_CONTENT_URL_CHARS[i])
{
throw new IllegalArgumentException(
"The content URL contains an invalid char: \n" +
" content URL: " + contentUrl + "\n" +
" char: " + INVALID_CONTENT_URL_CHARS[i] + "\n" +
" position: " + j);
}
}
}
// check that mimetype is present if URL is present
if (mimetype == null)
{
throw new IllegalArgumentException(
"The content mimetype must be set whenever the URL is set: \n" +
" content URL: " + contentUrl + "\n" +
" mimetype: " + mimetype);
}
}
}
/**
* Gets content's mimetype.
*
* @return Returns a standard mimetype for the content or null if the mimetype
* is unkown
*/
public String getMimetype()
{
return mimetype;
}
/**
* Get the content's size
*
* @return Returns the size of the content
*/
public long getSize()
{
return size;
}
/**
* Gets the content's encoding.
*
* @return Returns a valid Java encoding, typically a character encoding, or
* null if the encoding is unkown
*/
public String getEncoding()
{
return encoding;
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.service.cmr.repository;
import junit.framework.TestCase;
/**
* @see org.alfresco.service.cmr.repository.ContentData
*
* @author Derek Hulley
*/
public class ContentDataTest extends TestCase
{
public ContentDataTest(String name)
{
super(name);
}
public void testToAndFromString() throws Exception
{
ContentData property = new ContentData(null, null, 0L, null);
// check null string
String propertyStr = property.toString();
assertEquals("Null values not converted correctly",
"contentUrl=|mimetype=|size=0|encoding=", propertyStr);
// convert back
ContentData checkProperty = ContentData.createContentProperty(propertyStr);
assertEquals("Conversion from string failed", property, checkProperty);
property = new ContentData("uuu", "mmm", 123L, "eee");
// convert to a string
propertyStr = property.toString();
assertEquals("Incorrect property string representation",
"contentUrl=uuu|mimetype=mmm|size=123|encoding=eee", propertyStr);
// convert back
checkProperty = ContentData.createContentProperty(propertyStr);
assertEquals("Conversion from string failed", property, checkProperty);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.service.cmr.repository;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Wraps a general <code>Exceptions</code> that occurred while reading or writing
* content.
*
* @see Throwable#getCause()
*
* @author Derek Hulley
*/
public class ContentIOException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = 3258130249983276087L;
public ContentIOException(String msg)
{
super(msg);
}
public ContentIOException(String msg, Throwable cause)
{
super(msg, cause);
}
}

View File

@@ -0,0 +1,168 @@
/*
* 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.service.cmr.repository;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.ReadableByteChannel;
/**
* Represents a handle to read specific content. Content may only be accessed
* once per instance.
* <p>
* Implementations of this interface <b>might</b> be <code>Serializable</code>
* but client code could should check suitability before attempting to serialize
* it.
* <p>
* Implementations that are able to provide inter-VM streaming, such as accessing
* WebDAV, would be <code>Serializable</code>. An accessor that has to access a
* local file on the server could not provide inter-VM streaming unless it specifically
* makes remote calls and opens sockets, etc.
*
* @see org.alfresco.service.cmr.repository.ContentWriter
*
* @author Derek Hulley
*/
public interface ContentReader extends ContentAccessor
{
/**
* Convenience method to get another reader onto the underlying content.
*
* @return Returns a reader onto the underlying content
* @throws ContentIOException
*/
public ContentReader getReader() throws ContentIOException;
/**
* Check if the {@link ContentAccessor#getContentUrl() underlying content} is present.
*
* @return Returns true if there is content at the URL refered to by this reader
*/
public boolean exists();
/**
* Gets the time of the last modification of the underlying content.
*
* @return Returns the last modification time using the standard <tt>long</tt>
* time, or <code>0L</code> if the content doesn't {@link #exists() exist}.
*
* @see System#currentTimeMillis()
*/
public long getLastModified();
/**
* Convenience method to find out if this reader has been closed.
* Once closed, the content can no longer be read. This method could
* be used to wait for a particular read operation to complete, for example.
*
* @return Return true if the content input stream has been used and closed
* otherwise false.
*/
public boolean isClosed();
/**
* Provides low-level access to the underlying content.
* <p>
* Once the stream is provided to a client it should remain active
* (subject to any timeouts) until closed by the client.
*
* @return Returns a stream that can be read at will, but must be closed when completed
* @throws ContentIOException
*/
public ReadableByteChannel getReadableChannel() throws ContentIOException;
/**
* Get a stream to read from the underlying channel
*
* @return Returns an input stream onto the underlying channel
* @throws ContentIOException
*
* @see #getReadableChannel()
*/
public InputStream getContentInputStream() throws ContentIOException;
/**
* Gets content from the repository.
* <p>
* All resources will be closed automatically.
* <p>
* Care must be taken that the bytes read from the stream are properly
* decoded according to the {@link ContentAccessor#getEncoding() encoding}
* property.
*
* @param os the stream to which to write the content
* @throws ContentIOException
*
* @see #getReadableChannel()
*/
public void getContent(OutputStream os) throws ContentIOException;
/**
* Gets content from the repository direct to file
* <p>
* All resources will be closed automatically.
*
* @param file the file to write the content to - it will be overwritten
* @throws ContentIOException
*
* @see #getContentInputStream()
*/
public void getContent(File file) throws ContentIOException;
/**
* Gets content from the repository direct to <code>String</code>.
* <p>
* If the {@link ContentAccessor#getEncoding() encoding } is known then it will be used
* otherwise the default system <tt>byte[]</tt> to <tt>String</tt> conversion
* will be used.
* <p>
* All resources will be closed automatically.
* <p>
* <b>WARNING: </b> This should only be used when the size of the content
* is known in advance.
*
* @return Returns a String representation of the content
* @throws ContentIOException
*
* @see #getContentString(int)
* @see #getContentInputStream()
* @see String#String(byte[])
*/
public String getContentString() throws ContentIOException;
/**
* Gets content from the repository direct to <code>String</code>, but limiting
* the string size to a given number of characters.
* <p>
* If the {@link ContentAccessor#getEncoding() encoding } is known then it will be used
* otherwise the default system <tt>byte[]</tt> to <tt>String</tt> conversion
* will be used.
* <p>
* All resources will be closed automatically.
*
* @param length the maximum number of characters to retrieve
* @return Returns a truncated String representation of the content
* @throws ContentIOException
* @throws java.lang.IllegalArgumentException if the length is < 0 or > {@link Integer#MAX_VALUE}
*
* @see #getContentString()
* @see #getContentInputStream()
* @see String#String(byte[])
*/
public String getContentString(int length) throws ContentIOException;
}

View File

@@ -0,0 +1,124 @@
/*
* 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.service.cmr.repository;
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
import org.alfresco.service.namespace.QName;
/**
* Provides methods for accessing and transforming content.
* <p>
* Implementations of this service are primarily responsible for ensuring
* that the correct store is used to access content, and that reads and
* writes for the same node reference are routed to the same store instance.
* <p>
* The mechanism for selecting an appropriate store is not prescribed by
* the interface, but typically the decision will be made on the grounds
* of content type.
* <p>
* Whereas the content stores have no knowledge of nodes other than their
* references, the <code>ContentService</code> <b>is</b> responsible for
* ensuring that all the relevant node-content relationships are maintained.
*
* @see org.alfresco.repo.content.ContentStore
* @see org.alfresco.service.cmr.repository.ContentReader
* @see org.alfresco.service.cmr.repository.ContentWriter
*
* @author Derek Hulley
*/
public interface ContentService
{
/**
* Gets a reader for the content associated with the given node property.
* <p>
* If a content URL is present for the given node then a reader <b>must</b>
* be returned. The {@link ContentReader#exists() exists} method should then
* be used to detect 'missing' content.
*
* @param nodeRef a reference to a node having a content property
* @param propertyQName the name of the property, which must be of type <b>content</b>
* @return Returns a reader for the content associated with the node property,
* or null if no content has been written for the property
* @throws InvalidNodeRefException if the node doesn't exist
* @throws InvalidTypeException if the node is not of type <b>content</b>
*
* @see org.alfresco.repo.content.filestore.FileContentReader#getSafeContentReader(ContentReader, String, Object[])
*/
public ContentReader getReader(NodeRef nodeRef, QName propertyQName)
throws InvalidNodeRefException, InvalidTypeException;
/**
* Get a content writer for the given node property, choosing to optionally have
* the node property updated automatically when the content stream closes.
* <p>
* If the update flag is off, then the state of the node property will remain unchanged
* regardless of the state of the written binary data. If the flag is on, then the node
* property will be updated on the same thread as the code that closed the write
* channel.
*
* @param nodeRef a reference to a node having a content property
* @param propertyQName the name of the property, which must be of type <b>content</b>
* @param update true if the property must be updated atomically when the content write
* stream is closed (attaches a listener to the stream); false if the client code
* will perform the updates itself.
* @return Returns a writer for the content associated with the node property
* @throws InvalidNodeRefException if the node doesn't exist
* @throws InvalidTypeException if the node property is not of type <b>content</b>
*/
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
throws InvalidNodeRefException, InvalidTypeException;
/**
* Gets a writer to a temporary location. The longevity of the stored
* temporary content is determined by the system.
*
* @return Returns a writer onto a temporary location
*/
public ContentWriter getTempWriter();
/**
* Transforms the content from the reader and writes the content
* back out to the writer.
* <p>
* The mimetypes used for the transformation must be set both on
* the {@link ContentAccessor#getMimetype() reader} and on the
* {@link ContentAccessor#getMimetype() writer}.
*
* @param reader the source content location and mimetype
* @param writer the target content location and mimetype
* @throws NoTransformerException if no transformer exists for the
* given source and target mimetypes of the reader and writer
* @throws ContentIOException if the transformation fails
*/
public void transform(ContentReader reader, ContentWriter writer)
throws NoTransformerException, ContentIOException;
/**
* Returns whether a transformer exists that can read the content from
* the reader and write the content back out to the writer.
* <p>
* The mimetypes used for the transformation must be set both on
* the {@link ContentAccessor#getMimetype() reader} and on the
* {@link ContentAccessor#getMimetype() writer}.
*
* @param reader the source content location and mimetype
* @param writer the target content location and mimetype
*
* @return true if a transformer exists, false otherwise
*/
public boolean isTransformable(ContentReader reader, ContentWriter writer);
}

View File

@@ -0,0 +1,33 @@
/*
* 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.service.cmr.repository;
/**
* Listens for notifications w.r.t. content. This includes receiving notifications
* of the opening and closing of the content streams.
*
* @author Derek Hulley
*/
public interface ContentStreamListener
{
/**
* Called when the stream associated with a reader or writer is closed
*
* @throws ContentIOException
*/
public void contentStreamClosed() throws ContentIOException;
}

View File

@@ -0,0 +1,142 @@
/*
* 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.service.cmr.repository;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.WritableByteChannel;
/**
* Represents a handle to write specific content. Content may only be accessed
* once per instance.
* <p>
* Implementations of this interface <b>might</b> be <code>Serializable</code>
* but client code could should check suitability before attempting to serialize
* it.
* <p>
* Implementations that are able to provide inter-VM streaming, such as accessing
* WebDAV, would be <code>Serializable</code>. An accessor that has to access a
* local file on the server could not provide inter-VM streaming unless it specifically
* makes remote calls and opens sockets, etc.
*
* @see org.alfresco.service.cmr.repository.ContentReader
*
* @author Derek Hulley
*/
public interface ContentWriter extends ContentAccessor
{
/**
* Convenience method to get a reader onto newly written content. This
* method will return null if the content has not yet been written by the
* writer or if the output stream is still open.
*
* @return Returns a reader onto the underlying content that this writer
* will or has written to
* @throws ContentIOException
*/
public ContentReader getReader() throws ContentIOException;
/**
* Convenience method to find out if this writer has been closed.
* Once closed, the content can no longer be written to and it become possible
* to get readers onto the written content.
*
* @return Return true if the content output stream has been used and closed
* otherwise false.
*/
public boolean isClosed();
/**
* Provides low-level access to write to repository content.
* <p>
* The channel returned to the client should remain open (subject to timeouts)
* until closed by the client. All lock detection, read-only access and other
* concurrency issues are dealt with during this operation. It remains
* possible that implementations will throw exceptions when the channel is closed.
* <p>
* The stream will notify any listeners according to the listener interface.
*
* @return Returns a channel with which to write content
* @throws ContentIOException
*/
public WritableByteChannel getWritableChannel() throws ContentIOException;
/**
* Get a stream to write to the underlying channel.
*
* @return Returns an output stream onto the underlying channel
* @throws ContentIOException
*
* @see #getWritableChannel()
*/
public OutputStream getContentOutputStream() throws ContentIOException;
/**
* Copies content from the reader.
* <p>
* All resources will be closed automatically.
*
* @param reader the reader acting as the source of the content
* @throws ContentIOException
*
* @see #getWritableChannel()
*/
public void putContent(ContentReader reader) throws ContentIOException;
/**
* Puts content to the repository
* <p>
* All resources will be closed automatically.
*
* @param is the input stream from which the content will be read
* @throws ContentIOException
*
* @see #getWritableChannel()
*/
public void putContent(InputStream is) throws ContentIOException;
/**
* Puts content to the repository direct from file
* <p>
* All resources will be closed automatically.
*
* @param file the file to load the content from
* @throws ContentIOException
*
* @see #getWritableChannel()
*/
public void putContent(File file) throws ContentIOException;
/**
* Puts content to the repository direct from <code>String</code>.
* <p>
* If the {@link ContentAccessor#getEncoding() encoding } is known then it will be used
* otherwise the default system <tt>String</tt> to <tt>byte[]</tt> conversion
* will be used.
* <p>
* All resources will be closed automatically.
*
* @param content a string representation of the content
* @throws ContentIOException
*
* @see #getWritableChannel()
* @see String#getBytes(java.lang.String)
*/
public void putContent(String content) throws ContentIOException;
}

View File

@@ -0,0 +1,119 @@
/*
* 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.service.cmr.repository;
import org.alfresco.service.namespace.QName;
/**
* Node operations service interface.
* <p>
* This interface provides methods to copy nodes within and across workspaces and to
* update the state of a node, with that of another node, within and across workspaces.
*
* @author Roy Wetherall
*/
public interface CopyService
{
/**
* Creates a copy of the given node.
* <p>
* If the new node resides in a different workspace the new node will
* have the same id.
* <p>
* If the new node resides in the same workspace then
* the new node will have the Copy aspect applied to it which will
* reference the origional node.
* <p>
* The aspects applied to source node will also be applied to destination node
* and all the property value will be duplicated accordingly. This is with the
* exception of the aspects that have been marked as having 'Non-Transferable State'.
* In this case the aspect will be applied to the copy, but the properties will take
* on the default values.
* <p>
* Child associations are copied onto the destination node. If the child of
* copied association is not present in the destination workspace the child
* association is not copied. This is unless is has been specfied that the
* children of the source node should also be copied.
* <p>
* Target associations are copied to the destination node. If the target of the
* association is not present in the destination workspace then the association is
* not copied.
* <p>
* Source association are not copied.
*
* @param sourceNodeRef the node reference used as the source of the copy
* @param destinationParent the intended parent of the new node
* @param destinationAssocTypeQName the type of the new child assoc
* @param destinationQName the qualified name of the child association from the
* parent to the new node
*
* @return the new node reference
*/
public NodeRef copy(
NodeRef sourceNodeRef,
NodeRef destinationParent,
QName destinationAssocTypeQName,
QName destinationQName,
boolean copyChildren);
/**
* By default children of the source node are not copied.
*
* @see NodeCopyService#copy(NodeRef, NodeRef, QName, QName, boolean)
*
* @param sourceNodeRef the node reference used as the source of the copy
* @param destinationParent the intended parent of the new node
* @param destinationAssocTypeQName the type of the new child assoc
* @param destinationQName the qualified name of the child association from the
* parent to the new node
* @return the new node reference
*/
public NodeRef copy(
NodeRef sourceNodeRef,
NodeRef destinationParent,
QName destinationAssocTypeQName,
QName destinationQName);
/**
* Copies the state of one node on top of another.
* <p>
* The state of destination node is overlayed with the state of the
* source node. Any conflicts are resolved by setting the state to
* that of the source node.
* <p>
* If data (for example an association) does not exist on the source
* node, but does exist on the detination node this data is NOT deleted
* from the destination node.
* <p>
* Child associations and target associations are updated on the destination
* based on the current state of the source node.
* <p>
* If the node that either a child or target association points to on the source
* node is not present in the destinations workspace then the association is not
* updated to the destination node.
* <p>
* All aspects found on the source node are applied to the destination node where
* missing. The properties of the apects are updated accordingly except in the case
* where the aspect has been marked as having 'Non-Transferable State'. In this case
* aspect properties will take on the values already assigned to them in the
* destination node.
*
* @param sourceNodeRef the source node reference
* @param destinationNodeRef the destination node reference
*/
public void copy(NodeRef sourceNodeRef, NodeRef destinationNodeRef);
}

View File

@@ -0,0 +1,48 @@
/*
* 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.service.cmr.repository;
/**
* Nodes operations service exception class.
*
* @author Roy Wetherall
*/
public class CopyServiceException extends RuntimeException
{
/**
* Serial version UID
*/
private static final long serialVersionUID = 3256727273112614964L;
/**
* Constructor
*/
public CopyServiceException()
{
super();
}
/**
* Constructor
*
* @param message the error message
*/
public CopyServiceException(String message)
{
super(message);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.service.cmr.repository;
import org.alfresco.repo.domain.ChildAssoc;
/**
* Thrown when a cyclic parent-child relationship is detected.
*
* @author Derek Hulley
*/
public class CyclicChildRelationshipException extends RuntimeException
{
private static final long serialVersionUID = 3545794381924874036L;
private ChildAssoc assoc;
public CyclicChildRelationshipException(String msg, ChildAssoc assoc)
{
super(msg);
this.assoc = assoc;
}
public ChildAssoc getAssoc()
{
return assoc;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.service.cmr.repository;
/**
* A marker interface for entity reference classes.
* <p>
* This is used primarily as a means of ensuring type safety in collections
* of mixed type references.
*
* @see org.alfresco.service.cmr.repository.NodeService#removeChildren(NodeRef, QName)
*
* @author Derek Hulley
*/
public interface EntityRef
{
}

View File

@@ -0,0 +1,49 @@
/*
* 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.service.cmr.repository;
/**
* Thrown when an operation cannot be performed because the<b>child association</b>
* reference no longer exists.
*
* @author Derek Hulley
*/
public class InvalidChildAssociationRefException extends RuntimeException
{
private static final long serialVersionUID = -7493054268618534572L;
private ChildAssociationRef childAssociationRef;
public InvalidChildAssociationRefException(ChildAssociationRef childAssociationRef)
{
this(null, childAssociationRef);
}
public InvalidChildAssociationRefException(String msg, ChildAssociationRef childAssociationRef)
{
super(msg);
this.childAssociationRef = childAssociationRef;
}
/**
* @return Returns the offending child association reference
*/
public ChildAssociationRef getChildAssociationRef()
{
return childAssociationRef;
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.service.cmr.repository;
/**
* Thrown when an operation cannot be performed because the <b>node</b> reference
* no longer exists.
*
* @author Derek Hulley
*/
public class InvalidNodeRefException extends RuntimeException
{
private static final long serialVersionUID = 3689345520586273336L;
private NodeRef nodeRef;
public InvalidNodeRefException(NodeRef nodeRef)
{
this(null, nodeRef);
}
public InvalidNodeRefException(String msg, NodeRef nodeRef)
{
super(msg);
this.nodeRef = nodeRef;
}
/**
* @return Returns the offending node reference
*/
public NodeRef getNodeRef()
{
return nodeRef;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.service.cmr.repository;
/**
* Thrown when an operation cannot be performed because the <b>store</b> reference
* no longer exists.
*
* @author Derek Hulley
*/
public class InvalidStoreRefException extends AbstractStoreException
{
private static final long serialVersionUID = 3258126938479409463L;
public InvalidStoreRefException(StoreRef storeRef)
{
super(storeRef);
}
public InvalidStoreRefException(String msg, StoreRef storeRef)
{
super(msg, storeRef);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.service.cmr.repository;
import java.util.List;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* This service interface provides support for Mimetypes.
*
* @author Derek Hulley
*
*/
public interface MimetypeService
{
/**
* Get the extension for the specified mimetype
*
* @param mimetype a valid mimetype
* @return Returns the default extension for the mimetype
* @throws AlfrescoRuntimeException if the mimetype doesn't exist
*/
public String getExtension(String mimetype);
/**
* Get all human readable mimetype descriptions indexed by mimetype extension
*
* @return the map of displays indexed by extension
*/
public Map<String, String> getDisplaysByExtension();
/**
* Get all human readable mimetype descriptions indexed by mimetype
*
* @return the map of displays indexed by mimetype
*/
public Map<String, String> getDisplaysByMimetype();
/**
* Get all mimetype extensions indexed by mimetype
*
* @return the map of extension indexed by mimetype
*/
public Map<String, String> getExtensionsByMimetype();
/**
* Get all mimetypes indexed by extension
*
* @return the map of mimetypes indexed by extension
*/
public Map<String, String> getMimetypesByExtension();
/**
* Get all mimetypes
*
* @return all mimetypes
*/
public List<String> getMimetypes();
/**
* Provides a non-null best guess of the appropriate mimetype given a
* filename.
*
* @param filename the name of the file with an optional file extension
* @return Returns the best guess mimetype or the mimetype for
* straight binary files if no extension could be found.
*/
public String guessMimetype(String filename);
}

View File

@@ -0,0 +1,59 @@
/*
* 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.service.cmr.repository;
import java.text.MessageFormat;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Thrown when a transformation request cannot be honoured due to
* no transformers being present for the requested transformation.
*
* @author Derek Hulley
*/
public class NoTransformerException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = 3689067335554183222L;
private static final MessageFormat MSG =
new MessageFormat("No transformation exists between mimetypes {0} and {1}");
private String sourceMimetype;
private String targetMimetype;
/**
* @param sourceMimetype the attempted source mimetype
* @param targetMimetype the attempted target mimetype
*/
public NoTransformerException(String sourceMimetype, String targetMimetype)
{
super(MSG.format(new Object[] {sourceMimetype, targetMimetype}));
this.sourceMimetype = sourceMimetype;
this.targetMimetype = targetMimetype;
}
public String getSourceMimetype()
{
return sourceMimetype;
}
public String getTargetMimetype()
{
return targetMimetype;
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Reference to a node
*
* @author Derek Hulley
*/
public final class NodeRef implements EntityRef, Serializable
{
private static final long serialVersionUID = 3760844584074227768L;
private static final String URI_FILLER = "/";
private final StoreRef storeRef;
private final String id;
/**
* Construct a Node Reference from a Store Reference and Node Id
*
* @param storeRef store reference
* @param id the manually assigned identifier of the node
*/
public NodeRef(StoreRef storeRef, String id)
{
if (storeRef == null)
{
throw new IllegalArgumentException(
"Store reference may not be null");
}
if (id == null)
{
throw new IllegalArgumentException("Node id may not be null");
}
this.storeRef = storeRef;
this.id = id;
}
/**
* Construct a Node Reference from a string representation of a Node Reference.
* <p>
* The string representation of a Node Reference is as follows:
* <p>
* <pre><storeref>/<nodeId></pre>
*
* @param nodeRef the string representation of a node ref
*/
public NodeRef(String nodeRef)
{
int lastForwardSlash = nodeRef.lastIndexOf('/');
if(lastForwardSlash == -1)
{
throw new AlfrescoRuntimeException("Invalid node ref - does not contain forward slash: " + nodeRef);
}
this.storeRef = new StoreRef(nodeRef.substring(0, lastForwardSlash));
this.id = nodeRef.substring(lastForwardSlash+1);
}
public String toString()
{
return storeRef.toString() + URI_FILLER + id;
}
/**
* Override equals for this ref type
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj instanceof NodeRef)
{
NodeRef that = (NodeRef) obj;
return (this.id.equals(that.id)
&& this.storeRef.equals(that.storeRef));
}
else
{
return false;
}
}
/**
* Hashes on ID alone. As the number of copies of a particular node will be minimal, this is acceptable
*/
public int hashCode()
{
return id.hashCode();
}
/**
* @return The StoreRef part of this reference
*/
public final StoreRef getStoreRef()
{
return storeRef;
}
/**
* @return The Node Id part of this reference
*/
public final String getId()
{
return id;
}
/**
* Helper class to convey the status of a <b>node</b>.
*
* @author Derek Hulley
*/
public static class Status
{
private final String changeTxnId;
private final boolean deleted;
public Status(String changeTxnId, boolean deleted)
{
this.changeTxnId = changeTxnId;
this.deleted = deleted;
}
/**
* @return Returns the ID of the last transaction to change the node
*/
public String getChangeTxnId()
{
return changeTxnId;
}
/**
* @return Returns true if the node has been deleted, otherwise false
*/
public boolean isDeleted()
{
return deleted;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.service.cmr.repository;
import junit.framework.TestCase;
/**
* @see org.alfresco.service.cmr.repository.NodeRef
*
* @author Derek Hulley
*/
public class NodeRefTest extends TestCase
{
public NodeRefTest(String name)
{
super(name);
}
public void testStoreRef() throws Exception
{
StoreRef storeRef = new StoreRef("ABC", "123");
assertEquals("toString failure", "ABC://123", storeRef.toString());
StoreRef storeRef2 = new StoreRef(storeRef.getProtocol(), storeRef
.getIdentifier());
assertEquals("equals failure", storeRef, storeRef2);
}
public void testNodeRef() throws Exception
{
StoreRef storeRef = new StoreRef("ABC", "123");
NodeRef nodeRef = new NodeRef(storeRef, "456");
assertEquals("toString failure", "ABC://123/456", nodeRef.toString());
NodeRef nodeRef2 = new NodeRef(storeRef, "456");
assertEquals("equals failure", nodeRef, nodeRef2);
}
}

View File

@@ -0,0 +1,478 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.service.cmr.dictionary.InvalidAspectException;
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNamePattern;
/**
* Interface for public and internal <b>node</b> and <b>store</b> operations.
*
* @author Derek Hulley
*/
public interface NodeService
{
/**
* Gets a list of all available node store references
*
* @return Returns a list of store references
*/
public List<StoreRef> getStores();
/**
* Create a new store for the given protocol and identifier. The implementation
* may create the store in any number of locations, including a database or
* Subversion.
*
* @param protocol the implementation protocol
* @param identifier the protocol-specific identifier
* @return Returns a reference to the store
* @throws StoreExistsException
*/
public StoreRef createStore(String protocol, String identifier) throws StoreExistsException;
/**
* @param storeRef a reference to the store to look for
* @return Returns true if the store exists, otherwise false
*/
public boolean exists(StoreRef storeRef);
/**
* @param nodeRef a reference to the node to look for
* @return Returns true if the node exists, otherwise false
*/
public boolean exists(NodeRef nodeRef);
/**
* Gets the ID of the last transaction that caused the node to change. This includes
* deletions, so it is possible that the node being referenced no longer exists.
* If the node never existed, then null is returned.
*
* @param nodeRef a reference to a current or previously existing node
* @return Returns the status of the node, or null if the node never existed
*/
public NodeRef.Status getNodeStatus(NodeRef nodeRef);
/**
* @param storeRef a reference to an existing store
* @return Returns a reference to the root node of the store
* @throws InvalidStoreRefException if the store could not be found
*/
public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException;
/**
* @see #createNode(NodeRef, QName, QName, QName, Map)
*/
public ChildAssociationRef createNode(
NodeRef parentRef,
QName assocTypeQName,
QName assocQName,
QName nodeTypeQName)
throws InvalidNodeRefException, InvalidTypeException;
/**
* Creates a new, non-abstract, real node as a primary child of the given parent node.
*
* @param parentRef the parent node
* @param assocTypeQName the type of the association to create. This is used
* for verification against the data dictionary.
* @param assocQName the qualified name of the association
* @param nodeTypeQName a reference to the node type
* @param properties optional map of properties to keyed by their qualified names
* @return Returns a reference to the newly created child association
* @throws InvalidNodeRefException if the parent reference is invalid
* @throws InvalidTypeException if the node type reference is not recognised
*
* @see org.alfresco.service.cmr.dictionary.DictionaryService
*/
public ChildAssociationRef createNode(
NodeRef parentRef,
QName assocTypeQName,
QName assocQName,
QName nodeTypeQName,
Map<QName, Serializable> properties)
throws InvalidNodeRefException, InvalidTypeException;
/**
* Moves the primary location of the given node.
* <p>
* This involves changing the node's primary parent and possibly the name of the
* association referencing it.
*
* @param nodeToMoveRef the node to move
* @param newParentRef the new parent of the moved node
* @param assocTypeQName the type of the association to create. This is used
* for verification against the data dictionary.
* @param assocQName the qualified name of the new child association
* @return Returns a reference to the newly created child association
* @throws InvalidNodeRefException if either the parent node or move node reference is invalid
* @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add
*
* @see #getPrimaryParent(NodeRef)
*/
public ChildAssociationRef moveNode(
NodeRef nodeToMoveRef,
NodeRef newParentRef,
QName assocTypeQName,
QName assocQName)
throws InvalidNodeRefException;
/**
* Set the ordering index of the child association. This affects the ordering of
* of the return values of methods that return a set of children or child
* associations.
*
* @param childAssocRef the child association that must be moved in the order
* @param index an arbibrary index that will affect the return order
*
* @see #getChildAssocs(NodeRef)
* @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern)
* @see ChildAssociationRef#getNthSibling()
*/
public void setChildAssociationIndex(
ChildAssociationRef childAssocRef,
int index)
throws InvalidChildAssociationRefException;
/**
* @param nodeRef
* @return Returns the type name
* @throws InvalidNodeRefException if the node could not be found
*
* @see org.alfresco.service.cmr.dictionary.DictionaryService
*/
public QName getType(NodeRef nodeRef) throws InvalidNodeRefException;
/**
* Re-sets the type of the node. Can be called in order specialise a node to a sub-type.
*
* This should be used with caution since calling it changes the type of the node and thus
* implies a different set of aspects, properties and associations. It is the calling codes
* responsibility to ensure that the node is in a approriate state after changing the type.
*
* @param nodeRef the node reference
* @param typeQName the type QName
*
* @since 1.1
*/
public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException;
/**
* Applies an aspect to the given node. After this method has been called,
* the node with have all the aspect-related properties present
*
* @param nodeRef
* @param aspectTypeQName the aspect to apply to the node
* @param aspectProperties a minimum of the mandatory properties required for
* the aspect
* @throws InvalidNodeRefException
* @throws InvalidAspectException if the class reference is not to a valid aspect
*
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getAspect(QName)
* @see org.alfresco.service.cmr.dictionary.ClassDefinition#getProperties()
*/
public void addAspect(
NodeRef nodeRef,
QName aspectTypeQName,
Map<QName, Serializable> aspectProperties)
throws InvalidNodeRefException, InvalidAspectException;
/**
* Remove an aspect and all related properties from a node
*
* @param nodeRef
* @param aspectTypeQName the type of aspect to remove
* @throws InvalidNodeRefException if the node could not be found
* @throws InvalidAspectException if the the aspect is unknown or if the
* aspect is mandatory for the <b>class</b> of the <b>node</b>
*/
public void removeAspect(NodeRef nodeRef, QName aspectTypeQName)
throws InvalidNodeRefException, InvalidAspectException;
/**
* Determines if a given aspect is present on a node. Aspects may only be
* removed if they are <b>NOT</b> mandatory.
*
* @param nodeRef
* @param aspectRef
* @return Returns true if the aspect has been applied to the given node,
* otherwise false
* @throws InvalidNodeRefException if the node could not be found
* @throws InvalidAspectException if the aspect reference is invalid
*/
public boolean hasAspect(NodeRef nodeRef, QName aspectRef)
throws InvalidNodeRefException, InvalidAspectException;
/**
* @param nodeRef
* @return Returns a set of all aspects applied to the node, including mandatory
* aspects
* @throws InvalidNodeRefException if the node could not be found
*/
public Set<QName> getAspects(NodeRef nodeRef) throws InvalidNodeRefException;
/**
* Deletes the given node.
* <p>
* All associations (both children and regular node associations)
* will be deleted, and where the given node is the primary parent,
* the children will also be cascade deleted.
*
* @param nodeRef reference to a node within a store
* @throws InvalidNodeRefException if the reference given is invalid
*/
public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException;
/**
* Makes a parent-child association between the given nodes. Both nodes must belong to the same store.
* <p>
*
*
* @param parentRef
* @param childRef
* @param assocTypeQName the qualified name of the association type as defined in the datadictionary
* @param qname the qualified name of the association
* @return Returns a reference to the newly created child association
* @throws InvalidNodeRefException if the parent or child nodes could not be found
* @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add
*/
public ChildAssociationRef addChild(
NodeRef parentRef,
NodeRef childRef,
QName assocTypeQName,
QName qname) throws InvalidNodeRefException;
/**
* Severs all parent-child relationships between two nodes.
* <p>
* The child node will be cascade deleted if one of the associations was the
* primary association, i.e. the one with which the child node was created.
*
* @param parentRef the parent end of the association
* @param childRef the child end of the association
* @return Returns a collection of deleted entities - both associations and node references.
* @throws InvalidNodeRefException if the parent or child nodes could not be found
*/
public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException;
/**
* @param nodeRef
* @return Returns all properties keyed by their qualified name
* @throws InvalidNodeRefException if the node could not be found
*/
public Map<QName, Serializable> getProperties(NodeRef nodeRef) throws InvalidNodeRefException;
/**
* @param nodeRef
* @param qname the qualified name of the property
* @return Returns the value of the property, or null if not yet set
* @throws InvalidNodeRefException if the node could not be found
*/
public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException;
/**
* Set the values of all properties to be an <code>Serializable</code> instances.
* The properties given must still fulfill the requirements of the class and
* aspects relevant to the node.
* <p>
* <b>NOTE:</b> Null values <u>are</u> allowed.
*
* @param nodeRef
* @param properties all the properties of the node keyed by their qualified names
* @throws InvalidNodeRefException if the node could not be found
*/
public void setProperties(NodeRef nodeRef, Map<QName, Serializable> properties) throws InvalidNodeRefException;
/**
* Sets the value of a property to be any <code>Serializable</code> instance.
* To remove a property value, use {@link #getProperties(NodeRef)}, remove the
* value and call {@link #setProperties(NodeRef, Map<QName,Serializable>)}.
* <p>
* <b>NOTE:</b> Null values <u>are</u> allowed.
*
* @param nodeRef
* @param qname the fully qualified name of the property
* @param propertyValue the value of the property - never null
* @throws InvalidNodeRefException if the node could not be found
*/
public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException;
/**
* @param nodeRef the child node
* @return Returns a list of all parent-child associations that exist where the given
* node is the child
* @throws InvalidNodeRefException if the node could not be found
*
* @see #getParentAssocs(NodeRef, QNamePattern, QNamePattern)
*/
public List<ChildAssociationRef> getParentAssocs(NodeRef nodeRef) throws InvalidNodeRefException;
/**
* Gets all parent associations where the pattern of the association qualified
* name is a match
* <p>
* The resultant list is ordered by (a) explicit index and (b) association creation time.
*
* @param nodeRef the child node
* @param typeQNamePattern the pattern that the type qualified name of the association must match
* @param qnamePattern the pattern that the qnames of the assocs must match
* @return Returns a list of all parent-child associations that exist where the given
* node is the child
* @throws InvalidNodeRefException if the node could not be found
*
* @see ChildAssociationRef#getNthSibling()
* @see #setChildAssociationIndex(ChildAssociationRef, int)
* @see QName
* @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
*/
public List<ChildAssociationRef> getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
throws InvalidNodeRefException;
/**
* Get all child associations of the given node.
* <p>
* The resultant list is ordered by (a) explicit index and (b) association creation time.
*
* @param nodeRef the parent node - usually a <b>container</b>
* @return Returns a collection of <code>ChildAssocRef</code> instances. If the
* node is not a <b>container</b> then the result will be empty.
* @throws InvalidNodeRefException if the node could not be found
*
* @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern)
* @see #setChildAssociationIndex(ChildAssociationRef, int)
* @see ChildAssociationRef#getNthSibling()
*/
public List<ChildAssociationRef> getChildAssocs(NodeRef nodeRef) throws InvalidNodeRefException;
/**
* Gets all child associations where the pattern of the association qualified
* name is a match.
*
* @param nodeRef the parent node - usually a <b>container</b>
* @param typeQNamePattern the pattern that the type qualified name of the association must match
* @param qnamePattern the pattern that the qnames of the assocs must match
* @return Returns a list of <code>ChildAssocRef</code> instances. If the
* node is not a <b>container</b> then the result will be empty.
* @throws InvalidNodeRefException if the node could not be found
*
* @see QName
* @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
*/
public List<ChildAssociationRef> getChildAssocs(
NodeRef nodeRef,
QNamePattern typeQNamePattern,
QNamePattern qnamePattern)
throws InvalidNodeRefException;
/**
* Fetches the primary parent-child relationship.
* <p>
* For a root node, the parent node reference will be null.
*
* @param nodeRef
* @return Returns the primary parent-child association of the node
* @throws InvalidNodeRefException if the node could not be found
*/
public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException;
/**
*
* @param sourceRef a reference to a <b>real</b> node
* @param targetRef a reference to a node
* @param assocTypeQName the qualified name of the association type
* @return Returns a reference to the new association
* @throws InvalidNodeRefException if either of the nodes could not be found
* @throws AssociationExistsException
*/
public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
throws InvalidNodeRefException, AssociationExistsException;
/**
*
* @param sourceRef the associaton source node
* @param targetRef the association target node
* @param assocTypeQName the qualified name of the association type
* @throws InvalidNodeRefException if either of the nodes could not be found
*/
public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
throws InvalidNodeRefException;
/**
* Fetches all associations <i>from</i> the given source where the associations'
* qualified names match the pattern provided.
*
* @param sourceRef the association source
* @param qnamePattern the association qname pattern to match against
* @return Returns a list of <code>NodeAssocRef</code> instances for which the
* given node is a source
* @throws InvalidNodeRefException if the source node could not be found
*
* @see QName
* @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
*/
public List<AssociationRef> getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
throws InvalidNodeRefException;
/**
* Fetches all associations <i>to</i> the given target where the associations'
* qualified names match the pattern provided.
*
* @param targetRef the association target
* @param qnamePattern the association qname pattern to match against
* @return Returns a list of <code>NodeAssocRef</code> instances for which the
* given node is a target
* @throws InvalidNodeRefException
*
* @see QName
* @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
*/
public List<AssociationRef> getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
throws InvalidNodeRefException;
/**
* The root node has an entry in the path(s) returned. For this reason, there
* will always be <b>at least one</b> path element in the returned path(s).
* The first element will have a null parent reference and qname.
*
* @param nodeRef
* @return Returns the path to the node along the primary node path
* @throws InvalidNodeRefException if the node could not be found
*
* @see #getPaths(NodeRef, boolean)
*/
public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException;
/**
* The root node has an entry in the path(s) returned. For this reason, there
* will always be <b>at least one</b> path element in the returned path(s).
* The first element will have a null parent reference and qname.
*
* @param nodeRef
* @param primaryOnly true if only the primary path must be retrieved. If true, the
* result will have exactly one entry.
* @return Returns a List of all possible paths to the given node
* @throws InvalidNodeRefException if the node could not be found
*/
public List<Path> getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException;
}

View File

@@ -0,0 +1,580 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedList;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.ISO9075;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
/**
* Representation of a simple path e.g.
* <b><pre>
* /x/y/z
* </pre></b>
* In the above example, there will be <b>4</b> elements, the first being a reference
* to the root node, followed by qname elements for <b>x</b>, <b>x</b> and <b>z</b>.
* <p>
* Methods and constructors are available to construct a <code>Path</code> instance
* from a path string or by building the path incrementally, including the ability to
* append and prepend path elements.
* <p>
* Path elements supported:
* <ul>
* <li><b>/{namespace}name</b> fully qualified element</li>
* <li><b>/name</b> element using default namespace</li>
* <li><b>/{namespace}name[n]</b> nth sibling</li>
* <li><b>/name[n]</b> nth sibling using default namespace</li>
* <li><b>/descendant-or-self::node()</b> descendent or self</li>
* <li><b>/.</b> self</li>
* <li><b>/..</b> parent</li>
* </ul>
*
* @author Derek Hulley
*/
public final class Path implements Iterable<Path.Element>, Serializable
{
private static final long serialVersionUID = 3905520514524328247L;
private LinkedList<Element> elements;
public Path()
{
// use linked list so as random access is not required, but both prepending and appending is
elements = new LinkedList<Element>();
}
/**
* @return Returns a typed iterator over the path elements
*/
public Iterator<Path.Element> iterator()
{
return elements.iterator();
}
/**
* Add a path element to the beginning of the path. This operation is useful in cases where
* a path is built by traversing up a hierarchy.
*
* @param pathElement
* @return Returns this instance of the path
*/
public Path prepend(Path.Element pathElement)
{
elements.addFirst(pathElement);
return this;
}
/**
* Merge the given path into the beginning of this path.
*
* @param path
* @return Returns this instance of the path
*/
public Path prepend(Path path)
{
elements.addAll(0, path.elements);
return this;
}
/**
* Appends a path element to the end of the path
*
* @param pathElement
* @return Returns this instance of the path
*/
public Path append(Path.Element pathElement)
{
elements.addLast(pathElement);
return this;
}
/**
* Append the given path of this path.
*
* @param path
* @return Returns this instance of the path
*/
public Path append(Path path)
{
elements.addAll(path.elements);
return this;
}
/**
* @return Returns the first element in the path or null if the path is empty
*/
public Element first()
{
return elements.getFirst();
}
/**
* @return Returns the last element in the path or null if the path is empty
*/
public Element last()
{
return elements.getLast();
}
public int size()
{
return elements.size();
}
public Element get(int n)
{
return elements.get(n);
}
/**
* @return Returns a string path made up of the component elements of this instance
*/
public String toString()
{
StringBuilder sb = new StringBuilder(128);
for (Element element : elements)
{
if((sb.length() > 1) || ((sb.length() == 1) && (sb.charAt(0) != '/')))
{
sb.append("/");
}
sb.append(element.getElementString());
}
return sb.toString();
}
/**
* @return Returns a string path made up of the component elements of this instance (prefixed where appropriate)
*/
public String toPrefixString(NamespacePrefixResolver resolver)
{
StringBuilder sb = new StringBuilder(128);
for (Element element : elements)
{
if((sb.length() > 1) || ((sb.length() == 1) && (sb.charAt(0) != '/')))
{
sb.append("/");
}
sb.append(element.getPrefixedString(resolver));
}
return sb.toString();
}
/**
* Return the human readable form of the specified node Path. Slow version of the method
* that extracts the name of each node in the Path from the supplied NodeService.
*
* @return human readable form of the Path excluding the final element
*/
public String toDisplayPath(NodeService nodeService)
{
StringBuilder buf = new StringBuilder(64);
for (int i=0; i<elements.size()-1; i++)
{
String elementString = null;
Element element = elements.get(i);
if (element instanceof ChildAssocElement)
{
ChildAssociationRef elementRef = ((ChildAssocElement)element).getRef();
if (elementRef.getParentRef() != null)
{
Serializable nameProp = null;
try
{
nameProp = nodeService.getProperty(elementRef.getChildRef(), ContentModel.PROP_NAME);
}
catch (AccessDeniedException err)
{
// unable to access this property on the path - so we cannot display it's name
}
if (nameProp != null)
{
// use the name property if we find it
elementString = nameProp.toString();
}
else
{
// revert to using QName if not found
elementString = elementRef.getQName().getLocalName();
}
}
}
else
{
elementString = element.getElementString();
}
if (elementString != null)
{
buf.append("/");
buf.append(elementString);
}
}
return buf.toString();
}
/**
* Return a new Path representing this path to the specified depth
*
* @param depth the path depth (0 based)
* @return the sub-path
*/
public Path subPath(int depth)
{
return subPath(0, depth);
}
/**
* Return a new Path representing this path to the specified depth
*
* @param depth the path depth (0 based)
* @return the sub-path
*/
public Path subPath(int start, int end)
{
if (start < 0 || start > (elements.size() -1))
{
throw new IndexOutOfBoundsException("Start index " + start + " must be between 0 and " + (elements.size() -1));
}
if (end < 0 || end > (elements.size() -1))
{
throw new IndexOutOfBoundsException("End index " + end + " must be between 0 and " + (elements.size() -1));
}
if (end < start)
{
throw new IndexOutOfBoundsException("End index " + end + " cannot be before start index " + start);
}
Path subPath = new Path();
for (int i = start; i <= end; i++)
{
subPath.append(this.get(i));
}
return subPath;
}
/**
* Override equals to check equality of Path instances
*/
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(!(o instanceof Path))
{
return false;
}
Path other = (Path)o;
return this.elements.equals(other.elements);
}
/**
* Override hashCode to check hash equality of Path instances
*/
public int hashCode()
{
return elements.hashCode();
}
/**
* Represents a path element.
* <p>
* In <b>/x/y/z</b>, elements are <b>x</b>, <b>y</b> and <b>z</b>.
*/
public abstract static class Element implements Serializable
{
/**
* @return Returns the path element portion including leading '/' and never null
*/
public abstract String getElementString();
/**
* @param resolver namespace prefix resolver
* @return the path element portion (with namespaces converted to prefixes)
*/
public String getPrefixedString(NamespacePrefixResolver resolver)
{
return getElementString();
}
/**
* @see #getElementString()
*/
public String toString()
{
return getElementString();
}
}
/**
* Represents a qualified path between a parent and a child node,
* including the sibling to retrieve e.g. <b>/{namespace}name[5]</b>
*/
public static class ChildAssocElement extends Element
{
private static final long serialVersionUID = 3689352104636790840L;
private ChildAssociationRef ref;
/**
* @param ref a reference to the specific parent-child association
*/
public ChildAssocElement(ChildAssociationRef ref)
{
this.ref = ref;
}
@Override
public String getElementString()
{
return createElementString(null);
}
@Override
public String getPrefixedString(NamespacePrefixResolver resolver)
{
return createElementString(resolver);
}
public ChildAssociationRef getRef()
{
return ref;
}
@Override
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(!(o instanceof ChildAssocElement))
{
return false;
}
ChildAssocElement other = (ChildAssocElement)o;
return this.ref.equals(other.ref);
}
@Override
public int hashCode()
{
return ref.hashCode();
}
private String createElementString(NamespacePrefixResolver resolver)
{
StringBuilder sb = new StringBuilder(32);
if (ref.getParentRef() == null)
{
sb.append("/");
}
else
{
// a parent is present
sb.append(resolver == null ? ISO9075.getXPathName(ref.getQName()) : ISO9075.getXPathName(ref.getQName(), resolver));
}
if (ref.getNthSibling() > -1)
{
sb.append("[").append(ref.getNthSibling()).append("]");
}
return sb.toString();
}
}
/**
* Represents a qualified path to an attribute,
* including the sibling for repeated properties/attributes to retrieve e.g. <b>/@{namespace}name[5]</b>
*/
public static class AttributeElement extends Element
{
private static final long serialVersionUID = 3256727281668863544L;
private QName attribute;
private int position = -1;
/**
* @param ref a reference to the specific parent-child association
*/
public AttributeElement(QName attribute)
{
this.attribute = attribute;
}
public AttributeElement(QName attribute, int position)
{
this(attribute);
this.position = position;
}
@Override
public String getElementString()
{
return createElementString(null);
}
@Override
public String getPrefixedString(NamespacePrefixResolver resolver)
{
return createElementString(resolver);
}
private String createElementString(NamespacePrefixResolver resolver)
{
StringBuilder sb = new StringBuilder(32);
sb.append("@").append(resolver == null ? ISO9075.getXPathName(attribute) : ISO9075.getXPathName(attribute, resolver));
if (position > -1)
{
sb.append("[").append(position).append("]");
}
return sb.toString();
}
public QName getQName()
{
return attribute;
}
public int position()
{
return position;
}
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(!(o instanceof AttributeElement))
{
return false;
}
AttributeElement other = (AttributeElement)o;
return this.getQName().equals(other.getQName()) && (this.position() == other.position());
}
public int hashCode()
{
return getQName().hashCode()*32 + position();
}
}
/**
* Represents the <b>//</b> or <b>/descendant-or-self::node()</b> xpath element
*/
public static class DescendentOrSelfElement extends Element
{
private static final long serialVersionUID = 3258410616875005237L;
public String getElementString()
{
return "descendant-or-self::node()";
}
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(!(o instanceof DescendentOrSelfElement))
{
return false;
}
return true;
}
public int hashCode()
{
return "descendant-or-self::node()".hashCode();
}
}
/**
* Represents the <b>/.</b> xpath element
*/
public static class SelfElement extends Element
{
private static final long serialVersionUID = 3834311739151300406L;
public String getElementString()
{
return ".";
}
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(!(o instanceof SelfElement))
{
return false;
}
return true;
}
public int hashCode()
{
return ".".hashCode();
}
}
/**
* Represents the <b>/..</b> xpath element
*/
public static class ParentElement extends Element
{
private static final long serialVersionUID = 3689915080477456179L;
public String getElementString()
{
return "..";
}
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(!(o instanceof ParentElement))
{
return false;
}
return true;
}
public int hashCode()
{
return "..".hashCode();
}
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.service.cmr.repository;
import org.alfresco.service.namespace.QName;
import junit.framework.TestCase;
/**
* @see org.alfresco.service.cmr.repository.Path
*
* @author Derek Hulley
*/
public class PathTest extends TestCase
{
private Path absolutePath;
private Path relativePath;
private QName typeQName;
private QName qname;
private StoreRef storeRef;
private NodeRef parentRef;
private NodeRef childRef;
public PathTest(String name)
{
super(name);
}
public void setUp() throws Exception
{
super.setUp();
absolutePath = new Path();
relativePath = new Path();
typeQName = QName.createQName("http://www.alfresco.org/PathTest/1.0", "testType");
qname = QName.createQName("http://www.google.com", "documentx");
storeRef = new StoreRef("x", "y");
parentRef = new NodeRef(storeRef, "P");
childRef = new NodeRef(storeRef, "C");
}
public void testQNameElement() throws Exception
{
// plain
Path.Element element = new Path.ChildAssocElement(new ChildAssociationRef(typeQName, parentRef, qname, childRef));
assertEquals("Element string incorrect",
qname.toString(),
element.getElementString());
// sibling
element = new Path.ChildAssocElement(new ChildAssociationRef(typeQName, parentRef, qname, childRef, true, 5));
assertEquals("Element string incorrect", "{http://www.google.com}documentx[5]", element.getElementString());
}
public void testElementTypes() throws Exception
{
Path.Element element = new Path.DescendentOrSelfElement();
assertEquals("DescendentOrSelf element incorrect",
"descendant-or-self::node()",
element.getElementString());
element = new Path.ParentElement();
assertEquals("Parent element incorrect", "..", element.getElementString());
element = new Path.SelfElement();
assertEquals("Self element incorrect", ".", element.getElementString());
}
public void testAppendingAndPrepending() throws Exception
{
Path.Element element0 = new Path.ChildAssocElement(new ChildAssociationRef(null, null, null, parentRef));
Path.Element element1 = new Path.ChildAssocElement(new ChildAssociationRef(typeQName, parentRef, qname, childRef, true, 4));
Path.Element element2 = new Path.DescendentOrSelfElement();
Path.Element element3 = new Path.ParentElement();
Path.Element element4 = new Path.SelfElement();
// append them all to the path
absolutePath.append(element0).append(element1).append(element2).append(element3).append(element4);
relativePath.append(element1).append(element2).append(element3).append(element4);
// check
assertEquals("Path appending didn't work",
"/{http://www.google.com}documentx[4]/descendant-or-self::node()/../.",
absolutePath.toString());
// copy the path
Path copy = new Path();
copy.append(relativePath).append(relativePath);
// check
assertEquals("Path appending didn't work",
relativePath.toString() + "/" + relativePath.toString(),
copy.toString());
// prepend
relativePath.prepend(element2);
// check
assertEquals("Prepending didn't work",
"descendant-or-self::node()/{http://www.google.com}documentx[4]/descendant-or-self::node()/../.",
relativePath.toString());
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.service.cmr.repository;
/**
* Thrown when an operation cannot be performed because the <b>store</b> reference
* no longer exists.
*
* @author Derek Hulley
*/
public class StoreExistsException extends AbstractStoreException
{
private static final long serialVersionUID = 3906369320370975030L;
public StoreExistsException(StoreRef storeRef)
{
super(storeRef);
}
public StoreExistsException(String msg, StoreRef storeRef)
{
super(msg, storeRef);
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Reference to a node store
*
* @author Derek Hulley
*/
public final class StoreRef implements EntityRef, Serializable
{
private static final long serialVersionUID = 3905808565129394486L;
public static final String PROTOCOL_WORKSPACE = "workspace";
public static final String URI_FILLER = "://";
private final String protocol;
private final String identifier;
/**
* @param protocol
* well-known protocol for the store, e.g. <b>workspace</b> or
* <b>versionstore</b>
* @param identifier
* the identifier, which may be specific to the protocol
*/
public StoreRef(String protocol, String identifier)
{
if (protocol == null)
{
throw new IllegalArgumentException("Store protocol may not be null");
}
if (identifier == null)
{
throw new IllegalArgumentException("Store identifier may not be null");
}
this.protocol = protocol;
this.identifier = identifier;
}
public StoreRef(String string)
{
int dividerPatternPosition = string.indexOf(URI_FILLER);
if(dividerPatternPosition == -1)
{
throw new AlfrescoRuntimeException("Invalid store ref: Does not contain " + URI_FILLER + " " + string);
}
this.protocol = string.substring(0, dividerPatternPosition);
this.identifier = string.substring(dividerPatternPosition+3);
}
public String toString()
{
return protocol + URI_FILLER + identifier;
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj instanceof StoreRef)
{
StoreRef that = (StoreRef) obj;
return (this.protocol.equals(that.protocol)
&& this.identifier.equals(that.identifier));
} else
{
return false;
}
}
/**
* Creates a hashcode from both the {@link #getProtocol()} and {@link #getIdentifier()}
*/
public int hashCode()
{
return (protocol.hashCode() + identifier.hashCode());
}
public String getProtocol()
{
return protocol;
}
public String getIdentifier()
{
return identifier;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.service.cmr.repository;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* @author Kevin Roast
*/
public class TemplateException extends AlfrescoRuntimeException
{
/**
* @param msgId
*/
public TemplateException(String msgId)
{
super(msgId);
}
/**
* @param msgId
* @param cause
*/
public TemplateException(String msgId, Throwable cause)
{
super(msgId, cause);
}
/**
* @param msgId
* @param params
*/
public TemplateException(String msgId, Object[] params)
{
super(msgId, params);
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public TemplateException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.service.cmr.repository;
/**
* Interface contract for the conversion of file name to a fully qualified icon image path for use by
* the templating engine.
*
* Generally this contract will be implemented by classes that have access to say the webserver
* context which can be used to generate an icon image for a specific filename.
*
* @author Kevin Roast
*/
public interface TemplateImageResolver
{
/**
* Resolve the qualified icon image path for the specified filename
*
* @param filename The file name to resolve image path for
* @param small True to resolve to the small 16x16 image, else large 32x32 image
*/
public String resolveImagePathForName(String filename, boolean small);
}

View File

@@ -0,0 +1,614 @@
/*
* 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.service.cmr.repository;
import java.io.Serializable;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.template.NamePathResultsMap;
import org.alfresco.repo.template.XPathResultsMap;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNameMap;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;
import freemarker.ext.dom.NodeModel;
/**
* Node class specific for use by Template pages that support Bean objects as part of the model.
* The default template engine FreeMarker can use these objects and they are provided to support it.
* A single method is completely freemarker specific - getXmlNodeModel()
* <p>
* The class exposes Node properties, children as dynamically populated maps and lists.
* <p>
* Various helper methods are provided to access common and useful node variables such
* as the content url and type information.
*
* @author Kevin Roast
*/
public final class TemplateNode implements Serializable
{
private static final long serialVersionUID = 1234390333739034171L;
private static Log logger = LogFactory.getLog(TemplateNode.class);
private final static String NAMESPACE_BEGIN = "" + QName.NAMESPACE_BEGIN;
private final static String CONTENT_DEFAULT_URL = "/download/direct/{0}/{1}/{2}/{3}";
private final static String CONTENT_PROP_URL = "/download/direct/{0}/{1}/{2}/{3}?property={4}";
/** The children of this node */
private List<TemplateNode> children = null;
/** The associations from this node */
private Map<String, List<TemplateNode>> assocs = null;
/** Cached values */
private NodeRef nodeRef;
private String name;
private QName type;
private String path;
private String id;
private Set<QName> aspects = null;
private QNameMap<String, Object> properties;
private boolean propsRetrieved = false;
private ServiceRegistry services = null;
private Boolean isDocument = null;
private Boolean isContainer = null;
private String displayPath = null;
private String mimetype = null;
private Long size = null;
private TemplateImageResolver imageResolver = null;
private TemplateNode parent = null;
/**
* Constructor
*
* @param nodeRef The NodeRef this Node wrapper represents
* @param services The ServiceRegistry the TemplateNode can use to access services
* @param resolver Image resolver to use to retrieve icons
*/
public TemplateNode(NodeRef nodeRef, ServiceRegistry services, TemplateImageResolver resolver)
{
if (nodeRef == null)
{
throw new IllegalArgumentException("NodeRef must be supplied.");
}
if (services == null)
{
throw new IllegalArgumentException("The ServiceRegistry must be supplied.");
}
this.nodeRef = nodeRef;
this.id = nodeRef.getId();
this.services = services;
this.imageResolver = resolver;
this.properties = new QNameMap<String, Object>(this.services.getNamespaceService());
}
/**
* @return The GUID for the node
*/
public String getId()
{
return this.id;
}
/**
* @return Returns the NodeRef this Node object represents
*/
public NodeRef getNodeRef()
{
return this.nodeRef;
}
/**
* @return Returns the type.
*/
public QName getType()
{
if (this.type == null)
{
this.type = this.services.getNodeService().getType(this.nodeRef);
}
return type;
}
/**
* @return The display name for the node
*/
public String getName()
{
if (this.name == null)
{
// try and get the name from the properties first
this.name = (String)getProperties().get("cm:name");
// if we didn't find it as a property get the name from the association name
if (this.name == null)
{
ChildAssociationRef parentRef = this.services.getNodeService().getPrimaryParent(this.nodeRef);
if (parentRef != null && parentRef.getQName() != null)
{
this.name = parentRef.getQName().getLocalName();
}
else
{
this.name = "";
}
}
}
return this.name;
}
/**
* @return The children of this Node as TemplateNode wrappers
*/
public List<TemplateNode> getChildren()
{
if (this.children == null)
{
List<ChildAssociationRef> childRefs = this.services.getNodeService().getChildAssocs(this.nodeRef);
this.children = new ArrayList<TemplateNode>(childRefs.size());
for (ChildAssociationRef ref : childRefs)
{
// create our Node representation from the NodeRef
TemplateNode child = new TemplateNode(ref.getChildRef(), this.services, this.imageResolver);
this.children.add(child);
}
}
return this.children;
}
/**
* @return A map capable of returning the TemplateNode at the specified Path as a child of this node.
*/
public Map getChildByNamePath()
{
return new NamePathResultsMap(this, this.services);
}
/**
* @return A map capable of returning a List of TemplateNode objects from an XPath query
* as children of this node.
*/
public Map getChildrenByXPath()
{
return new XPathResultsMap(this, this.services);
}
/**
* @return The associations for this Node. As a Map of assoc name to a List of TemplateNodes.
*/
public Map<String, List<TemplateNode>> getAssocs()
{
if (this.assocs == null)
{
List<AssociationRef> refs = this.services.getNodeService().getTargetAssocs(this.nodeRef, RegexQNamePattern.MATCH_ALL);
this.assocs = new QNameMap<String, List<TemplateNode>>(this.services.getNamespaceService());
for (AssociationRef ref : refs)
{
String qname = ref.getTypeQName().toString();
List<TemplateNode> nodes = assocs.get(qname);
if (nodes == null)
{
// first access for the list for this qname
nodes = new ArrayList<TemplateNode>(4);
this.assocs.put(ref.getTypeQName().toString(), nodes);
}
nodes.add( new TemplateNode(ref.getTargetRef(), this.services, this.imageResolver) );
}
}
return this.assocs;
}
/**
* @return All the properties known about this node.
*/
public Map<String, Object> getProperties()
{
if (this.propsRetrieved == false)
{
Map<QName, Serializable> props = this.services.getNodeService().getProperties(this.nodeRef);
for (QName qname : props.keySet())
{
Serializable propValue = props.get(qname);
if (propValue instanceof NodeRef)
{
// NodeRef object properties are converted to new TemplateNode objects
// so they can be used as objects within a template
propValue = new TemplateNode(((NodeRef)propValue), this.services, this.imageResolver);
}
else if (propValue instanceof ContentData)
{
// ContentData object properties are converted to TemplateContentData objects
// so the content and other properties of those objects can be accessed
propValue = new TemplateContentData((ContentData)propValue, qname);
}
this.properties.put(qname.toString(), propValue);
}
this.propsRetrieved = true;
}
return this.properties;
}
/**
* @return true if this Node is a container (i.e. a folder)
*/
public boolean getIsContainer()
{
if (isContainer == null)
{
DictionaryService dd = this.services.getDictionaryService();
isContainer = Boolean.valueOf( (dd.isSubClass(getType(), ContentModel.TYPE_FOLDER) == true &&
dd.isSubClass(getType(), ContentModel.TYPE_SYSTEM_FOLDER) == false) );
}
return isContainer.booleanValue();
}
/**
* @return true if this Node is a Document (i.e. with content)
*/
public boolean getIsDocument()
{
if (isDocument == null)
{
DictionaryService dd = this.services.getDictionaryService();
isDocument = Boolean.valueOf(dd.isSubClass(getType(), ContentModel.TYPE_CONTENT));
}
return isDocument.booleanValue();
}
/**
* @return The list of aspects applied to this node
*/
public Set<QName> getAspects()
{
if (this.aspects == null)
{
this.aspects = this.services.getNodeService().getAspects(this.nodeRef);
}
return this.aspects;
}
/**
* @param aspect The aspect name to test for
*
* @return true if the node has the aspect false otherwise
*/
public boolean hasAspect(String aspect)
{
if (this.aspects == null)
{
this.aspects = this.services.getNodeService().getAspects(this.nodeRef);
}
if (aspect.startsWith(NAMESPACE_BEGIN))
{
return aspects.contains((QName.createQName(aspect)));
}
else
{
boolean found = false;
for (QName qname : this.aspects)
{
if (qname.toPrefixString(this.services.getNamespaceService()).equals(aspect))
{
found = true;
break;
}
}
return found;
}
}
/**
* @return FreeMarker NodeModel for the XML content of this node, or null if no parsable XML found
*/
public NodeModel getXmlNodeModel()
{
try
{
return NodeModel.parse(new InputSource(new StringReader(getContent())));
}
catch (Throwable err)
{
if (logger.isDebugEnabled())
logger.debug(err.getMessage(), err);
return null;
}
}
/**
* @return Display path to this node
*/
public String getDisplayPath()
{
if (displayPath == null)
{
try
{
displayPath = this.services.getNodeService().getPath(this.nodeRef).toDisplayPath(this.services.getNodeService());
}
catch (AccessDeniedException err)
{
displayPath = "";
}
}
return displayPath;
}
/**
* @return the small icon image for this node
*/
public String getIcon16()
{
if (this.imageResolver != null)
{
if (getIsDocument())
{
return this.imageResolver.resolveImagePathForName(getName(), true);
}
else
{
return "/images/icons/space_small.gif";
}
}
else
{
return "/images/filetypes/_default.gif";
}
}
/**
* @return the large icon image for this node
*/
public String getIcon32()
{
if (this.imageResolver != null)
{
if (getIsDocument())
{
return this.imageResolver.resolveImagePathForName(getName(), false);
}
else
{
String icon = (String)getProperties().get("app:icon");
if (icon != null)
{
return "/images/icons/" + icon + ".gif";
}
else
{
return "/images/icons/space-icon-default.gif";
}
}
}
else
{
return "/images/filetypes32/_default.gif";
}
}
/**
* @return true if the node is currently locked
*/
public boolean getIsLocked()
{
boolean locked = false;
if (getAspects().contains(ContentModel.ASPECT_LOCKABLE))
{
LockStatus lockStatus = this.services.getLockService().getLockStatus(this.nodeRef);
if (lockStatus == LockStatus.LOCKED || lockStatus == LockStatus.LOCK_OWNER)
{
locked = true;
}
}
return locked;
}
/**
* @return the parent node
*/
public TemplateNode getParent()
{
if (parent == null)
{
NodeRef parentRef = this.services.getNodeService().getPrimaryParent(nodeRef).getParentRef();
// handle root node (no parent!)
if (parentRef != null)
{
parent = new TemplateNode(parentRef, this.services, this.imageResolver);
}
}
return parent;
}
/**
* @return the content String for this node from the default content property
* (@see ContentModel.PROP_CONTENT)
*/
public String getContent()
{
ContentService contentService = this.services.getContentService();
ContentReader reader = contentService.getReader(this.nodeRef, ContentModel.PROP_CONTENT);
return reader != null ? reader.getContentString() : "";
}
/**
* @return url to the content stream for this node for the default content property
* (@see ContentModel.PROP_CONTENT)
*/
public String getUrl()
{
try
{
return MessageFormat.format(CONTENT_DEFAULT_URL, new Object[] {
nodeRef.getStoreRef().getProtocol(),
nodeRef.getStoreRef().getIdentifier(),
nodeRef.getId(),
URLEncoder.encode(getName(), "US-ASCII") } );
}
catch (UnsupportedEncodingException err)
{
throw new TemplateException("Failed to encode content URL for node: " + nodeRef, err);
}
}
/**
* @return The mimetype encoding for content attached to the node from the default content property
* (@see ContentModel.PROP_CONTENT)
*/
public String getMimetype()
{
if (mimetype == null)
{
TemplateContentData content = (TemplateContentData)this.getProperties().get(ContentModel.PROP_CONTENT);
if (content != null)
{
mimetype = content.getMimetype();
}
}
return mimetype;
}
/**
* @return The size in bytes of the content attached to the node from the default content property
* (@see ContentModel.PROP_CONTENT)
*/
public long getSize()
{
if (size == null)
{
TemplateContentData content = (TemplateContentData)this.getProperties().get(ContentModel.PROP_CONTENT);
if (content != null)
{
size = content.getSize();
}
}
return size != null ? size.longValue() : 0L;
}
/**
* @return the image resolver instance used by this node
*/
public TemplateImageResolver getImageResolver()
{
return this.imageResolver;
}
/**
* Override Object.toString() to provide useful debug output
*/
public String toString()
{
if (this.services.getNodeService().exists(nodeRef))
{
return "Node Type: " + getType() +
"\nNode Properties: " + this.getProperties().toString() +
"\nNode Aspects: " + this.getAspects().toString();
}
else
{
return "Node no longer exists: " + nodeRef;
}
}
/**
* Inner class wrapping and providing access to a ContentData property
*/
public class TemplateContentData implements Serializable
{
public TemplateContentData(ContentData contentData, QName property)
{
this.contentData = contentData;
this.property = property;
}
public String getContent()
{
ContentService contentService = services.getContentService();
ContentReader reader = contentService.getReader(nodeRef, property);
return reader != null ? reader.getContentString() : "";
}
public String getUrl()
{
try
{
return MessageFormat.format(CONTENT_PROP_URL, new Object[] {
nodeRef.getStoreRef().getProtocol(),
nodeRef.getStoreRef().getIdentifier(),
nodeRef.getId(),
URLEncoder.encode(getName(), "US-ASCII"),
URLEncoder.encode(property.toString(), "US-ASCII") } );
}
catch (UnsupportedEncodingException err)
{
throw new TemplateException("Failed to encode content URL for node: " + nodeRef, err);
}
}
public long getSize()
{
return contentData.getSize();
}
public String getMimetype()
{
return contentData.getMimetype();
}
private ContentData contentData;
private QName property;
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.service.cmr.repository;
import java.io.Writer;
/**
* Interface to be implemented by template engine wrapper classes. The developer is responsible
* for interfacing to an appropriate template engine, using the supplied data model as input to
* the template and directing the output to the Writer stream.
*
* @author Kevin Roast
*/
public interface TemplateProcessor
{
/**
* Process a template against the supplied data model and write to the out.
*
* @param template Template name/path
* @param model Object model to process template against
* @param out Writer object to send output too
*/
public void process(String template, Object model, Writer out);
}

View File

@@ -0,0 +1,67 @@
/*
* 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.service.cmr.repository;
import java.io.Writer;
/**
* Template Service.
* <p>
* Provides an interface to services for executing template engine against a template file
* and data model.
* <p>
* The service provides a configured list of available template engines. The template file
* can either be in the repository (passed as NodeRef string) or on the classpath. The data
* model is specified to the template engine. The FreeMarker template engine is used by default.
*
* @author Kevin Roast
*/
public interface TemplateService
{
/**
* Process a template against the supplied data model and write to the out.
*
* @param engine Name of the template engine to use
* @param template Template (qualified classpath name or noderef)
* @param model Object model to process template against
*
* @return output of the template process as a String
*/
public String processTemplate(String engine, String template, Object model)
throws TemplateException;
/**
* Process a template against the supplied data model and write to the out.
*
* @param engine Name of the template engine to use
* @param template Template (qualified classpath name or noderef)
* @param model Object model to process template against
* @param out Writer object to send output too
*/
public void processTemplate(String engine, String template, Object model, Writer out)
throws TemplateException;
/**
* Return a TemplateProcessor instance for the specified engine name.
* Note that the processor instance is NOT thread safe!
*
* @param engine Name of the template engine to get or null for default
*
* @return TemplateProcessor
*/
public TemplateProcessor getTemplateProcessor(String engine);
}

View File

@@ -0,0 +1,34 @@
/*
* 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.service.cmr.repository;
import org.alfresco.error.AlfrescoRuntimeException;
public class XPathException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = 3544955454552815923L;
public XPathException(String msg)
{
super(msg);
}
public XPathException(String msg, Throwable cause)
{
super(msg, cause);
}
}

View File

@@ -0,0 +1,705 @@
/*
* 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.service.cmr.repository.datatype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ISO8601DateFormat;
/**
* Support for generic conversion between types.
*
* Additional conversions may be added. Basic interoperabikitynos supported.
*
* Direct conversion and two stage conversions via Number are supported. We do
* not support conversion by any route at the moment
*
* TODO: Add support for Path
*
* TODO: Add support for lucene
*
* TODO: Add suport to check of a type is convertable
*
* TODO: Support for dynamically managing conversions
*
* @author andyh
*
*/
public class DefaultTypeConverter
{
/**
* Default Type Converter
*/
public static TypeConverter INSTANCE = new TypeConverter();
/**
* Initialise default set of Converters
*/
static
{
//
// From string
//
INSTANCE.addConverter(String.class, Boolean.class, new TypeConverter.Converter<String, Boolean>()
{
public Boolean convert(String source)
{
return Boolean.valueOf(source);
}
});
INSTANCE.addConverter(String.class, Character.class, new TypeConverter.Converter<String, Character>()
{
public Character convert(String source)
{
if ((source == null) || (source.length() == 0))
{
return null;
}
return Character.valueOf(source.charAt(0));
}
});
INSTANCE.addConverter(String.class, Number.class, new TypeConverter.Converter<String, Number>()
{
public Number convert(String source)
{
try
{
return DecimalFormat.getNumberInstance().parse(source);
}
catch (ParseException e)
{
throw new TypeConversionException("Failed to parse number " + source, e);
}
}
});
INSTANCE.addConverter(String.class, Byte.class, new TypeConverter.Converter<String, Byte>()
{
public Byte convert(String source)
{
return Byte.valueOf(source);
}
});
INSTANCE.addConverter(String.class, Short.class, new TypeConverter.Converter<String, Short>()
{
public Short convert(String source)
{
return Short.valueOf(source);
}
});
INSTANCE.addConverter(String.class, Integer.class, new TypeConverter.Converter<String, Integer>()
{
public Integer convert(String source)
{
return Integer.valueOf(source);
}
});
INSTANCE.addConverter(String.class, Long.class, new TypeConverter.Converter<String, Long>()
{
public Long convert(String source)
{
return Long.valueOf(source);
}
});
INSTANCE.addConverter(String.class, Float.class, new TypeConverter.Converter<String, Float>()
{
public Float convert(String source)
{
return Float.valueOf(source);
}
});
INSTANCE.addConverter(String.class, Double.class, new TypeConverter.Converter<String, Double>()
{
public Double convert(String source)
{
return Double.valueOf(source);
}
});
INSTANCE.addConverter(String.class, BigInteger.class, new TypeConverter.Converter<String, BigInteger>()
{
public BigInteger convert(String source)
{
return new BigInteger(source);
}
});
INSTANCE.addConverter(String.class, BigDecimal.class, new TypeConverter.Converter<String, BigDecimal>()
{
public BigDecimal convert(String source)
{
return new BigDecimal(source);
}
});
INSTANCE.addConverter(String.class, Date.class, new TypeConverter.Converter<String, Date>()
{
public Date convert(String source)
{
Date date = ISO8601DateFormat.parse(source);
if (date == null)
{
throw new TypeConversionException("Failed to parse date " + source);
}
return date;
}
});
INSTANCE.addConverter(String.class, Duration.class, new TypeConverter.Converter<String, Duration>()
{
public Duration convert(String source)
{
return new Duration(source);
}
});
INSTANCE.addConverter(String.class, QName.class, new TypeConverter.Converter<String, QName>()
{
public QName convert(String source)
{
return QName.createQName(source);
}
});
INSTANCE.addConverter(String.class, ContentData.class, new TypeConverter.Converter<String, ContentData>()
{
public ContentData convert(String source)
{
return ContentData.createContentProperty(source);
}
});
INSTANCE.addConverter(String.class, NodeRef.class, new TypeConverter.Converter<String, NodeRef>()
{
public NodeRef convert(String source)
{
return new NodeRef(source);
}
});
INSTANCE.addConverter(String.class, InputStream.class, new TypeConverter.Converter<String, InputStream>()
{
public InputStream convert(String source)
{
try
{
return new ByteArrayInputStream(source.getBytes("UTF-8"));
}
catch (UnsupportedEncodingException e)
{
throw new TypeConversionException("Encoding not supported", e);
}
}
});
//
// Number to Subtypes and Date
//
INSTANCE.addConverter(Number.class, Byte.class, new TypeConverter.Converter<Number, Byte>()
{
public Byte convert(Number source)
{
return Byte.valueOf(source.byteValue());
}
});
INSTANCE.addConverter(Number.class, Short.class, new TypeConverter.Converter<Number, Short>()
{
public Short convert(Number source)
{
return Short.valueOf(source.shortValue());
}
});
INSTANCE.addConverter(Number.class, Integer.class, new TypeConverter.Converter<Number, Integer>()
{
public Integer convert(Number source)
{
return Integer.valueOf(source.intValue());
}
});
INSTANCE.addConverter(Number.class, Long.class, new TypeConverter.Converter<Number, Long>()
{
public Long convert(Number source)
{
return Long.valueOf(source.longValue());
}
});
INSTANCE.addConverter(Number.class, Float.class, new TypeConverter.Converter<Number, Float>()
{
public Float convert(Number source)
{
return Float.valueOf(source.floatValue());
}
});
INSTANCE.addConverter(Number.class, Double.class, new TypeConverter.Converter<Number, Double>()
{
public Double convert(Number source)
{
return Double.valueOf(source.doubleValue());
}
});
INSTANCE.addConverter(Number.class, Date.class, new TypeConverter.Converter<Number, Date>()
{
public Date convert(Number source)
{
return new Date(source.longValue());
}
});
INSTANCE.addConverter(Number.class, String.class, new TypeConverter.Converter<Number, String>()
{
public String convert(Number source)
{
return source.toString();
}
});
INSTANCE.addConverter(Number.class, BigInteger.class, new TypeConverter.Converter<Number, BigInteger>()
{
public BigInteger convert(Number source)
{
if (source instanceof BigDecimal)
{
return ((BigDecimal) source).toBigInteger();
}
else
{
return BigInteger.valueOf(source.longValue());
}
}
});
INSTANCE.addConverter(Number.class, BigDecimal.class, new TypeConverter.Converter<Number, BigDecimal>()
{
public BigDecimal convert(Number source)
{
if (source instanceof BigInteger)
{
return new BigDecimal((BigInteger) source);
}
else
{
return BigDecimal.valueOf(source.longValue());
}
}
});
INSTANCE.addDynamicTwoStageConverter(Number.class, String.class, InputStream.class);
//
// Date, Timestamp ->
//
INSTANCE.addConverter(Timestamp.class, Date.class, new TypeConverter.Converter<Timestamp, Date>()
{
public Date convert(Timestamp source)
{
return new Date(source.getTime());
}
});
INSTANCE.addConverter(Date.class, Number.class, new TypeConverter.Converter<Date, Number>()
{
public Number convert(Date source)
{
return Long.valueOf(source.getTime());
}
});
INSTANCE.addConverter(Date.class, String.class, new TypeConverter.Converter<Date, String>()
{
public String convert(Date source)
{
return ISO8601DateFormat.format(source);
}
});
INSTANCE.addConverter(Date.class, Calendar.class, new TypeConverter.Converter<Date, Calendar>()
{
public Calendar convert(Date source)
{
Calendar calendar = Calendar.getInstance();
calendar.setTime(source);
return calendar;
}
});
INSTANCE.addDynamicTwoStageConverter(Date.class, String.class, InputStream.class);
//
// Boolean ->
//
INSTANCE.addConverter(Boolean.class, String.class, new TypeConverter.Converter<Boolean, String>()
{
public String convert(Boolean source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Boolean.class, String.class, InputStream.class);
//
// Character ->
//
INSTANCE.addConverter(Character.class, String.class, new TypeConverter.Converter<Character, String>()
{
public String convert(Character source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Character.class, String.class, InputStream.class);
//
// Duration ->
//
INSTANCE.addConverter(Duration.class, String.class, new TypeConverter.Converter<Duration, String>()
{
public String convert(Duration source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Duration.class, String.class, InputStream.class);
//
// Byte
//
INSTANCE.addConverter(Byte.class, String.class, new TypeConverter.Converter<Byte, String>()
{
public String convert(Byte source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Byte.class, String.class, InputStream.class);
//
// Short
//
INSTANCE.addConverter(Short.class, String.class, new TypeConverter.Converter<Short, String>()
{
public String convert(Short source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Short.class, String.class, InputStream.class);
//
// Integer
//
INSTANCE.addConverter(Integer.class, String.class, new TypeConverter.Converter<Integer, String>()
{
public String convert(Integer source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Integer.class, String.class, InputStream.class);
//
// Long
//
INSTANCE.addConverter(Long.class, String.class, new TypeConverter.Converter<Long, String>()
{
public String convert(Long source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Long.class, String.class, InputStream.class);
//
// Float
//
INSTANCE.addConverter(Float.class, String.class, new TypeConverter.Converter<Float, String>()
{
public String convert(Float source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Float.class, String.class, InputStream.class);
//
// Double
//
INSTANCE.addConverter(Double.class, String.class, new TypeConverter.Converter<Double, String>()
{
public String convert(Double source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Double.class, String.class, InputStream.class);
//
// BigInteger
//
INSTANCE.addConverter(BigInteger.class, String.class, new TypeConverter.Converter<BigInteger, String>()
{
public String convert(BigInteger source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(BigInteger.class, String.class, InputStream.class);
//
// Calendar
//
INSTANCE.addConverter(Calendar.class, Date.class, new TypeConverter.Converter<Calendar, Date>()
{
public Date convert(Calendar source)
{
return source.getTime();
}
});
INSTANCE.addConverter(Calendar.class, String.class, new TypeConverter.Converter<Calendar, String>()
{
public String convert(Calendar source)
{
return ISO8601DateFormat.format(source.getTime());
}
});
//
// BigDecimal
//
INSTANCE.addConverter(BigDecimal.class, String.class, new TypeConverter.Converter<BigDecimal, String>()
{
public String convert(BigDecimal source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(BigDecimal.class, String.class, InputStream.class);
//
// QName
//
INSTANCE.addConverter(QName.class, String.class, new TypeConverter.Converter<QName, String>()
{
public String convert(QName source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(QName.class, String.class, InputStream.class);
//
// NodeRef
//
INSTANCE.addConverter(NodeRef.class, String.class, new TypeConverter.Converter<NodeRef, String>()
{
public String convert(NodeRef source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(NodeRef.class, String.class, InputStream.class);
//
// ContentData
//
INSTANCE.addConverter(ContentData.class, String.class, new TypeConverter.Converter<ContentData, String>()
{
public String convert(ContentData source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(ContentData.class, String.class, InputStream.class);
//
// Path
//
INSTANCE.addConverter(Path.class, String.class, new TypeConverter.Converter<Path, String>()
{
public String convert(Path source)
{
return source.toString();
}
});
INSTANCE.addDynamicTwoStageConverter(Path.class, String.class, InputStream.class);
//
// Content Reader
//
INSTANCE.addConverter(ContentReader.class, InputStream.class, new TypeConverter.Converter<ContentReader, InputStream>()
{
public InputStream convert(ContentReader source)
{
return source.getContentInputStream();
}
});
INSTANCE.addConverter(ContentReader.class, String.class, new TypeConverter.Converter<ContentReader, String>()
{
public String convert(ContentReader source)
{
String encoding = source.getEncoding();
if (encoding == null || !encoding.equals("UTF-8"))
{
throw new TypeConversionException("Cannot convert non UTF-8 streams to String.");
}
// TODO: Throw error on size limit
return source.getContentString();
}
});
INSTANCE.addDynamicTwoStageConverter(ContentReader.class, String.class, Date.class);
INSTANCE.addDynamicTwoStageConverter(ContentReader.class, String.class, Double.class);
INSTANCE.addDynamicTwoStageConverter(ContentReader.class, String.class, Long.class);
INSTANCE.addDynamicTwoStageConverter(ContentReader.class, String.class, Boolean.class);
INSTANCE.addDynamicTwoStageConverter(ContentReader.class, String.class, QName.class);
INSTANCE.addDynamicTwoStageConverter(ContentReader.class, String.class, Path.class);
INSTANCE.addDynamicTwoStageConverter(ContentReader.class, String.class, NodeRef.class);
//
// Input Stream
//
INSTANCE.addConverter(InputStream.class, String.class, new TypeConverter.Converter<InputStream, String>()
{
public String convert(InputStream source)
{
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int read;
while ((read = source.read(buffer)) > 0)
{
out.write(buffer, 0, read);
}
byte[] data = out.toByteArray();
return new String(data, "UTF-8");
}
catch (UnsupportedEncodingException e)
{
throw new TypeConversionException("Cannot convert input stream to String.", e);
}
catch (IOException e)
{
throw new TypeConversionException("Conversion from stream to string failed", e);
}
finally
{
if (source != null)
{
try { source.close(); } catch(IOException e) {};
}
}
}
});
INSTANCE.addDynamicTwoStageConverter(InputStream.class, String.class, Date.class);
INSTANCE.addDynamicTwoStageConverter(InputStream.class, String.class, Double.class);
INSTANCE.addDynamicTwoStageConverter(InputStream.class, String.class, Long.class);
INSTANCE.addDynamicTwoStageConverter(InputStream.class, String.class, Boolean.class);
INSTANCE.addDynamicTwoStageConverter(InputStream.class, String.class, QName.class);
INSTANCE.addDynamicTwoStageConverter(InputStream.class, String.class, Path.class);
INSTANCE.addDynamicTwoStageConverter(InputStream.class, String.class, NodeRef.class);
}
}

View File

@@ -0,0 +1,282 @@
/*
* 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.service.cmr.repository.datatype;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import junit.framework.TestCase;
import org.alfresco.util.ISO8601DateFormat;
public class DefaultTypeConverterTest extends TestCase
{
public DefaultTypeConverterTest()
{
super();
}
public DefaultTypeConverterTest(String arg0)
{
super(arg0);
}
public void testPrimitives()
{
assertEquals(Boolean.valueOf(false), DefaultTypeConverter.INSTANCE.convert(Boolean.class, false));
assertEquals(Boolean.valueOf(true), DefaultTypeConverter.INSTANCE.convert(Boolean.class, true));
assertEquals(Character.valueOf('a'), DefaultTypeConverter.INSTANCE.convert(Character.class, 'a'));
assertEquals(Byte.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Byte.class, (byte) 3));
assertEquals(Short.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Short.class, (short) 4));
assertEquals(Integer.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Integer.class, (int) 5));
assertEquals(Long.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Long.class, (long) 6));
assertEquals(Float.valueOf("7.1"), DefaultTypeConverter.INSTANCE.convert(Float.class, (float) 7.1));
assertEquals(Double.valueOf("123.123"), DefaultTypeConverter.INSTANCE.convert(Double.class, (double) 123.123));
}
public void testNoConversion()
{
assertEquals(Boolean.valueOf(false), DefaultTypeConverter.INSTANCE.convert(Boolean.class, Boolean.valueOf(false)));
assertEquals(Boolean.valueOf(true), DefaultTypeConverter.INSTANCE.convert(Boolean.class, Boolean.valueOf(true)));
assertEquals(Character.valueOf('w'), DefaultTypeConverter.INSTANCE.convert(Character.class, Character.valueOf('w')));
assertEquals(Byte.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Byte.class, Byte.valueOf("3")));
assertEquals(Short.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Short.class, Short.valueOf("4")));
assertEquals(Integer.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Integer.class, Integer.valueOf("5")));
assertEquals(Long.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Long.class, Long.valueOf("6")));
assertEquals(Float.valueOf("7.1"), DefaultTypeConverter.INSTANCE.convert(Float.class, Float.valueOf("7.1")));
assertEquals(Double.valueOf("123.123"), DefaultTypeConverter.INSTANCE.convert(Double.class, Double.valueOf("123.123")));
assertEquals(Double.valueOf("123.123"), DefaultTypeConverter.INSTANCE.convert(Double.class, Double.valueOf("123.123")));
assertEquals(new BigInteger("1234567890123456789"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, new BigInteger("1234567890123456789")));
assertEquals(new BigDecimal("12345678901234567890.12345678901234567890"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, new BigDecimal("12345678901234567890.12345678901234567890")));
Date date = new Date();
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, date));
assertEquals(new Duration("P25D"), DefaultTypeConverter.INSTANCE.convert(Duration.class, new Duration("P25D")));
assertEquals("woof", DefaultTypeConverter.INSTANCE.convert(String.class, "woof"));
}
public void testToString()
{
assertEquals("true", DefaultTypeConverter.INSTANCE.convert(String.class, new Boolean(true)));
assertEquals("false", DefaultTypeConverter.INSTANCE.convert(String.class, new Boolean(false)));
assertEquals("v", DefaultTypeConverter.INSTANCE.convert(String.class, Character.valueOf('v')));
assertEquals("3", DefaultTypeConverter.INSTANCE.convert(String.class, Byte.valueOf("3")));
assertEquals("4", DefaultTypeConverter.INSTANCE.convert(String.class, Short.valueOf("4")));
assertEquals("5", DefaultTypeConverter.INSTANCE.convert(String.class, Integer.valueOf("5")));
assertEquals("6", DefaultTypeConverter.INSTANCE.convert(String.class, Long.valueOf("6")));
assertEquals("7.1", DefaultTypeConverter.INSTANCE.convert(String.class, Float.valueOf("7.1")));
assertEquals("123.123", DefaultTypeConverter.INSTANCE.convert(String.class, Double.valueOf("123.123")));
assertEquals("1234567890123456789", DefaultTypeConverter.INSTANCE.convert(String.class, new BigInteger("1234567890123456789")));
assertEquals("12345678901234567890.12345678901234567890", DefaultTypeConverter.INSTANCE.convert(String.class, new BigDecimal("12345678901234567890.12345678901234567890")));
Date date = new Date();
assertEquals(ISO8601DateFormat.format(date), DefaultTypeConverter.INSTANCE.convert(String.class, date));
assertEquals("P0Y25D", DefaultTypeConverter.INSTANCE.convert(String.class, new Duration("P0Y25D")));
assertEquals("woof", DefaultTypeConverter.INSTANCE.convert(String.class, "woof"));
}
public void testFromString()
{
assertEquals(Boolean.valueOf(true), DefaultTypeConverter.INSTANCE.convert(Boolean.class, "True"));
assertEquals(Boolean.valueOf(false), DefaultTypeConverter.INSTANCE.convert(Boolean.class, "woof"));
assertEquals(Character.valueOf('w'), DefaultTypeConverter.INSTANCE.convert(Character.class, "w"));
assertEquals(Byte.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Byte.class, "3"));
assertEquals(Short.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Short.class, "4"));
assertEquals(Integer.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Integer.class, "5"));
assertEquals(Long.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Long.class, "6"));
assertEquals(Float.valueOf("7.1"), DefaultTypeConverter.INSTANCE.convert(Float.class, "7.1"));
assertEquals(Double.valueOf("123.123"), DefaultTypeConverter.INSTANCE.convert(Double.class, "123.123"));
assertEquals(new BigInteger("1234567890123456789"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, "1234567890123456789"));
assertEquals(new BigDecimal("12345678901234567890.12345678901234567890"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, "12345678901234567890.12345678901234567890"));
assertEquals("2004-03-12T00:00:00.000Z", ISO8601DateFormat.format(DefaultTypeConverter.INSTANCE.convert(Date.class, "2004-03-12T00:00:00.000Z")));
assertEquals(new Duration("P25D"), DefaultTypeConverter.INSTANCE.convert(Duration.class, "P25D"));
assertEquals("woof", DefaultTypeConverter.INSTANCE.convert(String.class, "woof"));
}
public void testPrimativeAccessors()
{
assertEquals(false, DefaultTypeConverter.INSTANCE.convert(Boolean.class, false).booleanValue());
assertEquals(true, DefaultTypeConverter.INSTANCE.convert(Boolean.class, true).booleanValue());
assertEquals('a', DefaultTypeConverter.INSTANCE.convert(Character.class, 'a').charValue());
assertEquals((byte) 3, DefaultTypeConverter.INSTANCE.convert(Byte.class, (byte) 3).byteValue());
assertEquals((short) 4, DefaultTypeConverter.INSTANCE.convert(Short.class, (short) 4).shortValue());
assertEquals((int) 5, DefaultTypeConverter.INSTANCE.convert(Integer.class, (int) 5).intValue());
assertEquals((long) 6, DefaultTypeConverter.INSTANCE.convert(Long.class, (long) 6).longValue());
assertEquals((float) 7.1, DefaultTypeConverter.INSTANCE.convert(Float.class, (float) 7.1).floatValue());
assertEquals((double) 123.123, DefaultTypeConverter.INSTANCE.convert(Double.class, (double) 123.123).doubleValue());
}
public void testInterConversions()
{
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, Byte.valueOf("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, Byte.valueOf("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, Byte.valueOf("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, Byte.valueOf("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, Byte.valueOf("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, Byte.valueOf("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, Byte.valueOf("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, Byte.valueOf("8")));
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, Short.valueOf("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, Short.valueOf("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, Short.valueOf("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, Short.valueOf("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, Short.valueOf("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, Short.valueOf("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, Short.valueOf("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, Short.valueOf("8")));
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, Integer.valueOf("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, Integer.valueOf("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, Integer.valueOf("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, Integer.valueOf("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, Integer.valueOf("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, Integer.valueOf("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, Integer.valueOf("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, Integer.valueOf("8")));
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, Long.valueOf("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, Long.valueOf("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, Long.valueOf("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, Long.valueOf("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, Long.valueOf("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, Long.valueOf("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, Long.valueOf("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, Long.valueOf("8")));
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, Float.valueOf("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, Float.valueOf("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, Float.valueOf("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, Float.valueOf("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, Float.valueOf("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, Float.valueOf("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, Float.valueOf("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, Float.valueOf("8")));
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, Double.valueOf("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, Double.valueOf("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, Double.valueOf("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, Double.valueOf("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, Double.valueOf("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, Double.valueOf("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, Double.valueOf("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, Double.valueOf("8")));
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, new BigInteger("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, new BigInteger("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, new BigInteger("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, new BigInteger("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, new BigInteger("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, new BigInteger("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, new BigInteger("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, new BigInteger("8")));
assertEquals(Byte.valueOf("1"), DefaultTypeConverter.INSTANCE.convert(Byte.class, new BigDecimal("1")));
assertEquals(Short.valueOf("2"), DefaultTypeConverter.INSTANCE.convert(Short.class, new BigDecimal("2")));
assertEquals(Integer.valueOf("3"), DefaultTypeConverter.INSTANCE.convert(Integer.class, new BigDecimal("3")));
assertEquals(Long.valueOf("4"), DefaultTypeConverter.INSTANCE.convert(Long.class, new BigDecimal("4")));
assertEquals(Float.valueOf("5"), DefaultTypeConverter.INSTANCE.convert(Float.class, new BigDecimal("5")));
assertEquals(Double.valueOf("6"), DefaultTypeConverter.INSTANCE.convert(Double.class, new BigDecimal("6")));
assertEquals(new BigInteger("7"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, new BigDecimal("7")));
assertEquals(new BigDecimal("8"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, new BigDecimal("8")));
}
public void testDate()
{
Date date = new Date(101);
assertEquals(Byte.valueOf("101"), DefaultTypeConverter.INSTANCE.convert(Byte.class, date));
assertEquals(Short.valueOf("101"), DefaultTypeConverter.INSTANCE.convert(Short.class, date));
assertEquals(Integer.valueOf("101"), DefaultTypeConverter.INSTANCE.convert(Integer.class, date));
assertEquals(Long.valueOf("101"), DefaultTypeConverter.INSTANCE.convert(Long.class, date));
assertEquals(Float.valueOf("101"), DefaultTypeConverter.INSTANCE.convert(Float.class, date));
assertEquals(Double.valueOf("101"), DefaultTypeConverter.INSTANCE.convert(Double.class, date));
assertEquals(new BigInteger("101"), DefaultTypeConverter.INSTANCE.convert(BigInteger.class, date));
assertEquals(new BigDecimal("101"), DefaultTypeConverter.INSTANCE.convert(BigDecimal.class, date));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, (byte)101));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, (short)101));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, (int)101));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, (long)101));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, (float)101));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, (double)101));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, new BigInteger("101")));
assertEquals(date, DefaultTypeConverter.INSTANCE.convert(Date.class, (Object)(new BigDecimal("101"))));
assertEquals(101, DefaultTypeConverter.INSTANCE.intValue(date));
}
public void testMultiValue()
{
ArrayList<Object> list = makeList();
assertEquals(true, DefaultTypeConverter.INSTANCE.isMultiValued(list));
assertEquals(14, DefaultTypeConverter.INSTANCE.size(list));
for(String stringValue: DefaultTypeConverter.INSTANCE.getCollection(String.class, list))
{
System.out.println("Value is "+stringValue);
}
}
private ArrayList<Object> makeList()
{
ArrayList<Object> list = new ArrayList<Object>();
list.add(Boolean.valueOf(true));
list.add(Boolean.valueOf(false));
list.add(Character.valueOf('q'));
list.add(Byte.valueOf("1"));
list.add(Short.valueOf("2"));
list.add(Integer.valueOf("3"));
list.add(Long.valueOf("4"));
list.add(Float.valueOf("5"));
list.add(Double.valueOf("6"));
list.add(new BigInteger("7"));
list.add(new BigDecimal("8"));
list.add(new Date());
list.add(new Duration("P5Y0M"));
list.add("Hello mum");
return list;
}
public void testSingleValuseAsMultiValue()
{
Integer integer = Integer.valueOf(43);
assertEquals(false, DefaultTypeConverter.INSTANCE.isMultiValued(integer));
assertEquals(1, DefaultTypeConverter.INSTANCE.size(integer));
for(String stringValue: DefaultTypeConverter.INSTANCE.getCollection(String.class, integer))
{
System.out.println("Value is "+stringValue);
}
}
public void testNullAndEmpty()
{
assertNull(DefaultTypeConverter.INSTANCE.convert(Boolean.class, null));
ArrayList<Object> list = new ArrayList<Object>();
assertNotNull(DefaultTypeConverter.INSTANCE.convert(Boolean.class, list));
list.add(null);
assertNotNull(DefaultTypeConverter.INSTANCE.convert(Boolean.class, list));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/*
* 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.service.cmr.repository.datatype;
/**
* Base Exception of Type Converter Exceptions.
*
* @author David Caruana
*/
public class TypeConversionException extends RuntimeException
{
private static final long serialVersionUID = 3257008761007847733L;
public TypeConversionException(String msg)
{
super(msg);
}
public TypeConversionException(String msg, Throwable cause)
{
super(msg, cause);
}
}

View File

@@ -0,0 +1,596 @@
/*
* 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.service.cmr.repository.datatype;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.util.ParameterCheck;
/**
* Support for generic conversion between types.
*
* Additional conversions may be added.
*
* Direct conversion and two stage conversions via Number are supported. We do
* not support conversion by any route at the moment
*/
public class TypeConverter
{
/**
* General conversion method to Object types (note it cannot support
* conversion to primary types due the restrictions of reflection. Use the
* static conversion methods to primitive types)
*
* @param propertyType - the target property type
* @param value - the value to be converted
* @return - the converted value as the correct type
*/
@SuppressWarnings("unchecked")
public final Object convert(DataTypeDefinition propertyType, Object value)
{
ParameterCheck.mandatory("Property type definition", propertyType);
// Convert property type to java class
Class javaClass = null;
String javaClassName = propertyType.getJavaClassName();
try
{
javaClass = Class.forName(javaClassName);
}
catch (ClassNotFoundException e)
{
throw new DictionaryException("Java class " + javaClassName + " of property type " + propertyType.getName() + " is invalid", e);
}
return convert(javaClass, value);
}
/**
* General conversion method to Object types (note it cannot support
* conversion to primary types due the restrictions of reflection. Use the
* static conversion methods to primitive types)
*
* @param <T> The target type for the result of the conversion
* @param c - a class for the target type
* @param value - the value to be converted
* @return - the converted value as the correct type
* @throws TypeConversionException if the conversion cannot be performed
*/
@SuppressWarnings("unchecked")
public final <T> T convert(Class<T> c, Object value)
{
if(value == null)
{
return null;
}
// Primative types
if (c.isPrimitive())
{
// We can not suport primitive type conversion
throw new TypeConversionException("Can not convert direct to primitive type " + c.getName());
}
// Check if we already have the correct type
if (c.isInstance(value))
{
return c.cast(value);
}
// Find the correct conversion - if available and do the converiosn
Converter converter = getConverter(value, c);
if (converter == null)
{
throw new TypeConversionException(
"There is no conversion registered for the value: \n" +
" value class: " + value.getClass().getName() + "\n" +
" to class: " + c.getName() + "\n" +
" value: " + value.toString());
}
return (T) converter.convert(value);
}
/**
* General conversion method to convert collection contents to the specified
* type.
*
* @param propertyType - the target property type
* @param value - the value to be converted
* @return - the converted value as the correct type
* @throws DictionaryException if the property type's registered java class is invalid
* @throws TypeConversionException if the conversion cannot be performed
*/
@SuppressWarnings("unchecked")
public final Collection convert(DataTypeDefinition propertyType, Collection values)
{
ParameterCheck.mandatory("Property type definition", propertyType);
// Convert property type to java class
Class javaClass = null;
String javaClassName = propertyType.getJavaClassName();
try
{
javaClass = Class.forName(javaClassName);
}
catch (ClassNotFoundException e)
{
throw new DictionaryException("Java class " + javaClassName + " of property type " + propertyType.getName() + " is invalid", e);
}
return convert(javaClass, values);
}
/**
* General conversion method to convert collection contents to the specified
* type.
*
* @param <T> The target type for the result of the conversion
* @param c - a class for the target type
* @param value - the collection to be converted
* @return - the converted collection
* @throws TypeConversionException if the conversion cannot be performed
*/
public final <T> Collection<T> convert(Class<T> c, Collection values)
{
if(values == null)
{
return null;
}
Collection<T> converted = new ArrayList<T>(values.size());
for (Object value : values)
{
converted.add(convert(c, value));
}
return converted;
}
/**
* Get the boolean value for the value object
* May have conversion failure
*
* @param value
* @return
*/
public final boolean booleanValue(Object value)
{
return convert(Boolean.class, value).booleanValue();
}
/**
* Get the char value for the value object
* May have conversion failure
*
* @param value
* @return
*/
public final char charValue(Object value)
{
return convert(Character.class, value).charValue();
}
/**
* Get the byte value for the value object
* May have conversion failure
*
* @param value
* @return
*/
public final byte byteValue(Object value)
{
if (value instanceof Number)
{
return ((Number) value).byteValue();
}
return convert(Byte.class, value).byteValue();
}
/**
* Get the short value for the value object
* May have conversion failure
*
* @param value
* @return
*/
public final short shortValue(Object value)
{
if (value instanceof Number)
{
return ((Number) value).shortValue();
}
return convert(Short.class, value).shortValue();
}
/**
* Get the int value for the value object
* May have conversion failure
*
* @param value
* @return
*/
public final int intValue(Object value)
{
if (value instanceof Number)
{
return ((Number) value).intValue();
}
return convert(Integer.class, value).intValue();
}
/**
* Get the long value for the value object
* May have conversion failure
*
* @param value
* @return
*/
public final long longValue(Object value)
{
if (value instanceof Number)
{
return ((Number) value).longValue();
}
return convert(Long.class, value).longValue();
}
/**
* Get the bollean value for the value object
* May have conversion failure
*
* @param float
* @return
*/
public final float floatValue(Object value)
{
if (value instanceof Number)
{
return ((Number) value).floatValue();
}
return convert(Float.class, value).floatValue();
}
/**
* Get the bollean value for the value object
* May have conversion failure
*
* @param double
* @return
*/
public final double doubleValue(Object value)
{
if (value instanceof Number)
{
return ((Number) value).doubleValue();
}
return convert(Double.class, value).doubleValue();
}
/**
* Is the value multi valued
*
* @param value
* @return true - if the underlyinf is a collection of values and not a singole value
*/
public final boolean isMultiValued(Object value)
{
return (value instanceof Collection);
}
/**
* Get the number of values represented
*
* @param value
* @return 1 for normal values and the size of the collection for MVPs
*/
public final int size(Object value)
{
if (value instanceof Collection)
{
return ((Collection) value).size();
}
else
{
return 1;
}
}
/**
* Get a collection for the passed value
*
* @param value
* @return
*/
private final Collection createCollection(Object value)
{
Collection coll;
if (isMultiValued(value))
{
coll = (Collection) value;
}
else
{
ArrayList<Object> list = new ArrayList<Object>(1);
list.add(value);
coll = list;
}
return coll;
}
/**
* Get a collection for the passed value converted to the specified type
*
* @param c
* @param value
* @return
*/
public final <T> Collection<T> getCollection(Class<T> c, Object value)
{
Collection coll = createCollection(value);
return convert(c, coll);
}
/**
* Add a converter to the list of those available
*
* @param <F>
* @param <T>
* @param source
* @param destination
* @param converter
*/
public final <F, T> void addConverter(Class<F> source, Class<T> destination, Converter<F, T> converter)
{
Map<Class, Converter> map = conversions.get(source);
if (map == null)
{
map = new HashMap<Class, Converter>();
conversions.put(source, map);
}
map.put(destination, converter);
}
/**
* Add a dynamic two stage converter
* @param <F> from
* @param <I> intermediate
* @param <T> to
* @param source
* @param intermediate
* @param destination
*/
public final <F, I, T> Converter<F, T> addDynamicTwoStageConverter(Class<F> source, Class<I> intermediate, Class<T> destination)
{
Converter<F, T> converter = new TypeConverter.DynamicTwoStageConverter<F, I, T>(source, intermediate, destination);
addConverter(source, destination, converter);
return converter;
}
/**
* Find conversion for the specified object
*
* Note: Takes into account the class of the object and any interfaces it may
* also support.
*
* @param <F>
* @param <T>
* @param source
* @param dest
* @return
*/
@SuppressWarnings("unchecked")
public final <T> Converter getConverter(Object value, Class<T> dest)
{
Converter converter = null;
if (value == null)
{
return null;
}
// find via class of value
Class valueClass = value.getClass();
converter = getConverter(valueClass, dest);
if (converter != null)
{
return converter;
}
// find via supported interfaces of value
do
{
Class[] ifClasses = valueClass.getInterfaces();
for (Class ifClass : ifClasses)
{
converter = getConverter(ifClass, dest);
if (converter != null)
{
return converter;
}
}
valueClass = valueClass.getSuperclass();
}
while (valueClass != null);
return null;
}
/**
* Find a conversion for a specific Class
*
* @param <F>
* @param <T>
* @param source
* @param dest
* @return
*/
public <F, T> Converter getConverter(Class<F> source, Class<T> dest)
{
Converter<?, ?> converter = null;
Class clazz = source;
do
{
Map<Class, Converter> map = conversions.get(clazz);
if (map == null)
{
continue;
}
converter = map.get(dest);
if (converter == null)
{
// attempt to establish converter from source to dest via Number
Converter<?, ?> first = map.get(Number.class);
Converter<?, ?> second = null;
if (first != null)
{
map = conversions.get(Number.class);
if (map != null)
{
second = map.get(dest);
}
}
if (second != null)
{
converter = new TwoStageConverter<F, Number, T>(first, second);
}
}
}
while ((converter == null) && ((clazz = clazz.getSuperclass()) != null));
return converter;
}
/**
* Map of conversion
*/
private Map<Class, Map<Class, Converter>> conversions = new HashMap<Class, Map<Class, Converter>>();
// Support for pluggable conversions
/**
* Conversion interface
*
* @author andyh
*
* @param <F> From type
* @param <T> To type
*/
public interface Converter<F, T>
{
public T convert(F source);
}
/**
* Support for chaining conversions
*
* @author andyh
*
* @param <F> From Type
* @param <I> Intermediate type
* @param <T> To Type
*/
public static class TwoStageConverter<F, I, T> implements Converter<F, T>
{
Converter first;
Converter second;
TwoStageConverter(Converter first, Converter second)
{
this.first = first;
this.second = second;
}
@SuppressWarnings("unchecked")
public T convert(F source)
{
return (T) second.convert((I) first.convert(source));
}
}
/**
* Support for chaining conversions
*
* @author David Caruana
*
* @param <F> From Type
* @param <I> Intermediate type
* @param <T> To Type
*/
protected class DynamicTwoStageConverter<F, I, T> implements Converter<F, T>
{
Class<F> from;
Class<I> intermediate;
Class<T> to;
DynamicTwoStageConverter(Class<F> from, Class<I> intermediate, Class<T> to)
{
this.from = from;
this.intermediate = intermediate;
this.to = to;
}
/**
* @return from class
*/
public Class<F> getFrom()
{
return from;
}
/**
* @return intermediate class
*/
public Class<I> getIntermediate()
{
return intermediate;
}
/**
* @return to class
*/
public Class<T> getTo()
{
return to;
}
@SuppressWarnings("unchecked")
public T convert(F source)
{
Converter iConverter = TypeConverter.this.getConverter(from, intermediate);
Converter tConverter = TypeConverter.this.getConverter(intermediate, to);
if (iConverter == null || tConverter == null)
{
throw new TypeConversionException("Cannot convert from " + from.getName() + " to " + to.getName());
}
Object iValue = iConverter.convert(source);
Object tValue = tConverter.convert(iValue);
return (T)tValue;
}
}
}