// Copyright 2011 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.meta.xml;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import org.w3c.dom.Element;
import nexj.core.meta.MetadataException;
import nexj.core.meta.xml.XMLMetadataHelper.ContextFixup;
import nexj.core.scripting.Compiler;
import nexj.core.scripting.GlobalEnvironment;
import nexj.core.scripting.Machine;
import nexj.core.scripting.PCodeFunction;
import nexj.core.scripting.Pair;
import nexj.core.scripting.Symbol;
import nexj.core.util.HashHolder;
import nexj.core.util.HashHolderList;
import nexj.core.util.HashTab;
import nexj.core.util.Logger;
import nexj.core.util.Lookup;
import nexj.core.util.Named;
import nexj.core.util.ObjUtil;
import nexj.core.util.StringUtil;
import nexj.core.util.SysUtil;
import nexj.core.util.TextPosition;
import nexj.core.util.XMLUtil;
/**
* Loader for SOA Definitions and SOA Implementations.
*/
public class XMLSOAMetadataLoader
{
// constants
/**
* The symbol for defining a new dynamic object system class.
*/
public final static Symbol DEFINE_CLASS = Symbol.define("define-class");
/**
* The symbol for defining a class method of a dynamic object system class.
*/
public final static Symbol CLASS_METHOD = Symbol.define("class-method");
/**
* The symbol for defining a class attribute of a dynamic object system class.
*/
public final static Symbol CLASS_ATTRIBUTE = Symbol.define("class-attribute");
/**
* The symbol for defining an instance attribute of a dynamic object system class.
*/
public final static Symbol ATTRIBUTE = Symbol.define("attribute");
/**
* The interfaces attribute symbol for specifying the interfaces of a service.
*/
public final static Symbol INTERFACES = Symbol.define("interfaces");
/**
* The implementations attribute symbol for specifying the implementations of a service.
*/
public final static Symbol IMPLEMENTATIONS = Symbol.define("implementations");
/**
* The :init symbol to specify attribute initializers.
*/
public final static Symbol INIT = Symbol.define(":init");
/**
* Regular expression pattern for matching one or more whitespace characters.
*/
protected final static Pattern SPACE_PATTERN = Pattern.compile("\\s+");
// associations
/**
* The metadata loading helper.
*/
protected XMLMetadataHelper m_helper;
/**
* The definitions loaded by this loader.
*/
protected Lookup m_definitionMap = new HashTab(); // of type Definition[String]
/**
* The implementations loaded by this loader.
*/
protected Lookup m_implementationMap = new HashTab(); // of type Implementation[String]
/**
* Fixups for resolving references by name.
*/
protected List m_resolutionFixupList = new ArrayList();
/**
* Holds every independently-referenceable object loaded by the loader, indexed by global name.
*/
protected Lookup m_globalMap = new HashTab(); // of type Object[String]
// constructors
/**
* Creates a new loader for loading SOA metadata from an XML file.
* @param helper The XML metadata loading helper.
*/
public XMLSOAMetadataLoader(XMLMetadataHelper helper)
{
m_helper = helper;
}
// operations
/**
* Loads an SOA Definition from a DOM element.
* @param element The DOM element containing the definition.
* @param sName The definition name.
*/
public void loadDefinition(Element element, String sName)
{
XMLMetadataHelper.verifyRootElement(element, "SOADefinition");
final Definition definition = new Definition();
loadPart(element, definition);
definition.setVersion(XMLUtil.getStringAttr(element, "version", definition.getVersion()));
if (m_definitionMap.put(definition.getNamePrefix(), definition) != null)
{
throw new MetadataException("err.meta.soa.definitionDup",
new Object[] {definition.getName(), definition.getVersion(), definition.getNamePrefix()});
}
Element child = XMLUtil.findChildElement(element, "Types");
if (child != null)
{
XMLUtil.forEachChildElement(child, "Type", new XMLUtil.ElementHandler()
{
public void handleElement(Element typeElement)
{
final ModelType type = new ModelType(definition);
loadPart(typeElement, type);
if (!definition.addType(type))
{
throw new MetadataException("err.meta.soa.typeDup",
new Object[] {type.getName(), definition.getNamePrefix()});
}
m_globalMap.put(type.getGlobalName(), type);
String sBases = StringUtil.trimToNull(XMLUtil.getStringAttr(typeElement, "bases"));
if (sBases != null)
{
final String[] sBaseArray = SPACE_PATTERN.split(sBases);
m_resolutionFixupList.add(new ContextFixup(m_helper)
{
public void fixup()
{
for (int i = 0; i < sBaseArray.length; i++)
{
String sBaseTypeName = definition.resolveTypeRef(sBaseArray[i]);
ModelType baseType = (ModelType)m_globalMap.get(sBaseTypeName);
if (baseType == null)
{
throw new MetadataException("err.meta.soa.unknownType",
new Object[] {sBaseTypeName, type.getGlobalName(), definition.getNamePrefix()});
}
type.addBase(baseType);
}
}
});
}
Element attributesElement = XMLUtil.findChildElement(typeElement, "Attributes");
if (attributesElement != null)
{
XMLUtil.forEachChildElement(attributesElement, "Attribute", new XMLUtil.ElementHandler()
{
public void handleElement(Element attributeElement)
{
Attribute attribute = new Attribute();
loadPart(attributeElement, attribute);
attribute.setType(XMLUtil.getReqStringAttr(attributeElement, "type")); // TODO: Resolve later
attribute.setCollection(XMLUtil.getBooleanAttr(attributeElement, "collection", attribute.isCollection()));
attribute.setRequired(XMLUtil.getBooleanAttr(attributeElement, "required", attribute.isRequired()));
if (!type.addAttribute(attribute))
{
throw new MetadataException("err.meta.soa.attributeDup",
new Object[] {attribute.getName(), type.getName(), definition.getNamePrefix()});
}
}
});
}
}
});
}
child = XMLUtil.findChildElement(element, "Interfaces");
if (child != null)
{
XMLUtil.forEachChildElement(child, "Interface", new XMLUtil.ElementHandler()
{
public void handleElement(Element interfaceElement)
{
final Interface iface = new Interface(definition);
loadPart(interfaceElement, iface);
if (!definition.addInterface(iface))
{
throw new MetadataException("err.meta.soa.interfaceDup",
new Object[] {iface.getName(), definition.getNamePrefix()});
}
m_globalMap.put(iface.getGlobalName(), iface);
XMLUtil.forEachChildElement(interfaceElement, "Method", new XMLUtil.ElementHandler()
{
public void handleElement(Element methodElement)
{
final Method method = new Method();
loadPart(methodElement, method);
Element argumentsElement = XMLUtil.findChildElement(methodElement, "Arguments");
if (argumentsElement != null)
{
XMLUtil.forEachChildElement(argumentsElement, "Argument", new XMLUtil.ElementHandler()
{
public void handleElement(Element argumentElement)
{
Argument argument = new Argument();
loadPart(argumentElement, argument);
if (!method.addArgument(argument))
{
throw new MetadataException("err.meta.soa.argumentDup",
new Object[] {argument.getName(), method.getName(), iface.getName(), definition.getNamePrefix()});
}
argument.setType(XMLUtil.getReqStringAttr(argumentElement, "type")); // TODO: Resolve later
argument.setCollection(XMLUtil.getBooleanAttr(argumentElement, "collection", argument.isCollection()));
}
});
}
if (!iface.addMethod(method))
{
throw new MetadataException("err.meta.soa.methodDup",
new Object[] {method.getName(), method.getArgString(), iface.getName(), definition.getNamePrefix()});
}
Element resultElement = XMLUtil.findChildElement(methodElement, "Result");
if (resultElement != null)
{
Result result = new Result();
method.setResult(result);
result.setType(XMLUtil.getReqStringAttr(resultElement, "type")); // TODO: Resolve later
result.setCollection(XMLUtil.getBooleanAttr(resultElement, "collection", result.isCollection()));
result.setDescription(XMLUtil.getStringAttr(resultElement, "description", result.getDescription()));
}
Element faultsElement = XMLUtil.findChildElement(methodElement, "Faults");
if (faultsElement != null)
{
XMLUtil.forEachChildElement(faultsElement, "Fault", new XMLUtil.ElementHandler()
{
public void handleElement(Element faultElement)
{
Fault fault = new Fault();
fault.m_sRef = XMLUtil.getReqStringAttr(faultElement, "type"); // TODO: Resolve later
method.addFault(fault);
}
});
}
}
});
}
});
}
child = XMLUtil.findChildElement(element, "Services");
if (child != null)
{
XMLUtil.forEachChildElement(child, "Service", new XMLUtil.ElementHandler()
{
public void handleElement(Element serviceElement)
{
final Service service = new Service(definition);
loadPart(serviceElement, service);
if (!definition.addService(service))
{
throw new MetadataException("err.meta.soa.serviceDup",
new Object[] {service.getName(), definition.getNamePrefix()});
}
m_globalMap.put(service.getGlobalName(), service);
Element interfacesElement = XMLUtil.findChildElement(serviceElement, "Interfaces");
final int nCounter[] = {0};
XMLUtil.forEachChildElement(interfacesElement, "Interface", new XMLUtil.ElementHandler()
{
public void handleElement(Element interfaceElement)
{
final String sInterfaceName = definition.resolveInterfaceRef(XMLUtil.getReqStringAttr(interfaceElement, "ref"));
final InterfaceRef ref = new InterfaceRef();
ref.setDefault(XMLUtil.getBooleanAttr(interfaceElement, "default", ref.isDefault()));
service.addInterfaceRef(ref);
if (ref.isDefault())
{
nCounter[0]++;
}
m_resolutionFixupList.add(new ContextFixup(m_helper)
{
public void fixup()
{
Interface iface = (Interface)m_globalMap.get(sInterfaceName);
if (iface == null)
{
throw new MetadataException("err.meta.soa.unknownInterface",
new Object[] {sInterfaceName, service.getName(), definition.getNamePrefix()});
}
ref.setInterface(iface);
}
});
}
});
if (nCounter[0] == 0)
{
throw new MetadataException("err.meta.soa.missingDefaultInterface",
new Object[] {service.getName(), definition.getNamePrefix()});
}
else if (nCounter[0] >= 2)
{
throw new MetadataException("err.meta.soa.multipleDefaultInterface",
new Object[] {service.getName(), definition.getNamePrefix()});
}
}
});
}
child = XMLUtil.findChildElement(element, "Bindings");
XMLUtil.forEachChildElement(child, "Binding", new XMLUtil.ElementHandler()
{
public void handleElement(Element bindingElement)
{
String sBindingName = XMLUtil.getReqStringAttr(bindingElement, "protocol");
if (!definition.addBinding(sBindingName))
{
throw new MetadataException("err.meta.soa.bindingDup",
new Object[] {sBindingName, definition.getNamePrefix()});
}
}
});
EmbeddedResourceURLStreamHandler handler = new EmbeddedResourceURLStreamHandler();
String sPrefix = definition.getNamePrefix();
loadXMLResources(element, "Channels", "Channel", sPrefix + ":channel:", ".channel", handler);
loadXMLResources(element, "IntegrationInterfaces", "Interface", sPrefix + ":integration:interface:", ".interface", handler);
loadTextResources(element, "Libraries", "Library", ".scm", handler);
loadXMLResources(element, "Messages", "Message", sPrefix + ":message:", ".message", handler);
loadXMLResources(element, "IntegrationServices", sPrefix + ":integration:service:", "Service", ".service", handler);
loadXMLResources(element, "Transformations", "Transformation", sPrefix + ":transformation:", ".transformation", handler);
}
/**
* Loads XML resources that are embedded in the SOA Definition. The resources are added to the listing
* maintained by the XML helper.
* @param element The parent element of the resource container.
* @param sContainerElementName The name of the element that contains the resources to load.
* @param sElementName The name of the element at the resource root.
* @param sNSPrefix The namespace prefix for the resource.
* @param sFileExtension The file extension for these resources.
* @param handler The handler for resolving the URLs.
*/
protected void loadXMLResources(Element element, String sContainerElementName, String sElementName,
final String sNSPrefix, final String sFileExtension, final EmbeddedResourceURLStreamHandler handler)
{
Element child = XMLUtil.findChildElement(element, sContainerElementName);
if (child == null)
{
return;
}
final XMLMetadataListing listing = m_helper.getListing();
XMLUtil.forEachChildElement(child, sElementName, new XMLUtil.ElementHandler()
{
public void handleElement(Element libraryElement)
{
String sName = sNSPrefix + XMLUtil.getReqStringAttr(libraryElement, "name") + sFileExtension;
libraryElement.removeAttribute("name");
String sBody = XMLUtil.formatXML(libraryElement);
URL url = handler.getURL(sName);
handler.register(url, sBody);
listing.addResource(sName, new XMLResource(sName, url, true));
}
});
}
/**
* Loads text resources that are embedded in the SOA Definition. The resources are added to the listing
* maintained by the XML helper.
* @param element The parent element of the resource container.
* @param sContainerElementName The name of the element that contains the resources to load.
* @param sElementName The name of the element at the resource root.
* @param sFileExtension The file extension for these resources.
* @param handler The handler for resolving the URLs.
*/
protected void loadTextResources(Element element, String sContainerElementName, String sElementName,
final String sFileExtension, final EmbeddedResourceURLStreamHandler handler)
{
Element child = XMLUtil.findChildElement(element, sContainerElementName);
if (child == null)
{
return;
}
final XMLMetadataListing listing = m_helper.getListing();
XMLUtil.forEachChildElement(child, sElementName, new XMLUtil.ElementHandler()
{
public void handleElement(Element libraryElement)
{
String sName = XMLUtil.getReqStringAttr(libraryElement, "name") + sFileExtension;
String sBody = XMLUtil.getElementValue(libraryElement);
URL url = handler.getURL(sName);
handler.register(url, sBody);
listing.addResource(sName, new XMLResource(sName, url, true));
}
});
}
/**
* Resolves references in the SOA metadata. Must be called before any implementations are loaded, but after
* all definitions have been loaded.
*/
public void resolveReferences()
{
m_helper.fixup(m_resolutionFixupList.iterator());
m_resolutionFixupList.clear();
}
/**
* Loads an SOA Implementation from a DOM element and store the implementations in this loader.
* @param element The DOM element containing the implementation.
* @param sName The implementation name.
* @param env The environment for script parsing.
*/
public void loadImplementation(Element element, String sName, final GlobalEnvironment env)
{
XMLMetadataHelper.verifyRootElement(element, "SOAImplementation");
String sServiceName = XMLUtil.getReqStringAttr(element, "service");
final Service service = (Service)m_globalMap.get(sServiceName);
if (service == null)
{
throw new MetadataException("err.meta.soa.unknownService", new Object[] {sServiceName});
}
final Implementation impl = new Implementation(service);
if (m_implementationMap.put(impl.getNamePrefix(), impl) != null)
{
throw new MetadataException("err.meta.soa.implementationDup",
new Object[] {impl.getNamePrefix(), service.getName(), service.getDefinition().getNamePrefix()});
}
XMLUtil.forEachChildElement(element, "Interface", new XMLUtil.ElementHandler()
{
public void handleElement(Element interfaceElement)
{
String sInterfaceName = XMLUtil.getReqStringAttr(interfaceElement, "name");
final Interface iface = service.getInterface(sInterfaceName);
if (iface == null)
{
throw new MetadataException("err.meta.soa.implementation.undefinedInterface",
new Object[] {sInterfaceName, service.getName(), service.getDefinition().getNamePrefix()});
}
final InterfaceImplementation ifaceImpl = new InterfaceImplementation(impl, iface);
if (!impl.addInterface(ifaceImpl))
{
throw new MetadataException("err.meta.soa.implementation.interfaceDup",
new Object[] {iface.getGlobalName(), service.getGlobalName()});
}
XMLUtil.forEachChildElement(interfaceElement, "Method", new XMLUtil.ElementHandler()
{
public void handleElement(Element methodElement)
{
MethodImplementation method = new MethodImplementation();
method.setName(XMLUtil.getReqStringAttr(methodElement, "name"));
method.setArgs(XMLUtil.getStringAttr(methodElement, "args"));
if (!ifaceImpl.addMethod(method))
{
throw new MetadataException("err.meta.soa.implementation.methodDup",
new Object[] {method.getName(), method.getArgString(), iface.getName(), impl.getService().getGlobalName()});
}
if (iface.getMethod(method.getName(), method.getArgCount()) == null)
{
throw new MetadataException("err.meta.soa.implementation.unknownMethod",
new Object[] {method.getName(), method.getArgString(), iface.getName(), impl.getService().getGlobalName()});
}
Element scriptElement = XMLUtil.findChildElement(methodElement, "Script");
if (scriptElement != null)
{
method.setScript(m_helper.parse(XMLUtil.getElementValue(scriptElement), true, null, null, env));
}
}
});
}
});
}
/**
* Loads common attributes from the element into the part.
* @param element The DOM element to load the attributes.
* @param part The part to initialize.
*/
public void loadPart(Element element, SOAObject part)
{
part.setName(XMLUtil.getReqStringAttr(element, "name"));
part.setDescription(XMLUtil.getStringAttr(element, "description", part.getDescription()));
}
// code generation operations
/**
* Initializes the definitions and implementations into the global environment.
* @param machine The virtual machine.
*/
public void initDefinitions(Machine machine)
{
Compiler compiler = new Compiler();
Lookup posMap = new HashTab();
for (Iterator defItr = m_definitionMap.valueIterator(); defItr.hasNext(); )
{
Definition def = (Definition)defItr.next();
for (Iterator typeItr = def.getTypesIterator(); typeItr.hasNext(); )
{
ModelType type = (ModelType)typeItr.next();
Object code = type.getCode();
PCodeFunction fun;
posMap.clear();
posMap.put(code, new TextPosition(0, 0, "definition:" + type.getGlobalName()));
fun = compiler.compile(code, posMap, machine, false);
machine.invoke(fun, (Pair)null);
}
for (Iterator intItr = def.getInterfacesIterator(); intItr.hasNext(); )
{
Interface iface = (Interface)intItr.next();
Object code = iface.getCode();
PCodeFunction fun;
posMap.clear();
posMap.put(code, new TextPosition(0, 0, "definition:" + iface.getGlobalName()));
fun = compiler.compile(code, posMap, machine, false);
machine.invoke(fun, (Pair)null);
}
for (Iterator svcItr = def.getServicesIterator(); svcItr.hasNext(); )
{
Service service = (Service)svcItr.next();
Object code = service.getCode();
PCodeFunction fun;
posMap.clear();
posMap.put(code, new TextPosition(0, 0, "definition:" + service.getGlobalName()));
fun = compiler.compile(code, posMap, machine, false);
machine.invoke(fun, (Pair)null);
}
}
for (Iterator implItr = m_implementationMap.valueIterator(); implItr.hasNext(); )
{
Implementation impl = (Implementation)implItr.next();
Object code = impl.getCode();
PCodeFunction fun;
posMap.clear();
posMap.put(code, new TextPosition(0, 0, "implementation:" + impl.getNamePrefix()));
try
{
machine.getGlobalEnvironment().defineVariable(Symbol.SYS_CURRENT_LOGGER,
Logger.getLogger(SysUtil.NAMESPACE + ".soa." +
impl.getService().getDefinition().getNamePrefix().replace('.', '_') + '.' + impl.getService().getName()));
fun = compiler.compile(code, posMap, machine, false);
machine.invoke(fun, (Pair)null);
}
finally
{
machine.getGlobalEnvironment().removeVariable(Symbol.SYS_CURRENT_LOGGER);
}
}
m_definitionMap.clear();
m_implementationMap.clear();
}
// inner classes
/**
* URL handler for processing "soadef:/" URLs, which are used to load metadata resources embedded within
* an SOA definition.
*/
protected static class EmbeddedResourceURLStreamHandler extends URLStreamHandler
{
// constants
/**
* The URL scheme handled by this handler.
*/
protected final static String SCHEME = "soadef";
// associations
/**
* Map of URLs to content data.
*/
protected Lookup m_urlContentMap = new HashTab(); // of type (byte[])[URL]
// operations
/**
* @see java.net.URLStreamHandler#openConnection(java.net.URL)
*/
protected URLConnection openConnection(URL u) throws IOException
{
byte[] nContentArray = (byte[])m_urlContentMap.get(u);
if (nContentArray == null)
{
throw new IOException("Resource not found: " + u);
}
return new MemoryURLConnection(u, nContentArray);
}
/**
* Registers some string data as the content of the given URL.
* @param url The URL.
* @param sContent The content of the URL.
*/
public void register(URL url, String sContent)
{
try
{
m_urlContentMap.put(url, sContent.getBytes("UTF-8"));
}
catch (UnsupportedEncodingException e)
{
ObjUtil.rethrow(e);
}
}
/**
* Gets a URL to the given resource.
* @param sName The resource name.
* @return A URL to the resource.
*/
public URL getURL(String sName)
{
try
{
return new URL(null, SCHEME + ':' + sName, this);
}
catch (MalformedURLException ex)
{
throw ObjUtil.rethrow(ex);
}
}
/**
* Allows soadef URLs to be used as keys in a hash table.
* @see java.net.URLStreamHandler#equals(java.net.URL, java.net.URL)
*/
protected boolean equals(URL u1, URL u2)
{
if (!u1.getProtocol().equals(u2.getProtocol()))
{
return false;
}
return u1.getPath().equals(u2.getPath());
}
/**
* Allows soadef URLs to be used as keys in a hash table.
* @see java.net.URLStreamHandler#hashCode(java.net.URL)
*/
protected int hashCode(URL u)
{
assert u.getProtocol().equals(SCHEME);
return u.getPath().hashCode();
}
}
/**
* A URL connection to data that are held in memory.
*/
protected static class MemoryURLConnection extends URLConnection
{
/**
* The contents of this URL.
*/
protected byte[] m_nContentArray;
// constructors
/**
* Creates a new connection to data that are held in memory.
* @param url The URL.
* @param nContentArray The contents of this URL.
*/
public MemoryURLConnection(URL url, byte[] nContentArray)
{
super(url);
m_nContentArray = nContentArray;
}
// operations
/**
* @see java.net.URLConnection#connect()
*/
public void connect() throws IOException
{
if (m_nContentArray == null)
{
throw new IOException("Resource not found: " + url);
}
}
/**
* @see java.net.URLConnection#getInputStream()
*/
public InputStream getInputStream() throws IOException
{
return new ByteArrayInputStream(m_nContentArray);
}
}
/**
* The object trees loaded by this loader must be converted to Scheme code to load them into
* the dynamic object system, so they are marked as CodeProvider.
*/
protected interface CodeProvider
{
/**
* Gets the dynamic object system representation of this object.
* @return The Scheme code for this object.
*/
public Object getCode();
}
/**
* An object that is put in the global environment.
*/
protected interface GlobalObject
{
/**
* Gets the fully-qualified name of this object.
* @return The fully-qualified name of this object.
*/
public String getGlobalName();
}
/**
* An intermediate form for holding the definitions of SOA metadata during metadata loading.
*/
protected static abstract class SOAObject implements Named, CodeProvider
{
// attributes
/**
* The name, local to the context in which it is defined.
*/
protected String m_sName;
/**
* Human-readable documentation about this object.
*/
protected String m_sDescription = "";
// operations
/**
* @see nexj.core.util.Named#getName()
*/
public String getName()
{
return m_sName;
}
/**
* Sets the object name.
* @param sName The name.
*/
public void setName(String sName)
{
m_sName = sName;
}
/**
* Gets the documentation on this object.
* @return The object description.
*/
public String getDescription()
{
return m_sDescription;
}
/**
* Sets the documentation on this object.
* @param sDescription The object description.
*/
public void setDescription(String sDescription)
{
m_sDescription = (sDescription == null) ? "" : sDescription.trim();
}
/**
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.CodeProvider#getCode()
*/
public Object getCode()
{
return Symbol.define(m_sName);
}
}
/**
* An SOA definition.
*/
protected static class Definition extends SOAObject
{
// attributes
/**
* The version of this definition.
*/
protected String m_sVersion = "1.0";
// associations
/**
* A list of the information model types.
*/
protected Set m_typeSet = new HashHolder(); // of type ModelType[]
/**
* A list of the interfaces defined here.
*/
protected Set m_interfaceSet = new HashHolder(4); // of type Interface[]
/**
* A list of the services.
*/
protected Set m_serviceSet = new HashHolder(4); // of type Service[]
/**
* The bindings supported by this definition.
*/
protected Set m_bindingSet = new HashSet(2); // of type Symbol[]
// operations
/**
* Sets the version.
* @param sVersion The version string.
*/
public void setVersion(String sVersion)
{
m_sVersion = sVersion;
}
/**
* Gets the version.
* @return The version string.
*/
public String getVersion()
{
return m_sVersion;
}
/**
* Adds a binding.
* @param sBinding The binding to add.
* @return True if the binding was added; false if it is a duplicate.
*/
public boolean addBinding(String sBinding)
{
return m_bindingSet.add(Symbol.define(sBinding));
}
/**
* Gets the name prefix of this definition. Used as a prefix for the other metadata
* in the definition.
* @return The name prefix of this definition.
*/
public String getNamePrefix()
{
return m_sName + ':' + m_sVersion;
}
/**
* Adds an information model type to this definition.
* @param type The type to add.
* @return True if the type was added; false if it is a duplicate.
*/
public boolean addType(ModelType type)
{
return m_typeSet.add(type);
}
/**
* Gets an iterator over the types defined in this definition.
* @return The ModelType iterator.
*/
public Iterator getTypesIterator()
{
return m_typeSet.iterator();
}
/**
* Adds an interface to this definition.
* @param iface The interface to add.
* @return True if the interface was added; false if it is a duplicate.
*/
public boolean addInterface(Interface iface)
{
return m_interfaceSet.add(iface);
}
/**
* Gets an iterator over the interfaces defined in this definition.
* @return The Interface iterator.
*/
public Iterator getInterfacesIterator()
{
return m_interfaceSet.iterator();
}
/**
* Adds a service to this definition.
* @param service The service to add.
* @return True if the service was added; false if it is a duplicate.
*/
public boolean addService(Service service)
{
return m_serviceSet.add(service);
}
/**
* Gets an iterator over the services defined in this definition.
* @return The Service iterator.
*/
public Iterator getServicesIterator()
{
return m_serviceSet.iterator();
}
/**
* Resolves a reference to an interface.
* @param sRef The interface reference, relative to this definition.
* @return The fully-qualified name of the interface.
*/
public String resolveInterfaceRef(String sRef)
{
return (sRef.indexOf(':') < 0) ? getNamePrefix() + ":interface:" + sRef : sRef;
}
/**
* Resolves a reference to a service.
* @param sRef The service reference, relative to this definition.
* @return The fully-qualified name of the service.
*/
public String resolveTypeRef(String sRef)
{
return (sRef.indexOf(':') < 0) ? getNamePrefix() + ":type:" + sRef : sRef;
}
}
/**
* A service. Consists of one or more interfaces.
*/
protected class Service extends SOAObject implements GlobalObject
{
// associations
/**
* The definition where this service is defined.
*/
protected Definition m_definition;
/**
* A list of references to the interfaces of this service.
* The default interface must be the first interface.
*/
protected List m_interfaceList = new ArrayList(2); // of type InterfaceRef
/**
* The implementation of this service, if any.
*/
protected Implementation m_implementation;
// constructors
/**
* Creates a new service definition.
* @param definition The definition where this service is defined.
*/
public Service(Definition definition)
{
m_definition = definition;
}
// operations
/**
* Gets the definition where this service is defined.
* @return The definition.
*/
public Definition getDefinition()
{
return m_definition;
}
/**
* Adds an interface to this service.
* @param iface A reference to the interface.
*/
public void addInterfaceRef(InterfaceRef iface)
{
if (iface.isDefault())
{
m_interfaceList.add(0, iface);
}
else
{
m_interfaceList.add(iface);
}
}
/**
* Gets the number of interfaces on this service.
* @return The number of interfaces.
*/
public int getInterfaceCount()
{
return m_interfaceList.size();
}
/**
* Gets an iterator over this service's interfaces.
* @return An iterator over the InterfaceRefs.
*/
public Iterator getInterfaceIterator()
{
return m_interfaceList.iterator();
}
/**
* Gets an interface by name. If the name is a fully-qualified reference, then look it up
* in the global environment.
* This only works after references have been resolved.
* @param sRef The interface name, relative to this service.
* @return The interface.
*/
public Interface getInterface(String sRef)
{
String sQName = m_definition.resolveInterfaceRef(sRef);
Object obj = m_globalMap.get(sQName);
if (obj instanceof Interface)
{
Interface iface = (Interface)obj;
// Reference is local but the found interface is on a different definition: not found
if (sQName != sRef && iface.getDefinition() != m_definition)
{
return null;
}
return iface;
}
return null;
}
/**
* Sets the implementation of this service.
* @param implementation The implementation, if any.
*/
public void setImplementation(Implementation implementation)
{
m_implementation = implementation;
}
/**
* Generates the following code:
*
* (define-class <definition QName>:service:<name> () "Description"
* (class-attribute interfaces :init '(<default interface symbol> <interface #2 symbol> ...))
* (class-attribute implementations :init '(<interface impl #1> <interface impl #2> ...))
* )
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.SOAObject#getCode()
*/
public Object getCode()
{
Pair interfaces = null;
for (int i = m_interfaceList.size() - 1; i >= 0; i--)
{
interfaces = new Pair(
Symbol.define(((InterfaceRef)m_interfaceList.get(i)).getInterface().getGlobalName()),
interfaces
);
}
Pair implementations = null;
if (m_implementation != null)
{
for (Iterator itr = m_implementation.getInterfaceImplementationIterator(); itr.hasNext(); )
{
InterfaceImplementation ifaceImpl = (InterfaceImplementation)itr.next();
implementations = new Pair(Symbol.define(ifaceImpl.getGlobalName()), implementations);
}
}
return Pair.list(
DEFINE_CLASS,
Symbol.define(getGlobalName()),
null,
m_sDescription,
Pair.list(
CLASS_ATTRIBUTE,
INTERFACES,
INIT,
Pair.quote(interfaces)
),
Pair.list(
CLASS_ATTRIBUTE,
IMPLEMENTATIONS,
INIT,
Pair.quote(implementations)
)
);
}
/**
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.GlobalObject#getGlobalName()
*/
public String getGlobalName()
{
StringBuilder buf = new StringBuilder();
buf.append(m_definition.getNamePrefix());
buf.append(":service:");
buf.append(m_sName);
return buf.toString();
}
/**
* Ensures unique naming of a service within its SOA definition.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (!(obj instanceof Service))
{
return false;
}
return m_sName.equals(((Service)obj).m_sName);
}
/**
* Ensures unique naming of a service within its SOA definition.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_sName.hashCode();
}
}
/**
* The reference to an interface from a service.
*/
protected static class InterfaceRef
{
// associations
/**
* The referenced interface.
*/
protected Interface m_interface;
// attributes
/**
* True if this is the service's default interface; false otherwise.
*/
protected boolean m_bDefault;
// operations
/**
* Sets the interface.
* @param iface The referenced interface.
*/
public void setInterface(Interface iface)
{
m_interface = iface;
}
/**
* Gets the interface.
* @return The referenced interface.
*/
public Interface getInterface()
{
return m_interface;
}
/**
* Sets whether the referenced interface is the default interface.
* @param bDefault True if this is the service's default interface; false otherwise.
*/
public void setDefault(boolean bDefault)
{
m_bDefault = bDefault;
}
/**
* Gets whether the referenced interface is the default interface.
* @return True if this is the service's default interface; false otherwise.
*/
public boolean isDefault()
{
return m_bDefault;
}
}
/**
* An interface. Consists of one or more methods.
*/
protected static class Interface extends SOAObject implements GlobalObject
{
// associations
/**
* The definition where this interface is defined.
*/
protected Definition m_definition;
/**
* The methods defined on this interface.
*/
protected Set m_methodSet = new HashHolder();
// constructors
/**
* Creates a new interface.
* @param definition The definition where this interface is defined.
*/
public Interface(Definition definition)
{
m_definition = definition;
}
// operations
/**
* Gets the parent definition.
* @return The definition where this interface is defined.
*/
public Definition getDefinition()
{
return m_definition;
}
/**
* Adds a method to this interface.
* @param method The method to add.
* @return True if the method was added; false if a method with the same signature has already
* been added.
*/
public boolean addMethod(Method method)
{
return m_methodSet.add(method);
}
/**
* Given a method signature (name and argument count), finds the method.
* @param sName The method name.
* @param nArgCount The method argument count.
* @return The method; null if not found.
*/
public Method getMethod(String sName, int nArgCount)
{
for (Iterator itr = m_methodSet.iterator(); itr.hasNext(); )
{
Method method = (Method)itr.next();
if (!method.getName().equals(sName))
{
continue;
}
if (method.getArgCount() != nArgCount)
{
continue;
}
return method;
}
return null;
}
/**
* Generates the following code:
*
* (define-class <definition QName>:interface:<name> () "Description"
* (class-method ...)
* ...
* )
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.SOAObject#getCode()
*/
public Object getCode()
{
Pair methods = null;
for (Iterator itr = m_methodSet.iterator(); itr.hasNext(); )
{
methods = new Pair(((Method)itr.next()).getCode(), methods);
}
return new Pair(
DEFINE_CLASS,
new Pair(Symbol.define(getGlobalName()),
new Pair(null,
new Pair(getDescription(), methods)
)
)
);
}
/**
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.GlobalObject#getGlobalName()
*/
public String getGlobalName()
{
StringBuilder buf = new StringBuilder(m_definition.getNamePrefix().length() + 11 + m_sName.length());
buf.append(m_definition.getNamePrefix());
buf.append(":interface:");
buf.append(m_sName);
return buf.toString();
}
/**
* Ensures unique naming of an interface within its SOA definition.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (!(obj instanceof Interface))
{
return false;
}
return m_sName.equals(((Interface)obj).m_sName);
}
/**
* Ensures unique naming of an interface within its SOA definition.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_sName.hashCode();
}
}
/**
* A method from an interface. Declares zero or more arguments, an optional return value, and zero
* or more faults.
*/
protected static class Method extends SOAObject
{
// associations
/**
* The ordered set of arguments.
*/
protected HashHolderList m_argumentList = new HashHolderList(); // of type Argument[]
/**
* The return value type & information. Null for no return value (void).
*/
protected Result m_result;
/**
* The faults that this method can raise.
*/
protected List m_faultList = new ArrayList(); // of type Fault[]
// operations
/**
* Adds an argument to this method.
* @param argument The argument to add.
* @return True if the argument was added; false if there is already an argument with the same name.
*/
public boolean addArgument(Argument argument)
{
return m_argumentList.add(argument);
}
/**
* Gets a list of the arguments.
* @return A string representation of the method arguments.
*/
public String getArgString()
{
StringBuilder buf = new StringBuilder();
buf.append('(');
for (int i = 0; i < m_argumentList.size(); i++)
{
Argument arg = (Argument)m_argumentList.get(i);
if (i > 0)
{
buf.append(' ');
}
buf.append(arg.getName());
}
buf.append(')');
return buf.toString();
}
/**
* Gets the number of arguments for this method.
* @return The method argument count.
*/
public int getArgCount()
{
return m_argumentList.size();
}
/**
* Sets the return value type/etc. of this method.
* @param result The method result.
*/
public void setResult(Result result)
{
m_result = result;
}
/**
* Gets the return value type/etc. of this method.
* @return The method result.
*/
public Result getResult()
{
return m_result;
}
/**
* Adds a fault to this method.
* @param fault The fault to add.
*/
public void addFault(Fault fault)
{
m_faultList.add(fault);
}
/**
* Generates the following code:
*
* (class-method <name> (<args>) "Description"
* (throw (java.lang.UnsupportedOperationException'new <name>))
* )
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.SOAObject#getCode()
*/
public Object getCode()
{
Pair argList = null;
for (int i = m_argumentList.size() - 1; i >= 0; i--)
{
argList = new Pair(((Argument)m_argumentList.get(i)).getCode(), argList);
}
return Pair.list(
CLASS_METHOD,
Symbol.define(m_sName),
argList,
getDescription(),
Pair.list(
Symbol.THROW,
Pair.list(
UnsupportedOperationException.class,
Pair.quote(Symbol.NEW),
m_sName
)
)
);
}
/**
* Ensures two methods with equivalent signatures cannot be added to the same interface.
* Signatures are equivalent if the method name matches and the number of arguments is
* the same.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (!(obj instanceof Method))
{
return false;
}
Method other = (Method)obj;
if (!m_sName.equals(other.m_sName))
{
return false;
}
if (m_argumentList.size() != other.m_argumentList.size())
{
return false;
}
return true;
}
/**
* Ensures two methods with equivalent signatures cannot be added to the same interface.
* Signatures are equivalent if the method name matches and the number of arguments is
* the same.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_sName.hashCode() ^ m_argumentList.size();
}
}
/**
* An argument to a method.
*/
protected static class Argument extends SOAObject
{
// attributes
/**
* The type name.
*/
protected String m_sType;
/**
* True if the argument is a collection; false otherwise.
*/
protected boolean m_bCollection;
// operations
/**
* Sets the type of data passed via this argument.
* @param sType The argument data type.
*/
public void setType(String sType)
{
m_sType = sType;
}
/**
* Gets the type of data passed via this argument.
* @return The argument data type.
*/
public String getType()
{
return m_sType;
}
/**
* Sets whether this argument holds a collection.
* @param bCollection True if the argument is a collection; false otherwise.
*/
public void setCollection(boolean bCollection)
{
m_bCollection = bCollection;
}
/**
* Gets whether this argument holds a collection.
* @return True if the argument is a collection; false otherwise.
*/
public boolean isCollection()
{
return m_bCollection;
}
/**
* Ensures unique naming of an argument for its method.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (!(obj instanceof Argument))
{
return false;
}
return m_sName.equals(((Argument)obj).m_sName);
}
/**
* Ensures unique naming of an argument for its method.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_sName.hashCode();
}
}
/**
* The result of a method.
*/
protected static class Result
{
// attributes
/**
* The return value type.
*/
public String m_sType;
/**
* The documentation for this result.
*/
public String m_sDescription = "";
/**
* True if the result is a collection; false otherwise.
*/
public boolean m_bCollection;
// operations
/**
* Sets the return value type.
* @param sType The type name.
*/
public void setType(String sType)
{
m_sType = sType;
}
/**
* Gets the return value type.
* @return The type name.
*/
public String getType()
{
return m_sType;
}
/**
* Sets the documentation on this result.
* @param sDescription The result description.
*/
public void setDescription(String sDescription)
{
m_sDescription = (sDescription == null) ? "" : sDescription.trim();
}
/**
* Gets the documentation on this result.
* @return The result description.
*/
public String getDescription()
{
return m_sDescription;
}
/**
* Sets whether the result returns a collection.
* @param bCollection True if the result is a collection; false otherwise.
*/
public void setCollection(boolean bCollection)
{
m_bCollection = bCollection;
}
/**
* Gets whether the result returns a collection.
* @return True if the result is a collection; false otherwise.
*/
public boolean isCollection()
{
return m_bCollection;
}
}
/**
* A fault. Can be thrown by a method.
*/
protected static class Fault
{
/**
* The fault type.
*/
public String m_sRef;
}
/**
* An information model type.
*/
protected static class ModelType extends SOAObject implements GlobalObject
{
/**
* The definition where this information model type is defined.
*/
public Definition m_definition;
/**
* The attributes on this type.
*/
protected Set m_attributeSet = new HashHolder();
/**
* The list of base types.
*/
protected List m_baseList = new ArrayList(2);
// constructors
/**
* Creates a new information model type.
* @param definition The definition where this type is defined.
*/
public ModelType(Definition definition)
{
m_definition = definition;
}
// operations
/**
* Adds a base to this type.
* @param base The base type to add.
*/
public void addBase(ModelType base)
{
m_baseList.add(base);
}
/**
* Adds an attribute to this type.
* @param attribute The attribute to add.
* @return True if the attribute was added; false if an attribute of the same name already exists.
*/
public boolean addAttribute(Attribute attribute)
{
return m_attributeSet.add(attribute);
}
/**
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.GlobalObject#getGlobalName()
*/
public String getGlobalName()
{
StringBuilder buf = new StringBuilder(m_definition.getNamePrefix().length() + 6 + m_sName.length());
buf.append(m_definition.getNamePrefix());
buf.append(":type:");
buf.append(m_sName);
return buf.toString();
}
/**
* Generates the following code:
*
* (define-class <definition QName>:type:<name> (<bases>) "Description"
* (attribute ...)
* ...
* )
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.SOAObject#getCode()
*/
public Object getCode()
{
Pair attributes = null;
for (Iterator itr = m_attributeSet.iterator(); itr.hasNext(); )
{
attributes = new Pair(((Attribute)itr.next()).getCode(), attributes);
}
Pair bases = null;
for (int i = m_baseList.size() - 1; i >= 0; i--)
{
bases = new Pair(Symbol.define(((ModelType)m_baseList.get(i)).getGlobalName()), bases);
}
return new Pair(
DEFINE_CLASS,
new Pair(Symbol.define(getGlobalName()),
new Pair(bases,
new Pair(getDescription(), attributes)
)
)
);
}
/**
* Ensures unique naming of a type within its SOA definition.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (!(obj instanceof ModelType))
{
return false;
}
return m_sName.equals(((ModelType)obj).m_sName);
}
/**
* Ensures unique naming of a type within its SOA definition.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_sName.hashCode();
}
}
/**
* An attribute of an information model type.
*/
protected static class Attribute extends SOAObject
{
// attributes
/**
* The type of data stored in this attribute.
*/
protected String m_sType;
/**
* True if the attribute must be non-null at RPC boundaries.
*/
protected boolean m_bRequired;
/**
* True if the attribute holds a collection; false otherwise.
*/
protected boolean m_bCollection;
// operations
/**
* Gets the type of data stored in this attribute.
* @return The attribute data type.
*/
public String getType()
{
return m_sType;
}
/**
* Sets the type of data stored in this attribute.
* @param sType The attribute data type.
*/
public void setType(String sType)
{
m_sType = sType;
}
/**
* Gets whether the attribute must be non-null at RPC boundaries.
* @return True if this attribute must be non-null; false otherwise.
*/
public boolean isRequired()
{
return m_bRequired;
}
/**
* Sets whether this attribute must be non-null at RPC boundaries.
* @param bRequired True if this attribute must be non-null; false otherwise.
*/
public void setRequired(boolean bRequired)
{
m_bRequired = bRequired;
}
/**
* Gets whether the attribute holds a collection.
* @return True if the attribute is a collection; false otherwise.
*/
public boolean isCollection()
{
return m_bCollection;
}
/**
* Sets whether the attribute holds a collection.
* @param bCollection True if the attribute is a collection; false otherwise.
*/
public void setCollection(boolean bCollection)
{
m_bCollection = bCollection;
}
/**
* Generates the following code:
*
* (attribute <attribute name> "Description")
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.SOAObject#getCode()
*/
public Object getCode()
{
return Pair.list(ATTRIBUTE, Symbol.define(m_sName), getDescription());
}
/**
* Ensures unique naming of an attribute within its type.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (!(obj instanceof Attribute))
{
return false;
}
return m_sName.equals(((Attribute)obj).m_sName);
}
/**
* Ensures unique naming of an attribute within its type.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_sName.hashCode();
}
}
/**
* An SOA implementation. This is an implementation of a service.
*/
protected static class Implementation implements CodeProvider
{
// associations
/**
* The service that is implemented.
*/
protected Service m_service;
/**
* The interface implementations provided by this implementation.
*/
protected Set m_interfaceSet; // of type ImplementationInterface[]
// constructors
public Implementation(Service service)
{
m_service = service;
m_interfaceSet = new HashHolder(service.getInterfaceCount());
service.setImplementation(this);
}
// operations
/**
* Gets the service that is implemented.
* @return The implemented service.
*/
public Service getService()
{
return m_service;
}
/**
* Adds the implementation of an interface.
* @param iface An interface implementation.
* @return True if the implementation was added; false if there is already an implementation of the same
* interface.
*/
public boolean addInterface(InterfaceImplementation iface)
{
return m_interfaceSet.add(iface);
}
/**
* Gets an iterator over the interface implementations.
* @return An InterfaceImplementation iterator.
*/
public Iterator getInterfaceImplementationIterator()
{
return m_interfaceSet.iterator();
}
/**
* Generates the following code:
*
* (begin
* (define-class <definition QName>:implementation:<service name>:<interface name>
* ...
* )
* ...
* )
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.CodeProvider#getCode()
*/
public Object getCode()
{
Pair code = null;
for (Iterator itr = m_interfaceSet.iterator(); itr.hasNext(); )
{
InterfaceImplementation iface = (InterfaceImplementation)itr.next();
code = new Pair(iface.getCode(), code);
}
return new Pair(Symbol.BEGIN, code);
}
/**
* Gets the name prefix of this implementation. Used as a prefix for the interface
* implementations.
* @return The name prefix of this implementation.
*/
public String getNamePrefix()
{
StringBuilder buf = new StringBuilder();
buf.append(m_service.getDefinition().getNamePrefix());
buf.append(":implementation:");
buf.append(m_service.getName());
return buf.toString();
}
}
/**
* The implementation of an interface (within the context of a service implementation).
*/
protected static class InterfaceImplementation implements CodeProvider, GlobalObject
{
// associations
/**
* The service implementation.
*/
protected Implementation m_implementation;
/**
* The interface that is implemented.
*/
protected Interface m_interface;
/**
* The implemented methods.
*/
protected Set m_methodSet = new HashHolder(); // of type ImplementationMethod[]
// constructors
/**
* Creates a new implementation of an interface.
* @param implementation The implementation parent.
* @param iface The implemented interface.
*/
public InterfaceImplementation(Implementation implementation, Interface iface)
{
m_implementation = implementation;
m_interface = iface;
}
// operations
/**
* Adds the implementation of a method to this interface.
* @param method A method implementation.
* @return True if the method was added; false if there is already an implementation for the same method.
*/
public boolean addMethod(MethodImplementation method)
{
return m_methodSet.add(method);
}
/**
* Generates the following code:
*
* (define-class <implementation QName>:<interface name> (<interface>) ""
* (class-method ...)
* (class-method ...)
* ...
* )
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.CodeProvider#getCode()
*/
public Object getCode()
{
Pair methods = null;
for (Iterator itr = m_methodSet.iterator(); itr.hasNext(); )
{
MethodImplementation method = (MethodImplementation)itr.next();
methods = new Pair(method.getCode(), methods);
}
return new Pair(DEFINE_CLASS,
new Pair(Symbol.define(getGlobalName()),
new Pair(Pair.list(
Symbol.define(m_interface.getGlobalName())
),
new Pair("", methods)
)
)
);
}
/**
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.GlobalObject#getGlobalName()
*/
public String getGlobalName()
{
return m_implementation.getNamePrefix() + ':' + m_interface.getName();
}
/**
* Ensures only one implementation of an interface may be added to an SOA implementation.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (obj instanceof InterfaceImplementation)
{
return m_interface == ((InterfaceImplementation)obj).m_interface;
}
return false;
}
/**
* Ensures only one implementation of an interface may be added to an SOA implementation.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_interface.hashCode();
}
}
/**
* The implementation of a method.
*/
protected static class MethodImplementation implements CodeProvider, Named
{
// attributes
/**
* The method name.
*/
protected String m_sName;
// associations
/**
* The method argument list.
*/
protected Pair m_args;
/**
* The method script.
*/
protected Object m_script;
// operations
/**
* Sets the method name.
* @param sName The method name.
*/
public void setName(String sName)
{
m_sName = sName;
}
/**
* @see nexj.core.util.Named#getName()
*/
public String getName()
{
return m_sName;
}
/**
* Sets the method script.
* @param script The method script.
*/
public void setScript(Object script)
{
m_script = script;
}
/**
* Sets the argument list.
* @param sArgs A space-separated list of argument names.
*/
public void setArgs(String sArgs)
{
sArgs = StringUtil.trimToNull(sArgs);
m_args = (sArgs == null) ? null : Pair.attributeList(sArgs, null);
}
/**
* Gets a list of the arguments.
* @return A string representation of the method arguments.
*/
public String getArgString()
{
return m_args.toString();
}
/**
* Gets the number of arguments for this method.
* @return The method argument count.
*/
public int getArgCount()
{
return Pair.length(m_args);
}
/**
* Generates the following code:
*
* (class-method <name> (<args>) ""
* <script>
* )
*
* @see nexj.core.meta.xml.XMLSOAMetadataLoader.SOAObject#getCode()
*/
public Object getCode()
{
return new Pair(CLASS_METHOD,
new Pair(Symbol.define(m_sName),
new Pair(m_args,
new Pair("", m_script)
)
)
);
}
/**
* Ensures that two methods with the same signature can't be added to the same interface.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (!(obj instanceof MethodImplementation))
{
return false;
}
MethodImplementation other = (MethodImplementation)obj;
if (!m_sName.equals(other.m_sName))
{
return false;
}
if (Pair.length(m_args) != Pair.length(other.m_args))
{
return false;
}
return true;
}
/**
* Ensures that two methods with the same signature can't be added to the same interface.
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return m_sName.hashCode() ^ Pair.length(m_args);
}
}
}