Package org.openquark.samples.bam

Source Code of org.openquark.samples.bam.GemGraphGenerator$GeneratorBindingContext

/*
* 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.
*/


/*
* GemGraphGenerator.java
* Created: 15-Mar-2004
* By: Rick Cameron
*/

package org.openquark.samples.bam;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.openquark.cal.compiler.CodeAnalyser;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.SourceModel;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.compiler.TypeChecker.TypeCheckInfo;
import org.openquark.cal.module.Cal.Collections.CAL_List;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.services.BasicCALServices;
import org.openquark.gems.client.CodeGem;
import org.openquark.gems.client.CollectorGem;
import org.openquark.gems.client.FunctionalAgentGem;
import org.openquark.gems.client.Gem;
import org.openquark.gems.client.GemGraph;
import org.openquark.gems.client.ReflectorGem;
import org.openquark.samples.bam.model.ActionDescription;
import org.openquark.samples.bam.model.BoundGemDescription;
import org.openquark.samples.bam.model.InputBinding;
import org.openquark.samples.bam.model.MetricDescription;
import org.openquark.samples.bam.model.MonitorJobDescription;
import org.openquark.samples.bam.model.TriggerDescription;
import org.openquark.samples.bam.model.MessageSourceDescription.MessagePropertyDescription;
import org.openquark.util.DurationLogger;




/**
* The GemGraphGenerator is used to assemble and compile the gem graph based
* on a job description.
*/
class GemGraphGenerator {

    private final BasicCALServices calServices;
    private final MonitorJobDescription jobDescription;
   
    //the names of gems used to 'zip' 0, 1, ... lists together
    private final QualifiedName[] zippingGems = {
        CAL_List.Functions.repeat, //constant value - repeat
        CAL_List.Functions.map,
        CAL_List.Functions.zipWith,
        CAL_List.Functions.zipWith3,
        CAL_List.Functions.zipWith4,
        CAL_List.Functions.zipWith5,
        CAL_List.Functions.zipWith6,
        CAL_List.Functions.zipWith7
        };
   
    private GemGraph gemGraph = null;
    private CodeAnalyser codeAnalyser = null;
       
    private CollectorGem messageGem = null;
   
    /**
     * this class is used to store and retrieve the collector gems for
     * binding trigger and actions to metrics and message properties.
     */
    private class GeneratorBindingContext implements BindingContext {
        private final Map<Object, CollectorGem> collectorMap = new HashMap<Object, CollectorGem>();
        /**
         * @see org.openquark.samples.bam.BindingContext#getCollector(java.lang.Object)
         */
        public CollectorGem getCollector (Object id) {
            if (collectorMap != null) {
                CollectorGem gem = collectorMap.get (id);
               
                if (gem != null) {
                    return gem;
                } else {
                    throw new IllegalArgumentException ("There is no collector for " + id);
                }
            }
           
            throw new IllegalStateException ("The collector map has not been built");
        }
       
        /**
         * Add a new collector to the binding context
         * @param id
         * @param gem
         */
        public void addCollector(Object id, CollectorGem gem) {
            collectorMap.put(id, gem);
        }
    }
   
    private final GeneratorBindingContext bindingContext = new GeneratorBindingContext ();
   
    /**
     * constructs the GemGraphGenerator with the job description and the calServices
     */
    public GemGraphGenerator (BasicCALServices calServices, MonitorJobDescription jobDescription) {
        this.calServices = calServices;
        this.jobDescription = jobDescription;
       
        codeAnalyser =new CodeAnalyser(
            calServices.getTypeChecker(),
            calServices.getCALWorkspace().getMetaModule(MonitorApp.TARGET_MODULE).getTypeInfo(),
            true,
            false);
    }
   
   
    /**
     * get the source representing the gem
     * @return the source function definition that represents the gem
     */
    public SourceModel.FunctionDefn getCalSource() {
        GemGraph gem = getGemGraph ();
       
        return gem.getCALSource();
    }
   
    /**
     * Gets the gem representing the JobDescription
     * builds the gem graph if it has not already been built.
     *
     * @return Returns the completed gem graph
     */
    private GemGraph getGemGraph () {
        if (gemGraph == null) {
            if (!buildGemGraph()) {
                return null;
            }
        }
       
        return gemGraph;
    }

    /**
     * Builds the gem graph
     * @return true if the graph is created successfully
     */
    private boolean buildGemGraph () {
        try {
            gemGraph = new GemGraph ();
            DurationLogger logger = new DurationLogger ();
           
            addCollectorForMessage();
            addCollectorsForMessageProperties ();
            addCollectorsForMetrics();
           
            logger.report("Time to add collectors: ", true);
           
            Gem triggerResultGem = addTriggerGems ();          
            logger.report("Time to add triggers: ", true);
           
            Gem actionResultGem = addActionGems ();           
            logger.report("Time to add actions: ", true);
           
            finishGemGraph (triggerResultGem, actionResultGem);
           
            logger.report("Time to finish graph: ", true);
           
            assert graphIsValid() : gemGraph.toString();
                      
            return true;
        } catch (Exception e) {
            e.printStackTrace();
           
            return false;
        }
    }
   
