
com.tangosol.dev.component.Interface Maven / Gradle / Ivy
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.dev.component;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.Collator;
import java.util.Enumeration;
import com.tangosol.util.ErrorList;
import com.tangosol.util.StringTable;
import com.tangosol.util.NullImplementation;
import com.tangosol.util.SimpleEnumerator;
import com.tangosol.run.xml.XmlElement;
/**
* An interface is a helper class for navigating to associated properties
* and behaviors. Note that an interface contains immutable information
* about behaviors and properties; all derived/modified interfaces therefore
* can directly copy the data structures from the base interface trait.
*
* @version 1.00, 08/13/98
* @author Cameron Purdy
*/
public class Interface
extends Trait
implements Constants
{
// ----- construction ---------------------------------------------------
/**
* Construct an Interface. This is the "default" constructor. This
* constructor is typically not used directly; there are two helper
* constructors which instantiate "implements", and "dispatches" interfaces.
*
* @param cd the Component with this Interface
* @param sName the name of this Interface
* @param nType the type of this Interface
* (one of IMPLEMENTS, or DISPATCHES)
* @param enmrBehavior an enumeration of behavior signatures
* @param enmrState an enumeration of property names
*/
protected Interface(Component cd, String sName, int nType,
Enumeration enmrBehavior, Enumeration enmrState)
{
super(cd, RESOLVED);
// assertion: Component required
if (cd == null)
{
throw new IllegalArgumentException(CLASS +
": Containing Component required.");
}
// assertion: valid name
if (sName == null || sName.length() == 0)
{
throw new IllegalArgumentException(CLASS +
": Invalid interface name (" + sName + ")");
}
// assertion: valid type
if (nType != IMPLEMENTS && nType != DISPATCHES)
{
throw new IllegalArgumentException(CLASS +
": Invalid interface type (" + nType + ")");
}
// store interface name/type
m_sName = sName;
m_nType = nType;
StringTable tbl;
// populate behaviors table if provided
if (enmrBehavior == null)
{
tbl = null;
}
else
{
tbl = new StringTable();
while (enmrBehavior.hasMoreElements())
{
String sBhvr = (String) enmrBehavior.nextElement();
// this could be the static initializer from a JCS
if (!Behavior.isConstructor(sBhvr))
{
tbl.add(sBhvr);
}
}
}
m_tblBehavior = tbl;
// populate properties table if provided
if (enmrState == null)
{
tbl = null;
}
else
{
tbl = new StringTable();
while (enmrState.hasMoreElements())
{
tbl.add((String) enmrState.nextElement());
}
}
m_tblState = tbl;
}
/**
* Construct an implements/dispatches Interface.
*
* @param cd the Component with this Interface
* @param cdJCS the Java Class Signature
* @param fDispatch true if an event interface (i.e. "dispatches");
* false if a normal Java interface (i.e. "implements")
*/
protected Interface(Component cd, Component cdJCS, boolean fDispatch)
{
this(cd, cdJCS.getName(), (fDispatch ? DISPATCHES : IMPLEMENTS),
new SimpleEnumerator(cdJCS.getBehavior()),
new SimpleEnumerator(cdJCS.getProperty()));
if (!cdJCS.isInterface())
{
throw new IllegalArgumentException(CLASS +
": Interface type required!");
}
// event interfaces have implied add/remove methods
if (fDispatch)
{
String sIface = m_sName;
String sClass = DataType.getClassType(sIface).getTypeString();
String sSig = sIface.substring(sIface.lastIndexOf('.') + 1) + '(' + sClass + ')';
StringTable tbl = m_tblBehavior;
tbl.add("add" + sSig);
tbl.add("remove" + sSig);
}
}
/**
* Construct an Interface using reflection.
*
* @param cd the Component with this Interface
* @param clz the Java Class Signature
*/
protected Interface(Component cd, Class clz)
{
this(cd, clz.getName(), IMPLEMENTS,
NullImplementation.getEnumeration(), NullImplementation.getEnumeration());
if (!clz.isInterface())
{
throw new IllegalArgumentException(CLASS +
": Interface type required!");
}
}
/**
* Construct an Interface for JCS Derivation
*
* @param cdJCS the Java Class Signature with this Interface
* @param sInterface the name of the interface
* false if a normal Java interface (i.e. "implements")
*/
protected Interface(Component cdJCS, String sInterface)
{
this(cdJCS, sInterface, IMPLEMENTS, null, null);
if (!cdJCS.isSignature())
{
throw new IllegalArgumentException(CLASS +
": Java Class Signature required!");
}
}
/**
* Construct a blank Interface Trait.
*
* @param base the base Interface to derive from
* @param cd the containing Component
* @param nMode one of RESOLVED, DERIVATION, MODIFICATION
*
* @see #getBlankDerivedTrait(Trait, int)
*/
protected Interface(Interface base, Component cd, int nMode)
{
super(base, cd, nMode);
}
/**
* Copy constructor.
*
* @param cd the Component containing the new Interface
* @param that the Interface to copy from
*/
protected Interface(Component cd, Interface that)
{
super(cd, that);
this.m_nType = that.m_nType;
this.m_sName = that.m_sName;
this.m_tblBehavior = that.m_tblBehavior;
this.m_tblState = that.m_tblState;
}
/**
* Construct an Interface from a stream.
*
* This is a custom serialization implementation that is unrelated to the
* Serializable interface and the Java implementation of persistence.
*
* @param cd the containing Component
* @param stream the stream to read this Interface from
* @param nVersion version of the data structure in the stream
*
* @exception IOException An IOException is thrown if an error occurs
* reading the Interface from the stream or if the stream
* does not contain a valid Interface
*/
protected Interface(Component cd, DataInput stream, int nVersion)
throws IOException
{
super(cd, stream, nVersion);
// name of interface
m_sName = stream.readUTF();
// interface type
m_nType = stream.readInt();
// details only exist if component was stored resolved
if (stream.readBoolean())
{
// behaviors
StringTable tbl = new StringTable();
int c = stream.readInt();
for (int i = 0; i < c; ++i)
{
tbl.add(stream.readUTF());
}
m_tblBehavior = tbl;
// properties
tbl = new StringTable();
c = stream.readInt();
for (int i = 0; i < c; ++i)
{
tbl.add(stream.readUTF());
}
m_tblState = tbl;
}
}
/**
* Construct a Trait object from persistent data. All traits implement
* implement a constructor from persistent data.
*
* @param parent the trait that contains this trait
* @param xml the XmlElement to read the persistent information from
* @param nVersion version of the data structure in the stream
*
* @throws IOException if an exception occurs reading the trait data
*/
protected Interface(Trait parent, XmlElement xml, int nVersion)
throws IOException
{
super(parent, xml, nVersion);
String sName = readString(xml.getElement("name"));
String sType = readString(xml.getElement("type"));
if (sName == BLANK || sType == BLANK)
{
throw new IOException("name or type is missing");
}
m_sName = sName;
for (int i = /* NOT 0! */ 1, c = DESCRIPTORS.length; i < c; ++i)
{
if (DESCRIPTORS[i].equalsIgnoreCase(sType))
{
m_nType = i;
break;
}
}
if (m_nType < 1)
{
throw new IOException("illegal type: " + sType);
}
m_tblBehavior = readTableKeys(xml, "methods", "method");
m_tblState = readTableKeys(xml, "fields", "field");
if (m_tblBehavior != null || m_tblState != null)
{
// binary serialization would have both null, or neither null
if (m_tblBehavior == null)
{
m_tblBehavior = new StringTable();
}
if (m_tblState == null)
{
m_tblState = new StringTable();
}
}
}
// ----- persistence ----------------------------------------------------
/**
* Save the Interface to a stream.
*
* This is a custom serialization implementation that is unrelated to the
* Serializable interface and the Java implementation of persistence.
*
* @param stream the stream to write this Interface to
*
* @exception IOException An IOException is thrown if an error occurs
* writing the Interface to the stream
*/
protected synchronized void save(DataOutput stream)
throws IOException
{
super.save(stream);
// name of interface
stream.writeUTF(m_sName);
// interface type
stream.writeInt(m_nType);
// only save details if component resolved (i.e. a cached component)
boolean fCached = (m_tblBehavior != null && m_tblState != null);
stream.writeBoolean(fCached);
if (fCached)
{
// behaviors
stream.writeInt(m_tblBehavior.getSize());
for (Enumeration enmr = m_tblBehavior.keys(); enmr.hasMoreElements(); )
{
stream.writeUTF((String) enmr.nextElement());
}
// properties
stream.writeInt(m_tblState.getSize());
for (Enumeration enmr = m_tblState.keys(); enmr.hasMoreElements(); )
{
stream.writeUTF((String) enmr.nextElement());
}
}
}
/**
* Save the Trait information to XML. If derived traits have any
* data of their own, then they must implement (i.e. supplement) this
* method.
*
* This is a custom serialization implementation that is unrelated to the
* Serializable interface and the Java implementation of persistence.
*
* @param xml an XmlElement to write the persistent information to
*
* @throws IOException if an exception occurs saving the trait data
*/
protected synchronized void save(XmlElement xml)
throws IOException
{
xml.addElement("name").setString(m_sName);
xml.addElement("type").setString(DESCRIPTORS[m_nType]);
super.save(xml);
saveTableKeys(xml, m_tblBehavior, "methods", "method");
saveTableKeys(xml, m_tblState, "fields", "field");
}
// ----- derivation/modification ----------------------------------------
/**
* Construct a blank Interface from this base.
*
* @param parent the containing Component
* @param nMode RESOLVED, DERIVATION or MODIFICATION
*
* @return a new blank derived trait of this trait's class with the
* specified parent and mode
*/
protected Trait getBlankDerivedTrait(Trait parent, int nMode)
{
return new Interface(this, (Component) parent, nMode);
}
/**
* Create a derived/modified Trait by combining the information from this
* Trait with the super or base level's Trait information. Neither the
* base ("this") nor the delta may be modified in any way.
*
* @param traitDelta the Trait derivation or modification
* @param parent the Trait which will contain the resulting Trait or
* null if the resulting Trait will not be contained
* @param loader the Loader object for JCS, CIM, and CD dependencies
* @param errlist the error list object to log error information to
*
* @return the result of applying the specified delta Trait to this Trait
*
* @exception ComponentException thrown only if a fatal error occurs
*/
protected Trait resolve(Trait traitDelta, Trait parent, Loader loader, ErrorList errlist)
throws ComponentException
{
Interface base = this;
Interface delta = (Interface) resolveDelta(traitDelta, loader, errlist);
Interface derived = (Interface) super.resolve(delta, parent, loader, errlist);
derived.m_nType = base.m_nType;
derived.m_sName = base.m_sName;
derived.m_tblBehavior = base.m_tblBehavior;
derived.m_tblState = base.m_tblState;
return derived;
}
/**
* Create a derivation/modification Trait by determining the differences
* between the derived Trait ("this") and the passed base Trait. Neither
* the derived Trait ("this") nor the base may be modified in any way.
*
* @param traitBase the base Trait to extract
* @param parent the Trait which will contain the resulting Trait or
* null if the resulting Trait will not be contained
* @param loader the Loader object for JCS, CIM, and CD dependencies
* @param errlist the error list object to log error information to
*
* @return the delta between this derived Trait and the base Trait
*
* @exception ComponentException thrown only if a fatal error occurs
*/
protected Trait extract(Trait traitBase, Trait parent, Loader loader, ErrorList errlist)
throws ComponentException
{
Interface base = (Interface) traitBase;
Interface delta = (Interface) super.extract(base, parent, loader, errlist);
// redundant information ... delta interfaces are discarded anyways
delta.m_nType = base.m_nType;
delta.m_sName = base.m_sName;
delta.m_tblBehavior = base.m_tblBehavior;
delta.m_tblState = base.m_tblState;
return delta;
}
/**
* Finalize the extract process. This means that this trait will not
* be asked to extract again. A trait is considered persistable after
* it has finalized the extract process.
*
* @param loader the Loader object for JCS, CIM, and CD dependencies
* @param errlist the error list object to log error information to
*
* @exception ComponentException thrown only if a fatal error occurs
*/
protected synchronized void finalizeExtract(Loader loader, ErrorList errlist)
throws ComponentException
{
super.finalizeExtract(loader, errlist);
// discard behaviors/properties
m_tblState = null;
m_tblBehavior = null;
}
// ----- miscellaneous Trait methods ------------------------------------
/**
* Reset state, discarding all information.
*/
protected synchronized void invalidate()
{
super.invalidate();
m_sName = null;
m_tblBehavior = null;
m_tblState = null;
}
/**
* Determine the unique name for this trait.
*
* A sub-trait that exists within a collection of sub-traits must have
* two ways of being identified:
*
* 1) A unique name, which is a string identifier
* 2) A UID as a secondary identifier (dog tag)
*
* @return the primary string identifier of the trait
*/
protected String getUniqueName()
{
return m_sName;
}
/**
* Determine the unique description for this trait.
*
* All traits must be uniquely identifiable by a "description string".
* This enables the origin implementation built into Trait to use the
* description string to differentiate between Trait origins.
*
* @return the description string for the trait
*/
protected String getUniqueDescription()
{
return DESCRIPTORS[m_nType] + ' ' + getUniqueName();
}
// ----- accessors ------------------------------------------------------
/**
* Determine the Component which contains this Interface.
*
* @return the Component trait that contains this Interface
*/
public Component getComponent()
{
return (Component) getParentTrait();
}
/**
* Determine the Interface type.
*
* @return one of IMPLEMENTS, or DISPATCHES
*/
public int getType()
{
return m_nType;
}
/**
* Determine the Interface name.
*
* @return the Interface name
*/
public String getName()
{
return m_sName;
}
/**
* Determine the Behaviors declared by this Interface.
*
* @return an enumeration of Behavior signatures
*/
public Enumeration getBehaviors()
{
return m_tblBehavior.keys();
}
/**
* Determine the Properties declared by this Interface.
*
* @return an enumeration of Property names
*/
public Enumeration getProperties()
{
return m_tblState.keys();
}
// ----- helpers --------------------------------------------------------
/**
* Determine if a signature follows the design pattern for Java Bean
* event interface registration, and if so, what the action (either
* registration or unregistration) is.
*
* @param sSig a behavior signature
*
* @return the action verb (either "add" or "remove") or null if the
* signature is not related to event interface registration
*/
public static String getDispatchesVerb(String sSig)
{
final Collator INSENS = Constants.INSENS;
final String REG = "add";
final String UNREG = "remove";
// check for registration
if (INSENS.equals(sSig.substring(0, 3), REG))
{
return REG;
}
// check for unregistration (note: minimum signature length
// is 3 characters, e.g. "f()")
if (sSig.length() > 6 && INSENS.equals(sSig.substring(0, 6), UNREG))
{
return UNREG;
}
return null;
}
/**
* Determine if a signature follows the design pattern for Java Bean
* event interface registration, and if so, what the interface name is.
*
* @param sSig a behavior signature
*
* @return the very short (not qualified, without the "Listener"
* suffix) interface name implied by the signature, or null
*/
public static String getDispatchesName(String sSig)
{
String sVerb = getDispatchesVerb(sSig);
if (sVerb == null)
{
return null;
}
// get the event interface short name
String sIface = sSig.substring(sVerb.length(), sSig.indexOf('('));
if (sIface.length() <= 0)
{
return null;
}
// verify that it ends with Listener
final String SUFFIX = "Listener";
int cchName = sIface.length() - SUFFIX.length();
if (cchName <= 0)
{
return null;
}
String sEnd = sIface.substring(cchName);
if (INSENS.equals(sEnd, SUFFIX))
{
return sIface.substring(0, cchName);
}
return null;
}
// ----- Object methods -------------------------------------------------
/**
* Compare this Interface to another Object for equality.
*
* @param obj the other Object to compare to this
*
* @return true if this Interface equals that Object
*/
public boolean equals(Object obj)
{
if (obj instanceof Interface)
{
Interface that = (Interface) obj;
return this == that
|| this.m_nType == that.m_nType
&& this.m_sName .equals(that.m_sName )
&& (this.m_tblBehavior == null ? that.m_tblBehavior == null :
this.m_tblBehavior.equals(that.m_tblBehavior))
&& (this.m_tblState == null ? that.m_tblState == null :
this.m_tblState .equals(that.m_tblState ))
&& super.equals(that);
}
return false;
}
// ----- debugging ------------------------------------------------------
/**
* Provide the entire set of trait information in a printed format.
*
* @param out PrintWriter object to dump the information to
* @param sIndent a string used to indent each line of dumped information
*/
public void dump(PrintWriter out, String sIndent)
{
out.println(sIndent + toString());
super.dump(out, sIndent);
String sBehavior = (m_tblBehavior == null ? "" : m_tblBehavior.toString());
String sState = (m_tblState == null ? "" : m_tblState .toString());
out.println(sIndent + "Behavior: " + sBehavior);
out.println(sIndent + "State: " + sState);
}
// ----- data members ---------------------------------------------------
/**
* The name of this class.
*/
private static final String CLASS = "Interface";
/**
* Interface type: Implements.
*/
public static final int IMPLEMENTS = 1;
/**
* Interface type: Dispatches.
*/
public static final int DISPATCHES = 2;
/**
* The descriptor string for Implements.
*/
protected static final String DESCRIPTOR_IMPLEMENTS = "Implements";
/**
* The descriptor string for Dispatches.
*/
protected static final String DESCRIPTOR_DISPATCHES = "Dispatches";
/**
* The descriptor strings indexed by the interface type.
*/
private String[] DESCRIPTORS = {"",
DESCRIPTOR_IMPLEMENTS,
DESCRIPTOR_DISPATCHES};
/**
* The interface type.
*/
private int m_nType;
/**
* The interface name.
*/
private String m_sName;
/**
* The table of behavior names declared by this interface.
*/
private StringTable m_tblBehavior;
/**
* The table of property names declared by this interface.
*/
private StringTable m_tblState;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy