/*
* 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.
*/
/*
* TemplateStringBinding.java
* Created: 23-Apr-2004
* By: Rick Cameron
*/
package org.openquark.samples.bam.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.module.Cal.Collections.CAL_List;
import org.openquark.cal.services.BasicCALServices;
import org.openquark.gems.client.FunctionalAgentGem;
import org.openquark.gems.client.Gem;
import org.openquark.gems.client.GemGraph;
import org.openquark.gems.client.ValueGem;
import org.openquark.gems.client.services.GemFactory;
import org.openquark.samples.bam.BindingContext;
import org.openquark.samples.bam.model.MessageSourceDescription.MessagePropertyDescription;
import org.openquark.util.xml.BadXMLDocumentException;
import org.openquark.util.xml.XMLPersistenceHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* A template binding represents a gem input that is bound to a template string.
*/
public class TemplateStringBinding extends InputBinding {
private final String template;
private final List<InputBinding> argumentBindings;
public TemplateStringBinding (String template, List<InputBinding> argumentBindings) {
this.template = template;
this.argumentBindings = new ArrayList<InputBinding> (argumentBindings);
}
/**
* {@inheritDoc}
*/
@Override
public Collection<MetricDescription> getRequiredMetrics() {
Set<MetricDescription> metrics=new HashSet<MetricDescription>();
for (final InputBinding binding : argumentBindings) {
metrics.addAll( binding.getRequiredMetrics());
}
return metrics;
}
/**
* @see org.openquark.samples.bam.model.InputBinding#getOutputGem(BasicCALServices, org.openquark.gems.client.GemGraph, org.openquark.samples.bam.BindingContext)
*/
@Override
public Gem getOutputGem (BasicCALServices calServices, GemGraph gemGraph, BindingContext bindingContext) {
List<InputBinding> allBindings = parseAndSubstitute (template, argumentBindings);
Gem resultGem = null;
if (allBindings.isEmpty()) {
resultGem = new GemFactory(calServices).makeValueGem("");
gemGraph.addGem(resultGem);
} else {
// build the string up from right to left
for (int bindingN = allBindings.size() - 1; bindingN >= 0; --bindingN) {
InputBinding binding = allBindings.get (bindingN);
Gem gem = binding.getOutputGem(calServices, gemGraph, bindingContext);
if (resultGem == null) {
resultGem = gem;
} else {
resultGem = appendGems(calServices, gemGraph, gem, resultGem);
}
}
}
return resultGem;
}
private Gem appendGems(BasicCALServices calServices, GemGraph gemGraph, Gem gem1, Gem gem2) {
Gem appendGem = new FunctionalAgentGem(calServices.getGemEntity(QualifiedName.makeFromCompoundName("Cal.Samples.BusinessActivityMonitor.BAM.appendStrings")));
gemGraph.addGem(appendGem);
if ( !(gem1 instanceof ValueGem)) {
appendGem.getInputPart(0).setBurnt(true);
}
if ( !(gem2 instanceof ValueGem)) {
appendGem.getInputPart(1).setBurnt(true);
}
switch (appendGem.getTargetInputs().size()) {
case 2:
gemGraph.connectGems(gem1.getOutputPart(), appendGem.getInputPart(0));
gemGraph.connectGems(gem2.getOutputPart(), appendGem.getInputPart(1));
return appendGem;
case 1:
Gem mapGem = new FunctionalAgentGem(calServices.getGemEntity(CAL_List.Functions.map));
gemGraph.addGem(mapGem);
gemGraph.connectGems(appendGem.getOutputPart(), mapGem.getInputPart(0));
if (!(gem1 instanceof ValueGem)) {
gemGraph.connectGems(gem1.getOutputPart(), mapGem.getInputPart(1));
gemGraph.connectGems(gem2.getOutputPart(), appendGem.getInputPart(1));
} else {
gemGraph.connectGems(gem2.getOutputPart(), mapGem.getInputPart(1));
gemGraph.connectGems(gem1.getOutputPart(), appendGem.getInputPart(0));
}
return mapGem;
default:
Gem zipGem = new FunctionalAgentGem(calServices.getGemEntity(CAL_List.Functions.zipWith));
gemGraph.addGem(zipGem);
gemGraph.connectGems(appendGem.getOutputPart(), zipGem.getInputPart(0));
gemGraph.connectGems(gem1.getOutputPart(), zipGem.getInputPart(1));
gemGraph.connectGems(gem2.getOutputPart(), zipGem.getInputPart(2));
return zipGem;
}
}
/**
* Method getTemplate
*
* @return Returns the template string
*/
public String getTemplate () {
return template;
}
/**
* Method getArgumentBindings
*
* @return Returns an unmodifiable copy of the list or argument bindings
*/
public List<InputBinding> getArgumentBindings () {
return Collections.unmodifiableList(argumentBindings);
}
/**
* Method parseAndSubstitute
*
* @param template
* @param argumentBindings
* @return Returns a List of InputBindings that represent the template with the given arguments substituted
*/
private static List<InputBinding> parseAndSubstitute (String template, List<InputBinding> argumentBindings) {
List<InputBinding> items = new ArrayList<InputBinding> ();
Pattern pattern = Pattern.compile ("\\{(\\d+)\\}");
Matcher matcher = pattern.matcher (template);
int index = 0;
while (matcher.find ()) {
if (matcher.start () > index) {
items.add (new ConstantBinding (template.substring(index, matcher.start ())));
}
String argIndexString = matcher.group(1);
int argIndex = Integer.parseInt(argIndexString);
if (argIndex < argumentBindings.size()) {
items.add (argumentBindings.get(argIndex));
}
index = matcher.end();
}
if (index < template.length()) {
items.add (new ConstantBinding (template.substring(index)));
}
return items;
}
/**
* @see org.openquark.samples.bam.model.InputBinding#getPresentation()
*/
@Override
public String getPresentation () {
return "<Template String>";
}
/**
* @see org.openquark.samples.bam.model.InputBinding#store(org.w3c.dom.Element)
*/
@Override
public void store (Element parentElem) {
Document document = parentElem.getOwnerDocument();
Element bindingElement = document.createElement(getXmlTag());
parentElem.appendChild(bindingElement);
bindingElement.setAttribute(MonitorSaveConstants.TemplateAttr, template);
storeArguments (bindingElement);
}
/**
* Method storeBindings
*
* @param parentElement
*/
private void storeArguments (Element parentElement) {
Document document = parentElement.getOwnerDocument();
Element bindingsElem = document.createElement(MonitorSaveConstants.ArgumentBindings);
parentElement.appendChild(bindingsElem);
for (final InputBinding binding : argumentBindings) {
binding.store (bindingsElem);
}
}
/**
* Method getXmlTag
*
* @return Returns the XML tag used to store a TemplateStringBinding
*/
public static String getXmlTag () {
return MonitorSaveConstants.TemplateStringBinding;
}
/**
* Method Load
*
* @param bindingElem
* @param messagePropertyInfos
* @return Returns a TemplateStringBinding loaded from the given XML element
*/
public static InputBinding Load (Element bindingElem, Collection<MessagePropertyDescription> messagePropertyInfos) throws BadXMLDocumentException, InvalidFileFormat {
XMLPersistenceHelper.checkTag(bindingElem, getXmlTag());
String template = bindingElem.getAttribute(MonitorSaveConstants.TemplateAttr);
List<InputBinding> argumentBindings = loadArguments (bindingElem, messagePropertyInfos);
return new TemplateStringBinding (template, argumentBindings);
}
/**
* Method loadArguments
*
* @param bindingElem
* @return Returns a List of InputBindings
*/
private static List<InputBinding> loadArguments (Element bindingElem, Collection<MessagePropertyDescription> messagePropertyInfos) throws BadXMLDocumentException, InvalidFileFormat {
Element bindingsElem = XMLPersistenceHelper.getChildElement(bindingElem, MonitorSaveConstants.ArgumentBindings);
List<InputBinding> inputBindings = new ArrayList<InputBinding> ();
List<Element> bindingElemList = XMLPersistenceHelper.getChildElements(bindingsElem);
for (final Element argumentElem : bindingElemList) {
inputBindings.add(loadArgument (argumentElem, messagePropertyInfos));
}
return inputBindings;
}
/**
* Method loadArgument
*
* @param argumentElem
* @param messagePropertyInfos
* @return Returns an InputBinding loaded from the given XML element
*/
private static InputBinding loadArgument (Element argumentElem, Collection<MessagePropertyDescription> messagePropertyInfos) throws BadXMLDocumentException, InvalidFileFormat {
String tag = argumentElem.getTagName();
// The only kind of binding we allow as an argument is a PropertyBinding
if (tag.equals(PropertyBinding.getXmlTag ())) {
return PropertyBinding.Load (argumentElem, messagePropertyInfos);
} else if (tag.equals(MetricBinding.getXmlTag())) {
return MetricBinding.Load (argumentElem, messagePropertyInfos);
} else {
throw new InvalidFileFormat ("Unexpected argument binding tag: " + tag);
}
}
@Override
public boolean isConstant() {
return false;
}
}