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.List;
030:
031: import org.cougaar.lib.aggagent.query.CompoundKey;
032: import org.cougaar.lib.aggagent.query.DataAtomMelder;
033: import org.python.core.PyFunction;
034: import org.python.core.PyObject;
035: import org.python.util.PythonInterpreter;
036:
037: /**
038: * An implementation of DataAtomMelder that derives its functionality from a
039: * script written in the JPython language. A PythMelder is configured in two
040: * stages: one is to declare a function or class, and the other is to pass
041: * the function or an instance of the class to the controlling Java context.
042: * <br><br>
043: * The necessity of using a two-stage initialization procedure for both types
044: * of PythMelder implementations is due to JPython's resolute refusal to
045: * allow an expression to declare a new function or class (or, indeed, any
046: * multiline construct). One possibility is to use a "magic" function through
047: * which the Java and JPython contexts can communicate. For the sake of
048: * uniformity, this option is used here. The magic function is "instantiate",
049: * which the script should define in the global context as a no-arg function
050: * that returns either an DataAtomMelder instance or a function designed to
051: * act as the "aggregate" method of an DataAtomMelder.
052: * <br><br>
053: * This class implements the DataAtomMelder interface, and can be instantiated
054: * by calling the constructor or a static factory method, melderFromScript().
055: */
056: public class PythMelder implements DataAtomMelder {
057: // This is the JPython instruction evaluated to retrieve the product of the
058: // script. The script is responsible for providing the correct behavior to
059: // the named function.
060: private static String MAGIC_FUNCTION = "instantiate()";
061:
062: // DataAtomMelder implementation that uses a JPython script as its aggregate
063: // method.
064: private static class Func implements DataAtomMelder {
065: private PyFunction delegateFunction = null;
066:
067: public Func(PyFunction f) {
068: delegateFunction = f;
069: }
070:
071: public void meld(List idNames, CompoundKey id, List atoms,
072: List output) {
073: delegateFunction._jcall(new Object[] { idNames, id, atoms,
074: output });
075: }
076: }
077:
078: // Each instance carrys a delegate DataAtomMelder derived from a script
079: private DataAtomMelder delegate = null;
080:
081: /**
082: * Create a PythMelder instance by using a script-generated DataAtomMelder
083: * as a delegate.
084: * @param script the JPython script that defines the embodied functionality
085: */
086: public PythMelder(String script) {
087: delegate = melderFromScript(script);
088: }
089:
090: /**
091: * An implementation of the aggregate method of interface DataAtomMelder. The
092: * function is actually delegated to a script-generated implementation,
093: * which is fabricated in the constructor.
094: *
095: * @param idNames a list of names for the identifier fields
096: */
097: public void meld(List idNames, CompoundKey id, List atoms,
098: List output) {
099: delegate.meld(idNames, id, atoms, output);
100: }
101:
102: /**
103: * Create an DataAtomMelder from a JPython script. There are two acceptable
104: * modes for the script. It must produce either a JPython subclass of Java
105: * interface DataAtomMelder or a JPython function that behaves like the method
106: * DataAtomMelder.aggregate(). Either way, the script is required to define the
107: * magic function "instantiate()" to provide the function or DataAtomMelder
108: * instance to the Java context.
109: *
110: * @param script the executable script that declares classes and variables
111: * @return an DataAtomMelder instance derived from the JPython scripts
112: */
113: public static DataAtomMelder melderFromScript(String script) {
114: PythonInterpreter pi = new NoErrorPython();
115: if (script != null)
116: pi.exec(script);
117: PyObject product = pi.eval(MAGIC_FUNCTION);
118: if (product instanceof PyFunction) {
119: return new Func((PyFunction) product);
120: } else {
121: Object obj = product.__tojava__(DataAtomMelder.class);
122: if (obj instanceof DataAtomMelder)
123: return (DataAtomMelder) obj;
124: }
125: throw new IllegalArgumentException(
126: "JPython script did not yield a function or an DataAtomMelder");
127: }
128: }
|