001/* Class providing additional functions in the Ptolemy II expression language. 002 003 Copyright (c) 1998-2016 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 028 */ 029package ptolemy.data.expr; 030 031import java.io.BufferedReader; 032import java.io.File; 033import java.io.FileInputStream; 034import java.io.FileNotFoundException; 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.InputStreamReader; 038import java.net.URL; 039import java.util.Arrays; 040import java.util.Comparator; 041import java.util.HashMap; 042import java.util.HashSet; 043import java.util.Iterator; 044import java.util.LinkedList; 045import java.util.List; 046import java.util.Map; 047import java.util.Random; 048import java.util.Set; 049import java.util.StringTokenizer; 050import java.util.Vector; 051 052import ptolemy.data.ActorToken; 053import ptolemy.data.ArrayToken; 054import ptolemy.data.BooleanToken; 055import ptolemy.data.ComplexMatrixToken; 056import ptolemy.data.ComplexToken; 057import ptolemy.data.DoubleMatrixToken; 058import ptolemy.data.DoubleToken; 059import ptolemy.data.FunctionToken; 060import ptolemy.data.IntMatrixToken; 061import ptolemy.data.IntToken; 062import ptolemy.data.LongMatrixToken; 063import ptolemy.data.LongToken; 064import ptolemy.data.MatrixToken; 065import ptolemy.data.ObjectToken; 066import ptolemy.data.RecordToken; 067import ptolemy.data.ScalarToken; 068import ptolemy.data.StringToken; 069import ptolemy.data.Token; 070import ptolemy.data.UnsignedByteToken; 071import ptolemy.data.UnsizedArrayToken; 072import ptolemy.data.type.ArrayType; 073import ptolemy.data.type.BaseType; 074import ptolemy.data.type.FunctionType; 075import ptolemy.data.type.Type; 076import ptolemy.data.type.TypeLattice; 077import ptolemy.graph.CPO; 078import ptolemy.kernel.util.IllegalActionException; 079import ptolemy.kernel.util.InternalErrorException; 080import ptolemy.math.ComplexMatrixMath; 081import ptolemy.util.MessageHandler; 082import ptolemy.util.StringUtilities; 083 084/////////////////////////////////////////////////////////////////// 085//// UtilityFunctions 086 087/** 088 This class provides additional functions for use in the Ptolemy II 089 expression language. All of the methods in this class are static 090 and return an instance of Token. The expression language identifies 091 the appropriate method to use by using reflection, matching the 092 types of the arguments. 093 094 @author Christopher Hylands Brooks, Tobin Fricke, Bart Kienhuis, Edward A. Lee, 095 Steve Neuendorffer, Neil Smyth, Yang Zhao. Contributor: Daniel Crawl 096 @version $Id$ 097 @since Ptolemy II 0.2 098 @Pt.ProposedRating Yellow (eal) 099 @Pt.AcceptedRating Red (cxh) 100 */ 101public class UtilityFunctions { 102 /////////////////////////////////////////////////////////////////// 103 //// public methods //// 104 105 /** Return a new UnsizedArrayToken whose element type is the same 106 * as the given type. 107 * @return a new UnsizedArrayToken. 108 */ 109 public static ArrayToken arrayType(Token t) { 110 // Note that this method is deliberately not listed in the Expression 111 // chapter because it is very specialized. 112 return new UnsizedArrayToken(t.getType()); 113 } 114 115 /** Return a new ArrayType whose element type is the same as the given 116 * type. 117 * @param type The type of the array. 118 * @return a new ArrayType. 119 */ 120 public static Type arrayTypeReturnType(Type type) { 121 // Note that this method is deliberately not listed in the Expression 122 // chapter because it is very specialized. 123 return new ArrayType(type); 124 } 125 126 /** Return a new UnsizedArrayToken whose element type is the same 127 * as the given type, and whose length is the given length. 128 * @param t The element type of the array. 129 * @param numberOfTimes The array length. 130 * @return a new ArrayToken. 131 */ 132 public static ArrayToken arrayType(Token t, IntToken numberOfTimes) { 133 // Note that this method is deliberately not listed in the Expression 134 // chapter because it is very specialized. 135 return repeat(numberOfTimes, t); 136 } 137 138 /** Return the (not quite exact) return type of the arrayType 139 * function above. This function always returns an ArrayType 140 * whose element type is the first argument, with length equal to 141 * the second argument. The result type is reported as being 142 * unsized, which may result in an inexact type being inferred 143 * for expressions including the arrayType function. 144 * @param type1 The type of the first argument to the 145 * corresponding function. 146 * @param type2 The type of the second argument to the 147 * corresponding function. 148 * @return The type of the value returned from the corresponding function. 149 */ 150 public static Type arrayTypeReturnType(Type type1, Type type2) { 151 // FIXME: The method comment makes no sense, the type1 152 // argument is not used. 153 154 // Note that this method is deliberately not listed in the Expression 155 // chapter because it is very specialized. 156 return new ArrayType(type2); 157 } 158 159 /** Convert the argument from a fileName to a URL that begins with 160 * "file:". 161 * @param fileName The name of the file to be converted. 162 * @return the URL that is equivalent to the file 163 */ 164 public static String asURL(String fileName) { 165 File file = new File(fileName); 166 167 try { 168 URL url = file.toURI().toURL(); 169 return url.toString(); 170 } catch (java.net.MalformedURLException malformed) { 171 throw new RuntimeException( 172 "could not convert '" + file + "' to a URL", malformed); 173 } 174 } 175 176 /** Convert the second token to the type of the first. 177 * @exception IllegalActionException If the token cannot be converted. 178 */ 179 public static Token cast(Token token1, Token token2) 180 throws IllegalActionException { 181 return token1.getType().convert(token2); 182 } 183 184 /** Return the type of the first argument. 185 * @param type1 The type being cast to. 186 * @param type2 The type being cast. 187 * @return The type being cast to. 188 */ 189 public static Type castReturnType(Type type1, Type type2) 190 throws IllegalActionException { 191 return type1; 192 } 193 194 /** 195 * Returns the lower triangular matrix L that satisfies LL*=A 196 * @param A a positive definite matrix (implies also symmetric) 197 * @return lower triangular matrix L such that A=LL* 198 * @exception IllegalActionException 199 */ 200 public static double[][] choleskyDecomposition(double[][] A) 201 throws IllegalActionException { 202 203 if (A == null || A[0] == null || A.length != A[0].length) { 204 throw new IllegalArgumentException( 205 "The input must be a square matrix."); 206 } 207 int N = A.length; 208 209 //A should also be positive-definite. If found not to be so, an error will be thrown. 210 double[][] L = new double[N][N]; // the lower triangular Cholesky factor s.t. L*L^A 211 for (int i = 0; i < N; i++) { 212 for (int j = 0; j <= i; j++) { 213 double lowerSum = 0; 214 for (int k = 0; k < j; k++) { 215 lowerSum += L[i][k] * L[j][k]; 216 } 217 if (i != j) { 218 L[i][j] = 1.0 / L[j][j] * (A[i][j] - lowerSum); 219 } else { 220 if (A[i][i] - lowerSum < 0.0) { 221 throw new IllegalActionException( 222 "The input matrix must be positive-definite."); 223 } 224 L[i][j] = Math.sqrt(A[i][i] - lowerSum); 225 } 226 } 227 } 228 return L; 229 } 230 231 /** Concatenate two arrays. 232 * The arrays must have the same type. 233 * Example: concatenate({1,2,3},{4,5,6}) == {1,2,3,4,5,6}. 234 * @param token1 First array from which tokens are copied to the result. 235 * @param token2 Second array from which tokens are copied to the result. 236 * @return An array containing all of the elements of the first argument 237 * (in order), followed by all of the arguments of the second argument 238 * (in order). 239 * @exception IllegalActionException If the arrays do not have 240 * compatible types. 241 * @since Ptolemy II 4.1 242 */ 243 public static ArrayToken concatenate(ArrayToken token1, ArrayToken token2) 244 throws IllegalActionException { 245 Token[] array1 = token1.arrayValue(); 246 Token[] array2 = token2.arrayValue(); 247 248 int nElements = array1.length + array2.length; 249 Token[] resultArray = new Token[nElements]; 250 251 System.arraycopy(array1, 0, resultArray, 0, array1.length); 252 System.arraycopy(array2, 0, resultArray, array1.length, array2.length); 253 254 return new ArrayToken(resultArray); 255 } 256 257 /** Concatenate an array of arrays into a single array. 258 * Example: concatenate({{1,2,3},{4,5},{6,7}}) == {1,2,3,4,5,6,7}. 259 * @param token Array of arrays which are to be concatenated. 260 * @exception IllegalActionException If the argument is not an array of 261 * arrays. 262 * @since Ptolemy II 4.1 263 */ 264 public static ArrayToken concatenate(ArrayToken token) 265 throws IllegalActionException { 266 if (!(token.getElementType() instanceof ArrayType)) { 267 throw new IllegalActionException( 268 "The argument to concatenate(ArrayToken) " 269 + "must be an array of arrays."); 270 } 271 272 int nElements = 0; 273 274 for (int i = 0; i < token.length(); i++) { 275 nElements += ((ArrayToken) token.getElement(i)).length(); 276 } 277 278 Token[] result = new Token[nElements]; 279 int cursor = 0; 280 281 for (int i = 0; i < token.length(); i++) { 282 Token[] array = ((ArrayToken) token.getElement(i)).arrayValue(); 283 System.arraycopy(array, 0, result, cursor, array.length); 284 cursor += array.length; 285 } 286 287 return new ArrayToken(result); 288 } 289 290 /** Return the return type of the two argument concatenate method, 291 * given the types of the arguments. This specialized method is 292 * called by the CachedMethod class and used to provide a better 293 * return type. 294 * @param firstArrayType The type of the predicate function. 295 * @param secondArrayType The type of the array to be filtered. 296 * @return The type of the result, which is an array of the least 297 * upper bound of the array arguments. The length of the array type 298 * is the sum of the length of the arguments. 299 * @exception IllegalActionException If the types of the array arguments 300 * are not Arrays. 301 */ 302 public static Type concatenateReturnType(Type firstArrayType, 303 Type secondArrayType) throws IllegalActionException { 304 // Note that this method is deliberately not listed in the Expression 305 // chapter because it is very specialized. 306 if (!(firstArrayType instanceof ArrayType) 307 || !(secondArrayType instanceof ArrayType)) { 308 throw new IllegalActionException("Type " + firstArrayType + " or " 309 + secondArrayType + " is not an ArrayType?"); 310 } else { 311 return new ArrayType( 312 ((ArrayType) TypeLattice.lattice() 313 .leastUpperBound(firstArrayType, secondArrayType)) 314 .getElementType(), 315 ((ArrayType) firstArrayType).length() 316 + ((ArrayType) secondArrayType).length()); 317 } 318 } 319 320 /** Return the return type of the one argument concatenate method, given the type 321 * of the argument. 322 * This specialized method is called by the CachedMethod class and used 323 * to provide a better return type. 324 * @param firstArrayType The type of the predicate function. 325 * @return The type of the result, which is an array of the 326 * firstArrayType arguments. The length of the array type 327 * may be unknown. 328 * @exception IllegalActionException If the types of the array argument 329 * is not an Array. 330 */ 331 public static Type concatenateReturnType(Type firstArrayType) 332 throws IllegalActionException { 333 // Note that this method is deliberately not listed in the Expression 334 // chapter because it is very specialized. 335 if (!(firstArrayType instanceof ArrayType)) { 336 throw new IllegalActionException( 337 "Type " + firstArrayType + " is not an ArrayType?"); 338 } else { 339 int nElements = 0; 340 ArrayType arrayType = (ArrayType) firstArrayType; 341 if (arrayType.getElementType() instanceof ArrayType) { 342 ArrayType arrayArrayType = (ArrayType) arrayType 343 .getElementType(); 344 if (arrayArrayType.hasKnownLength()) { 345 nElements += ((ArrayType) arrayType.getElementType()) 346 .length(); 347 } 348 } 349 if (nElements > 0) { 350 return new ArrayType(((ArrayType) arrayType.getElementType()) 351 .getElementType(), nElements); 352 } else { 353 return new ArrayType(((ArrayType) arrayType.getElementType()) 354 .getElementType()); 355 } 356 } 357 } 358 359 /** Return a record token that contains the names of all the 360 * constants and their values. 361 * @return A token containing the names of all the constants 362 * and their values. 363 * @since Ptolemy II 2.1 364 */ 365 public static RecordToken constants() { 366 return Constants.constants(); 367 } 368 369 /** Return an empty record. 370 * @return An empty record. 371 */ 372 public static RecordToken emptyRecord() throws IllegalActionException { 373 return new RecordToken(new String[0], new Token[0]); 374 } 375 376 /** Return an empty array with its element type matching 377 * the specified token. 378 * @param prototype A token specifying the element type. 379 * @return An empty array. 380 */ 381 public static ArrayToken emptyArray(Token prototype) { 382 return new ArrayToken(prototype.getType()); 383 } 384 385 /** Return the type of an empty array of unspecified length. 386 * @param prototype A token specifying the element type. 387 * @return An empty array. 388 */ 389 public static Type emptyArrayReturnType(Type prototype) { 390 return new ArrayType(prototype); 391 } 392 393 /** Extract a sub-array consisting of all of the elements of an 394 * array for which the given predicate function returns true. 395 * If the given array has type {X}, then the given function should 396 * have type function(x:X)(boolean). Example usage: 397 * <p><code>even = function(x:int)(x%2==0) 398 * filter(even,[1:1:20].toArray)</code></p> 399 * @param predicate A function that takes exactly one parameter (of the 400 * same type as the elements of the given array) and returns 401 * a boolean value. 402 * @param array An array, on whose elements the given function 403 * will operate. 404 * @return The subarray of this array containing exactly those 405 * elements, in order, for which the given function returns 406 * a BooleanToken with value true. Any other return value 407 * results in the element being omitted. If the given function 408 * never returns true, then return an empty array with an 409 * element type that matches the specified array. 410 * @exception IllegalActionException If applying the function 411 * triggers an exception. 412 * @since Ptolemy II 4.1 413 */ 414 public static ArrayToken filter(FunctionToken predicate, ArrayToken array) 415 throws IllegalActionException { 416 return filter(predicate, array, new IntToken(-1)); 417 } 418 419 /** Extract a sub-array consisting of all of the elements of an 420 * array for which the given predicate function returns true. 421 * If the given array has type {X}, then the given function should 422 * have type function(x:X)(boolean). Example usage: 423 * <p><code>even = function(x:int)(x%2==0) 424 * filter(even,[1:1:20].toArray)</code></p> 425 * @param predicate A function that takes exactly one parameter (of the 426 * same type as the elements of the given array) and returns 427 * a boolean value. 428 * @param array An array, on whose elements the given function 429 * will operate. 430 * @param sizeLimit The maximum size of the resulting array, 431 * or a negative number to specify no limit. 432 * @return The subarray of this array containing exactly those 433 * elements, in order, for which the given function returns 434 * a BooleanToken with value true. Any other return value 435 * results in the element being omitted. If the given function 436 * never returns true, then return an empty array with an 437 * element type that matches the specified array. 438 * @exception IllegalActionException If applying the function 439 * triggers an exception. 440 * @since Ptolemy II 4.1 441 */ 442 public static ArrayToken filter(FunctionToken predicate, ArrayToken array, 443 IntToken sizeLimit) throws IllegalActionException { 444 List result = new LinkedList(); 445 int arity = predicate.getNumberOfArguments(); 446 447 if (arity != 1) { 448 throw new IllegalActionException( 449 "The predicate argument of filter() must be a function" 450 + " that takes one argument."); 451 } 452 453 int sizeLimitValue = sizeLimit.intValue(); 454 455 for (int i = 0; i < array.length(); i++) { 456 Token element = array.getElement(i); 457 Token[] elementList = { element }; 458 Token include = predicate.apply(elementList); 459 460 if (include instanceof BooleanToken 461 && ((BooleanToken) include).booleanValue()) { 462 result.add(element); 463 } 464 465 if (sizeLimitValue >= 0 && result.size() >= sizeLimitValue) { 466 break; 467 } 468 } 469 470 if (result.size() > 0) { 471 Token[] resultArray = new Token[result.size()]; 472 resultArray = (Token[]) result.toArray(resultArray); 473 return new ArrayToken(resultArray); 474 } else { 475 return new ArrayToken(array.getElementType()); 476 } 477 } 478 479 /** Return the return type of the filter method, given the types 480 * of the argument. 481 * @param predicateType The type of the predicate function. 482 * @param arrayTokenType The type of the array to be filtered. 483 * @return The type of the result, which is the same as the array 484 * type to be filtered. 485 * @exception IllegalActionException If the specified function does not 486 * take exactly one argument, or if the type signature of the function 487 * is not compatible with the other array argument. 488 */ 489 public static Type filterReturnType(Type predicateType, Type arrayTokenType) 490 throws IllegalActionException { 491 // Note that this method is deliberately not listed in the Expression 492 // chapter because it is very specialized. 493 return filterReturnType(predicateType, arrayTokenType, null); 494 } 495 496 /** Return the return type of the filter method, given the types 497 * of the argument. 498 * @param predicateType The type of the predicate function. 499 * @param arrayTokenType The type of the array to be filtered. 500 * @param sizeLimitType The type of the sizeLimit argument, or 501 * null if there is no specified size limit. 502 * @return The type of the result, which is the same as the array 503 * type to be filtered. 504 * @exception IllegalActionException If the specified function does not 505 * take exactly one argument, or if the type signature of the function 506 * is not compatible with the other array argument. 507 */ 508 public static Type filterReturnType(Type predicateType, Type arrayTokenType, 509 Type sizeLimitType) throws IllegalActionException { 510 // Note that this method is deliberately not listed in the Expression 511 // chapter because it is very specialized. 512 if (predicateType instanceof FunctionType) { 513 FunctionType castPredicateType = (FunctionType) predicateType; 514 515 if (castPredicateType.getArgCount() != 1) { 516 throw new IllegalActionException( 517 "filter() can only be used on functions that take " 518 + "one argument."); 519 } else { 520 Type argType = castPredicateType.getArgType(0); 521 int comparison = TypeLattice.compare( 522 ((ArrayType) arrayTokenType).getElementType(), argType); 523 524 if (comparison != CPO.LOWER && comparison != CPO.SAME) { 525 throw new IllegalActionException( 526 "filter(): specified array element is not " 527 + "compatible with function argument type."); 528 } 529 530 // Force size to be unknown 531 return new ArrayType( 532 ((ArrayType) arrayTokenType).getElementType()); 533 } 534 } else { 535 return BaseType.UNKNOWN; 536 } 537 } 538 539 /** Find all true-valued elements in an array of boolean values, 540 * returning an array containing the indices (in ascending order) 541 * of all occurrences of the value 'true'. 542 * @param array An array of boolean tokens. 543 * @return An array of integers giving the indices of 'true' elements 544 * in the array given as an argument. 545 * @since Ptolemy II 4.1 546 * @see #find(ArrayToken, Token) 547 * @exception IllegalActionException If the specified array is not 548 * a boolean array. 549 */ 550 public static ArrayToken find(ArrayToken array) 551 throws IllegalActionException { 552 if (!(array.getElementType() == BaseType.BOOLEAN)) { 553 throw new IllegalActionException( 554 "The argument must be an array of boolean tokens."); 555 } 556 557 return find(array, BooleanToken.TRUE); 558 } 559 560 /** Find all elements in an array that match the specified token 561 * and return an array containing their indices (in ascending order). 562 * @param array An array. 563 * @return An array of integers giving the indices of elements 564 * in the specified array that match the specified token. 565 * @since Ptolemy II 4.1 566 * @see #find(ArrayToken) 567 */ 568 public static ArrayToken find(ArrayToken array, Token match) { 569 List result = new LinkedList(); 570 571 for (int i = 0; i < array.length(); i++) { 572 if (array.getElement(i).equals(match)) { 573 result.add(new IntToken(i)); 574 } 575 } 576 577 if (result.size() > 0) { 578 Token[] resultArray = new Token[result.size()]; 579 resultArray = (Token[]) result.toArray(resultArray); 580 581 try { 582 return new ArrayToken(BaseType.INT, resultArray); 583 } catch (IllegalActionException ex) { 584 throw new InternalErrorException(null, ex, 585 "UtilityFunctions.find: " 586 + "cannot create an an Array of Integers"); 587 } 588 } else { 589 return new ArrayToken(array.getElementType()); 590 } 591 } 592 593 /** Find a file or directory. If the file does not exist as is, then 594 * search the current working directory, the user's home directory, 595 * and finally, the classpath. 596 * @param name Path of a file or directory to find. 597 * @return Canonical absolute path if the file or directory is found, 598 * otherwise the argument is returned unchanged. 599 */ 600 public static String findFile(String name) { 601 File file = new File(name); 602 603 // File has problems if we change user.dir, which we do in 604 // ptolemy/actor/gui/jnlp/MenuApplication.java if we are running 605 // under Web Start or InstallAnywhere. What happens is that 606 // File ignores changes to user.dir, so findFile("ptmatlab.dll") 607 // will look in the old value of user.dir instead of the new 608 // value of user.dir. The hack is to get the canonical path 609 // and use that instead. 610 if (file.exists()) { 611 try { 612 file = new File(file.getCanonicalPath()); 613 } catch (IOException ex) { 614 file = file.getAbsoluteFile(); 615 } 616 } 617 618 if (!file.exists()) { 619 String curDir = StringUtilities.getProperty("user.dir"); 620 file = new File(curDir, name); 621 } 622 623 if (!file.exists()) { 624 String curDir = StringUtilities.getProperty("user.home"); 625 file = new File(curDir, name); 626 } 627 628 if (!file.exists()) { 629 String cp = System.getProperty("java.class.path"); 630 StringTokenizer tokens = new StringTokenizer(cp, 631 System.getProperty("path.separator")); 632 633 while (tokens.hasMoreTokens()) { 634 String token = tokens.nextToken(); 635 file = new File(token, name); 636 637 if (file.exists()) { 638 break; 639 } 640 } 641 } 642 643 if (file.exists()) { 644 try { 645 return file.getCanonicalPath(); 646 } catch (java.io.IOException ex) { 647 return file.getAbsolutePath(); 648 } 649 } else { 650 return name; 651 } 652 } 653 654 /** Return the approximate number of bytes available for future 655 * object allocation. Note that requesting a garbage collection 656 * may change this value. 657 * @return The approximate number of bytes available. 658 * @see #totalMemory() 659 */ 660 public static LongToken freeMemory() { 661 return new LongToken(Runtime.getRuntime().freeMemory()); 662 } 663 664 /** Return a Gaussian random number. 665 * @param mean The mean. 666 * @param standardDeviation The standard deviation. 667 * @return An observation of a Gaussian random variable. 668 */ 669 public static DoubleToken gaussian(double mean, double standardDeviation) { 670 if (_random == null) { 671 _random = new Random(); 672 } 673 674 double raw = _random.nextGaussian(); 675 double result = raw * standardDeviation + mean; 676 return new DoubleToken(result); 677 } 678 679 /** Return an array of Gaussian random numbers. 680 * @param mean The mean. 681 * @param standardDeviation The standard deviation. 682 * @param length The length of the array. 683 * @return An array of doubles with IID Gaussian random variables. 684 */ 685 public static ArrayToken gaussian(double mean, double standardDeviation, 686 int length) { 687 if (_random == null) { 688 _random = new Random(); 689 } 690 691 DoubleToken[] result = new DoubleToken[length]; 692 693 for (int i = 0; i < length; i++) { 694 double raw = _random.nextGaussian(); 695 result[i] = new DoubleToken(raw * standardDeviation + mean); 696 } 697 698 try { 699 return new ArrayToken(BaseType.DOUBLE, result); 700 } catch (IllegalActionException ex) { 701 // This should not happen since result should not be null. 702 throw new InternalErrorException(null, ex, 703 "UtilityFunction.gaussian: " 704 + "Cannot create the array that contains " 705 + "Gaussian random numbers."); 706 } 707 } 708 709 /** Return a matrix of Gaussian random numbers. 710 * @param mean The mean. 711 * @param standardDeviation The standard deviation. 712 * @param rows The number of rows. 713 * @param columns The number of columns. 714 * @return A matrix of observations of a Gaussian random variable. 715 */ 716 public static DoubleMatrixToken gaussian(double mean, 717 double standardDeviation, int rows, int columns) { 718 if (_random == null) { 719 _random = new Random(); 720 } 721 722 double[][] result = new double[rows][columns]; 723 724 for (int i = 0; i < rows; i++) { 725 for (int j = 0; j < columns; j++) { 726 double raw = _random.nextGaussian(); 727 result[i][j] = raw * standardDeviation + mean; 728 } 729 } 730 731 try { 732 return new DoubleMatrixToken(result); 733 } catch (IllegalActionException ex) { 734 // This should not happen since result should not be null. 735 throw new InternalErrorException(null, ex, 736 "UtilityFunction.gaussian: " 737 + "Cannot create the DoubleMatrixToken that contains " 738 + "Gaussian random numbers."); 739 } 740 } 741 742 /** Get a variable from the environment. 743 * @param variableName The name of the environment variable to get 744 * @return the environment variable, or null if the variable is not defined. 745 */ 746 public static StringToken getenv(String variableName) { 747 return new StringToken(System.getenv(variableName)); 748 } 749 750 /** Get a variable from the environment. 751 * @return the environment variable, or null if the variable is not defined. 752 */ 753 public static RecordToken getenv() throws IllegalActionException { 754 755 Map<String, Token> environmentMap = new HashMap<String, Token>(); 756 757 Map<String, String> environment = System.getenv(); 758 759 Iterator environmentVariables = environment.entrySet().iterator(); 760 while (environmentVariables.hasNext()) { 761 Map.Entry pairs = (Map.Entry) environmentVariables.next(); 762 environmentMap.put((String) pairs.getKey(), 763 new StringToken((String) pairs.getValue())); 764 } 765 return new RecordToken(environmentMap); 766 } 767 768 /** Get the specified property from the environment. An empty string 769 * is returned if the argument environment variable does not exist, 770 * though if certain properties are not defined, then we 771 * make various attempts to determine them and then set them. 772 * See the javadoc page for java.util.System.getProperties() for 773 * a list of system properties. 774 * <p>The following properties are handled specially 775 * <dl> 776 * <dt> "ptolemy.ptII.dir" 777 * <dd> vergil usually sets the ptolemy.ptII.dir property to the 778 * value of $PTII. However, if we are running under Web Start, 779 * then this property might not be set, in which case we look 780 * for "ptolemy/kernel/util/NamedObj.class" and set the 781 * property accordingly. 782 * <dt> "ptolemy.ptII.dirAsURL" 783 * <dd> Return $PTII as a URL. For example, if $PTII was c:\ptII, 784 * then return file:/c:/ptII/. 785 * <dt> "user.dir" 786 * <dd> Return the canonical path name to the current working directory. 787 * This is necessary because under JDK1.4.1 System.getProperty() 788 * returns <code><b>c</b>:/<i>foo</i></code> 789 * whereas most of the other methods that operate 790 * on path names return <code><b>C</b>:/<i>foo</i></code>. 791 * </dl> 792 * @param propertyName The name of property. 793 * @return A String containing the string value of the property. 794 * @deprecated Use 795 * {@link ptolemy.util.StringUtilities#getProperty(String)} 796 * instead 797 */ 798 @Deprecated 799 public static String getProperty(String propertyName) { 800 return StringUtilities.getProperty(propertyName); 801 } 802 803 /** Infer the type of the given string as an expression in the 804 * expression language. Return a string representation of the 805 * given type. 806 * @param string The string to be parsed and evaluated. 807 * @return A string representing an inferred type. 808 */ 809 public static String inferType(String string) 810 throws IllegalActionException { 811 // Note that this method is deliberately not listed in the Expression 812 // chapter because it is very specialized. 813 PtParser parser = new PtParser(); 814 ASTPtRootNode parseTree = parser.generateParseTree(string); 815 ParseTreeTypeInference typeInference = new ParseTreeTypeInference(); 816 return typeInference.inferTypes(parseTree).toString(); 817 } 818 819 /** Find the intersection of two records. The field names are 820 * intersected, and the values are taken from the first 821 * record. 822 * <p>Example: intersect({a = 1, b = 2}, {a = 3, c = 4}) returns {a = 1}. 823 * @param record1 The first record to intersect. The result gets 824 * its values from this record. 825 * @param record2 The second record to intersect. 826 * @return A RecordToken containing the result. 827 */ 828 public static Token intersect(RecordToken record1, RecordToken record2) 829 throws IllegalActionException { 830 Set commonNames = new HashSet(record1.labelSet()); 831 commonNames.retainAll(record2.labelSet()); 832 833 Token[] values = new Token[commonNames.size()]; 834 String[] names = new String[values.length]; 835 int i = 0; 836 837 for (Iterator iterator = commonNames.iterator(); iterator 838 .hasNext(); i++) { 839 String name = (String) iterator.next(); 840 values[i] = record1.get(name); 841 names[i] = name; 842 } 843 844 return new RecordToken(names, values); 845 } 846 847 /** Iterate the specified function to produce an array of the specified 848 * length. The first element of the output array is the <i>initial</i> 849 * argument, the second is the result of applying the function to 850 * <i>initial</i>, the third is the result of applying the function to 851 * that result, etc. The <i>length</i> argument is required to be 852 * greater than 1. 853 * @param function A single-argument function to iterate. 854 * @param length The length of the resulting array. 855 * @param initial The first element of the result. 856 * @return A new array that is the result of applying the function 857 * repeatedly. 858 * @exception IllegalActionException If the specified function does not 859 * take exactly one argument, or if an error occurs applying the function. 860 */ 861 public static ArrayToken iterate(FunctionToken function, int length, 862 Token initial) throws IllegalActionException { 863 int arity = function.getNumberOfArguments(); 864 865 if (arity != 1) { 866 throw new IllegalActionException( 867 "iterate() can only be used on functions that take " 868 + "one argument."); 869 } else if (length < 2) { 870 throw new IllegalActionException( 871 "iterate() requires the length argument to be greater " 872 + "than 1."); 873 } else { 874 Token[] result = new Token[length]; 875 Token iterate = initial; 876 result[0] = initial; 877 878 for (int i = 1; i < length; i++) { 879 Token[] args = new Token[1]; 880 args[0] = iterate; 881 iterate = function.apply(args); 882 result[i] = iterate; 883 } 884 885 return new ArrayToken(result); 886 } 887 } 888 889 /** Return the return type of the iterate method, given the types 890 * of the argument. 891 * @param functionType The type of the function to iterate. 892 * @param lengthType The type of the length argument. 893 * @param initialType The type of the first element of the result. 894 * @return The type of the result. 895 * @exception IllegalActionException If the specified function does not 896 * take exactly one argument, or if the type signature of the function 897 * is not compatible with the other arguments. 898 */ 899 public static Type iterateReturnType(Type functionType, Type lengthType, 900 Type initialType) throws IllegalActionException { 901 if (functionType instanceof FunctionType) { 902 FunctionType castFunctionType = (FunctionType) functionType; 903 904 if (castFunctionType.getArgCount() != 1) { 905 throw new IllegalActionException( 906 "iterate() can only be used on functions that take " 907 + "one argument."); 908 } else { 909 Type argType = castFunctionType.getArgType(0); 910 int comparison = TypeLattice.compare(initialType, argType); 911 912 if (comparison != CPO.LOWER && comparison != CPO.SAME) { 913 throw new IllegalActionException( 914 "iterate(): specified initial value is not " 915 + "compatible with function argument type."); 916 } 917 918 Type resultType = castFunctionType.getReturnType(); 919 int comparison2 = TypeLattice.compare(resultType, argType); 920 921 if (comparison2 != CPO.LOWER && comparison2 != CPO.SAME) { 922 throw new IllegalActionException( 923 "iterate(): invalid function: function return " 924 + "type is not " 925 + "compatible with function argument type."); 926 } 927 928 return new ArrayType( 929 TypeLattice.leastUpperBound(resultType, initialType)); 930 } 931 } else { 932 return BaseType.UNKNOWN; 933 } 934 } 935 936 /** Load a library by first using the default platform dependent 937 * System.loadLibrary() method. If the library cannot be loaded 938 * using System.loadLibrary(), then search for the library using 939 * {@link #findFile(String)} and if the library is found, 940 * load it using System.load(). If the library is not found 941 * by findFile() and the pathname contains a / 942 * then we call System.loadLibrary() the filename after the last 943 * /. This step is necessary to support native methods under 944 * Webstart, which looks for libraries at the top level. 945 * If all of the above fails, we throw the initial exception. 946 * 947 * @param library the name of the library to be loaded. The name 948 * should not include the platform dependent suffix. 949 */ 950 public static void loadLibrary(String library) { 951 final boolean debug = false; 952 if (debug) { 953 System.out.println("UtilityFunctions.loadLibrary(" + library + ")"); 954 } 955 try { 956 if (library.indexOf("/") == -1 && library.indexOf("\\") == -1) { 957 // loadLibrary does not work if the library has a \ or / in it. 958 // Unfortunately, pathnames tend to get specified with 959 // a forward slash, even under windows 960 if (debug) { 961 System.out.println("UtilityFunctions.loadLibrary(" + library 962 + "): System.loadLibrary()"); 963 } 964 System.loadLibrary(library); 965 } else { 966 // load() does not work with relative paths. 967 if (debug) { 968 System.out.println("UtilityFunctions.loadLibrary(" + library 969 + "): System.load()"); 970 } 971 System.load(library); 972 } 973 } catch (UnsatisfiedLinkError ex) { 974 if (debug) { 975 System.out.println( 976 "UtilityFunctions.loadLibrary(" + library + "): " + ex); 977 } 978 String sharedLibrarySuffix = "dll"; 979 980 String osName = StringUtilities.getProperty("os.name"); 981 982 if (osName.startsWith("SunOS") || osName.startsWith("Linux") 983 || osName.startsWith("Mac OS X")) { 984 985 if (osName.startsWith("Mac OS X")) { 986 // FIXME: we should also look for dylib? 987 sharedLibrarySuffix = "jnilib"; 988 try { 989 _loadLibrary(debug, library, "jnilib", ex); 990 } catch (Exception exMac) { 991 _loadLibrary(debug, library, "dylib", ex); 992 } 993 return; 994 } else { 995 sharedLibrarySuffix = "so"; 996 } 997 } 998 _loadLibrary(debug, library, sharedLibrarySuffix, ex); 999 } 1000 } 1001 1002 /** Apply the specified function to the specified array and return 1003 * an array with the results. The function must take at least one 1004 * argument. If the function takes more than one argument, then 1005 * the specified array should be an array of arrays, where each 1006 * subarray is a set of arguments. Since arrays in the expression 1007 * language can only contain elements of the same type, this method 1008 * will only work for functions whose arguments are all of the same 1009 * type. 1010 * @param function A function with at least one argument. 1011 * @param array The array to which to apply the function. 1012 * @return A new array that is the result of applying the function 1013 * to the specified array. 1014 * @exception IllegalActionException If the specified function does not 1015 * take at least one argument, or if an error occurs applying the 1016 * function, or if the number of arguments does not match the subarray 1017 * lengths. 1018 */ 1019 public static ArrayToken map(FunctionToken function, ArrayToken array) 1020 throws IllegalActionException { 1021 int arity = function.getNumberOfArguments(); 1022 Token[] result = new Token[array.length()]; 1023 1024 if (arity == 1) { 1025 for (int i = 0; i < array.length(); i++) { 1026 Token arg = array.getElement(i); 1027 Token[] args = new Token[1]; 1028 args[0] = arg; 1029 result[i] = function.apply(args); 1030 } 1031 } else if (arity > 1) { 1032 for (int i = 0; i < array.length(); i++) { 1033 Token args = array.getElement(i); 1034 1035 if (!(args instanceof ArrayToken)) { 1036 throw new IllegalActionException( 1037 "Invalid arguments to map(): mismatched arity."); 1038 } 1039 1040 Token[] invokeArgs = new Token[arity]; 1041 ArrayToken castArgs = (ArrayToken) args; 1042 1043 if (castArgs.length() != arity) { 1044 throw new IllegalActionException( 1045 "Invalid arguments to map(): mismatched arity."); 1046 } else { 1047 for (int j = 0; j < arity; j++) { 1048 invokeArgs[j] = castArgs.getElement(j); 1049 } 1050 1051 result[i] = function.apply(invokeArgs); 1052 } 1053 } 1054 } else { 1055 throw new IllegalActionException( 1056 "map() can only be used on functions that take at least " 1057 + "one argument."); 1058 } 1059 1060 return new ArrayToken(result); 1061 } 1062 1063 /** Return the return type of the map method, given the types 1064 * of the argument. 1065 * @param functionType The type of the function to map. 1066 * @param arrayTokenType The type of array to apply the 1067 * specified function on. 1068 * @return The type of the result. 1069 * @exception IllegalActionException If the specified function does not 1070 * take at least one argument, or if the type signature of the function 1071 * is not compatible with the array elements, or if the specified 1072 * function has different argument type. 1073 */ 1074 public static Type mapReturnType(Type functionType, Type arrayTokenType) 1075 throws IllegalActionException { 1076 // Note that this method is deliberately not listed in the Expression 1077 // chapter because it is very specialized. 1078 if (functionType instanceof FunctionType) { 1079 FunctionType castFunctionType = (FunctionType) functionType; 1080 1081 if (castFunctionType.getArgCount() == 1) { 1082 Type argType = castFunctionType.getArgType(0); 1083 int comparison = TypeLattice.compare( 1084 ((ArrayType) arrayTokenType).getElementType(), argType); 1085 1086 if (comparison != CPO.LOWER && comparison != CPO.SAME) { 1087 throw new IllegalActionException( 1088 "map(): specified array token is not compatible " 1089 + "with function argument type."); 1090 } 1091 } else if (castFunctionType.getArgCount() > 1) { 1092 Type firstArgType = castFunctionType.getArgType(0); 1093 boolean flag = true; 1094 1095 for (int i = 1; i < castFunctionType.getArgCount(); i++) { 1096 Type argType = castFunctionType.getArgType(i); 1097 1098 if (argType != firstArgType) { 1099 i = castFunctionType.getArgCount(); 1100 flag = false; 1101 throw new IllegalActionException("map() can only work " 1102 + "for functions whose arguments are all of " 1103 + "the same type."); 1104 } 1105 } 1106 1107 if (flag) { 1108 Type argType = castFunctionType.getArgType(0); 1109 Type elementType = ((ArrayType) arrayTokenType) 1110 .getElementType(); 1111 1112 if (!(elementType instanceof ArrayType)) { 1113 throw new IllegalActionException( 1114 "map(): specified array token is not " 1115 + "compatible with function arity."); 1116 } else { 1117 int comparison = TypeLattice.compare( 1118 ((ArrayType) elementType).getElementType(), 1119 argType); 1120 1121 if (comparison != CPO.LOWER && comparison != CPO.SAME) { 1122 throw new IllegalActionException( 1123 "map(): specified array token is not " 1124 + "compatible with function " 1125 + "argument type."); 1126 } 1127 } 1128 } 1129 } 1130 1131 Type resultType = castFunctionType.getReturnType(); 1132 return new ArrayType(resultType); 1133 } else { 1134 return BaseType.UNKNOWN; 1135 } 1136 } 1137 1138 /** Return the maximum of two unsigned bytes. 1139 * @param x An unsigned byte. 1140 * @param y An unsigned byte. 1141 * @return The maximum of x and y. 1142 */ 1143 public static UnsignedByteToken max(UnsignedByteToken x, 1144 UnsignedByteToken y) { 1145 if (x.intValue() > y.intValue()) { 1146 return x; 1147 } else { 1148 return y; 1149 } 1150 } 1151 1152 /** Return the maximum of the contents of the array. 1153 * @param array An array of scalar tokens. 1154 * @return The largest element of the array. 1155 * @exception IllegalActionException If the array is empty or 1156 * it contains tokens that are not scalar or it contains complex tokens. 1157 */ 1158 public static ScalarToken max(ArrayToken array) 1159 throws IllegalActionException { 1160 if (array.length() == 0 1161 || !BaseType.SCALAR.isCompatible(array.getElementType())) { 1162 throw new IllegalActionException( 1163 "max function can only be applied to arrays of scalars."); 1164 } 1165 1166 ScalarToken result = (ScalarToken) array.getElement(0); 1167 1168 for (int i = 1; i < array.length(); i++) { 1169 ScalarToken element = (ScalarToken) array.getElement(i); 1170 1171 if (element.isGreaterThan(result).booleanValue()) { 1172 result = element; 1173 } 1174 } 1175 1176 return result; 1177 } 1178 1179 /** Return the (exact) return type of the max function above. 1180 * If the argument is an array type, then return its element type, 1181 * otherwise return BaseType.UNKNOWN. 1182 * @param type The type of the argument to the corresponding function. 1183 * @return The type of the value returned from the corresponding 1184 * function. 1185 */ 1186 public static Type maxReturnType(Type type) { 1187 // Note that this method is deliberately not listed in the Expression 1188 // chapter because it is very specialized. 1189 if (type instanceof ArrayType) { 1190 ArrayType arrayType = (ArrayType) type; 1191 return arrayType.getElementType(); 1192 } else { 1193 return BaseType.UNKNOWN; 1194 } 1195 } 1196 1197 /** Return the minimum of two unsigned bytes. 1198 * @param x An unsigned byte. 1199 * @param y An unsigned byte. 1200 * @return The minimum of x and y. 1201 */ 1202 public static UnsignedByteToken min(UnsignedByteToken x, 1203 UnsignedByteToken y) { 1204 if (x.intValue() < y.intValue()) { 1205 return x; 1206 } else { 1207 return y; 1208 } 1209 } 1210 1211 /** Return the minimum of the contents of the array. 1212 * @param array An array of scalar tokens. 1213 * @return The largest element of the array. 1214 * @exception IllegalActionException If the array is empty or 1215 * it contains tokens that are not scalar or it contains complex tokens. 1216 */ 1217 public static ScalarToken min(ArrayToken array) 1218 throws IllegalActionException { 1219 if (array.length() == 0 1220 || !BaseType.SCALAR.isCompatible(array.getElementType())) { 1221 throw new IllegalActionException( 1222 "min function can only be applied to arrays of scalars."); 1223 } 1224 1225 ScalarToken result = (ScalarToken) array.getElement(0); 1226 1227 for (int i = 1; i < array.length(); i++) { 1228 ScalarToken element = (ScalarToken) array.getElement(i); 1229 1230 if (element.isLessThan(result).booleanValue()) { 1231 result = element; 1232 } 1233 } 1234 1235 return result; 1236 } 1237 1238 /** Return the (exact) return type of the min function above. 1239 * If the argument is an array type, then return its element type, 1240 * otherwise return BaseType.UNKNOWN. 1241 * @param type The type of the argument to the corresponding function. 1242 * @return The type of the value returned from the corresponding function. 1243 */ 1244 public static Type minReturnType(Type type) { 1245 // Note that this method is deliberately not listed in the Expression 1246 // chapter because it is very specialized. 1247 if (type instanceof ArrayType) { 1248 ArrayType arrayType = (ArrayType) type; 1249 return arrayType.getElementType(); 1250 } else { 1251 return BaseType.UNKNOWN; 1252 } 1253 } 1254 1255 /** FIXME. Placeholder for a function that will return a model. 1256 */ 1257 public static ObjectToken model(String classname) 1258 throws IllegalActionException { 1259 return new ObjectToken(classname); 1260 } 1261 1262 /** Generate a sample from a multivariate Gaussian distribution. 1263 * @param mean The mean. 1264 * @param covariance The covariance. 1265 * @return a sample from a multivariate Gaussian distribution 1266 */ 1267 public static ArrayToken multivariateGaussian(ArrayToken mean, 1268 DoubleMatrixToken covariance) throws IllegalActionException { 1269 1270 // Check dimensions. 1271 int N = mean.length(); // size of array 1272 if ((covariance.getColumnCount() != N) 1273 || (covariance.getRowCount() != covariance.getColumnCount())) { 1274 throw new IllegalActionException( 1275 "Covariance must be a square matrix and its dimension must " 1276 + "match the mean array length"); 1277 } else if (!BaseType.DOUBLE.isCompatible(mean.getElementType())) { 1278 throw new IllegalActionException( 1279 "Mean vector must consist of scalar type elements"); 1280 } 1281 double[] meanArray = new double[N]; 1282 for (int i = 0; i < N; i++) { 1283 meanArray[i] = ((DoubleToken) mean.getElement(i)).doubleValue(); 1284 } 1285 1286 double[][] sigma = covariance.doubleMatrix(); 1287 1288 return multivariateGaussian(meanArray, sigma); 1289 } 1290 1291 /** Generate a sample from a multivariate Gaussian distribution. 1292 * @param mean The mean array. 1293 * @param S The covariance matrix, whose length must match 1294 * that of the mean array and be square. 1295 * @return a sample from a multivariate Gaussian distribution 1296 */ 1297 public static ArrayToken multivariateGaussian(double[] mean, double[][] S) 1298 throws IllegalActionException { 1299 1300 // Check dimensions. 1301 int N = mean.length; // size of array 1302 if ((S.length != N) || (S[0].length != N)) { 1303 throw new IllegalActionException( 1304 "Covariance must be a square matrix and its dimension must " 1305 + "match the mean array length"); 1306 } 1307 // Check if the covariance matrix is symmetric. 1308 for (int i = 0; i < N; i++) { 1309 for (int j = 0; j < i; j++) { 1310 if (Math.abs(S[i][j] - S[j][i]) > 1E-6) { 1311 throw new IllegalActionException( 1312 "Covariance must be a symmetric matrix."); 1313 } 1314 } 1315 } 1316 1317 double[][] L = choleskyDecomposition(S); 1318 1319 // Draw uncorrelated samples from a standard Gaussian. 1320 ArrayToken uncorrelated = gaussian(0, 1, N); 1321 Token[] uncorrelatedTokens = uncorrelated.arrayValue(); 1322 Token[] correlatedTokens = new Token[N]; 1323 double[] correlatedSamples = new double[N]; 1324 for (int i = 0; i < N; i++) { 1325 for (int j = 0; j < N; j++) { 1326 double uncorr = ((DoubleToken) uncorrelatedTokens[j]) 1327 .doubleValue(); 1328 correlatedSamples[i] += L[i][j] * uncorr; 1329 } 1330 correlatedTokens[i] = new DoubleToken( 1331 correlatedSamples[i] + mean[i]); 1332 } 1333 return new ArrayToken(BaseType.DOUBLE, correlatedTokens); 1334 } 1335 1336 /** Parse the string provided and return the result wrapped in a token. 1337 * @param moml The MoML string. 1338 * @return The result of parsing the MoML. 1339 * @exception Exception If the MoML is invalid or the results is not an 1340 * instance of Entity. 1341 */ 1342 public static ActorToken parseMoML(String moml) throws Exception { 1343 return MoMLUtilities.parseMoML(moml); 1344 } 1345 1346 /** Get the specified property from the environment. An empty string 1347 * is returned if the argument environment variable does not exist. 1348 * See the javadoc page for java.util.System.getProperties() for 1349 * a list of system properties. Example properties include: 1350 * <dl> 1351 * <dt> "java.version" 1352 * <dd> the version of the JDK. 1353 * <dt> "ptolemy.ptII.dir" 1354 * <dd> The value of $PTII, which is the name of the directory in 1355 * which Ptolemy II is installed. 1356 * <dt> "ptolemy.ptII.dirAsURL" 1357 * <dd> The value of $PTII as a URL, which is the name of the directory in 1358 * which Ptolemy II is installed. 1359 * <dt> "user.dir" 1360 * <dd> The canonical path name to the current working directory. 1361 * </dl> 1362 * 1363 * @param propertyName The name of property. 1364 * @return A token containing the string value of the property. 1365 * @see ptolemy.util.StringUtilities#getProperty(String) 1366 */ 1367 public static StringToken property(String propertyName) { 1368 return new StringToken(StringUtilities.getProperty(propertyName)); 1369 } 1370 1371 /** Return an array of IID random numbers with value greater than 1372 * or equal to 0.0 and less than 1.0. 1373 * @param length The length of the array. 1374 * @return An array of doubles with IID random variables. 1375 */ 1376 public static ArrayToken random(int length) { 1377 DoubleToken[] result = new DoubleToken[length]; 1378 1379 for (int i = 0; i < length; i++) { 1380 result[i] = new DoubleToken(Math.random()); 1381 } 1382 1383 try { 1384 return new ArrayToken(BaseType.DOUBLE, result); 1385 } catch (IllegalActionException ex) { 1386 // This should not happen since result should not be null. 1387 throw new InternalErrorException(null, ex, 1388 "UtilityFunction.random: " 1389 + "Cannot create the array that contains " 1390 + "random numbers."); 1391 } 1392 } 1393 1394 /** Return a matrix of IID random numbers with value greater than 1395 * or equal to 0.0 and less than 1.0. 1396 * @param rows The number of rows. 1397 * @param columns The number of columns. 1398 * @return A matrix of IID random variables. 1399 */ 1400 public static DoubleMatrixToken random(int rows, int columns) { 1401 double[][] result = new double[rows][columns]; 1402 1403 for (int i = 0; i < rows; i++) { 1404 for (int j = 0; j < columns; j++) { 1405 result[i][j] = Math.random(); 1406 } 1407 } 1408 1409 try { 1410 return new DoubleMatrixToken(result); 1411 } catch (IllegalActionException ex) { 1412 // This should not happen since result should not be null. 1413 throw new InternalErrorException(null, ex, 1414 "UtilityFunction.random: " 1415 + "Cannot create the DoubleMatrixToken that contains " 1416 + "random numbers."); 1417 } 1418 } 1419 1420 /** Return the (exact) return type of the random function above. 1421 * If the argument is BaseType.INT, then return and ArrayType of 1422 * BaseType.DOUBLE, otherwise return BaseType.UNKNOWN. 1423 * @param type The type of the argument to the corresponding function. 1424 * @return The type of the value returned from the corresponding function. 1425 */ 1426 public static Type randomReturnType(Type type) { 1427 // Note that this method is deliberately not listed in the Expression 1428 // chapter because it is very specialized. 1429 if (type.equals(BaseType.INT)) { 1430 return new ArrayType(BaseType.DOUBLE); 1431 } else { 1432 return BaseType.UNKNOWN; 1433 } 1434 } 1435 1436 /** Get the string text contained in the specified file. The argument 1437 * is first interpreted using findFile(), so file names relative to 1438 * the current working directory, the user's home directory, or the 1439 * classpath are understood. If the file contains text that is a 1440 * valid expression in the expression language, then that text can 1441 * interpreted using the eval() function in 1442 * ptolemy.data.expr.ASTPtFunctionApplicationNode. 1443 * For example: <code>eval(readFile("<i>filename</i>"))</code><p> 1444 * Note that readFile() does not work inside applets, use 1445 * {@link #readResource(String)} instead. 1446 * 1447 * @param filename The name of the file to read from. 1448 * @return A StringToken containing the text contained in 1449 * the specified file. 1450 * @exception IllegalActionException If the file cannot be opened. 1451 * @see ptolemy.data.expr.ASTPtFunctionApplicationNode 1452 * @see #readResource(String) 1453 */ 1454 public static StringToken readFile(String filename) 1455 throws IllegalActionException { 1456 File file = new File(findFile(filename)); 1457 1458 //System.out.println("Trying to open file: " + file.toString()); 1459 BufferedReader fin = null; 1460 String line; 1461 StringBuffer result = new StringBuffer(""); 1462 String newline = System.getProperty("line.separator"); 1463 1464 try { 1465 fin = new BufferedReader(new java.io.InputStreamReader( 1466 new java.io.FileInputStream(file), 1467 java.nio.charset.Charset.defaultCharset())); 1468 1469 while (true) { 1470 try { 1471 line = fin.readLine(); 1472 } catch (IOException e) { 1473 break; 1474 } 1475 1476 if (line == null) { 1477 break; 1478 } 1479 1480 result.append(line + newline); 1481 } 1482 } catch (FileNotFoundException ex) { 1483 throw new IllegalActionException(null, ex, "File not found"); 1484 } finally { 1485 if (fin != null) { 1486 try { 1487 fin.close(); 1488 } catch (IOException ex) { 1489 throw new IllegalActionException(null, ex, 1490 "Problem closing '" + file + "'"); 1491 } 1492 } 1493 } 1494 1495 return new StringToken(result.toString()); 1496 } 1497 1498 /** Read a file that contains a matrix of reals in Matlab notation. 1499 * 1500 * @param filename The filename. 1501 * @return The matrix defined in the file. 1502 * @exception IllegalActionException If the file cannot be opened. 1503 * @deprecated Use eval(readFile()) instead. 1504 */ 1505 @Deprecated 1506 public static DoubleMatrixToken readMatrix(String filename) 1507 throws IllegalActionException { 1508 // Note that this method is deliberately not listed in the Expression 1509 // chapter because it is deprecated. 1510 1511 DoubleMatrixToken returnMatrix = null; 1512 1513 File file = new File(findFile(filename)); 1514 if (!file.exists()) { 1515 throw new IllegalActionException( 1516 "readMatrix: File " + filename + " not found."); 1517 } 1518 1519 // Vector containing the matrix 1520 Vector k = null; 1521 1522 // Parameters for the Matrix 1523 int row = -1; 1524 int column = -1; 1525 1526 int rowPosition = 0; 1527 int columnPosition = 0; 1528 double[][] mtr = null; 1529 1530 // At one point MatrixParser.ReInit() took a Reader. With 1531 // JavaCC-5.0, it takes an InputStream. 1532 // FileReader fin = null; 1533 FileInputStream fin = null; 1534 try { 1535 try { 1536 // Open the matrix file. 1537 // fin = new FileReader(file); 1538 fin = new FileInputStream(file); 1539 } catch (FileNotFoundException e) { 1540 throw new IllegalActionException("readMatrix: file \"" 1541 + file.getName() + "\" not found"); 1542 } 1543 1544 // Read the file and convert it into a matrix. 1545 if (_matrixParser == null) { 1546 _matrixParser = new MatrixParser(System.in); 1547 } 1548 1549 //MatrixParser.ReInit(fin); 1550 _matrixParser.ReInit(fin); 1551 1552 k = _matrixParser.readMatrix(); 1553 1554 if (column == -1) { 1555 // The column size of the matrix 1556 column = k.size(); 1557 } 1558 1559 Iterator i = k.iterator(); 1560 1561 while (i.hasNext()) { 1562 Vector l = (Vector) i.next(); 1563 1564 if (row == -1) { 1565 // the row size. 1566 row = l.size(); 1567 1568 // create a new matrix definition 1569 mtr = new double[column][row]; 1570 } else { 1571 if (row != l.size()) { 1572 throw new IllegalActionException(" The Row" 1573 + " size needs to be the same for all" 1574 + " rows"); 1575 } 1576 } 1577 1578 Iterator j = l.iterator(); 1579 1580 while (j.hasNext()) { 1581 Double s = (Double) j.next(); 1582 mtr[columnPosition][rowPosition++] = s.doubleValue(); 1583 } 1584 1585 rowPosition = 0; 1586 columnPosition++; 1587 } 1588 1589 // Vectors have now become obsolete, data is stored 1590 // in double[][]. 1591 k.clear(); 1592 returnMatrix = new DoubleMatrixToken(mtr); 1593 } finally { 1594 if (fin != null) { 1595 try { 1596 fin.close(); 1597 } catch (IOException ex) { 1598 throw new IllegalActionException(null, ex, 1599 "Problem closing '" + file + "'"); 1600 } 1601 } 1602 } 1603 return returnMatrix; 1604 } 1605 1606 /** Get the string text contained in the specified resource, which 1607 * is a file that is specified relative to the Java classpath. 1608 * Resource strings look like filenames without a leading slash. 1609 * If the file contains text that is a 1610 * valid expression in the expression language, then that text can 1611 * interpreted using the eval() function in 1612 * ptolemy.data.expr.ASTPtFunctionApplicationNode. 1613 * For example: <code>eval(readFile("<i>filename</i>"))</code><p> 1614 * 1615 * @param name The name of the resource to read from. 1616 * @return A StringToken containing the text contained in 1617 * the specified resource. 1618 * @exception IllegalActionException If the resource cannot be opened. 1619 * @see ptolemy.data.expr.ASTPtFunctionApplicationNode 1620 * @see #readFile(String) 1621 */ 1622 public static StringToken readResource(String name) 1623 throws IllegalActionException { 1624 URL url = ClassLoader.getSystemResource(name); 1625 StringBuffer result = new StringBuffer(""); 1626 BufferedReader fin = null; 1627 1628 try { 1629 InputStream stream = url.openStream(); 1630 String line; 1631 String newline = System.getProperty("line.separator"); 1632 fin = new BufferedReader(new InputStreamReader(stream)); 1633 1634 while (true) { 1635 try { 1636 line = fin.readLine(); 1637 } catch (IOException e) { 1638 break; 1639 } 1640 1641 if (line == null) { 1642 break; 1643 } 1644 1645 result.append(line + newline); 1646 } 1647 } catch (IOException ex) { 1648 throw new IllegalActionException(null, ex, "File not found"); 1649 } finally { 1650 if (fin != null) { 1651 try { 1652 fin.close(); 1653 } catch (IOException ex) { 1654 throw new IllegalActionException(null, ex, 1655 "Failed to close '" + name + "'"); 1656 } 1657 } 1658 } 1659 1660 return new StringToken(result.toString()); 1661 } 1662 1663 /** Create an array that contains the specified element 1664 * repeated the specified number of times. 1665 * @param numberOfTimes The number of times to repeat the element. 1666 * @param element The element to repeat. 1667 * @return A new array containing the specified element repeated the 1668 * specified number of times. 1669 */ 1670 public static ArrayToken repeat(IntToken numberOfTimes, Token element) { 1671 int length = numberOfTimes.intValue(); 1672 Token[] result = new Token[length]; 1673 1674 for (int i = 0; i < length; i++) { 1675 result[i] = element; 1676 } 1677 1678 ArrayToken arrayToken; 1679 1680 try { 1681 arrayToken = new ArrayToken(element.getType(), result); 1682 } catch (IllegalActionException ex) { 1683 // This should not happen since the elements of the array always 1684 // have the same type. 1685 throw new InternalErrorException(null, ex, 1686 "UtilityFunctions.repeat: " 1687 + "Cannot construct an ArrayToken."); 1688 } catch (IllegalArgumentException ex2) { 1689 // This should not happen since the elements of the array always 1690 // have the same type. 1691 throw new InternalErrorException(null, ex2, 1692 "UtilityFunctions.repeat: " 1693 + "Cannot construct an ArrayToken."); 1694 } 1695 1696 return arrayToken; 1697 } 1698 1699 /** Return the (exact) return type of the repeat function above. 1700 * This function always returns an ArrayType whose element type 1701 * is the second argument. 1702 * @param type1 The type of the first argument to the 1703 * corresponding function. 1704 * @param type2 The type of the second argument to the 1705 * corresponding function. 1706 * @return The type of the value returned from the corresponding function. 1707 */ 1708 public static Type repeatReturnType(Type type1, Type type2) { 1709 // Note that this method is deliberately not listed in the Expression 1710 // chapter because it is very specialized. 1711 return new ArrayType(type2); 1712 } 1713 1714 /** Return a new array that is the sorted contents of a specified 1715 * array, in ascending order. The specified array can contain 1716 * scalar tokens (except complex) or string tokens. If the 1717 * specified array is empty, then it is returned. 1718 * @param array An array of scalar tokens. 1719 * @return A new sorted array. 1720 * @exception IllegalActionException If the array contains 1721 * tokens that are complex or are not scalar or string tokens. 1722 */ 1723 public static ArrayToken sort(ArrayToken array) 1724 throws IllegalActionException { 1725 if (array.length() == 0) { 1726 return array; 1727 } 1728 1729 // NOTE: The following method returns a copy, so we can modify it. 1730 Token[] value = array.arrayValue(); 1731 Arrays.sort(value, _ASCENDING); 1732 return new ArrayToken(array.getElementType(), value); 1733 } 1734 1735 /** Return the (exact) return type of the sort function above. 1736 * If the argument is an array type with elements that are strings 1737 * or scalars other than complex, then return the argument; 1738 * otherwise return BaseType.UNKNOWN. 1739 * @param type The type of the argument to the corresponding function. 1740 * @return The type of the value returned from the corresponding function. 1741 */ 1742 public static Type sortReturnType(Type type) { 1743 // Note that this method is deliberately not listed in the Expression 1744 // chapter because it is very specialized. 1745 1746 // NOTE: This logic indicates which tokens are comparable 1747 // by the TokenComparator inner class. If that class is changed, 1748 // then this logic needs to be changed too. 1749 if (type instanceof ArrayType) { 1750 ArrayType arrayType = (ArrayType) type; 1751 Type elementType = arrayType.getElementType(); 1752 1753 if (elementType.equals(BaseType.COMPLEX)) { 1754 return BaseType.UNKNOWN; 1755 } else if (elementType.equals(BaseType.STRING) 1756 || BaseType.SCALAR.isCompatible(elementType)) { 1757 return type; 1758 } 1759 } 1760 1761 return BaseType.UNKNOWN; 1762 } 1763 1764 /** Return a new array that is the sorted contents of a specified 1765 * array, in ascending order. The specified array can contain 1766 * scalar tokens (except complex) or string tokens. If the 1767 * specified array is empty, then it is returned. 1768 * This method is identical to sort(), and is provided only 1769 * for naming uniformity. 1770 * @see #sort(ArrayToken) 1771 * @see #sortDescending(ArrayToken) 1772 * @param array An array of scalar tokens. 1773 * @return A new sorted array. 1774 * @exception IllegalActionException If the array contains 1775 * tokens that are complex or are not scalar or string tokens. 1776 */ 1777 public static ArrayToken sortAscending(ArrayToken array) 1778 throws IllegalActionException { 1779 return sort(array); 1780 } 1781 1782 /** Return the (exact) return type of the sortAscending function above. 1783 * If the argument is an array type with elements that are strings 1784 * or scalars other than complex, then return the argument; 1785 * otherwise return BaseType.UNKNOWN. 1786 * @param type The type of the argument to the corresponding function. 1787 * @return The type of the value returned from the corresponding function. 1788 */ 1789 public static Type sortAscendingReturnType(Type type) { 1790 // Note that this method is deliberately not listed in the Expression 1791 // chapter because it is very specialized. 1792 return sortReturnType(type); 1793 } 1794 1795 /** Return a new array that is the sorted contents of a specified 1796 * array, in descending order. The specified array can contain 1797 * scalar tokens (except complex) or string tokens. If the 1798 * specified array is empty, then it is returned. 1799 * @param array An array of scalar tokens. 1800 * @return A new sorted array. 1801 * @exception IllegalActionException If the array contains 1802 * tokens that are complex or are not scalar or string tokens. 1803 */ 1804 public static ArrayToken sortDescending(ArrayToken array) 1805 throws IllegalActionException { 1806 if (array.length() == 0) { 1807 return array; 1808 } 1809 1810 // NOTE: The following method returns a copy, so we can modify it. 1811 Token[] value = array.arrayValue(); 1812 Arrays.sort(value, _DESCENDING); 1813 return new ArrayToken(array.getElementType(), value); 1814 } 1815 1816 /** Return the (exact) return type of the sortDescending function above. 1817 * If the argument is an array type with elements that are strings 1818 * or scalars other than complex, then return the argument; 1819 * otherwise return BaseType.UNKNOWN. 1820 * @param type The type of the argument to the corresponding function. 1821 * @return The type of the value returned from the corresponding function. 1822 */ 1823 public static Type sortDescendingReturnType(Type type) { 1824 // Note that this method is deliberately not listed in the Expression 1825 // chapter because it is very specialized. 1826 return sortReturnType(type); 1827 } 1828 1829 /** Return the contiguous subarray of the specified array 1830 * starting at the specified index and of the specified length. 1831 * If the specified index is out of range, 1832 * or if the specified length extends beyond the end of the array, 1833 * then return an empty array with the same type as the specified array. 1834 * @param array The array. 1835 * @param index The index of the beginning of the subarray. 1836 * @param count The length of the subarray. 1837 * @return The extracted subarray. 1838 * @exception IllegalActionException If the index argument is less 1839 * than zero. 1840 * @since Ptolemy II 4.1 1841 */ 1842 public static ArrayToken subarray(ArrayToken array, IntToken index, 1843 IntToken count) throws IllegalActionException { 1844 return array.subarray(index.intValue(), count.intValue()); 1845 } 1846 1847 /** Return the return type of the subarray() method, which is the 1848 * same as the array type. 1849 * @param arrayType The type of the array argument. 1850 * @param indexType The type of the index argument. 1851 * @param countType The type of the count argument. 1852 * @return The extracted subarray. 1853 * @since Ptolemy II 4.1 1854 */ 1855 public static Type subarrayReturnType(Type arrayType, Type indexType, 1856 Type countType) { 1857 // Note that this method is deliberately not listed in the Expression 1858 // chapter because it is very specialized. 1859 return arrayType; 1860 } 1861 1862 /** Return the sum of the elements in the specified array. 1863 * This method is polymorphic in that it can sum any array 1864 * whose elements support addition. 1865 * There is special support for strings, which are added 1866 * not via their add method (which concatenates two strings), 1867 * but are accumulated in a StringBuffer, which is much more 1868 * efficient. 1869 * @param array An array. 1870 * @return The sum of the elements of the array. 1871 * @exception IllegalActionException If the length of the 1872 * array is zero, or if the array elements do not support 1873 * addition. 1874 */ 1875 public static final Token sum(ArrayToken array) 1876 throws IllegalActionException { 1877 if (array == null || array.length() < 1) { 1878 throw new IllegalActionException( 1879 "sum() function cannot be applied to an empty array"); 1880 } 1881 1882 if (array.getElement(0) instanceof StringToken) { 1883 int length = 0; 1884 1885 for (int i = 0; i < array.length(); i++) { 1886 length += ((StringToken) array.getElement(i)).stringValue() 1887 .length(); 1888 } 1889 1890 StringBuffer buffer = new StringBuffer(length); 1891 1892 for (int i = 0; i < array.length(); i++) { 1893 buffer.append( 1894 ((StringToken) array.getElement(i)).stringValue()); 1895 } 1896 1897 return new StringToken(buffer.toString()); 1898 } 1899 1900 Token result = array.getElement(0); 1901 1902 for (int i = 1; i < array.length(); i++) { 1903 result = result.add(array.getElement(i)); 1904 } 1905 1906 return result; 1907 } 1908 1909 /** Return the (exact) return type of the sum function above. 1910 * If the argument is an array type, then return its element type, 1911 * otherwise return BaseType.UNKNOWN. 1912 * @param type The type of the argument to the corresponding function. 1913 * @return The type of the value returned from the corresponding function. 1914 */ 1915 public static Type sumReturnType(Type type) { 1916 // Note that this method is deliberately not listed in the Expression 1917 // chapter because it is very specialized. 1918 if (type instanceof ArrayType) { 1919 ArrayType arrayType = (ArrayType) type; 1920 return arrayType.getElementType(); 1921 } else { 1922 return BaseType.UNKNOWN; 1923 } 1924 } 1925 1926 /** Return the approximate number of bytes used by current objects 1927 * and available for future object allocation. 1928 * @return The total number of bytes used by the JVM. 1929 * @see #freeMemory() 1930 */ 1931 public static LongToken totalMemory() { 1932 return new LongToken(Runtime.getRuntime().totalMemory()); 1933 } 1934 1935 /** Evaluate the given string as an expression in the expression 1936 * language. Instead of returning the resulting value, return a 1937 * trace of the evaluation, including such useful information as 1938 * what registered method is actually invoked. 1939 * @param string The string to be parsed and evaluated. 1940 * @return A string representing an evaluation trace. 1941 */ 1942 public static String traceEvaluation(String string) 1943 throws IllegalActionException { 1944 PtParser parser = new PtParser(); 1945 ASTPtRootNode parseTree = parser.generateParseTree(string); 1946 ParseTreeEvaluator evaluator = new ParseTreeEvaluator(); 1947 return evaluator.traceParseTreeEvaluation(parseTree, null); 1948 } 1949 1950 /** Return true if the first argument is close in value to the second, 1951 * where "close" means that it is within the distance given by the 1952 * third argument. Exactly what this means depends on the data type. 1953 * This method uses the isCloseTo() method of the first token. 1954 * @param token1 The first token. 1955 * @param token2 The second token. 1956 * @param distance The distance criterion. 1957 * @return a true-valued token if the first two arguments are close 1958 * enough. 1959 * @exception IllegalActionException If the first two arguments cannot 1960 * be compared. 1961 */ 1962 public static BooleanToken within(Token token1, Token token2, 1963 double distance) throws IllegalActionException { 1964 return token1.isCloseTo(token2, distance); 1965 } 1966 1967 /** Query the user for a yes-no answer and return a boolean. 1968 * This function will open a dialog if a GUI is available, 1969 * and otherwise will use standard input and output. 1970 */ 1971 public static BooleanToken yesNoQuestion(StringToken prompt) { 1972 if (MessageHandler.yesNoQuestion(prompt.stringValue())) { 1973 return BooleanToken.TRUE; 1974 } else { 1975 return BooleanToken.FALSE; 1976 } 1977 } 1978 1979 /** Return a double zero matrix with the given number of rows and 1980 * columns. 1981 * @return The zero matrix with the given number of rows and 1982 * columns. 1983 * @deprecated Use zeroMatrixDouble instead. 1984 */ 1985 @Deprecated 1986 public static DoubleMatrixToken zeroMatrix(int rows, int columns) { 1987 return zeroMatrixDouble(rows, columns); 1988 } 1989 1990 /** Return a complex zero matrix with the given number of rows and 1991 * columns. 1992 * @return The zero matrix with the given number of rows and 1993 * columns. 1994 */ 1995 public static ComplexMatrixToken zeroMatrixComplex(int rows, int columns) { 1996 try { 1997 return new ComplexMatrixToken( 1998 ComplexMatrixMath.zero(rows, columns)); 1999 } catch (IllegalActionException ex) { 2000 throw new InternalErrorException(null, ex, 2001 "UtilityFunctions.zeroMatrixComplex: " 2002 + "Cannot create a ComplexMatrixToken."); 2003 } 2004 } 2005 2006 /** Return a double zero matrix with the given number of rows and 2007 * columns. 2008 * @return The zero matrix with the given number of rows and 2009 * columns. 2010 */ 2011 public static DoubleMatrixToken zeroMatrixDouble(int rows, int columns) { 2012 double[][] mtr = new double[rows][columns]; 2013 DoubleMatrixToken result = null; 2014 2015 try { 2016 result = new DoubleMatrixToken(mtr, MatrixToken.DO_NOT_COPY); 2017 } catch (IllegalActionException ex) { 2018 throw new InternalErrorException(null, ex, 2019 "UtilityFunctions.zeroMatrixDouble: " 2020 + "Cannot create a DoubleMatrixToken."); 2021 } 2022 2023 return result; 2024 } 2025 2026 /** Return a int zero matrix with the given number of rows and 2027 * columns. 2028 * @return The zero matrix with the given number of rows and 2029 * columns. 2030 */ 2031 public static IntMatrixToken zeroMatrixInt(int rows, int columns) { 2032 int[][] mtr = new int[rows][columns]; 2033 IntMatrixToken result = null; 2034 2035 try { 2036 result = new IntMatrixToken(mtr, MatrixToken.DO_NOT_COPY); 2037 } catch (IllegalActionException ex) { 2038 throw new InternalErrorException(null, ex, 2039 "UtilityFunctions.zeroMatrixInt: " 2040 + "Cannot create a IntMatrixToken."); 2041 } 2042 2043 return result; 2044 } 2045 2046 /** Return a long zero matrix with the given number of rows and 2047 * columns. 2048 * @return The zero matrix with the given number of rows and 2049 * columns. 2050 */ 2051 public static LongMatrixToken zeroMatrixLong(int rows, int columns) { 2052 long[][] mtr = new long[rows][columns]; 2053 LongMatrixToken result = null; 2054 2055 try { 2056 result = new LongMatrixToken(mtr, MatrixToken.DO_NOT_COPY); 2057 } catch (IllegalActionException ex) { 2058 throw new InternalErrorException(null, ex, 2059 "UtilityFunctions.zeroMatrixLong: " 2060 + "Cannot create a LongMatrixToken."); 2061 } 2062 2063 return result; 2064 } 2065 2066 /////////////////////////////////////////////////////////////////// 2067 //// private methods //// 2068 2069 /** Load a shared library. 2070 * @param library the name of the library to be loaded. The name 2071 * should not include the platform dependent suffix. 2072 * @param sharedLibrarySuffix The suffix for the shared library, without the dot. 2073 * Under Windows, this would be ".dll", under Mac OS X, "jnilib" or "dylib", 2074 * under Solaris or Linux, ".o". 2075 * @param throwable The throwable that occurred when System.load() or System.loadLibrary() 2076 * was called. 2077 */ 2078 private static void _loadLibrary(final boolean debug, String library, 2079 String sharedLibrarySuffix, Throwable throwable) { 2080 // We have a separate method here so that we can call it with 2081 // jnilib and then with dylib because under Web Start on Mac OS X 10.5 2082 // with Java 10.5, shared libraries need to have a .jnilib extension. Sigh. 2083 2084 // The name of the library (everything after the last /) 2085 String shortLibraryName = null; 2086 2087 String osName = StringUtilities.getProperty("os.name"); 2088 if (debug) { 2089 System.out.println("UtilityFunctions._loadLibrary(" + library + ", " 2090 + sharedLibrarySuffix + "): osName: " + osName); 2091 } 2092 if (osName.startsWith("SunOS") || osName.startsWith("Linux") 2093 || osName.startsWith("Mac OS X")) { 2094 // Under Solaris, libraries start with lib, so 2095 // we find the last /, and if the next chars are not "lib" 2096 // then we insert "lib". 2097 2098 int index = library.lastIndexOf("/"); 2099 2100 if (index == -1) { 2101 if (!library.startsWith("lib")) { 2102 library = "lib" + library; 2103 } 2104 2105 shortLibraryName = library; 2106 } else { 2107 if (!library.substring(index, index + 4).equals("/lib")) { 2108 if (osName.startsWith("Linux")) { 2109 library = library.substring(index + 1); 2110 shortLibraryName = library; 2111 } else { 2112 // Under SunOS, we add lib to 2113 // the path. If we don't do this on the Mac, 2114 // then libptymatlab.dynlib will not be found. 2115 shortLibraryName = "/lib" 2116 + library.substring(index + 1); 2117 library = library.substring(0, index) 2118 + shortLibraryName; 2119 } 2120 } else { 2121 // Needed to find $PTII/lib/libJNIFMU.jnilib if 2122 // actor/lib/fmi/jni/libJNIFMU.jnilib does not exist 2123 // $PTII/bin/vergil $PTII/ptolemy/actor/lib/fmi/jni/test/auto/EightFourInFourOutsJNI.xml 2124 library = library.substring(index + 1); 2125 shortLibraryName = library; 2126 } 2127 } 2128 } else { 2129 // Windows 2130 int index = library.lastIndexOf("/"); 2131 2132 if (index != -1) { 2133 // Everything after the trailing / 2134 shortLibraryName = library.substring(index + 1); 2135 } 2136 } 2137 2138 String libraryWithSuffix = library + "." + sharedLibrarySuffix; 2139 String libraryPath = UtilityFunctions.findFile(libraryWithSuffix); 2140 boolean libraryPathExists = false; 2141 2142 try { 2143 // It turns out that when looking for libraries under 2144 // InstallAnywhere, findFile() can somehow end up returning 2145 // a bogus value in C:/Documents and Settings 2146 File libraryPathFile = new File(libraryPath); 2147 if (libraryPathFile.exists()) { 2148 libraryPathExists = true; 2149 } 2150 } catch (Throwable throwable2) { 2151 // Ignore, the file can't be found 2152 } 2153 2154 if (debug) { 2155 System.out.println("UtilityFunctions._loadLibrary(" + library + ", " 2156 + sharedLibrarySuffix + "): libraryWithSuffix: " 2157 + libraryWithSuffix + " libraryPathExists" 2158 + libraryPathExists); 2159 } 2160 2161 if (libraryPath.equals(libraryWithSuffix) || !libraryPathExists) { 2162 if (debug) { 2163 System.out.println("UtilityFunctions._loadLibrary(" + library 2164 + ", " + sharedLibrarySuffix 2165 + "): findFile did not find the library try the short name: " 2166 + shortLibraryName); 2167 } 2168 2169 try { 2170 // findFile did not find the library, so we try using 2171 // just the short library name. This is necessary 2172 // for Web Start because Web Start requires that 2173 // native code be in the top level of a jar file 2174 // that is specially marked. Matlab under Web Start 2175 // requires this. 2176 if (shortLibraryName != null) { 2177 if (debug) { 2178 System.out.println("UtilityFunctions._loadLibrary(" 2179 + library + ", " + sharedLibrarySuffix 2180 + "): System.loadLibrary(" + shortLibraryName 2181 + ")"); 2182 } 2183 System.loadLibrary(shortLibraryName); 2184 if (debug) { 2185 System.out.println("UtilityFunctions._loadLibrary(" 2186 + library + ", " + sharedLibrarySuffix 2187 + "): loaded " + shortLibraryName); 2188 } 2189 } 2190 return; 2191 } catch (UnsatisfiedLinkError ex2) { 2192 String shortLibraryName2 = shortLibraryName; 2193 try { 2194 int index = shortLibraryName2.lastIndexOf("/"); 2195 if (index != -1) { 2196 shortLibraryName2 = shortLibraryName2 2197 .substring(index + 1); 2198 } 2199 index = shortLibraryName2.lastIndexOf("lib"); 2200 if (index != -1) { 2201 shortLibraryName2 = shortLibraryName2 2202 .substring(index + 3); 2203 } 2204 if (debug) { 2205 System.out.println("UtilityFunctions._loadLibrary(" 2206 + library + ", " + sharedLibrarySuffix 2207 + "): System.loadLibrary(" + shortLibraryName2 2208 + ")"); 2209 } 2210 System.loadLibrary(shortLibraryName2); 2211 return; 2212 } catch (UnsatisfiedLinkError ex3) { 2213 // UnsatisfiedLinkError does not have a (String, Throwable) 2214 // constructor, so we call initCause(). 2215 String userDir = "<<user.dir unknown>>"; 2216 try { 2217 userDir = System.getProperty("user.dir"); 2218 } catch (Throwable throwable3) { 2219 // Ignore. 2220 } 2221 2222 String userHome = "<<user.home unknown>>"; 2223 try { 2224 userHome = System.getProperty("user.home"); 2225 } catch (Throwable throwable3) { 2226 // Ignore. 2227 } 2228 2229 String javaLibraryPath = "<<java.library.path unknown>>"; 2230 try { 2231 javaLibraryPath = System 2232 .getProperty("java.library.path"); 2233 } catch (Throwable throwable3) { 2234 // Ignore. 2235 } 2236 2237 String classpath = "<<classpath unknown>>"; 2238 try { 2239 classpath = System.getProperty("java.class.path"); 2240 } catch (Throwable throwable3) { 2241 // Ignore. 2242 } 2243 2244 Error error = new UnsatisfiedLinkError("Did not find '" 2245 + library + "' in path, searched " + "user.home (" 2246 + userDir + ") user.dir (" + userHome 2247 + ") and the classpath for '" + libraryPath 2248 + "', but that was not found either.\n" 2249 + "The java.library.path property was: " 2250 + javaLibraryPath + "\nThe Java classpath was: " 2251 + classpath + "\nIn addition, loadLibrary(\"" 2252 + shortLibraryName 2253 + "\") was called, the exception for the " 2254 + "loadLibrary() call was: " + ex2 2255 + (shortLibraryName.equals(shortLibraryName2) ? "" 2256 : "\nAlso, loadlibrary(\"" 2257 + shortLibraryName2 2258 + "\") was called, " 2259 + "the exception for the loadLibrary call was: " 2260 + ex3)); 2261 error.initCause(throwable); 2262 error.printStackTrace(); 2263 throw error; 2264 } 2265 } 2266 } 2267 2268 // System.loadLibrary() does not handle pathnames with separators. 2269 // If we get to here and load a library that includes references 2270 // to libraries not in the PATH or LD_LIBRARY_PATH, then we will 2271 // get and UnsatisfiedLinkError on the file we depend on. 2272 // For example, if liba.so uses libb.so and we call this 2273 // method on a, then libb.so will not be found. 2274 if (debug) { 2275 System.out.println("UtilityFunctions._loadLibrary(" + library + ", " 2276 + sharedLibrarySuffix + "): System.load(" + libraryPath 2277 + ")"); 2278 } 2279 System.load(libraryPath); 2280 } 2281 2282 /////////////////////////////////////////////////////////////////// 2283 //// private variables //// 2284 2285 /** Comparator for ascending sort. */ 2286 private static TokenComparator _ASCENDING = new TokenComparator(true); 2287 2288 /** Comparator for descending sort. */ 2289 private static TokenComparator _DESCENDING = new TokenComparator(false); 2290 2291 /** The Matrix Parser. The Matrix parser is recreated for the standard 2292 * in. However, we use ReInit for the specific matrix files. 2293 */ 2294 private static MatrixParser _matrixParser; 2295 2296 /** The random number generator. 2297 */ 2298 private static Random _random; 2299 2300 /////////////////////////////////////////////////////////////////// 2301 //// inner classes //// 2302 2303 /** Comparator for tokens. 2304 */ 2305 private static class TokenComparator implements Comparator { 2306 /** Construct a new comparator. 2307 * @param ascending True for ascending comparisons. 2308 */ 2309 public TokenComparator(boolean ascending) { 2310 super(); 2311 _ascending = ascending; 2312 } 2313 2314 /** Return -1, 0, or 1 depending on whether the first 2315 * argument is less than, equal to, or greater 2316 * than the second. If the argument to the constructor 2317 * was false, then return the converse (to get descending 2318 * sorts). The arguments are expected to be instances of 2319 * Token, and in particular, to be instances of ScalarToken 2320 * (except ComplexToken) or StringToken; otherwise a 2321 * ClassCastException will be thrown. 2322 * @param arg0 The first argument to compare. 2323 * @param arg1 The second argument to compare. 2324 * @return The result of comparison. 2325 * @exception ClassCastException If the arguments cannot 2326 * be compared. 2327 */ 2328 @Override 2329 public int compare(Object arg0, Object arg1) throws ClassCastException { 2330 // NOTE: This logic indicates which tokens are comparable 2331 // and is replicated in the sortReturnType() method. If 2332 // this changes, then that method should also be changed. 2333 if (arg0 instanceof StringToken && arg1 instanceof StringToken) { 2334 String string0 = ((StringToken) arg0).stringValue(); 2335 String string1 = ((StringToken) arg1).stringValue(); 2336 int result = string0.compareTo(string1); 2337 2338 if (_ascending) { 2339 return result; 2340 } else { 2341 return -1 * result; 2342 } 2343 } else if (arg0 instanceof ScalarToken 2344 && arg1 instanceof ScalarToken 2345 && !(arg0 instanceof ComplexToken) 2346 && !(arg1 instanceof ComplexToken)) { 2347 ScalarToken cast0 = (ScalarToken) arg0; 2348 ScalarToken cast1 = (ScalarToken) arg1; 2349 2350 try { 2351 if (cast0.isEqualTo(cast1).booleanValue()) { 2352 return 0; 2353 } else { 2354 if (cast0.isLessThan(cast1).booleanValue()) { 2355 if (_ascending) { 2356 return -1; 2357 } else { 2358 return 1; 2359 } 2360 } else { 2361 if (_ascending) { 2362 return 1; 2363 } else { 2364 return -1; 2365 } 2366 } 2367 } 2368 } catch (IllegalActionException ex) { 2369 // Fall through to exception below. 2370 } 2371 } 2372 2373 // If we get here, then arguments are not of the correct type. 2374 // NOTE: The error message is appropriate only for the use of 2375 // this inside the sort() methods. 2376 throw new ClassCastException( 2377 "Sorting only works on arrays of strings" 2378 + " or non-complex scalars."); 2379 } 2380 2381 private boolean _ascending; 2382 } 2383}