/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* FunctionalAgentGem.java
* Creation date: (10/18/00 1:03:35 PM)
* By: Luke Evans
*/
package org.openquark.gems.client;
import java.util.List;
import org.openquark.cal.compiler.CALDocComment;
import org.openquark.cal.compiler.CompositionNode;
import org.openquark.cal.compiler.FieldName;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.metadata.ArgumentMetadata;
import org.openquark.cal.metadata.FunctionalAgentMetadata;
import org.openquark.cal.services.CALPersistenceHelper;
import org.openquark.cal.services.CALWorkspace;
import org.openquark.cal.services.GemEntity;
import org.openquark.util.xml.BadXMLDocumentException;
import org.openquark.util.xml.XMLPersistenceHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* A FunctionalAgentGem is a named gem which can represent a supercombinator, data constructor, or class method.
* Creation date: (10/18/00 1:03:35 PM)
* @author Luke Evans
*/
public class FunctionalAgentGem extends Gem implements CompositionNode.GemEntityNode, NamedGem {
/** The gemEntity that this Gem represents */
private GemEntity gemEntity;
/**
* Construct a FunctionalAgentGem from a Supercombinator and its type.
* @param gemEntity the GemEntity from which to construct the gem.
*/
public FunctionalAgentGem(GemEntity gemEntity) {
super(gemEntity.getTypeArity());
this.gemEntity = gemEntity;
// Some of the supplied argument names might be null, but that's OK because
// the input metadata will automatically use a default name if it's given a null value.
// NOTE: We want any gems created with names from code or from metadata to consider those names
// as NOT being user saved names. This works fine here as a new input name object will be
// created with an initial name of whatever we supply.
FunctionalAgentMetadata metadata = gemEntity.getMetadata(GemCutter.getLocaleFromPreferences());
ArgumentMetadata[] argMetadata = metadata.getArguments();
int numNamedArgs = gemEntity.getNNamedArguments();
CALDocComment caldoc = gemEntity.getFunctionalAgent().getCALDocComment();
for (int i = 0, numInputs = getNInputs(); i < numInputs; i++) {
PartInput inputPart = getInputPart(i);
String nameFromEntity = i < numNamedArgs ? gemEntity.getNamedArgument(i) : null;
String displayName = i < argMetadata.length ? argMetadata[i].getDisplayName() : null;
FieldName caldocName = (caldoc != null && i < caldoc.getNArgBlocks()) ? caldoc.getNthArgBlock(i).getArgName() : null;
String originalName;
if (displayName != null) {
originalName = displayName;
} else if (caldocName != null && caldocName instanceof FieldName.Textual) {
// if the CALDoc name is present, we will use it instead of the name from the entity
// since the CALDoc name is specified by the user, where the name from the entity
// may have come from an automatically extracted parameter name for a foreign function
originalName = caldocName.getCalSourceForm();
} else if (nameFromEntity != null) {
originalName = nameFromEntity;
} else {
originalName = null;
}
inputPart.setOriginalInputName(originalName);
inputPart.setArgumentName(new ArgumentName(originalName));
}
setPartTypes(gemEntity.getTypeExpr().getTypePieces());
}
/**
* Construct an empty gem.
* A gem created using this constructor is appropriate for use in deserialization.
*/
private FunctionalAgentGem() {
// Initialize with no input parts.
super(0);
}
/**
* Get the qualified name of the Gem (the name of the underlying supercombinator)
* @return QualifiedName the name
*/
public QualifiedName getName() {
return gemEntity.getName();
}
/**
* Get the unqualified name of the Gem (the name of the underlying supercombinator)
* @return The node's unqualified name.
*/
public String getUnqualifiedName(){
return gemEntity.getName().getUnqualifiedName();
}
/**
* Return the entity represented by this gem
* @return GemEntity the entity represented by this gem
*/
public final GemEntity getGemEntity() {
return gemEntity;
}
/**
* Update the entity represented by this FunctionalAgentGem.
* This method is used when the program is recompiled, and entities are regenerated.
* @param workspace the workspace from which to reload the entity.
*/
void updateGemEntity(CALWorkspace workspace) throws GemEntityNotPresentException {
updateGemEntity(workspace, gemEntity.getName());
}
/**
* Update the entity represented by this FunctionalAgentGem.
* This method is used when the program is recompiled, and entities are regenerated.
* @param workspace the workspace from which to reload the entity.
* @param entityName If the gem has been renamed, this should be the new name of the gem.
*/
void updateGemEntity(CALWorkspace workspace, QualifiedName entityName) throws GemEntityNotPresentException {
// TODO: we should also check/morph for sc arity and type changes, and revalidate any connections.
GemEntity newEntity = workspace.getGemEntity(entityName);
QualifiedName oldName = gemEntity.getName();
if (newEntity == null) {
throw new GemEntityNotPresentException(gemEntity.getName());
}
this.gemEntity = newEntity;
if (oldName == entityName){
nameChangeListener.nameChanged(new NameChangeEvent(this, oldName.getQualifiedName()));
}
}
/**
* Store the local type information for this Gem's parts.
* The order is: inputs first (in order), then output (if any).
* @param tes the types
*/
private final void setPartTypes(TypeExpr[] tes) {
// The output
PartOutput outputPart = getOutputPart();
if (outputPart != null) {
int last = tes.length - 1;
getOutputPart().setType(tes[last]);
}
// The inputs (if we have any)
int numInputs = getNInputs();
for (int i = 0; i < numInputs; i++) {
getInputPart(i).setType(tes[i]);
}
}
/**
* Describe this Gem
* @return the description
*/
@Override
public String toString() {
return getName().getQualifiedName();
}
/*
* Methods supporting XMLPersistable ********************************************
*/
/**
* {@inheritDoc}
*/
@Override
public void saveXML(Node parentNode, GemContext gemContext) {
Document document = (parentNode instanceof Document) ? (Document)parentNode : parentNode.getOwnerDocument();
// Create the functional agent gem element
Element resultElement = document.createElementNS(GemPersistenceConstants.GEM_NS, GemPersistenceConstants.FUNCTIONAL_AGENT_GEM_TAG);
resultElement.setPrefix(GemPersistenceConstants.GEM_NS_PREFIX);
parentNode.appendChild(resultElement);
// Add info for the superclass gem.
super.saveXML(resultElement, gemContext);
// Now add FunctionalAgentGem-specific info
// Add an element for the name.
Element nameElement = CALPersistenceHelper.qualifiedNameToElement(getName(), document);
resultElement.appendChild(nameElement);
}
/**
* Create a new FunctionalAgentGem and loads its state from the specified XML element.
* @param gemElement Element the element representing the structure to deserialize.
* @param gemContext the context in which the gem is being instantiated.
* @param workspace the workspace from which to retrieve functional agents by name
* @return FunctionalAgentGem
* @throws BadXMLDocumentException
*/
public static FunctionalAgentGem getFromXML(Element gemElement, GemContext gemContext, CALWorkspace workspace) throws BadXMLDocumentException {
FunctionalAgentGem gem = new FunctionalAgentGem();
gem.loadXML(gemElement, gemContext, workspace);
return gem;
}
/**
* Load this object's state.
* @param gemElement the element representing the structure to deserialize.
* @param gemContext the context in which the gem is being instantiated.
* @param workspace the program from which to retrieve functional agents by name
*/
void loadXML(Element gemElement, GemContext gemContext, CALWorkspace workspace) throws BadXMLDocumentException {
XMLPersistenceHelper.checkTag(gemElement, GemPersistenceConstants.FUNCTIONAL_AGENT_GEM_TAG);
XMLPersistenceHelper.checkPrefix(gemElement, GemPersistenceConstants.GEM_NS_PREFIX);
List<Element> childElems = XMLPersistenceHelper.getChildElements(gemElement);
// Get info for the underlying gem.
Element superGemElem = (childElems.size() < 1) ? null : (Element) childElems.get(0);
XMLPersistenceHelper.checkIsElement(superGemElem);
super.loadXML(superGemElem, gemContext);
// Get the name
Element nameElem = (childElems.size() < 2) ? null : (Element) childElems.get(1);
XMLPersistenceHelper.checkIsElement(nameElem);
QualifiedName gemName = CALPersistenceHelper.elementToQualifiedName(nameElem);
// Get the supercombinator and its type
this.gemEntity = workspace.getGemEntity(gemName);
if (this.gemEntity == null) {
XMLPersistenceHelper.handleBadDocument(nameElem, "No functional agent found for " + gemName);
}
// Set the part types if any
TypeExpr scType = gemEntity.getTypeExpr();
if (scType != null) {
// Check that the entity has the right number of applications.
if (scType.getArity() != getNInputs()) {
XMLPersistenceHelper.handleBadDocument(nameElem,
"Error loading " + gemName + ". Functional agent has " +
scType.getArity() + " inputs, saved with " + getNInputs() + ".");
}
setPartTypes(scType.getTypePieces());
}
}
}