001: /*
002: * <copyright>
003: *
004: * Copyright 2003-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.lib.aggagent.script;
028:
029: import java.util.Iterator;
030: import java.util.List;
031:
032: import org.cougaar.lib.aggagent.query.Aggregator;
033: import org.python.core.PyFunction;
034: import org.python.core.PyObject;
035: import org.python.util.PythonInterpreter;
036:
037: /**
038: * An implementation of Aggregator that derives its functionality from a
039: * script written in the JPython language. A PythAggregator is configured in
040: * two stages: one is to declare a function or class, and the other is to
041: * pass the function or an instance of the class to the controlling Java
042: * context.
043: * <br><br>
044: * The necessity of using a two-stage initialization procedure for both types
045: * of PythAggregator implementations is due to JPython's resolute refusal to
046: * allow an expression to declare a new function or class (or, indeed, any
047: * multiline construct). One possibility is to use a "magic" function through
048: * which the Java and JPython contexts can communicate. For the sake of
049: * uniformity, this option is used here. The magic function is "instantiate",
050: * which the script should define in the global context as a no-arg function
051: * that returns either an Aggregator instance or a function designed to act as
052: * the "aggregate" method of an Aggregator.
053: * <br><br>
054: * This class implements the Aggregator interface, and can be instantiated by
055: * calling the constructor or a static factory method, aggregatorFromScript().
056: */
057: public class PythAggregator implements Aggregator {
058: // This is the JPython instruction evaluated to retrieve the product of the
059: // script. The script is responsible for providing the correct behavior to
060: // the named function.
061: private static String MAGIC_FUNCTION = "instantiate()";
062:
063: // Aggregator implementation that uses a JPython script as its aggregate
064: // method.
065: private static class Func implements Aggregator {
066: private PyFunction delegateFunction = null;
067:
068: public Func(PyFunction f) {
069: delegateFunction = f;
070: }
071:
072: public void aggregate(Iterator dataAtoms, List output) {
073: delegateFunction._jcall(new Object[] { dataAtoms, output });
074: }
075: }
076:
077: // Each instance carrys a delegate Aggregator derived from a script
078: private Aggregator delegate = null;
079:
080: /**
081: * Create a PythAggregator instance by using a script-generated Aggregator
082: * as a delegate.
083: * @param script the JPython script that defines the embodied functionality
084: */
085: public PythAggregator(String script) {
086: delegate = aggregatorFromScript(script);
087: }
088:
089: /**
090: * An implementation of the aggregate method of interface Aggregator. The
091: * function is actually delegated to a script-generated implementation,
092: * which is fabricated in the constructor.
093: *
094: * @param dataAtoms an iterator that iterates through raw, unaggregated
095: * data atoms
096: * @param output a List into which the produced ResultSetDataAtoms should be
097: * placed
098: */
099: public void aggregate(Iterator dataAtoms, List output) {
100: delegate.aggregate(dataAtoms, output);
101: }
102:
103: /**
104: * Create an Aggregator from a JPython script. There are two acceptable
105: * modes for the script. It must produce either a JPython subclass of Java
106: * interface Aggregator or a JPython function that behaves like the method
107: * Aggregator.aggregate(). Either way, the script is required to define the
108: * magic function "instantiate()" to provide the function or Aggregator
109: * instance to the Java context.
110: *
111: * @param script the executable script that declares classes and variables
112: * @return an Aggregator instance derived from the JPython scripts
113: */
114: public static Aggregator aggregatorFromScript(String script) {
115: PythonInterpreter pi = new NoErrorPython();
116: if (script != null)
117: pi.exec(script);
118: PyObject product = pi.eval(MAGIC_FUNCTION);
119: if (product instanceof PyFunction) {
120: return new Func((PyFunction) product);
121: } else {
122: Object obj = product.__tojava__(Aggregator.class);
123: if (obj instanceof Aggregator)
124: return (Aggregator) obj;
125: }
126: throw new IllegalArgumentException(
127: "JPython script did not yield a function or an Aggregator");
128: }
129: }
|