    /**
     * This adds a collector for the message input
     */
    private void addCollectorForMessage () {
        messageGem = new CollectorGem ();
        messageGem.setName ("messages");
       
        // We must set the type of the message collector gem to ensure that the type of the
        // gem graph will not be ambiguous. To see why the type might be ambiguous,
        // consider the average metric (BAM.average),
        // which has a type signature of Num a => [a] -> [Double]. Using this metric
        // is not sufficient to unambiguously constrain the type of a message property.
        // This is true of most of the metrics. Once the type of the message is defined, it
        // will ensure all the other types in the gem graph are unambiguous.
        ArrayList<TypeExpr> types=new ArrayList<TypeExpr>();
        for (final MessagePropertyDescription propertyDescription : jobDescription.getMessagePropertyDescriptions()) {
            types.add(propertyDescription.getCalType(calServices));
        }
       
        messageGem.setDeclaredType(
            TypeExpr.makeListType(
                TypeExpr.makeTupleType(types),
                calServices.getCALWorkspace().getMetaModule(MonitorApp.TARGET_MODULE).getTypeInfo())
        );

        gemGraph.addGem(messageGem);
               
        assert graphIsValid() : gemGraph.toString();
    }
   
   
    /**
     * This adds collector gems for each of the message properties
     */
    private void addCollectorsForMessageProperties () {
        Collection<MessagePropertyDescription> propertyInfos = jobDescription.getMessagePropertyDescriptions();
       
        int i=0;
        for (final MessagePropertyDescription propertyInfo : propertyInfos) {
            //create the message reflector
            ReflectorGem messageReflector = new ReflectorGem (messageGem);
            gemGraph.addGem(messageReflector);
           
            // Create a code gem to extract the ith property from the message list input
            // e.g. the code gem for the 5th message property would be:
            // List.map (\msg -> msg.#5) msg
            // which takes the input list of message tuples as input, and outputs a new list
            // containing the 5th element of every message.
            Gem propertyExtractorGem = new CodeGem(codeAnalyser, CAL_List.Functions.map.getQualifiedName() + " (\\msg -> msg.#" + ++i +") msg", Collections.<String>emptySet());
            gemGraph.addGem(propertyExtractorGem);
                    
            // create the collector gem for this message property
            CollectorGem propertyGem = new CollectorGem ();
            propertyGem.setName (makeCollectorName (propertyInfo.name));  
            gemGraph.addGem(propertyGem);
           
            gemGraph.connectGems(messageReflector.getOutputPart(), propertyExtractorGem.getInputPart(0));   
            gemGraph.connectGems(propertyExtractorGem.getOutputPart(), propertyGem.getInputPart(0));   

            bindingContext.addCollector(propertyInfo, propertyGem);
           
            assert graphIsValid() : gemGraph.toString();
        }
    }
   
    /**
     * This adds collector gems for all of the required metrics
     */
    private void addCollectorsForMetrics () {
        Collection<MetricDescription> metrics = jobDescription.getMetricDescriptions();
        for (final MetricDescription md : metrics) {
            //create the message reflector
            ReflectorGem messagePropertyReflector = new ReflectorGem (bindingContext.getCollector(md.getPropertyDescription()));
            gemGraph.addGem(messagePropertyReflector);

            //create the gem to compute this metric
            FunctionalAgentGem computeMetricGem = new FunctionalAgentGem(calServices.getGemEntity(md.getGemName()));
            gemGraph.addGem(computeMetricGem);
               
            //create the collector gem for this metric
            CollectorGem metricGem = new CollectorGem ();
            metricGem.setName(makeCollectorName(md.getInternalName()));
            gemGraph.addGem(metricGem);
           
            gemGraph.connectGems( messagePropertyReflector.getOutputPart(), computeMetricGem.getInputPart(0));
            gemGraph.connectGems( computeMetricGem.getOutputPart(), metricGem.getInputPart(0));
           
            bindingContext.addCollector(md, metricGem);
           
            assert graphIsValid() : gemGraph.toString();
        }
    }

    /**
     * Converts a name to a form suitable for use as the name of a CollectorGem
     *
     * @param name
     * @return collector name
     */
    private String makeCollectorName (String name) {
        return name.toLowerCase().replace('.', '_');
    }


    /**
     * Adds all of the trigger gems to the graph and connects them to the message property/metric collectors
     */
    private Gem addTriggerGems () {
        List<TriggerDescription> boundGemList = jobDescription.getTriggerDescriptions();
       
        return addGems (boundGemList);
    }

    /**
     * Adds all of the action gems to the graph and connects them to the message property/metric collectors
     */
    private Gem addActionGems () {
        List<ActionDescription> boundGemList = jobDescription.getActionDescriptions();
       
        return addGems (boundGemList);
    }

