/*******************************************************************************
* Copyright (c) 2004 Composent, Inc. and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: Composent, Inc. - initial API and implementation
******************************************************************************/
package org.eclipse.ecf.core.identity;
import java.io.Serializable;
import org.eclipse.core.runtime.*;
import org.eclipse.ecf.internal.core.identity.Activator;
/**
* Namespace base class.
* <p>
* This class and subclasses define a namespace for the creation and management
* of ID instances. Creation of ID instances is accomplished via the
* {@link #createInstance(Object[])} method, implemented by subclasses of this
* Namespace superclass.
* <p>
* All Namespace instances must have a unique name passed to the Namespace upon
* construction.
* <p>
* Typically Namespace instances are created via plugins that define extensions
* of the org.eclipse.ecf.namespace extension point. For example, to define a
* new Namespace subclass XMPPNamespace with name "ecf.xmpp" and add it to the
* ECF extension registry:
*
* <pre>
* <extension
* point="org.eclipse.ecf.namespace">
* <namespace
* class="XMPPNamespace"
* name="ecf.xmpp"/>
* </extension>
* </pre>
*
* @see ID
*/
public abstract class Namespace implements Serializable, IAdaptable {
private static final long serialVersionUID = 3976740272094720312L;
public static final String SCHEME_SEPARATOR = ":"; //$NON-NLS-1$
private String name;
private String description;
private int hashCode;
private boolean isInitialized = false;
public Namespace() {
// public null constructor
}
public final boolean initialize(String n, String desc) {
Assert.isNotNull(n, "Namespace<init> name cannot be null"); //$NON-NLS-1$
if (!isInitialized) {
this.name = n;
this.description = desc;
this.hashCode = name.hashCode();
this.isInitialized = true;
return true;
}
return false;
}
public Namespace(String name, String desc) {
initialize(name, desc);
}
/**
* Override of Object.equals. This equals method returns true if the
* provided Object is also a Namespace instance, and the names of the two
* instances match.
*
* @param other
* the Object to test for equality
*/
public boolean equals(Object other) {
if (!(other instanceof Namespace))
return false;
return ((Namespace) other).name.equals(name);
}
/**
* Hashcode implementation. Subclasses should not override.
*
* @return int hashCode for this Namespace. Should be unique.
*/
public int hashCode() {
return hashCode;
}
/**
* Test whether two IDs are equal to one another.
*
* @param first
* the first ID. Must not be <code>null</code>.
* @param second
* the second ID. Must not be <code>null</code>.
* @return <code>true</code> if this ID is equal to the given ID.
* <code>false</code> otherwise.
*/
protected boolean testIDEquals(BaseID first, BaseID second) {
// First check that namespaces are the same and non-null
Namespace sn = second.getNamespace();
if (sn == null || !this.equals(sn))
return false;
return first.namespaceEquals(second);
}
/**
* The default implementation of this method is to call
* id.namespaceGetName(). Subclasses may override.
*
* @param id
* the ID to get the name for. Must not be <code>null</code>.
* @return String that is the unique name for the given id within this
* Namespace.
*/
protected String getNameForID(BaseID id) {
return id.namespaceGetName();
}
/**
* The default implementation of this method is to call
* first.namespaceCompareTo(second). Subclasses may override.
*
* @param first
* the first id to compare. Must not be <code>null</code>.
* @param second
* the second id to compare. Must not be <code>null</code>.
* @return int as specified by {@link Comparable}.
*/
protected int getCompareToForObject(BaseID first, BaseID second) {
return first.namespaceCompareTo(second);
}
/**
* The default implementation of this method is to call
* id.namespaceHashCode(). Subclasses may override.
*
* @param id
* the id in this Namespace to get the hashcode for. Must not be
* <code>null</code>.
* @return the hashcode for the given id. Returned value must be unique
* within this process.
*/
protected int getHashCodeForID(BaseID id) {
return id.namespaceHashCode();
}
/**
* The default implementation of this method is to call
* id.namespaceToExternalForm(). Subclasses may override.
*
* @param id
* the id in this Namespace to convert to external form.
* @return String that represents the given id in an external form. Note
* that this external form may at some later time be passed to
* {@link #createInstance(Object[])} as a single String parameter,
* and should result in a valid ID instance of the appropriate
* Namespace.
*/
protected String toExternalForm(BaseID id) {
return id.namespaceToExternalForm();
}
/**
* Get the name of this namespace. Must not return <code>null</code>.
*
* @return String name of Namespace instance. Must not return
* <code>null</code>, and the returned value should be a globally
* unique name for this Namespace subclass.
*
*/
public String getName() {
return name;
}
/**
* Get the description, associated with this Namespace. The returned value
* may be <code>null</code>.
*
* @return the description associated with this Namespace. May be
* <code>null</code>.
*/
public String getDescription() {
return description;
}
/**
* Make an instance of this namespace. Namespace subclasses, provided by
* plugins must implement this method to construct ID instances for the
* given namespace.
* <p>
* </p>
* See {@link #getSupportedParameterTypes()} to get information relevant to
* deciding what parameter types are expected by this method.
* <p>
* </p>
*
* @param parameters
* an Object[] of parameters for creating ID instances. May be
* null.
*
* @return a non-null ID instance. The class used may extend BaseID or may
* implement the ID interface directly
* @throws IDCreateException
* if construction fails
*/
public abstract ID createInstance(Object[] parameters) throws IDCreateException;
/**
* Get the primary scheme associated with this namespace. Subclasses must
* provide an implementation that returns a non-<code>null</code> scheme
* identifier. Note that the returned scheme should <b>not</b> contain the
* Namespace.SCHEME_SEPARATOR (\":\").
*
* @return a String scheme identifier. Must not be <code>null</code>.
*/
public abstract String getScheme();
/**
* Get an array of schemes supported by this Namespace instance. Subclasses
* may override to support multiple schemes.
*
* @return String[] of schemes supported by this Namespace. Will not be
* <code>null</code>, but returned array may be of length 0.
*/
public String[] getSupportedSchemes() {
return new String[0];
}
/**
* Get the supported parameter types for IDs created via subsequent calls to
* {@link #createInstance(Object[])}. Callers may use this method to
* determine the available parameter types, and then create and pass in
* conforming Object arrays to to {@link #createInstance(Object[])}.
* <p>
* </p>
* An empty two-dimensional array (new Class[0][0]) is the default returned
* by this abstract superclass. This means that the Object [] passed to
* {@link #createInstance(Object[])} will be ignored.
* <p>
* </p>
* Subsclasses should override this method to specify the parameters that
* they will accept in calls to {@link #createInstance(Object[])}. The rows
* of the returned Class array are the acceptable types for a given
* invocation of createInstance.
* <p>
* </p>
* Consider the following example:
* <p>
* </p>
*
* <pre>
* public Class[][] getSupportedParameterTypes() {
* return new Class[][] { { String.class }, { String.class, String.class } };
* }
* </pre>
*
* The above means that there are two acceptable values for the Object []
* passed into {@link #createInstance(Object[])}: 1) a single String, and 2)
* two Strings. These would therefore be acceptable as input to
* createInstance:
*
* <pre>
* ID newID1 = namespace.createInstance(new Object[] { "Hello" });
* ID newID2 = namespace.createInstance(new Object[] { "Hello", "There"}};
* </pre>
*
* @return Class [][] an array of class []s. Rows of the returned
* two-dimensional array define the acceptable parameter types for a
* single call to {@link #createInstance(Object[])}. If zero-length
* Class arrays are returned (i.e. Class[0][0]), then Object []
* parameters to {@link #createInstance(Object[])} will be ignored.
*/
public Class<?>[][] getSupportedParameterTypes() {
return new Class[][] { {} };
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@SuppressWarnings("unchecked")
public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
if (adapter.isInstance(this)) {
return this;
}
IAdapterManager manager = Activator.getDefault().getAdapterManager();
if (manager == null)
return null;
return manager.loadAdapter(this, adapter.getName());
}
/**
* @since 3.1
*/
protected String getInitStringFromExternalForm(Object[] args) {
if (args == null || args.length < 1 || args[0] == null)
return null;
if (args[0] instanceof String) {
final String arg = (String) args[0];
if (arg.startsWith(getScheme() + SCHEME_SEPARATOR)) {
final int index = arg.indexOf(SCHEME_SEPARATOR);
if (index >= arg.length())
return null;
return arg.substring(index + 1);
}
}
return null;
}
public String toString() {
StringBuffer b = new StringBuffer("Namespace["); //$NON-NLS-1$
b.append("name=").append(name).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
b.append("scheme=").append(getScheme()).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
b.append("description=").append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return b.toString();
}
}