/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.engine.ox;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.jstatcom.engine.ConfigHolder;
import com.jstatcom.engine.Engine;
import com.jstatcom.engine.EngineTypes;
import com.jstatcom.engine.LoadTypes;
import com.jstatcom.engine.SysRun;
import com.jstatcom.model.JSCConstants;
import com.jstatcom.model.JSCData;
import com.jstatcom.model.JSCTypes;
/**
* Engine implementation to handle calls to the Ox program. Ox can be downloaded
* from http://www.nuff.ox.ac.uk/Users/Doornik/doc/ox/. It must be installed to
* run this engine. The exact location of the Ox dll must be set in
* jox/engine_config.xml. A dialog will appear to ask for it, if
* it was not set before.
*
* The engine can create objects from Ox classes and invoke methods defined by
* those classes. Modules (.ox or .oxo files) must reside in the
* jox subfolder. Static methods cannot be called directly, but
* only member functions of Ox classes.
*
* This engine takes JSCData arrays as arguments and return types and
* automatically does the conversion to and from the corresponding Ox types.
* Input and return parameters of the respective Ox function have to be known
* and the correct JSCTypes have to be specified.
*
* Usage Example:
* It is assumed, that there is a module oxmodule.ox(o) in the
* jox subfolder:
*
*
* #include <oxstd.h>
* // more includes
*
* class MyClass {
* decl lags;
* MyClass();
* setLags(const p);
* ... // more declarations
* }
* MyClass::setLags(const p){
* lags = p;
* }
* ... // more member methods
*
*
* The Java call to that module could look like this:
*
*
* Engine ox = EngineTypes.OX.getEngine();
* ox.load("oxmodule", OxLoadTypes.OXO, null);
* ox.load("MyClass", OxLoadTypes.CLASS, new JSCData[]{new JSCInt("arg", 3)}); // new MyClass(3)
* ox.call("setLags", new JSCData[]{new JSCInt("lags", 3), null) // myclass.setLags(3);
*
*
* @author Markus Kraetzig
*/
public final class OxEngine implements Engine {
private static final Logger log = Logger.getLogger(OxEngine.class);
private static OxEngine ox = null;
private boolean EXIT_STATUS = true;
private List loadedModules = new ArrayList();
/**
* Returns an instance of the OxEngine that is a Singleton.
*
* @return an engine instance
* @throws RuntimeException
* if the native support libraries cannot be found
*/
public static OxEngine getInstance() {
if (ox == null) {
ox = new OxEngine();
}
return ox;
}
/**
* Creates a OxEngine and loads the necessary system
* libraries:
*
* - the Ox dll specified in
jox/engine_config.xml
* - the jov dll in the
jox subdir
*
*
* @throws RuntimeException
* if the support library cannot be found
*/
private OxEngine() {
super();
String dllLocation = ConfigHolder.valueOf(EngineTypes.OX).getConfig(
OxConfigKeys.OXDLL);
File f = new File(dllLocation);
if (!f.isFile())
throw new RuntimeException("The system library "
+ f.getAbsolutePath() + " does not exist.");
System.load(f.getAbsolutePath());
String oxjavadll = EngineTypes.OX.DIR_NAME + File.separator
+ System.mapLibraryName("jox");
f = new File(oxjavadll);
if (!f.isFile())
throw new RuntimeException("The system library "
+ f.getAbsolutePath() + " does not exist.");
System.load(f.getAbsolutePath());
joxInitEx();
}
/**
* Makes a call to a memberfunction of an Ox object. The object has to be
* created before via the load method with the
* OxLoadTypes.CLASS argument.
*
* Input objects must not be empty or null. They are never
* changed by the Ox call. The following parameter conversion rules apply:
*
*
* JSCInt - OX_INT
* JSCNumber - OX_DOUBLE
* JSCNArray - OX_MATRIX
* JSCString - OX_STRING
* JSCSArray
*
* - if cols == 1;
OX_ARRAY (rows x 1) filled with
* OX_STRING values
* - else;
OX_ARRAY (rows x 1) with OX_ARRAY
* (cols x 1) values, each of them filled with OX_STRING
* values
*
*
*
* Return types may be empty but not null. They are
* overwritten by the return values from the Ox call. The following
* parameter conversion rules apply:
*
*
* - return is
JSCInt - takes
* OX_INT, OX_DOUBLE or 0:0 element of OX_MATRIX
* if dimension is 1:1, if OX_INT, OX_DOUBLE, OX_MATRIX is
* empty, then return type is cleared
*
* - return is
JSCNumber - takes
* OX_INT, OX_DOUBLE or 0:0 element of OX_MATRIX
* if dimension is 1:1, if OX_INT, OX_DOUBLE, OX_MATRIX is
* empty, then return type is cleared
*
* - return is
JSCNArray - takes
* OX_INT, OX_DOUBLE or OX_MATRIX, if
* OX_INT, OX_DOUBLE, OX_MATRIX is empty, then return type is
* cleared
*
* - return is
JSCString - takes OX_STRING
* or 0:0 element of OX_ARRAY if dimension is 1:1, if
* OX_STRING, OX_SARRAY is empty, then return type is
* cleared, OX_ARRAY can contain one value or another
* OX_ARRAY with just one value, higher array nesting is not
* allowed
*
* - return is
JSCSARRAY - takes OX_STRING
* or OX_ARRAY (as vector of values or other
* OX_ARRAYs) with elements that are either
* OX_STRING, OX_DOUBLE, OX_INT, if
* OX_STRING, OX_ARRAY is empty, then return type is cleared,
* only up to 2 dimensional arrays with scalar values are supported
*
*
*
* If none of the rules apply for the given return type, a
* RuntimeException is issued.
*
* @param procName
* name of the member function of the loaded class
* @param args
* arguments for function, number must match exactly with method
* signature
* @param retData
* return types from function, number must not match exactly with
* number of actually returned parameters, can be
* null
* @throws IllegalArgumentException
* if (procName == null || procName.length() == 0)
* or if args or retData contain
* null elements or unsupported types or if an
* element of args is empty but not BYREF
* @throws RuntimeException
* if the module to load could not be found or if the call to
* the Ox system library resulted in an error
*
*/
public void call(String procName, JSCData[] args, JSCData[] retData) {
if (procName == null || procName.length() == 0)
throw new IllegalArgumentException("Procedure name was empty.");
if (retData == null)
retData = new JSCData[0];
checkArgs(retData, false); // can be empty without BYREF
checkArgs(args, true);
// return values could be passed via call by reference with args
if (JSCConstants.DEBUG) {
StringBuffer msg = new StringBuffer();
msg.append("procedure: " + procName + "\n");
msg.append("arguments:\n");
if (args != null && args.length > 0)
for (int i = 0; i < args.length; i++)
msg.append("arg_" + (i + 1) + ": " + args[i] + "\n");
else
msg.append("none\n");
log.info(msg);
}
joxCallMemberEx(procName, args, retData);
}
/**
* Gets whether type can be handled by this engine.
*
* @param type
* the type to check
* @return true if type is one of
* NARRAY, INT, NUMBER, SARRAY or STRING,
* false otherwise
*/
public boolean isValid(JSCTypes type) {
if (type == JSCTypes.NARRAY || type == JSCTypes.NUMBER
|| type == JSCTypes.INT || type == JSCTypes.SARRAY
|| type == JSCTypes.STRING)
return true;
return false;
}
/**
* Loads a module according to the load type.
*
* Usage Note:
*
* OxLoadTypes.VIEWER - spawns the viewer with
* loadName as file argument, args is ignored.
* The file to view must be specified with the absolute path.
*
* OxLoadTypes.OXO - loads the ox file specified with
* loadName in the workspace. The filename must not contain
* the .ox or .oxo suffix. If JSCConstants.DEBUG == true,
* then the .ox file is searched first, otherwise the compiled object file
* .oxo. The argument args is ignored. All filenames must be
* specified relative to the resource directory for the Ox engine.
*
* OxLoadTypes.CLASS - creates an instance of the class
* with the name loadName and the constructor arguments
* args. The file containing the class must have been loaded
* before via OxLoadTypes.OXO. The created object is then
* set to be the current object and all calls via the call
* method assume, that this object is used.
*
*
* @param loadName
* the name of the module to load
* @param loadType
* the type of the load operation to invoke
* @param args
* ignored for OxLoadTypes.OXO and
* OxLoadTypes.VIEWER, but used for
* OxLoadTypes.CLASS as constructor arguments, can
* be null
* @throws IllegalArgumentException
* if (loadName == null || loadType == null) or
* if (loadName.length() == 0) or if
* args contains null or empty
* elements or unsupported types when used with
* OxLoadTypes.CLASS
* @throws RuntimeException
* if the module to load could not be found or if the call to
* the Ox system library resulted in an error
* @see com.jstatcom.engine.Engine
*/
public void load(String loadName, LoadTypes loadType, JSCData... args) {
if (loadName == null || loadType == null)
throw new IllegalArgumentException("Argument was null.");
if (loadName.length() == 0)
throw new IllegalArgumentException("Module name was empty.");
// spawns the viewer
if (loadType == OxLoadTypes.VIEWER) {
String command = ConfigHolder.valueOf(EngineTypes.OX).getConfig(
OxConfigKeys.GRAPH_VIEWER);
File f = new File(loadName);
if (!f.isFile())
throw new RuntimeException("The file " + f.getAbsolutePath()
+ " does not exist.");
log.debug("Invoking: " + command + " " + f.getAbsolutePath());
SysRun.invoke(command, f.getAbsolutePath());
return;
}
// loads .ox or .oxo file without executing it
if (loadType == OxLoadTypes.OXO) {
if (EXIT_STATUS) {
EXIT_STATUS = false;
joxInitEx();
}
// check whether module was already loaded
String checker = loadName.toUpperCase();
if (loadedModules.contains(checker))
return;
loadedModules.add(checker);
String oxSuffix = ".oxo";
String otherSuffix = ".ox";
if (JSCConstants.DEBUG == true) {
oxSuffix = ".ox";
otherSuffix = ".oxo";
}
File f = new File(EngineTypes.OX.DIR_NAME + File.separator
+ loadName + oxSuffix);
if (!f.isFile()) {
// try again with other suffix
f = new File(EngineTypes.OX.DIR_NAME + File.separator
+ loadName + otherSuffix);
if (!f.isFile()) // resign a throw ex
throw new RuntimeException("The OX resource "
+ f.getAbsolutePath() + " does not exist.");
}
log.debug("load: " + f.getAbsolutePath());
joxSetModuleEx(f.getAbsolutePath());
return;
}
// creates a new working object
if (loadType == OxLoadTypes.CLASS) {
checkArgs(args, true);
if (log.isDebugEnabled()) {
StringBuffer msg = new StringBuffer();
msg.append("class: " + loadName + "\n");
msg.append("arguments:\n");
if (args != null && args.length > 0)
for (int i = 0; i < args.length; i++)
msg.append("arg_" + (i + 1) + ": " + args[i] + "\n");
else
msg.append("none\n");
log.debug(msg);
}
joxCreateObjectEx(loadName, args);
return;
}
}
/**
* Checks arguments for conformance.
*
* @param args
* @param checkEmpty
*/
private void checkArgs(JSCData[] args, boolean checkEmpty) {
if (args == null)
return;
for (int i = 0; i < args.length; i++) {
if (args[i] == null)
throw new IllegalArgumentException(
"Argument array contains null element at position ["
+ i + "].");
if (!isValid(args[i].type()))
throw new IllegalArgumentException("Type " + args[i].type()
+ " of argument at position [" + i
+ "] is not supported by OxEngine.");
if (checkEmpty && args[i].isEmpty()) {
throw new IllegalArgumentException("Argument at position [" + i
+ "] is empty.");
}
}
}
/**
* Always throws an exception, because Ox engine tasks cannot be stopped.
*
* @throws UnsupportedOperationException
* @see com.jstatcom.engine.Engine#stop()
*/
public void stop() {
throw new UnsupportedOperationException(toString()
+ " tasks cannot be stopped.");
}
/**
* Shuts down Ox workspace.
*
* @see com.jstatcom.engine.Engine#shutdown()
*/
public void shutdown() {
joxExitEx();
EXIT_STATUS = true; // to invoke init on next load operation
loadedModules.clear();
}
/**
* Native method to call member funtion of an object.
*
* @param memberFkt
* name of the function
* @param args
* arguments for call
* @param retArgs
* return arguments
*/
private native void joxCallMemberEx(String memberFkt, JSCData[] args,
JSCData[] retArgs);
/**
* Native method to create an instance of a class in the currently loaded Ox
* module.
*
* @param className
* name of the class to create instance from
* @param args
* constructor arguments
*/
private native void joxCreateObjectEx(String className, JSCData[] args);
/**
* Loads a module from an .ox or .oxo file that contains the classes to be
* created.
*
* @param moduleName
* the name of the file to load with suffix
*/
private native void joxSetModuleEx(String moduleName);
/**
* Initializes Ox engine.
*/
private native void joxInitEx();
/**
* Shuts Ox engine down.
*/
private native void joxExitEx();
}
Ads help maintain this website.