/* Class providing additional functions in the Ptolemy II expression language. Copyright (c) 1998-2014 The Regents of the University of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY */ package ptolemy.data.expr; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.StringTokenizer; import java.util.Vector; import ptolemy.data.ActorToken; import ptolemy.data.ArrayToken; import ptolemy.data.BooleanToken; import ptolemy.data.ComplexMatrixToken; import ptolemy.data.ComplexToken; import ptolemy.data.DoubleMatrixToken; import ptolemy.data.DoubleToken; import ptolemy.data.FunctionToken; import ptolemy.data.IntMatrixToken; import ptolemy.data.IntToken; import ptolemy.data.LongMatrixToken; import ptolemy.data.LongToken; import ptolemy.data.MatrixToken; import ptolemy.data.ObjectToken; import ptolemy.data.RecordToken; import ptolemy.data.ScalarToken; import ptolemy.data.StringToken; import ptolemy.data.Token; import ptolemy.data.UnsignedByteToken; import ptolemy.data.UnsizedArrayToken; import ptolemy.data.type.ArrayType; import ptolemy.data.type.BaseType; import ptolemy.data.type.FunctionType; import ptolemy.data.type.Type; import ptolemy.data.type.TypeLattice; import ptolemy.graph.CPO; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.InternalErrorException; import ptolemy.math.ComplexMatrixMath; import ptolemy.util.MessageHandler; import ptolemy.util.StringUtilities; /////////////////////////////////////////////////////////////////// //// UtilityFunctions /** This class provides additional functions for use in the Ptolemy II expression language. All of the methods in this class are static and return an instance of Token. The expression language identifies the appropriate method to use by using reflection, matching the types of the arguments. @author Christopher Hylands Brooks, Tobin Fricke, Bart Kienhuis, Edward A. Lee, Steve Neuendorffer, Neil Smyth, Yang Zhao. Contributor: Daniel Crawl @version $Id: UtilityFunctions.java 70402 2014-10-23 00:52:20Z cxh $ @since Ptolemy II 0.2 @Pt.ProposedRating Yellow (eal) @Pt.AcceptedRating Red (cxh) */ public class UtilityFunctions { /////////////////////////////////////////////////////////////////// //// public methods //// /** Return a new UnsizedArrayToken whose element type is the same * as the given type. * @return a new UnsizedArrayToken. */ public static ArrayToken arrayType(Token t) { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. return new UnsizedArrayToken(t.getType()); } /** Return a new ArrayType whose element type is the same as the given * type. * @param type The type of the array. * @return a new ArrayType. */ public static Type arrayTypeReturnType(Type type) { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. return new ArrayType(type); } /** Return a new UnsizedArrayToken whose element type is the same * as the given type, and whose length is the given length. * @param t The element type of the array. * @param numberOfTimes The array length. * @return a new ArrayToken. */ public static ArrayToken arrayType(Token t, IntToken numberOfTimes) { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. return repeat(numberOfTimes, t); } /** Return the (not quite exact) return type of the arrayType * function above. This function always returns an ArrayType * whose element type is the first argument, with length equal to * the second argument. The result type is reported as being * unsized, which may result in an inexact type being inferred * for expressions including the arrayType function. * @param type1 The type of the first argument to the * corresponding function. * @param type2 The type of the second argument to the * corresponding function. * @return The type of the value returned from the corresponding function. */ public static Type arrayTypeReturnType(Type type1, Type type2) { // FIXME: The method comment makes no sense, the type1 // argument is not used. // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. return new ArrayType(type2); } /** Convert the argument from a fileName to a URL that begins with * "file:". * @param fileName The name of the file to be converted. * @return the URL that is equivalent to the file */ public static String asURL(String fileName) { File file = new File(fileName); try { URL url = file.toURI().toURL(); return url.toString(); } catch (java.net.MalformedURLException malformed) { throw new RuntimeException("could not convert '" + file + "' to a URL", malformed); } } /** Convert the second token to the type of the first. * @exception IllegalActionException If the token cannot be converted. */ public static Token cast(Token token1, Token token2) throws IllegalActionException { return token1.getType().convert(token2); } /** Return the type of the first argument. * @param type1 The type being cast to. * @param type2 The type being cast. * @return The type being cast to. */ public static Type castReturnType(Type type1, Type type2) throws IllegalActionException { return type1; } /** Concatenate two arrays. * The arrays must have the same type. * Example: concatenate({1,2,3},{4,5,6}) == {1,2,3,4,5,6}. * @param token1 First array from which tokens are copied to the result. * @param token2 Second array from which tokens are copied to the result. * @return An array containing all of the elements of the first argument * (in order), followed by all of the arguments of the second argument * (in order). * @exception IllegalActionException If the arrays do not have * compatible types. * @since Ptolemy II 4.1 */ public static ArrayToken concatenate(ArrayToken token1, ArrayToken token2) throws IllegalActionException { Token[] array1 = token1.arrayValue(); Token[] array2 = token2.arrayValue(); int nElements = array1.length + array2.length; Token[] resultArray = new Token[nElements]; System.arraycopy(array1, 0, resultArray, 0, array1.length); System.arraycopy(array2, 0, resultArray, array1.length, array2.length); return new ArrayToken(resultArray); } /** Concatenate an array of arrays into a single array. * Example: concatenate({{1,2,3},{4,5},{6,7}}) == {1,2,3,4,5,6,7}. * @param token Array of arrays which are to be concatenated. * @exception IllegalActionException If the argument is not an array of * arrays. * @since Ptolemy II 4.1 */ public static ArrayToken concatenate(ArrayToken token) throws IllegalActionException { if (!(token.getElementType() instanceof ArrayType)) { throw new IllegalActionException( "The argument to concatenate(ArrayToken) " + "must be an array of arrays."); } int nElements = 0; for (int i = 0; i < token.length(); i++) { nElements += ((ArrayToken) token.getElement(i)).length(); } Token[] result = new Token[nElements]; int cursor = 0; for (int i = 0; i < token.length(); i++) { Token[] array = ((ArrayToken) token.getElement(i)).arrayValue(); System.arraycopy(array, 0, result, cursor, array.length); cursor += array.length; } return new ArrayToken(result); } /** Return the return type of the two argument concatenate method, * given the types of the arguments. This specialized method is * called by the CachedMethod class and used to provide a better * return type. * @param firstArrayType The type of the predicate function. * @param secondArrayType The type of the array to be filtered. * @return The type of the result, which is an array of the least * upper bound of the array arguments. The length of the array type * is the sum of the length of the arguments. * @exception IllegalActionException If the types of the array arguments * are not Arrays. */ public static Type concatenateReturnType(Type firstArrayType, Type secondArrayType) throws IllegalActionException { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. if (!(firstArrayType instanceof ArrayType) || !(secondArrayType instanceof ArrayType)) { throw new IllegalActionException("Type " + firstArrayType + " or " + secondArrayType + " is not an ArrayType?"); } else { return new ArrayType( ((ArrayType) TypeLattice.lattice().leastUpperBound( firstArrayType, secondArrayType)).getElementType(), ((ArrayType) firstArrayType).length() + ((ArrayType) secondArrayType).length()); } } /** Return the return type of the one argument concatenate method, given the type * of the argument. * This specialized method is called by the CachedMethod class and used * to provide a better return type. * @param firstArrayType The type of the predicate function. * @return The type of the result, which is an array of the * firstArrayType arguments. The length of the array type * may be unknown. * @exception IllegalActionException If the types of the array argument * is not an Array. */ public static Type concatenateReturnType(Type firstArrayType) throws IllegalActionException { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. if (!(firstArrayType instanceof ArrayType)) { throw new IllegalActionException("Type " + firstArrayType + " is not an ArrayType?"); } else { int nElements = 0; ArrayType arrayType = (ArrayType) firstArrayType; if (arrayType.getElementType() instanceof ArrayType) { ArrayType arrayArrayType = (ArrayType) arrayType .getElementType(); if (arrayArrayType.hasKnownLength()) { nElements += ((ArrayType) arrayType.getElementType()) .length(); } } if (nElements > 0) { return new ArrayType( ((ArrayType) arrayType.getElementType()) .getElementType(), nElements); } else { return new ArrayType( ((ArrayType) arrayType.getElementType()) .getElementType()); } } } /** Return a record token that contains the names of all the * constants and their values. * @return A token containing the names of all the constants * and their values. * @since Ptolemy II 2.1 */ public static RecordToken constants() { return Constants.constants(); } /** Return an empty record. * @return An empty record. */ public static RecordToken emptyRecord() throws IllegalActionException { return new RecordToken(new String[0], new Token[0]); } /** Return an empty array with its element type matching * the specified token. * @param prototype A token specifying the element type. * @return An empty array. */ public static ArrayToken emptyArray(Token prototype) { return new ArrayToken(prototype.getType()); } /** Extract a sub-array consisting of all of the elements of an * array for which the given predicate function returns true. * If the given array has type {X}, then the given function should * have type function(x:X)(boolean). Example usage: *
even = function(x:int)(x%2==0)
* filter(even,[1:1:20].toArray)
even = function(x:int)(x%2==0)
* filter(even,[1:1:20].toArray)
The following properties are handled specially *
c:/foo
* whereas most of the other methods that operate
* on path names return C:/foo
.
* Example: intersect({a = 1, b = 2}, {a = 3, c = 4}) returns {a = 1}. * @param record1 The first record to intersect. The result gets * its values from this record. * @param record2 The second record to intersect. * @return A RecordToken containing the result. */ public static Token intersect(RecordToken record1, RecordToken record2) throws IllegalActionException { Set commonNames = new HashSet(record1.labelSet()); commonNames.retainAll(record2.labelSet()); Token[] values = new Token[commonNames.size()]; String[] names = new String[values.length]; int i = 0; for (Iterator iterator = commonNames.iterator(); iterator.hasNext(); i++) { String name = (String) iterator.next(); values[i] = record1.get(name); names[i] = name; } return new RecordToken(names, values); } /** Iterate the specified function to produce an array of the specified * length. The first element of the output array is the initial * argument, the second is the result of applying the function to * initial, the third is the result of applying the function to * that result, etc. The length argument is required to be * greater than 1. * @param function A single-argument function to iterate. * @param length The length of the resulting array. * @param initial The first element of the result. * @return A new array that is the result of applying the function * repeatedly. * @exception IllegalActionException If the specified function does not * take exactly one argument, or if an error occurs applying the function. */ public static ArrayToken iterate(FunctionToken function, int length, Token initial) throws IllegalActionException { int arity = function.getNumberOfArguments(); if (arity != 1) { throw new IllegalActionException( "iterate() can only be used on functions that take " + "one argument."); } else if (length < 2) { throw new IllegalActionException( "iterate() requires the length argument to be greater " + "than 1."); } else { Token[] result = new Token[length]; Token iterate = initial; result[0] = initial; for (int i = 1; i < length; i++) { Token[] args = new Token[1]; args[0] = iterate; iterate = function.apply(args); result[i] = iterate; } return new ArrayToken(result); } } /** Return the return type of the iterate method, given the types * of the argument. * @param functionType The type of the function to iterate. * @param lengthType The type of the length argument. * @param initialType The type of the first element of the result. * @return The type of the result. * @exception IllegalActionException If the specified function does not * take exactly one argument, or if the type signature of the function * is not compatible with the other arguments. */ public static Type iterateReturnType(Type functionType, Type lengthType, Type initialType) throws IllegalActionException { if (functionType instanceof FunctionType) { FunctionType castFunctionType = (FunctionType) functionType; if (castFunctionType.getArgCount() != 1) { throw new IllegalActionException( "iterate() can only be used on functions that take " + "one argument."); } else { Type argType = castFunctionType.getArgType(0); int comparison = TypeLattice.compare(initialType, argType); if (comparison != CPO.LOWER && comparison != CPO.SAME) { throw new IllegalActionException( "iterate(): specified initial value is not " + "compatible with function argument type."); } Type resultType = castFunctionType.getReturnType(); int comparison2 = TypeLattice.compare(resultType, argType); if (comparison2 != CPO.LOWER && comparison2 != CPO.SAME) { throw new IllegalActionException( "iterate(): invalid function: function return " + "type is not " + "compatible with function argument type."); } return new ArrayType(TypeLattice.leastUpperBound(resultType, initialType)); } } else { return BaseType.UNKNOWN; } } /** Load a library by first using the default platform dependent * System.loadLibrary() method. If the library cannot be loaded * using System.loadLibrary(), then search for the library using * {@link #findFile(String)} and if the library is found, * load it using System.load(). If the library is not found * by findFile() and the pathname contains a / * then we call System.loadLibrary() the filename after the last * /. This step is necessary to support native methods under * Webstart, which looks for libraries at the top level. * If all of the above fails, we throw the initial exception. * * @param library the name of the library to be loaded. The name * should not include the platform dependent suffix. */ public static void loadLibrary(String library) { try { if (library.indexOf("/") == -1 && library.indexOf("\\") == -1) { // loadLibrary does not work if the library has a \ or / in it. // Unfortunately, pathnames tend to get specified with // a forward slash, even under windows System.loadLibrary(library); } else { // load() does not work with relative paths. System.load(library); } } catch (UnsatisfiedLinkError ex) { String sharedLibrarySuffix = "dll"; String osName = StringUtilities.getProperty("os.name"); if (osName.startsWith("SunOS") || osName.startsWith("Linux") || osName.startsWith("Mac OS X")) { if (osName.startsWith("Mac OS X")) { // FIXME: we should also look for dylib? sharedLibrarySuffix = "jnilib"; try { _loadLibrary(library, "jnilib", ex); } catch (Exception exMac) { _loadLibrary(library, "dylib", ex); } return; } else { sharedLibrarySuffix = "so"; } } _loadLibrary(library, sharedLibrarySuffix, ex); } } /** Apply the specified function to the specified array and return * an array with the results. The function must take at least one * argument. If the function takes more than one argument, then * the specified array should be an array of arrays, where each * subarray is a set of arguments. Since arrays in the expression * language can only contain elements of the same type, this method * will only work for functions whose arguments are all of the same * type. * @param function A function with at least one argument. * @param array The array to which to apply the function. * @return A new array that is the result of applying the function * to the specified array. * @exception IllegalActionException If the specified function does not * take at least one argument, or if an error occurs applying the * function, or if the number of arguments does not match the subarray * lengths. */ public static ArrayToken map(FunctionToken function, ArrayToken array) throws IllegalActionException { int arity = function.getNumberOfArguments(); Token[] result = new Token[array.length()]; if (arity == 1) { for (int i = 0; i < array.length(); i++) { Token arg = array.getElement(i); Token[] args = new Token[1]; args[0] = arg; result[i] = function.apply(args); } } else if (arity > 1) { for (int i = 0; i < array.length(); i++) { Token args = array.getElement(i); if (!(args instanceof ArrayToken)) { throw new IllegalActionException( "Invalid arguments to map(): mismatched arity."); } Token[] invokeArgs = new Token[arity]; ArrayToken castArgs = (ArrayToken) args; if (castArgs.length() != arity) { throw new IllegalActionException( "Invalid arguments to map(): mismatched arity."); } else { for (int j = 0; j < arity; j++) { invokeArgs[j] = castArgs.getElement(j); } result[i] = function.apply(invokeArgs); } } } else { throw new IllegalActionException( "map() can only be used on functions that take at least " + "one argument."); } return new ArrayToken(result); } /** Return the return type of the map method, given the types * of the argument. * @param functionType The type of the function to map. * @param arrayTokenType The type of array to apply the * specified function on. * @return The type of the result. * @exception IllegalActionException If the specified function does not * take at least one argument, or if the type signature of the function * is not compatible with the array elements, or if the specified * function has different argument type. */ public static Type mapReturnType(Type functionType, Type arrayTokenType) throws IllegalActionException { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. if (functionType instanceof FunctionType) { FunctionType castFunctionType = (FunctionType) functionType; if (castFunctionType.getArgCount() == 1) { Type argType = castFunctionType.getArgType(0); int comparison = TypeLattice.compare( ((ArrayType) arrayTokenType).getElementType(), argType); if (comparison != CPO.LOWER && comparison != CPO.SAME) { throw new IllegalActionException( "map(): specified array token is not compatible " + "with function argument type."); } } else if (castFunctionType.getArgCount() > 1) { Type firstArgType = castFunctionType.getArgType(0); boolean flag = true; for (int i = 1; i < castFunctionType.getArgCount(); i++) { Type argType = castFunctionType.getArgType(i); if (argType != firstArgType) { i = castFunctionType.getArgCount(); flag = false; throw new IllegalActionException("map() can only work " + "for functions whose arguments are all of " + "the same type."); } } if (flag) { Type argType = castFunctionType.getArgType(0); Type elementType = ((ArrayType) arrayTokenType) .getElementType(); if (!(elementType instanceof ArrayType)) { throw new IllegalActionException( "map(): specified array token is not " + "compatible with function arity."); } else { int comparison = TypeLattice.compare( ((ArrayType) elementType).getElementType(), argType); if (comparison != CPO.LOWER && comparison != CPO.SAME) { throw new IllegalActionException( "map(): specified array token is not " + "compatible with function " + "argument type."); } } } } Type resultType = castFunctionType.getReturnType(); return new ArrayType(resultType); } else { return BaseType.UNKNOWN; } } /** Return the maximum of two unsigned bytes. * @param x An unsigned byte. * @param y An unsigned byte. * @return The maximum of x and y. */ public static UnsignedByteToken max(UnsignedByteToken x, UnsignedByteToken y) { if (x.intValue() > y.intValue()) { return x; } else { return y; } } /** Return the maximum of the contents of the array. * @param array An array of scalar tokens. * @return The largest element of the array. * @exception IllegalActionException If the array is empty or * it contains tokens that are not scalar or it contains complex tokens. */ public static ScalarToken max(ArrayToken array) throws IllegalActionException { if (array.length() == 0 || !BaseType.SCALAR.isCompatible(array.getElementType())) { throw new IllegalActionException( "max function can only be applied to arrays of scalars."); } ScalarToken result = (ScalarToken) array.getElement(0); for (int i = 1; i < array.length(); i++) { ScalarToken element = (ScalarToken) array.getElement(i); if (element.isGreaterThan(result).booleanValue()) { result = element; } } return result; } /** Return the (exact) return type of the max function above. * If the argument is an array type, then return its element type, * otherwise return BaseType.UNKNOWN. * @param type The type of the argument to the corresponding function. * @return The type of the value returned from the corresponding * function. */ public static Type maxReturnType(Type type) { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. if (type instanceof ArrayType) { ArrayType arrayType = (ArrayType) type; return arrayType.getElementType(); } else { return BaseType.UNKNOWN; } } /** Return the minimum of two unsigned bytes. * @param x An unsigned byte. * @param y An unsigned byte. * @return The minimum of x and y. */ public static UnsignedByteToken min(UnsignedByteToken x, UnsignedByteToken y) { if (x.intValue() < y.intValue()) { return x; } else { return y; } } /** Return the minimum of the contents of the array. * @param array An array of scalar tokens. * @return The largest element of the array. * @exception IllegalActionException If the array is empty or * it contains tokens that are not scalar or it contains complex tokens. */ public static ScalarToken min(ArrayToken array) throws IllegalActionException { if (array.length() == 0 || !BaseType.SCALAR.isCompatible(array.getElementType())) { throw new IllegalActionException( "min function can only be applied to arrays of scalars."); } ScalarToken result = (ScalarToken) array.getElement(0); for (int i = 1; i < array.length(); i++) { ScalarToken element = (ScalarToken) array.getElement(i); if (element.isLessThan(result).booleanValue()) { result = element; } } return result; } /** Return the (exact) return type of the min function above. * If the argument is an array type, then return its element type, * otherwise return BaseType.UNKNOWN. * @param type The type of the argument to the corresponding function. * @return The type of the value returned from the corresponding function. */ public static Type minReturnType(Type type) { // Note that this method is deliberately not listed in the Expression // chapter because it is very specialized. if (type instanceof ArrayType) { ArrayType arrayType = (ArrayType) type; return arrayType.getElementType(); } else { return BaseType.UNKNOWN; } } /** FIXME. Placeholder for a function that will return a model. */ public static ObjectToken model(String classname) throws IllegalActionException { return new ObjectToken(classname); } /** Generate a sample from a multivariate Gaussian distribution. * @param mean The mean. * @param covariance The covariance. * @return a sample from a multivariate Gaussian distribution */ public static ArrayToken multivariateGaussian(ArrayToken mean, DoubleMatrixToken covariance) throws IllegalActionException { // Cholesky factorization // Check dimensions. int N = mean.length(); // size of array double[][] S = covariance.doubleMatrix(); if ((covariance.getColumnCount() != N) || (covariance.getRowCount() != covariance.getColumnCount())) { throw new IllegalActionException( "Covariance must be a square matrix and its dimension must " + "match the mean array length"); } else if (!BaseType.DOUBLE.isCompatible(mean.getElementType())) { throw new IllegalActionException( "Mean vector must consist of scalar type elements"); } // Check if the covariance matrix is symmetric. for (int i = 0; i < N; i++) { for (int j = 0; j < i; j++) { if (S[i][j] != S[j][i]) { throw new IllegalActionException( "Covariance must be a symmetric matrix."); } } } //S should also be positive semi-definite. The function does not currently check for this. double[][] L = new double[N][N]; // the lower triangular cholesky factor s.t. L*L^T = covariance for (int i = 0; i < N; i++) { for (int j = 0; j <= i; j++) { double lowerSum = 0; for (int k = 0; k < j; k++) { lowerSum += L[i][k] * L[j][k]; } if (i != j) { L[i][j] = 1.0 / L[j][j] * (S[i][j] - lowerSum); } else { L[i][j] = Math.pow(S[i][i] - lowerSum, 0.5); } } } // Draw uncorrelated samples from a standard Gaussian. ArrayToken uncorrelated = gaussian(0, 1, N); Token[] uncorrelatedTokens = uncorrelated.arrayValue(); Token[] correlatedTokens = new Token[N]; double[] correlatedSamples = new double[N]; Token[] meanArray = mean.arrayValue(); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { double uncorr = ((DoubleToken) uncorrelatedTokens[j]) .doubleValue(); correlatedSamples[i] += L[i][j] * uncorr; } correlatedTokens[i] = new DoubleToken(correlatedSamples[i]) .add(meanArray[i]); } return new ArrayToken(BaseType.DOUBLE, correlatedTokens); } /** Parse the string provided and return the result wrapped in a token. * @param moml The MoML string. * @return The result of parsing the MoML. * @exception Exception If the MoML is invalid or the results is not an * instance of Entity. */ public static ActorToken parseMoML(String moml) throws Exception { return MoMLUtilities.parseMoML(moml); } /** Get the specified property from the environment. An empty string * is returned if the argument environment variable does not exist. * See the javadoc page for java.util.System.getProperties() for * a list of system properties. Example properties include: *
eval(readFile("filename"))
* Note that readFile() does not work inside applets, use
* {@link #readResource(String)} instead.
*
* @param filename The name of the file to read from.
* @return A StringToken containing the text contained in
* the specified file.
* @exception IllegalActionException If the file cannot be opened.
* @see ptolemy.data.expr.ASTPtFunctionApplicationNode
* @see #readResource(String)
*/
public static StringToken readFile(String filename)
throws IllegalActionException {
File file = new File(findFile(filename));
//System.out.println("Trying to open file: " + file.toString());
BufferedReader fin = null;
String line;
StringBuffer result = new StringBuffer("");
String newline = System.getProperty("line.separator");
try {
fin = new BufferedReader(new FileReader(file));
while (true) {
try {
line = fin.readLine();
} catch (IOException e) {
break;
}
if (line == null) {
break;
}
result.append(line + newline);
}
} catch (FileNotFoundException ex) {
throw new IllegalActionException(null, ex, "File not found");
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException ex) {
throw new IllegalActionException(null, ex,
"Problem closing '" + file + "'");
}
}
}
return new StringToken(result.toString());
}
/** Read a file that contains a matrix of reals in Matlab notation.
*
* @param filename The filename.
* @return The matrix defined in the file.
* @exception IllegalActionException If the file cannot be opened.
* @deprecated Use eval(readFile()) instead.
*/
@Deprecated
public static DoubleMatrixToken readMatrix(String filename)
throws IllegalActionException {
// Note that this method is deliberately not listed in the Expression
// chapter because it is deprecated.
DoubleMatrixToken returnMatrix = null;
File file = new File(findFile(filename));
if (!file.exists()) {
throw new IllegalActionException("readMatrix: File " + filename
+ " not found.");
}
// Vector containing the matrix
Vector k = null;
// Parameters for the Matrix
int row = -1;
int column = -1;
int rowPosition = 0;
int columnPosition = 0;
double[][] mtr = null;
FileReader fin = null;
try {
try {
// Open the matrix file
fin = new FileReader(file);
} catch (FileNotFoundException e) {
throw new IllegalActionException("readMatrix: file \""
+ file.getName() + "\" not found");
}
// Read the file and convert it into a matrix
if (_matrixParser == null) {
_matrixParser = new MatrixParser(System.in);
}
//MatrixParser.ReInit(fin);
MatrixParser.ReInit(fin);
k = _matrixParser.readMatrix();
if (column == -1) {
// The column size of the matrix
column = k.size();
}
Iterator i = k.iterator();
while (i.hasNext()) {
Vector l = (Vector) i.next();
if (row == -1) {
// the row size.
row = l.size();
// create a new matrix definition
mtr = new double[column][row];
} else {
if (row != l.size()) {
throw new IllegalActionException(" The Row"
+ " size needs to be the same for all"
+ " rows");
}
}
Iterator j = l.iterator();
while (j.hasNext()) {
Double s = (Double) j.next();
mtr[columnPosition][rowPosition++] = s.doubleValue();
}
rowPosition = 0;
columnPosition++;
}
// Vectors have now become obsolete, data is stored
// in double[][].
k.clear();
returnMatrix = new DoubleMatrixToken(mtr);
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException ex) {
throw new IllegalActionException(null, ex,
"Problem closing '" + file + "'");
}
}
}
return returnMatrix;
}
/** Get the string text contained in the specified resource, which
* is a file that is specified relative to the Java classpath.
* Resource strings look like filenames without a leading slash.
* If the file contains text that is a
* valid expression in the expression language, then that text can
* interpreted using the eval() function in
* ptolemy.data.expr.ASTPtFunctionApplicationNode.
* For example: eval(readFile("filename"))
*
* @param name The name of the resource to read from.
* @return A StringToken containing the text contained in
* the specified resource.
* @exception IllegalActionException If the resource cannot be opened.
* @see ptolemy.data.expr.ASTPtFunctionApplicationNode
* @see #readFile(String)
*/
public static StringToken readResource(String name)
throws IllegalActionException {
URL url = ClassLoader.getSystemResource(name);
StringBuffer result = new StringBuffer("");
BufferedReader fin = null;
try {
InputStream stream = url.openStream();
String line;
String newline = System.getProperty("line.separator");
fin = new BufferedReader(new InputStreamReader(stream));
while (true) {
try {
line = fin.readLine();
} catch (IOException e) {
break;
}
if (line == null) {
break;
}
result.append(line + newline);
}
} catch (IOException ex) {
throw new IllegalActionException(null, ex, "File not found");
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException ex) {
throw new IllegalActionException(null, ex,
"Failed to close '" + name + "'");
}
}
}
return new StringToken(result.toString());
}
/** Create an array that contains the specified element
* repeated the specified number of times.
* @param numberOfTimes The number of times to repeat the element.
* @param element The element to repeat.
* @return A new array containing the specified element repeated the
* specified number of times.
*/
public static ArrayToken repeat(IntToken numberOfTimes, Token element) {
int length = numberOfTimes.intValue();
Token[] result = new Token[length];
for (int i = 0; i < length; i++) {
result[i] = element;
}
ArrayToken arrayToken;
try {
arrayToken = new ArrayToken(element.getType(), result);
} catch (IllegalActionException ex) {
// This should not happen since the elements of the array always
// have the same type.
throw new InternalErrorException(null, ex,
"UtilityFunctions.repeat: "
+ "Cannot construct an ArrayToken.");
} catch (IllegalArgumentException ex2) {
// This should not happen since the elements of the array always
// have the same type.
throw new InternalErrorException(null, ex2,
"UtilityFunctions.repeat: "
+ "Cannot construct an ArrayToken.");
}
return arrayToken;
}
/** Return the (exact) return type of the repeat function above.
* This function always returns an ArrayType whose element type
* is the second argument.
* @param type1 The type of the first argument to the
* corresponding function.
* @param type2 The type of the second argument to the
* corresponding function.
* @return The type of the value returned from the corresponding function.
*/
public static Type repeatReturnType(Type type1, Type type2) {
// Note that this method is deliberately not listed in the Expression
// chapter because it is very specialized.
return new ArrayType(type2);
}
/** Return a new array that is the sorted contents of a specified
* array, in ascending order. The specified array can contain
* scalar tokens (except complex) or string tokens. If the
* specified array is empty, then it is returned.
* @param array An array of scalar tokens.
* @return A new sorted array.
* @exception IllegalActionException If the array contains
* tokens that are complex or are not scalar or string tokens.
*/
public static ArrayToken sort(ArrayToken array)
throws IllegalActionException {
if (array.length() == 0) {
return array;
}
// NOTE: The following method returns a copy, so we can modify it.
Token[] value = array.arrayValue();
Arrays.sort(value, _ASCENDING);
return new ArrayToken(array.getElementType(), value);
}
/** Return the (exact) return type of the sort function above.
* If the argument is an array type with elements that are strings
* or scalars other than complex, then return the argument;
* otherwise return BaseType.UNKNOWN.
* @param type The type of the argument to the corresponding function.
* @return The type of the value returned from the corresponding function.
*/
public static Type sortReturnType(Type type) {
// Note that this method is deliberately not listed in the Expression
// chapter because it is very specialized.
// NOTE: This logic indicates which tokens are comparable
// by the TokenComparator inner class. If that class is changed,
// then this logic needs to be changed too.
if (type instanceof ArrayType) {
ArrayType arrayType = (ArrayType) type;
Type elementType = arrayType.getElementType();
if (elementType.equals(BaseType.COMPLEX)) {
return BaseType.UNKNOWN;
} else if (elementType.equals(BaseType.STRING)
|| BaseType.SCALAR.isCompatible(elementType)) {
return type;
}
}
return BaseType.UNKNOWN;
}
/** Return a new array that is the sorted contents of a specified
* array, in ascending order. The specified array can contain
* scalar tokens (except complex) or string tokens. If the
* specified array is empty, then it is returned.
* This method is identical to sort(), and is provided only
* for naming uniformity.
* @see #sort(ArrayToken)
* @see #sortDescending(ArrayToken)
* @param array An array of scalar tokens.
* @return A new sorted array.
* @exception IllegalActionException If the array contains
* tokens that are complex or are not scalar or string tokens.
*/
public static ArrayToken sortAscending(ArrayToken array)
throws IllegalActionException {
return sort(array);
}
/** Return the (exact) return type of the sortAscending function above.
* If the argument is an array type with elements that are strings
* or scalars other than complex, then return the argument;
* otherwise return BaseType.UNKNOWN.
* @param type The type of the argument to the corresponding function.
* @return The type of the value returned from the corresponding function.
*/
public static Type sortAscendingReturnType(Type type) {
// Note that this method is deliberately not listed in the Expression
// chapter because it is very specialized.
return sortReturnType(type);
}
/** Return a new array that is the sorted contents of a specified
* array, in descending order. The specified array can contain
* scalar tokens (except complex) or string tokens. If the
* specified array is empty, then it is returned.
* @param array An array of scalar tokens.
* @return A new sorted array.
* @exception IllegalActionException If the array contains
* tokens that are complex or are not scalar or string tokens.
*/
public static ArrayToken sortDescending(ArrayToken array)
throws IllegalActionException {
if (array.length() == 0) {
return array;
}
// NOTE: The following method returns a copy, so we can modify it.
Token[] value = array.arrayValue();
Arrays.sort(value, _DESCENDING);
return new ArrayToken(array.getElementType(), value);
}
/** Return the (exact) return type of the sortDescending function above.
* If the argument is an array type with elements that are strings
* or scalars other than complex, then return the argument;
* otherwise return BaseType.UNKNOWN.
* @param type The type of the argument to the corresponding function.
* @return The type of the value returned from the corresponding function.
*/
public static Type sortDescendingReturnType(Type type) {
// Note that this method is deliberately not listed in the Expression
// chapter because it is very specialized.
return sortReturnType(type);
}
/** Return the contiguous subarray of the specified array
* starting at the specified index and of the specified length.
* If the specified index is out of range,
* or if the specified length extends beyond the end of the array,
* then return an empty array with the same type as the specified array.
* @param array The array.
* @param index The index of the beginning of the subarray.
* @param count The length of the subarray.
* @return The extracted subarray.
* @exception IllegalActionException If the index argument is less
* than zero.
* @since Ptolemy II 4.1
*/
public static ArrayToken subarray(ArrayToken array, IntToken index,
IntToken count) throws IllegalActionException {
return array.subarray(index.intValue(), count.intValue());
}
/** Return the return type of the subarray() method, which is the
* same as the array type.
* @param arrayType The type of the array argument.
* @param indexType The type of the index argument.
* @param countType The type of the count argument.
* @return The extracted subarray.
* @since Ptolemy II 4.1
*/
public static Type subarrayReturnType(Type arrayType, Type indexType,
Type countType) {
// Note that this method is deliberately not listed in the Expression
// chapter because it is very specialized.
return arrayType;
}
/** Return the sum of the elements in the specified array.
* This method is polymorphic in that it can sum any array
* whose elements support addition.
* There is special support for strings, which are added
* not via their add method (which concatenates two strings),
* but are accumulated in a StringBuffer, which is much more
* efficient.
* @param array An array.
* @return The sum of the elements of the array.
* @exception IllegalActionException If the length of the
* array is zero, or if the array elements do not support
* addition.
*/
public static final Token sum(ArrayToken array)
throws IllegalActionException {
if (array == null || array.length() < 1) {
throw new IllegalActionException(
"sum() function cannot be applied to an empty array");
}
if (array.getElement(0) instanceof StringToken) {
int length = 0;
for (int i = 0; i < array.length(); i++) {
length += ((StringToken) array.getElement(i)).stringValue()
.length();
}
StringBuffer buffer = new StringBuffer(length);
for (int i = 0; i < array.length(); i++) {
buffer.append(((StringToken) array.getElement(i)).stringValue());
}
return new StringToken(buffer.toString());
}
Token result = array.getElement(0);
for (int i = 1; i < array.length(); i++) {
result = result.add(array.getElement(i));
}
return result;
}
/** Return the (exact) return type of the sum function above.
* If the argument is an array type, then return its element type,
* otherwise return BaseType.UNKNOWN.
* @param type The type of the argument to the corresponding function.
* @return The type of the value returned from the corresponding function.
*/
public static Type sumReturnType(Type type) {
// Note that this method is deliberately not listed in the Expression
// chapter because it is very specialized.
if (type instanceof ArrayType) {
ArrayType arrayType = (ArrayType) type;
return arrayType.getElementType();
} else {
return BaseType.UNKNOWN;
}
}
/** Return the approximate number of bytes used by current objects
* and available for future object allocation.
* @return The total number of bytes used by the JVM.
* @see #freeMemory()
*/
public static LongToken totalMemory() {
return new LongToken(Runtime.getRuntime().totalMemory());
}
/** Evaluate the given string as an expression in the expression
* language. Instead of returning the resulting value, return a
* trace of the evaluation, including such useful information as
* what registered method is actually invoked.
* @param string The string to be parsed and evaluated.
* @return A string representing an evaluation trace.
*/
public static String traceEvaluation(String string)
throws IllegalActionException {
PtParser parser = new PtParser();
ASTPtRootNode parseTree = parser.generateParseTree(string);
ParseTreeEvaluator evaluator = new ParseTreeEvaluator();
return evaluator.traceParseTreeEvaluation(parseTree, null);
}
/** Return true if the first argument is close in value to the second,
* where "close" means that it is within the distance given by the
* third argument. Exactly what this means depends on the data type.
* This method uses the isCloseTo() method of the first token.
* @param token1 The first token.
* @param token2 The second token.
* @param distance The distance criterion.
* @return a true-valued token if the first two arguments are close
* enough.
* @exception IllegalActionException If the first two arguments cannot
* be compared.
*/
public static BooleanToken within(Token token1, Token token2,
double distance) throws IllegalActionException {
return token1.isCloseTo(token2, distance);
}
/** Query the user for a yes-no answer and return a boolean.
* This function will open a dialog if a GUI is available,
* and otherwise will use standard input and output.
*/
public static BooleanToken yesNoQuestion(StringToken prompt) {
if (MessageHandler.yesNoQuestion(prompt.stringValue())) {
return BooleanToken.TRUE;
} else {
return BooleanToken.FALSE;
}
}
/** Return a double zero matrix with the given number of rows and
* columns.
* @return The zero matrix with the given number of rows and
* columns.
* @deprecated Use zeroMatrixDouble instead.
*/
@Deprecated
public static DoubleMatrixToken zeroMatrix(int rows, int columns) {
return zeroMatrixDouble(rows, columns);
}
/** Return a complex zero matrix with the given number of rows and
* columns.
* @return The zero matrix with the given number of rows and
* columns.
*/
public static ComplexMatrixToken zeroMatrixComplex(int rows, int columns) {
try {
return new ComplexMatrixToken(ComplexMatrixMath.zero(rows, columns));
} catch (IllegalActionException ex) {
throw new InternalErrorException(null, ex,
"UtilityFunctions.zeroMatrixComplex: "
+ "Cannot create a ComplexMatrixToken.");
}
}
/** Return a double zero matrix with the given number of rows and
* columns.
* @return The zero matrix with the given number of rows and
* columns.
*/
public static DoubleMatrixToken zeroMatrixDouble(int rows, int columns) {
double[][] mtr = new double[rows][columns];
DoubleMatrixToken result = null;
try {
result = new DoubleMatrixToken(mtr, MatrixToken.DO_NOT_COPY);
} catch (IllegalActionException ex) {
throw new InternalErrorException(null, ex,
"UtilityFunctions.zeroMatrixDouble: "
+ "Cannot create a DoubleMatrixToken.");
}
return result;
}
/** Return a int zero matrix with the given number of rows and
* columns.
* @return The zero matrix with the given number of rows and
* columns.
*/
public static IntMatrixToken zeroMatrixInt(int rows, int columns) {
int[][] mtr = new int[rows][columns];
IntMatrixToken result = null;
try {
result = new IntMatrixToken(mtr, MatrixToken.DO_NOT_COPY);
} catch (IllegalActionException ex) {
throw new InternalErrorException(null, ex,
"UtilityFunctions.zeroMatrixInt: "
+ "Cannot create a IntMatrixToken.");
}
return result;
}
/** Return a long zero matrix with the given number of rows and
* columns.
* @return The zero matrix with the given number of rows and
* columns.
*/
public static LongMatrixToken zeroMatrixLong(int rows, int columns) {
long[][] mtr = new long[rows][columns];
LongMatrixToken result = null;
try {
result = new LongMatrixToken(mtr, MatrixToken.DO_NOT_COPY);
} catch (IllegalActionException ex) {
throw new InternalErrorException(null, ex,
"UtilityFunctions.zeroMatrixLong: "
+ "Cannot create a LongMatrixToken.");
}
return result;
}
///////////////////////////////////////////////////////////////////
//// private methods ////
/** Load a shared library
* @param library the name of the library to be loaded. The name
* should not include the platform dependent suffix.
* @param sharedLibrarySuffix The suffix for the shared library, without the dot.
* Under Windows, this would be ".dll", under Mac OS X, "jnilib" or "dylib",
* under Solaris or Linux, ".o".
* @param throwable The throwable that occurred when System.load() or System.loadLibrary()
* was called.
*/
private static void _loadLibrary(String library,
String sharedLibrarySuffix, Throwable throwable) {
// We have a separate method here so that we can call it with
// jnilib and then with dylib because under Web Start on Mac OS X 10.5
// with Java 10.5, shared libraries need to have a .jnilib extension. Sigh.
// The name of the library (everything after the last /)
String shortLibraryName = null;
String osName = StringUtilities.getProperty("os.name");
if (osName.startsWith("SunOS") || osName.startsWith("Linux")
|| osName.startsWith("Mac OS X")) {
// Under Solaris, libraries start with lib, so
// we find the last /, and if the next chars are not "lib"
// then we insert "lib".
int index = library.lastIndexOf("/");
if (index == -1) {
if (!library.startsWith("lib")) {
library = "lib" + library;
}
shortLibraryName = library;
} else {
if (!library.substring(index, index + 4).equals("/lib")) {
if (osName.startsWith("Linux")) {
library = library.substring(index + 1);
shortLibraryName = library;
} else {
// Under SunOS, we add lib to
// the path. If we don't do this on the Mac,
// then libptymatlab.dynlib will not be found.
shortLibraryName = "/lib"
+ library.substring(index + 1);
library = library.substring(0, index)
+ shortLibraryName;
}
}
}
} else {
// Windows
int index = library.lastIndexOf("/");
if (index != -1) {
// Everything after the trailing /
shortLibraryName = library.substring(index + 1);
}
}
String libraryWithSuffix = library + "." + sharedLibrarySuffix;
String libraryPath = UtilityFunctions.findFile(libraryWithSuffix);
boolean libraryPathExists = false;
try {
// It turns out that when looking for libraries under
// InstallAnywhere, findFile() can somehow end up returning
// a bogus value in C:/Documents and Settings
File libraryPathFile = new File(libraryPath);
if (libraryPathFile.exists()) {
libraryPathExists = true;
}
} catch (Throwable throwable2) {
// Ignore, the file can't be found
}
if (libraryPath.equals(libraryWithSuffix) || !libraryPathExists) {
try {
// findFile did not find the library, so we try using
// just the short library name. This is necessary
// for Web Start because Web Start requires that
// native code be in the top level of a jar file
// that is specially marked. Matlab under Web Start
// requires this.
if (shortLibraryName != null) {
System.loadLibrary(shortLibraryName);
}
return;
} catch (UnsatisfiedLinkError ex2) {
String shortLibraryName2 = shortLibraryName;
try {
int index = shortLibraryName2.lastIndexOf("/");
if (index != -1) {
shortLibraryName2 = shortLibraryName2
.substring(index + 1);
}
index = shortLibraryName2.lastIndexOf("lib");
if (index != -1) {
shortLibraryName2 = shortLibraryName2
.substring(index + 3);
}
System.loadLibrary(shortLibraryName2);
return;
} catch (UnsatisfiedLinkError ex3) {
// UnsatisfiedLinkError does not have a (String, Throwable)
// constructor, so we call initCause().
String userDir = "<