    /**
     * Add a list of gems to the gem graph. Each gem and it's inputs are defined by a BoundGemDescription
     *
     * @param boundGemList
     * @return Returns the top-level gem that results from conjoining all the gems described by the boundGemList
     */
    private Gem addGems (List<? extends BoundGemDescription> boundGemList) {
        if (boundGemList == null || boundGemList.size() == 0) {
            throw new IllegalArgumentException ("The list of bound gems must not be empty");
        }
       
        Gem result = null;
       
        for (int gemN = 0; gemN < boundGemList.size(); ++gemN) {
            BoundGemDescription gemDescription = boundGemList.get(gemN);
                      
            FunctionalAgentGem zipGem = new FunctionalAgentGem(calServices.getGemEntity(zippingGems[gemDescription.getVariableBindingCount()]));
            FunctionalAgentGem zippingFunctionGem = new FunctionalAgentGem(calServices.getGemEntity(gemDescription.getQualifiedName()));
                 
            gemGraph.addGem(zipGem);
            gemGraph.addGem(zippingFunctionGem);

            connectInputs (zipGem, zippingFunctionGem, gemDescription, gemGraph);
           
            if (result == null) {
                result = zipGem;
            } else {
                FunctionalAgentGem zipGem2 = new FunctionalAgentGem(calServices.getGemEntity(CAL_List.Functions.zipWith));
                gemGraph.addGem (zipGem2);
               
                FunctionalAgentGem andGem = new FunctionalAgentGem(calServices.getGemEntity(CAL_Prelude.Functions.and));
                gemGraph.addGem (andGem);
                andGem.getInputPart(0).setBurnt(true);
                andGem.getInputPart(1).setBurnt(true);
               
                gemGraph.connectGems(andGem.getOutputPart(), zipGem2.getInputPart(0));
                gemGraph.connectGems(zipGem.getOutputPart(), zipGem2.getInputPart(1));
                gemGraph.connectGems(result.getOutputPart(), zipGem2.getInputPart(2));
               
                result = zipGem2;
               
        
           
            }
        }
       
       
        return result;
    }


    /**
     * Connect the inputs to a gem. The inputs are defined by InputBindings in the BoundGemDescription.
     *
     * @param zipGem
     * @param zippingFunctionGem
     * @param boundGemDescription
     * @param gemGraph
     */
    private void connectInputs (FunctionalAgentGem zipGem, FunctionalAgentGem zippingFunctionGem, BoundGemDescription boundGemDescription, GemGraph gemGraph) {
       
        //connect the zipping function
        int zipInput=0;
        gemGraph.connectGems(zippingFunctionGem.getOutputPart(),zipGem.getInputPart(zipInput++));
       
        //connect the parameters
        for (int inputN = 0; inputN < boundGemDescription.getBindingCount(); ++inputN) {
            InputBinding binding = boundGemDescription.getNthBinding(inputN);
            Gem outputGem = binding.getOutputGem(calServices, gemGraph, bindingContext);

            if (binding.isConstant()) {
                gemGraph.connectGems(outputGem.getOutputPart(), zippingFunctionGem.getInputPart(inputN));
            } else {
                zippingFunctionGem.getInputPart(inputN).setBurnt(true);
                gemGraph.connectGems(outputGem.getOutputPart(), zipGem.getInputPart(zipInput++));
            }
        }
       
        assert graphIsValid() : gemGraph.toString();
    }

    /**
     * Check that the graph is valid
     * @return true if the graph is valid
     */
    private boolean graphIsValid() {
        TypeCheckInfo typeCheckInfo = calServices.getTypeCheckInfo(MonitorApp.TARGET_MODULE);
        return (gemGraph.checkGraphValid(typeCheckInfo));
    }

    /**
     * This combines the output of the trigger gems with the action gems and connects the output
     * of the action gems to the result gem.
     *
     * @param triggerResultGem
     * @param actionResultGem
     */
    private void finishGemGraph (Gem triggerResultGem, Gem actionResultGem) {
        FunctionalAgentGem zipGem = new FunctionalAgentGem(calServices.getGemEntity(CAL_List.Functions.zipWith));
        gemGraph.addGem (zipGem);
       
        FunctionalAgentGem andGem = new FunctionalAgentGem(calServices.getGemEntity(CAL_Prelude.Functions.and));
        gemGraph.addGem (andGem);
        andGem.getInputPart(0).setBurnt(true);
        andGem.getInputPart(1).setBurnt(true);
       
        gemGraph.connectGems(andGem.getOutputPart(), zipGem.getInputPart(0));
        gemGraph.connectGems(triggerResultGem.getOutputPart(), zipGem.getInputPart(1));
        gemGraph.connectGems(actionResultGem.getOutputPart(), zipGem.getInputPart(2));
       
        Gem toIteratorGem = new FunctionalAgentGem(calServices.getGemEntity(CAL_List.Functions.toJIterator));
        gemGraph.addGem(toIteratorGem);
       
        gemGraph.connectGems(zipGem.getOutputPart(), toIteratorGem.getInputPart(0));          
        gemGraph.connectGems( toIteratorGem.getOutputPart(), gemGraph.getTargetCollector().getInputPart(0));
    }

}
TOP

Related Classes of org.openquark.samples.bam.GemGraphGenerator$GeneratorBindingContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.