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.Collection;
030:
031: import org.cougaar.lib.aggagent.session.XMLEncoder;
032: import org.python.core.PyFunction;
033: import org.python.core.PyObject;
034: import org.python.util.PythonInterpreter;
035:
036: /**
037: * An implementation of XMLEncoder that derives its functionality from
038: * a script written in the JPython language. A PythXMLEncoder is configured
039: * in two stages: one is to declare a function or class, and the other is to
040: * pass the function or an instance of the class to the controlling Java
041: * context.
042: * <br><br>
043: * The necessity of using a two-stage initialization procedure for both types
044: * of PythXMLEncoder 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 XMLEncoder instance or a function designed to act as
051: * the "encode" method of an XMLEncoder.
052: * <br><br>
053: * This class implements the XMLEncoder interface, and can be instantiated by
054: * calling the constructor or a static factory method, encoderFromScript().
055: */
056: public class PythXMLEncoder implements XMLEncoder {
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: // Implementation of an XMLEncoder that uses a JPython function as a
063: // delegate for the encode(Object, PrintStream) method. Arguments are
064: // coerced into the JPython context, where the function is executed.
065: private static class Func implements XMLEncoder {
066: private PyFunction delegateFunction = null;
067:
068: public Func(PyFunction f) {
069: delegateFunction = f;
070: }
071:
072: public void encode(Object o, Collection out) {
073: delegateFunction._jcall(new Object[] { o, out });
074: }
075: }
076:
077: // Each instance carrys a delegate XMLEncoder derived from a script
078: private XMLEncoder delegate = null;
079:
080: /**
081: * Create a PythUnaryPredicate instance by using a script-generated
082: * UnaryPredicate as a delegate.
083: * @param script the JPython script that defines the embodied functionality
084: */
085: public PythXMLEncoder(String script) {
086: delegate = encoderFromScript(script);
087: }
088:
089: /**
090: * An implementation of the encode method of interface XMLEncoder. The
091: * function is actually delegated to a script-generated implementation,
092: * which is fabricated in the constructor.
093: *
094: * @param o an object to be encoded
095: * @param out a Collection to which results of this operation are added
096: */
097: public void encode(Object o, Collection out) {
098: delegate.encode(o, out);
099: }
100:
101: /**
102: * Create an XMLEncoder from a JPython script. There are two acceptable
103: * modes for the script. Either it must produce a JPython subclass of Java
104: * interface XMLEncoder, or it must produce a JPython function that behaves
105: * like the method XMLEncoder.encode (i.e., takes two arguments and treats
106: * the second one like a PrintStream). Either way, the script is required
107: * to define the magic function "instantiate()" to provide the function or
108: * encoder instance to the Java context.
109: *
110: * @param script the executable script that declares classes and variables
111: * @return an XMLEncoder instance derived from the JPython scripts
112: */
113: public static XMLEncoder encoderFromScript(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__(XMLEncoder.class);
122: if (obj instanceof XMLEncoder)
123: return (XMLEncoder) obj;
124: }
125: throw new IllegalArgumentException(
126: "JPython script did not yield a function or an XMLEncoder");
127: }
128: }
|