mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
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:
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
246
source/java/org/alfresco/service/cmr/repository/ContentData.java
Normal file
246
source/java/org/alfresco/service/cmr/repository/ContentData.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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;
|
||||
}
|
119
source/java/org/alfresco/service/cmr/repository/CopyService.java
Normal file
119
source/java/org/alfresco/service/cmr/repository/CopyService.java
Normal 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);
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
||||
{
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
161
source/java/org/alfresco/service/cmr/repository/NodeRef.java
Normal file
161
source/java/org/alfresco/service/cmr/repository/NodeRef.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
478
source/java/org/alfresco/service/cmr/repository/NodeService.java
Normal file
478
source/java/org/alfresco/service/cmr/repository/NodeService.java
Normal 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;
|
||||
}
|
580
source/java/org/alfresco/service/cmr/repository/Path.java
Normal file
580
source/java/org/alfresco/service/cmr/repository/Path.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
111
source/java/org/alfresco/service/cmr/repository/PathTest.java
Normal file
111
source/java/org/alfresco/service/cmr/repository/PathTest.java
Normal 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());
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
111
source/java/org/alfresco/service/cmr/repository/StoreRef.java
Normal file
111
source/java/org/alfresco/service/cmr/repository/StoreRef.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user