/*
* 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.
*/
/*
* RecordFieldSelectionGem.java
* Creation date: Nov 28, 2006.
* By: Neil Corkum
*/
package org.openquark.gems.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.openquark.cal.compiler.CompositionNode;
import org.openquark.cal.compiler.FieldName;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.RecordType;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.services.CALPersistenceHelper;
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;
/**
* An RecordFieldSelectionGem is a Gem which extracts a field from a record.
* The field that the Gem will extract is specified by the user.
* @author Neil Corkum
* @author mbyne
*/
public final class RecordFieldSelectionGem extends Gem implements CompositionNode.RecordFieldSelectionNode {
/** Field name to select from record.
*/
private String fieldToSelect = GemCutter.getResourceString("DefaultRecordSelectionField");
/**
* This value indicates whether or not the field name can be changed.
* The Field name is not fixed initially and can be set when a gem is connected
* However, once a gem is connected and the field is choose, it can no longer be modified.
*/
private boolean isFixed = false;
/**
* Constructor for RecordFieldSelectionGem.
*/
public RecordFieldSelectionGem() {
super(1); // RecordFieldSelectionGem gem always has 1 argument
}
/**
* Gets the input part of the RecordFieldSelectionGem. RecordFieldSelectionGems always have
* exactly one input part.
* @return the input part of the gem
*/
public PartInput getInputPart() {
return getInputPart(0);
}
/**
* Sets the name of the field that this gem will extract. Should only be
* set to null as a temporary measure - setting the field name to null will
* result in an inconsistent state for the gem.
* @param name new extracted field name
*/
public void setFieldName(String name) {
String oldName = fieldToSelect;
if (!oldName.equals(name)) {
fieldToSelect = name;
if (nameChangeListener != null) {
nameChangeListener.nameChanged(new NameChangeEvent(this, oldName != null ? oldName.toString() : null));
}
}
}
/**
* Sets the name of the field that this gem will select.
* @param name new extracted field name
*/
public void setFieldName(FieldName name) {
setFieldName(name.getCalSourceForm());
}
/**
* Gets the field to be selected from a record.
* @return the field this Gem will select
*/
public FieldName getFieldName() {
return FieldName.make(fieldToSelect);
}
/**
* Gets the field to be selected from a record.
* @return the field this Gem will select
*/
public String getFieldNameString() {
return fieldToSelect;
}
/**
* @return true iff a field should not be modified to facilitate connections
*/
public boolean isFieldFixed() {
return isFixed;
}
/**
* Set whether or not this field is fixed. If the field is not fixed
* it may be modified in order to make connections
* @param fixed set whether or not the field is fixed
*/
public void setFieldFixed(boolean fixed) {
isFixed = fixed;
}
/**
* Returns the name of the first field in the sourceType (record) which is
* unifiable with the requiredOutputType - or null if there is no field that satisfies the type constraints.
* @param sourceType
* @param requiredOutputType
* @param currentModuleTypeInfo
* @return the first potential field name
*/
public static FieldName pickFieldName(TypeExpr sourceType,
TypeExpr requiredOutputType,
ModuleTypeInfo currentModuleTypeInfo) {
//cannot find a field if the source is not even a record
if (!(sourceType instanceof RecordType)) {
return null;
}
for (final FieldName fieldName : ((RecordType)sourceType).getHasFieldNames()) {
RecordType requiredRecordType = TypeExpr.makePolymorphicRecordType(Collections.singletonMap(fieldName, requiredOutputType));
if (TypeExpr.canUnifyType (requiredRecordType, sourceType, currentModuleTypeInfo)) {
return fieldName;
}
}
//if the record to match is polymorphic and none of the specified fields match
//choose the first available field name
if (((RecordType)sourceType).isRecordPolymorphic()) {
//make a field name that is not restricted
Set<FieldName> lacksFields = ((RecordType)sourceType).getLacksFieldsSet();
int i=0;
do {
i++;
} while (lacksFields.contains(FieldName.makeOrdinalField(i)));
return FieldName.makeOrdinalField(i);
}
return null;
}
/**
* Returns a set of fields that are members of the sourceType (record) and that are unifiable with
* the requiredOutputType - the set maybe empty if there are no such fields.
* @param sourceType
* @param requiredOutputType
* @param currentModuleTypeInfo
* @return List of possible field names
*/
public static List<FieldName> possibleFieldNames(RecordType sourceType,
TypeExpr requiredOutputType,
ModuleTypeInfo currentModuleTypeInfo) {
List<FieldName> fieldNames = new ArrayList<FieldName>();
for (final FieldName fieldName : sourceType.getHasFieldNames()) {
RecordType requiredRecordType = TypeExpr.makePolymorphicRecordType(Collections.singletonMap(fieldName, requiredOutputType));
if (TypeExpr.canUnifyType (requiredRecordType, sourceType, currentModuleTypeInfo)) {
fieldNames.add( fieldName);
}
}
return fieldNames;
}
/**
* Gets the displayed text of the Gem.
* @return text displayed on gem
*/
public String getDisplayedText() {
if (fieldToSelect == null) {
return "";
} else {
if (isFixed) {
return fieldToSelect;
} else {
return GemCutter.getResourceString("RecordFieldUnfixedDisplay", fieldToSelect);
}
}
}
/*
* Methods supporting XMLPersistable ********************************************
*/
/**
* {@inheritDoc}
*/
@Override
public void saveXML(Node parentNode, GemContext gemContext) {
String fieldName = getFieldNameString();
if (fieldName == null) {
throw new IllegalStateException("RecordFieldSelectionGem cannot be saved while field to extract is null");
}
Document document = (parentNode instanceof Document) ? (Document)parentNode : parentNode.getOwnerDocument();
// Create the RecordFieldSelectionGem element
Element resultElement = document.createElementNS(GemPersistenceConstants.GEM_NS, GemPersistenceConstants.RECORD_FIELD_SELECTION_GEM_TAG);
resultElement.setPrefix(GemPersistenceConstants.GEM_NS_PREFIX);
parentNode.appendChild(resultElement);
// Add info for the superclass gem.
super.saveXML(resultElement, gemContext);
// Now add RecordFieldSelectionGem-specific info
// Add an element for the field name.
Element nameElement = CALPersistenceHelper.unqualifiedNameToElement(fieldName.toString(), document);
resultElement.appendChild(nameElement);
}
/**
* Create a new RecordFieldSelectionGem 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 loadInfo the argument info for this load session.
* @return RecordFieldSelectionGem
* @throws BadXMLDocumentException
*/
public static RecordFieldSelectionGem getFromXML(Element gemElement, GemContext gemContext, Argument.LoadInfo loadInfo) throws BadXMLDocumentException {
RecordFieldSelectionGem gem = new RecordFieldSelectionGem();
gem.loadXML(gemElement, gemContext, loadInfo);
return gem;
}
/**
* Load this object's state.
* @param gemElement Element the element representing the structure to deserialize.
* @param gemContext the context in which the gem is being instantiated.
* @param loadInfo the argument info for this load session.
*/
void loadXML(Element gemElement, GemContext gemContext, Argument.LoadInfo loadInfo) throws BadXMLDocumentException {
XMLPersistenceHelper.checkTag(gemElement, GemPersistenceConstants.RECORD_FIELD_SELECTION_GEM_TAG);
XMLPersistenceHelper.checkPrefix(gemElement, GemPersistenceConstants.GEM_NS_PREFIX);
List<Element> childElems = XMLPersistenceHelper.getChildElements(gemElement);
int nChildElems = childElems.size();
// Get info for the underlying gem.
Element superGemElem = (nChildElems < 1) ? null : (Element) childElems.get(0);
XMLPersistenceHelper.checkIsElement(superGemElem);
super.loadXML(superGemElem, gemContext);
// Get the field name
Element fieldNameElem = (nChildElems < 2) ? null : (Element) childElems.get(1);
XMLPersistenceHelper.checkIsElement(fieldNameElem);
String fieldName = CALPersistenceHelper.elementToUnqualifiedName(fieldNameElem);
fieldToSelect = fieldName;
isFixed = true;
}
/**
* Makes a copy of the specified gem.
* @return a copy of the current RecordFieldSelectionGem
*/
RecordFieldSelectionGem makeCopy() {
// Create a new RecordFieldSelectionGem
RecordFieldSelectionGem recordFieldSelectionGem = new RecordFieldSelectionGem();
String fieldName = getFieldNameString();
if (fieldName == null) {
throw new IllegalStateException("Record Field Selection Gem cannot be copied while field to extract is null");
}
String name = getFieldName().toString();
recordFieldSelectionGem.setFieldName(name);
return recordFieldSelectionGem;
}
}