/**
* $Id: EntityReference.java 130233 2013-10-08 14:00:47Z azeckoski@unicon.net $
* $URL: https://source.sakaiproject.org/svn/entitybroker/trunk/api/src/java/org/sakaiproject/entitybroker/EntityReference.java $
* AutoRegister.java - entity-broker - 31 May 2007 7:01:11 PM - azeckoski
**************************************************************************
* Copyright (c) 2007, 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* 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.sakaiproject.entitybroker;
import java.io.Serializable;
import org.sakaiproject.entitybroker.entityprovider.EntityProvider;
import org.sakaiproject.entitybroker.util.TemplateParseUtil;
/**
* The class of all Entity references handled by the EntityBroker system. This provides the
* entity prefix, which uniquely identifies the {@link EntityProvider}
* responsible for handling the Entity. It optionally provides the entity id which
* uniquely identifies an entity locally within the prefix space.
* It also provides for all parsing methods for
* entity references (always of the form /prefix or /prefix/id)
*
* @author Aaron Zeckoski (aaronz@vt.edu)
*/
public class EntityReference implements Cloneable, Serializable {
public final static long serialVersionUID = 1l;
// NOTE: these are transient for serialization - ignore PMD SE_TRANSIENT_FIELD_NOT_RESTORED
public static transient final char SEPARATOR = EntityView.SEPARATOR;
public static transient final char PERIOD = EntityView.PERIOD;
protected transient String originalReference;
/**
* This is a special method and should not normally be used,
* use {@link #toString()} or {@link #getReference()}
* @return the reference string used to create this object or
* null if this was created using a constructor which does not supply a reference
*/
public String getOriginalReference() {
return originalReference;
}
protected void setOriginalReference(String reference) {
checkReference(reference);
this.originalReference = reference;
}
/**
* An entity prefix, should match with the prefix handled in an {@link EntityProvider},
* uniquely identifies an entity space or entity type<br/>
* <b>WARNING:</b> use the {@link #getPrefix()} method rather than referring to this directly
*/
public String prefix;
/**
* @return the entity prefix (uniquely identifies an entity space or entity type),
* this should never be null
*/
public String getPrefix() {
return makeEntityPrefix();
}
private String entityId;
/**
* A local entity id, represents an entity uniquely in a tool/webapp,
* could match with the actual id of a model data object,
* this will be null if this reference refers to an entity space only
*
* @return the entity id (locally unique id for an entity of this entity type)
* or null if this this reference refers to an entity space only
*/
public String getId() {
return makeEntityId();
}
// CONSTRUCTORS
private transient boolean empty = true;
/**
* @return true if this object contains no reference data, false if it contains a valid reference
*/
public boolean isEmpty() {
return empty;
}
public EntityReference() { }
/**
* Constructor which takes an entity reference,
* this is the most common way to construct an entity reference object
*
* @param reference a globally unique reference to an entity,
* consists of the entity prefix and optional path segments
*/
public EntityReference(String reference) {
// OVERRIDE THIS when creating your own EntityReference
this();
checkReference(reference);
this.originalReference = reference;
this.prefix = findPrefix(reference);
this.entityId = findId(reference);
empty = false;
}
/**
* Convenience constructor for when you know the prefix and/or id
*
* @param prefix the entity prefix
* @param id the local entity id (can be empty string if there is no id)
*/
public EntityReference(String prefix, String id) {
// OVERRIDE THIS when creating your own EntityReference
this();
checkPrefixId(prefix, id);
this.prefix = prefix;
if ("".equals(id)) { id = null; }
this.entityId = id;
empty = false;
}
// METHODS
/**
* Override this if you are making a new class to define your entity reference
*
* @param spaceOnly if this is true then only return the entity space reference (e.g. /prefix),
* otherwise return the full reference (e.g. /prefix/id)
* @return an entity reference string
*/
protected String makeEntityReference(boolean spaceOnly) {
// OVERRIDE THIS when creating your own EntityReference
if (getPrefix() == null) {
throw new IllegalStateException("prefix is null, cannot generate the string reference");
}
String ref = null;
if (spaceOnly || getId() == null) {
ref = SEPARATOR + getPrefix();
} else {
ref = SEPARATOR + getPrefix() + SEPARATOR + getId();
}
return ref;
}
/**
* Override this if you are making a new class to define your entity reference,
* called by public {@link #getPrefix()} method
*
* @return the prefix for the current entity reference
*/
protected String makeEntityPrefix() {
prefix = "".equals(prefix) ? null : prefix; // fix empty prefix to null
return prefix;
}
/**
* Override this if you are making a new class to define your entity reference,
* called by public {@link #getId()} method
*
* @return the prefix for the current entity reference
*/
protected String makeEntityId() {
entityId = "".equals(entityId) ? null : entityId; // fix empty id to null
return entityId;
}
/**
* Get the string reference for this entity reference object,
* same as calling {@link #toString()}
* @return the full entity reference
*/
public String getReference() {
return this.toString();
}
/**
* Get the space reference for this entity reference,
* this ignored any id and only returns the reference to the entity space
* @return the entity space reference (e.g. /myPrefix)
*/
public String getSpaceReference() {
return makeEntityReference(true);
}
/**
* @return the string version of this entity reference,
* example: /prefix if there is no id or /prefix/id if there is an id
*/
@Override
public String toString() {
return makeEntityReference(false);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return copy(this);
}
/**
* @return a copy of this reference
* @see #copy(EntityReference)
*/
public EntityReference copy() {
return copy(this);
}
// STATIC METHODS
/**
* Get the entity prefix based on an entity reference string,
* <b>WARNING:</b> this is meant for internal use,
* use {@link EntityReference#EntityReference(String)} and
* the methods in {@link EntityBroker} to parse references
*
* @param reference an entity reference or entity URL
* @return the entity prefix
*/
public static String getPrefix(String reference) {
return findPrefix(reference);
}
/**
* Will convert a reference into an id (even if it is not a reference)
* @param reference an entity reference (e.g. /user/aaronz)
* @return the id from the reference (e.g. aaronz), preserves null
*/
public static String getIdFromRef(String reference) {
String id = null;
if (reference != null) {
if (reference.startsWith("/")) {
// assume the form of "/user/userId"
id = new EntityReference(reference).getId();
} else {
// otherwise assume this is the id
id = reference;
}
}
return id;
}
/**
* Get the id value out of a reference by the key that preceeds it
* @param reference an entity reference (e.g. /site/siteId/group/groupId)
* @param key the key to get the id from (e.g. 'group' yields groupId)
* @return the id value OR null if no key is found or no id is available
*/
public static String getIdFromRefByKey(String reference, String key) {
String id = null;
if (reference != null) {
String[] parts = reference.split("/");
for (int i = 0; i < parts.length; i++) {
if (key.equals(parts[i])) {
if (parts.length > i+1) {
id = parts[i+1];
}
}
}
}
return id;
}
/**
* Get the entity prefix based on an entity reference
*
* @param reference a globally unique reference to an entity,
* consists of the entity prefix and optional id
* @return the entity prefix
*/
protected static String findPrefix(String reference) {
int spos = getSeparatorPos(reference, 1);
if (spos == -1) {
// trim off the extension from the end
spos = reference.lastIndexOf(PERIOD);
}
return spos == -1 ? reference.substring(1) : reference.substring(1, spos);
}
/**
* Get the local entity id based on a full entity reference
*
* @param reference a globally unique reference to an entity,
* consists of the entity prefix and optional id
* @return the local entity id or null if none can be found
*/
protected static String findId(String reference) {
String id = null;
int spos = getSeparatorPos(reference, 1);
if (spos != -1) {
int spos2 = getSeparatorPos(reference, 2);
if (spos2 == -1) {
// trim off the extension from the end if it seems valid
if (TemplateParseUtil.findExtension(reference)[2] != null) {
spos2 = reference.lastIndexOf(PERIOD);
}
}
id = spos2 == -1 ? reference.substring(spos + 1) : reference.substring(spos + 1, spos2);
}
return id;
}
/**
* @param reference a globally unique reference to an entity,
* consists of the entity prefix and optional id
* @param number this is the separator to get,
* 0 would return the first one found, 1 would return the second
* @return the location of the separator between the entity and the id or -1 if none found
*/
protected static int getSeparatorPos(String reference, int number) {
checkReference(reference);
int position = 0;
for (int i = 0; i < number; i++) {
position = reference.indexOf(SEPARATOR, position+1);
if (position < 0) {
break;
}
}
return position;
}
/**
* Check if a reference is basically valid
* @param reference
* @throws IllegalArgumentException if the reference is not even basically valid
*/
protected static void checkReference(String reference) {
if (reference == null
|| "".equals(reference)
|| SEPARATOR != reference.charAt(0) )
throw new IllegalArgumentException("Invalid entity reference for EntityBroker: "
+ reference + " - these begin with /prefix, e.g. " + SEPARATOR + "myentity"
+ SEPARATOR + "3 OR " + SEPARATOR + "myentity");
}
/**
* Checks this prefix and id to see if they are valid format, throw exceptions if they aren't
*/
protected static void checkPrefixId(String prefix, String id) {
if (prefix == null || prefix.equals("") || id == null) {
throw new IllegalArgumentException("prefix ("+prefix+") and id ("+id+") cannot be null (prefix cannot be empty) to get entity reference");
}
boolean prefixOK = prefix.matches(TemplateParseUtil.VALID_VAR_CHARS+"+");
boolean idOK = id.matches(TemplateParseUtil.VALID_VAR_CHARS+"*");
if (! prefixOK || ! idOK ) {
throw new IllegalArgumentException("prefix ("+prefix+") and id ("+id+") must contain only valid chars: " + TemplateParseUtil.VALID_VAR_CHARS);
}
}
/**
* Creates a copy of an entity reference
* @param ref an entity reference object
* @return the new copy of the ref
* @throws IllegalArgumentException if the ref is invalid OR null
*/
public static EntityReference copy(EntityReference ref) {
if (ref == null) {
throw new IllegalArgumentException("input entity reference must not be null");
}
EntityReference copy = new EntityReference(ref.getPrefix(), ref.getId() == null ? "" : ref.getId());
return copy;
}
}