001/* Matlab Engine Interface 002 003 Copyright (c) 1998-2016 The Regents of the University of California and 004 Research in Motion Limited. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA OR RESEARCH IN MOTION 013 LIMITED BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 014 INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS 015 SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA 016 OR RESEARCH IN MOTION LIMITED HAVE BEEN ADVISED OF THE POSSIBILITY OF 017 SUCH DAMAGE. 018 019 THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION LIMITED 020 SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 022 PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 023 BASIS, AND THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION 024 LIMITED HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 025 ENHANCEMENTS, OR MODIFICATIONS. 026 027 PT_COPYRIGHT_VERSION_2 028 COPYRIGHTENDKEY 029 030 */ 031package ptolemy.matlab; 032 033import ptolemy.data.ArrayToken; 034import ptolemy.data.BooleanToken; 035import ptolemy.data.ComplexMatrixToken; 036import ptolemy.data.ComplexToken; 037import ptolemy.data.DoubleMatrixToken; 038import ptolemy.data.DoubleToken; 039import ptolemy.data.IntMatrixToken; 040import ptolemy.data.IntToken; 041import ptolemy.data.MatrixToken; 042import ptolemy.data.RecordToken; 043import ptolemy.data.ScalarToken; 044import ptolemy.data.StringToken; 045import ptolemy.data.Token; 046import ptolemy.data.expr.UtilityFunctions; 047import ptolemy.kernel.util.IllegalActionException; 048import ptolemy.math.Complex; 049 050/////////////////////////////////////////////////////////////////// 051//// Engine 052 053/** 054 Provides a java API to the matlab environment. It uses an intermediary 055 C++ language layer (ptmatlab) that converts between the java environment 056 using the Java Native Interface and the matlab environment using the 057 matlab engine API and associated mx-functions.<p> 058 059 The intermediary layer is built as a DLL on Windows systems 060 (ptmatlab.dll). This shared library is placed into the $PTII/bin 061 directory (that should be in the user's path) when this package is 062 built. Ptmatlab depends on matlab's engine API shared libraries (libeng 063 and libmx) that should also be installed in the user's path (usually the 064 case when matlab is installed and matlab's bin directory is added to the 065 path).<p> 066 067 The bulk of the work done by this class is the conversion between 068 PtolemyII Tokens and matlab variables ("mxArrays").<p> 069 070 {@link #get(long[] eng, String name)} and 071 {@link ptolemy.matlab.Engine#get(long[], String, 072 Engine.ConversionParameters)} convert a matlab engine mxArray 073 (ma) variable to a Ptolemy II Token. Recursion is used if ma is a struct 074 or cell. The type of the Token returned is determined according to 075 the following table: 076 077 <table border="1"> 078 <caption><em>Conversion from matlab to PtolemyII types (get()) 079 </em></caption> 080 <tr><th>Matlab Type<th>PtolemyII Token 081 <tr> 082 <td>'double' 083 <td>Double, if mxArray dimension is 1x1 and 084 {@link Engine.ConversionParameters#getScalarMatrices} is true, 085 DoubleMatrix otherwise. 086 Complex, if mxArray is mxCOMPLEX, 1x1, and 087 {@link Engine.ConversionParameters#getScalarMatrices} is true, 088 ComplexMatrix otherwise.<br> 089 <em>Note:</em> 090 If {@link Engine.ConversionParameters#getIntMatrices} is true and 091 all matrix double values can be cast to integers without loss of 092 precision then an IntToken or IntTokenMatrix is returned. 093 <tr> 094 <td>'struct' 095 <td>RecordToken, if mxArray dimension 1x1, ArrayToken of ArrayTokens 096 of RecordTokens {{RecordToken,...}, {...}} ("two-dimensional" ArrayToken) 097 otherwise. 098 <tr> 099 <td>'cell' 100 <td>ArrayToken of whatever Tokens the cell elements resolve to through 101 recursion of _convertMxArrayToToken(). In the special case of a cell 102 array of doubles, an {int} is always returned if all cell double 103 values can be losslessly converted to integers. 104 Note that PtolemyII is more 105 restrictive here in that it requires all array elements to be of the 106 same type (not all matlab cell variables may be converted to PtolemyII 107 ArrayTokens). 108 <tr> 109 <td>'char' 110 <td>StringToken, if the mxArray is 1xn, ArrayToken of StringTokens 111 otherwise. 112 </table> 113 <p> 114 {@link #put(long[] eng, String name, Token t)} converts a PtolemyII 115 Token to a matlab engine mxArray. Recursion is used if t is a 116 RecordToken or ArrayToken. The type of mxArray created is determined 117 according to the following table. 118 119 <table border="1"> 120 <caption><em>Conversion from PtolemyII to matlab types (put()) 121 </em></caption> 122 <tr><th>PtolemyII Token<th>Matlab type 123 <tr> 124 <td>ArrayToken 125 <td>'cell', 1xn, elements are determined by recursing this method 126 on ArrayToken elements. 127 <tr> 128 <td>RecordToken 129 <td>'struct', 1x1, fields are determined by recursing this method on 130 RecordToken fields 131 <tr> 132 <td>StringToken 133 <td>'char', 1xn 134 <tr> 135 <td>ComplexMatrixToken 136 <td>'double', mxCOMPLEX, nxm 137 <tr> 138 <td>MatrixToken 139 <td>'double', mxREAL, nxm 140 <tr> 141 <td>ComplexToken 142 <td>'double', mxCOMPLEX, 1x1 143 <tr> 144 <td>ScalarToken 145 <td>'double', mxREAL, 1x1 146 </table> 147 <p> 148 Debug statements to stdout are enabled by calling 149 {@link #setDebugging} with a byte parameter > 0. 1 enables basic tracing, 150 2 includes traces from the dll as well. 151 152 <p>{@link #evalString(long[], String)} send a string to the matlab 153 engine for evaluation. 154 155 {@link #open} and {@link #close} are used to open / close the 156 connection to the matlab engine.<p> 157 158 All callers share the same matlab engine and its workspace. 159 Methods of Engine synchronize on the static {@link #semaphore} to 160 prevent overlapping calls to the same method from different threads. 161 Use Engine. {@link #semaphore} to synchronize across multiple method 162 calls if needed.<p> 163 164 @author Zoltan Kemenczy and Sean Simmons, Research in Motion Limited. 165 @version $Id$ 166 @since Ptolemy II 2.0 167 @Pt.ProposedRating Yellow (zkemenczy) 168 @Pt.AcceptedRating Red (cxh) 169 */ 170public class Engine { 171 /** Load the "ptmatlab" native interface. Use a classpath-relative 172 * pathname without the shared library suffix (which is selected 173 * and appended by {@link UtilityFunctions#loadLibrary}) for 174 * portability. */ 175 static { 176 UtilityFunctions.loadLibrary("ptolemy/matlab/ptmatlab"); 177 } 178 179 /** Output buffer (allocated for each opened instance) size. */ 180 static int engOutputBufferSize = 2048; 181 182 /** Used for synchronization. */ 183 public static final Object semaphore = new Object(); 184 185 // semaphore is public so that javadoc works. 186 // ptolemy.matlab.Expression also uses this semaphore 187 188 /** Data conversion parameters used by {@link 189 * ptolemy.matlab.Engine#get(long[], String, 190 * Engine.ConversionParameters)}. */ 191 public static class ConversionParameters { 192 /** If true (default), 1x1 matrices are returned as 193 * appropriate ScalarToken.*/ 194 public boolean getScalarMatrices = true; 195 196 /** If true, double matrices where all elements represent 197 * integers are returned as IntMatrixTokens (default false).*/ 198 public boolean getIntMatrices = false; 199 } 200 201 /** Construct an instance of the matlab engine interface. 202 * The matlab engine is not activated at this time. 203 * <p> 204 * Ptmatlab.dll is loaded by the system library loader the 205 * first time this class is loaded. 206 * @see #open() 207 */ 208 public Engine() { 209 debug = 0; 210 } 211 212 /** Enable/disable debug statements to stdout. 213 * @param d Non-zero to enable debug statements, zero to disable. 214 */ 215 public void setDebugging(byte d) { 216 debug = d; 217 } 218 219 /** Open a connection to the default matlab engine installed on 220 * this host with its output buffered. 221 * @return long[2] retval engine handle; retval[0] is the real 222 * engine handle, retval[1] is a pointer to the engine output 223 * buffer; both should be preserved and passed to subsequent engine 224 * calls. 225 * @exception IllegalActionException If the matlab engine open is 226 * unsuccessful. This will typically occur if ptmatlab (.dll) 227 * cannot be located or if the matlab bin directory is not in the 228 * path. 229 * @see #open(String, boolean) 230 */ 231 public long[] open() throws IllegalActionException { 232 return open(null, true); // Use default invocation, with 233 234 // output buffering 235 } 236 237 /** Open a connection to the default matlab engine installed on 238 * this host with specified output buffering. 239 * @param needOutput selects whether the output should be buffered 240 * or not. 241 * @return long[2] retval engine handle; retval[0] is the real 242 * engine handle, retval[1] is a pointer to the engine output 243 * buffer; both should be preserved and passed to subsequent engine 244 * calls. 245 * @exception IllegalActionException If the matlab engine open is 246 * unsuccessful. This will typically occur if ptmatlab (.dll) 247 * cannot be located or if the matlab bin directory is not in the 248 * path. 249 * @see #open(String, boolean) 250 */ 251 public long[] open(boolean needOutput) throws IllegalActionException { 252 return open(null, needOutput); // Use default invocation 253 254 // output buffering 255 } 256 257 /** Open a connection to a matlab engine.<p> 258 * For more information, see the matlab engine API reference engOpen() 259 * @param startCmd hostname or command to use to start the engine. 260 * @param needOutput selects whether the output should be buffered 261 * or not. 262 * @return long[2] retval engine handle; retval[0] is the real 263 * engine handle, retval[1] is a pointer to the engine output 264 * buffer; both should be preserved and passed to subsequent engine 265 * calls. 266 * @exception IllegalActionException If the matlab engine open is 267 * unsuccessful. This will typically occur if ptmatlab (.dll) 268 * cannot be located or if the matlab bin directory is not in the 269 * path. 270 * @see #getOutput(long[]) 271 */ 272 public long[] open(String startCmd, boolean needOutput) 273 throws IllegalActionException { 274 long[] retval = new long[2]; 275 276 synchronized (semaphore) { 277 retval[0] = ptmatlabEngOpen(startCmd); 278 279 if (retval[0] == 0) { 280 String Path = ""; 281 try { 282 Path = System.getenv("PATH"); 283 } catch (Throwable throwable) { 284 Path = throwable.toString(); 285 } 286 throw new IllegalActionException("matlabEngine.open(" + startCmd 287 + ") : can't find Matlab engine. " 288 + "The PATH for this process is \"" + Path 289 + "\". Try starting " 290 + "\"matlab\" by hand from a shell to verify that " 291 + "Matlab is set up properly and the license is " 292 + "correct.\n" 293 + "Under Windows, try running \"matlab /regserver\", " 294 + "the Matlab C API communicates with Matlab via COM, " 295 + "and apparently the COM interface is not " 296 + "automatically registered when Matlab is " 297 + "installed.\n" 298 + "Under Mac OS X, 'matlab' must be in the PATH, " 299 + "it may be easiest to create a link from /usr/bin/matlab " 300 + "to the location of the matlab script:\n " 301 + "sudo ln -s /Applications/MATLAB_R2011a.app/bin/matlab /usr/bin/matlab\n" 302 + "Under Linux and other types of UNIX, csh must be " 303 + "installed in /bin/csh."); 304 } 305 306 if (needOutput) { 307 retval[1] = ptmatlabEngOutputBuffer(retval[0], 308 engOutputBufferSize); 309 } // else retval[1] = 0; 310 311 if (debug > 0) { 312 System.out.println(retval[0] + " = matlabEngine.open(\"" 313 + startCmd + "\")"); 314 } 315 } 316 317 return retval; 318 } 319 320 /** Close a connection to a matlab engine. 321 * This will also close the matlab engine if this instance was the last 322 * user of the matlab engine. 323 * <p> 324 * For more information, see matlab engine API reference engClose() 325 * @param eng An array of longs with length 2. eng[0] is the real 326 * engine handle, eng[1] is a pointer to the engine output 327 * buffer. 328 * @return The value returned by calling engClose() in the 329 * Matlab interface. 330 */ 331 public int close(long[] eng) { 332 int retval = 0; 333 334 if (eng == null) { 335 return -1; 336 } 337 338 synchronized (semaphore) { 339 if (debug > 0) { 340 System.out.println("matlabEngine.close(" + eng[0] + ")"); 341 } 342 343 retval = ptmatlabEngClose(eng[0], eng[1]); 344 } 345 346 return retval; 347 } 348 349 /** Copy of a common error message. */ 350 static String errNotOpened = "matlab engine not opened."; 351 352 /** Send a string for evaluation to the matlab engine. 353 * @param eng An array of two longs; eng[0] is the real 354 * engine handle, eng[1] is a pointer to the engine output 355 * buffer. 356 * @param evalStr string to evaluate. 357 * @return The value returned by the ptmatlabEngEvalString() native method. 358 * @exception IllegalActionException If the matlab engine is not opened. 359 */ 360 public int evalString(long[] eng, String evalStr) 361 throws IllegalActionException { 362 int retval; 363 364 synchronized (semaphore) { 365 if (eng == null || eng[0] == 0) { 366 throw new IllegalActionException( 367 "matlabEngine.evalStr(): " + errNotOpened); 368 } 369 370 if (debug > 0) { 371 System.out.println( 372 "matlabEngine.evalString(\"" + evalStr + "\")"); 373 } 374 375 retval = ptmatlabEngEvalString(eng[0], evalStr); 376 } 377 378 return retval; 379 } 380 381 /** Return a Token from the matlab engine using default 382 * {@link Engine.ConversionParameters} values. 383 * @param eng An array of longs with length 2. eng[0] is the real 384 * engine handle, eng[1] is a pointer to the engine output 385 * buffer. 386 * @param name Matlab variable name used to initialize the returned Token 387 * @return PtolemyII Token. 388 * @exception IllegalActionException If the matlab engine is not opened, or 389 * if the matlab variable was not found in the engine. In this case, the 390 * matlab engine's stdout is included in the exception message. 391 * @see Expression 392 */ 393 public Token get(long[] eng, String name) throws IllegalActionException { 394 return get(eng, name, new ConversionParameters()); 395 } 396 397 /** Return a Token from the matlab engine using specified 398 * {@link Engine.ConversionParameters} values. 399 * @param eng An array of longs with length 2. eng[0] is the real 400 * engine handle, eng[1] is a pointer to the engine output 401 * buffer. 402 * @param name Matlab variable name used to initialize the returned Token 403 * @param par The ConversionParameter to use. 404 * @return PtolemyII Token. 405 * @exception IllegalActionException If the matlab engine is not opened, or 406 * if the matlab variable was not found in the engine. In this case, the 407 * matlab engine's stdout is included in the exception message. 408 * @see Expression 409 */ 410 public Token get(long[] eng, String name, ConversionParameters par) 411 throws IllegalActionException { 412 Token retval = null; 413 414 synchronized (semaphore) { 415 if (eng == null || eng[0] == 0) { 416 throw new IllegalActionException( 417 "matlabEngine.get(): " + errNotOpened); 418 } 419 420 long ma = ptmatlabEngGetArray(eng[0], name); 421 422 if (ma == 0) { 423 throw new IllegalActionException("matlabEngine.get(" + name 424 + "): can't find matlab " + "variable \"" + name 425 + "\"\n" + getOutput(eng).stringValue()); 426 } 427 428 retval = _convertMxArrayToToken(ma, par); 429 ptmatlabDestroy(ma, name); 430 431 if (debug > 0) { 432 System.out.println("matlabEngine.get(" + name + ") = " 433 + retval.toString()); 434 } 435 } 436 437 return retval; 438 } 439 440 /** Get last matlab stdout. 441 * @param eng An array of longs with length 2. eng[0] is the real 442 * engine handle, eng[1] is a pointer to the engine output 443 * buffer. 444 * @return PtolemyII StringToken 445 */ 446 public StringToken getOutput(long[] eng) { 447 String str = ""; 448 449 synchronized (semaphore) { 450 if (eng != null && eng[1] != 0) { 451 str = ptmatlabGetOutput(eng[1], engOutputBufferSize); 452 } 453 } 454 455 return new StringToken(str); 456 } 457 458 /** Create a matlab variable using name and a Token. 459 * @param eng An array of longs with length 2. eng[0] is the real 460 * engine handle, eng[1] is a pointer to the engine output 461 * buffer. 462 * @param name matlab variable name. 463 * @param t Token to provide value. 464 * @return The result of calling engPutArray() in the Matlab 465 * C library. 466 * @exception IllegalActionException If the engine is not opened. 467 * @see Engine 468 */ 469 public int put(long[] eng, String name, Token t) 470 throws IllegalActionException { 471 int retval; 472 473 synchronized (semaphore) { 474 if (eng == null || eng[0] == 0) { 475 throw new IllegalActionException( 476 "matlabEngine.put(): " + errNotOpened); 477 } 478 479 if (debug > 0) { 480 System.out.println( 481 "matlabEngine.put(" + name + ", " + t.toString() + ")"); 482 } 483 484 long ma = _createMxArray(name, t); 485 retval = ptmatlabEngPutArray(eng[0], name, ma); 486 ptmatlabDestroy(ma, name); 487 } 488 489 return retval; 490 } 491 492 /////////////////////////////////////////////////////////////////// 493 //// private methods //// 494 // Engine functions - native methods implemented in ptmatlab.cc. 495 private native long ptmatlabEngOpen(String startCmd); 496 497 private native int ptmatlabEngClose(long e, long outputBuffer); 498 499 private native int ptmatlabEngEvalString(long e, String s); 500 501 private native long ptmatlabEngGetArray(long e, String name); 502 503 private native int ptmatlabEngPutArray(long e, String name, long mxArray); 504 505 private native long ptmatlabEngOutputBuffer(long e, int n); 506 507 // C-Mx style functions 508 private native long ptmatlabCreateCellMatrix(String name, int n, int m); 509 510 private native long ptmatlabCreateString(String name, String s, int n, 511 int m); 512 513 private native long ptmatlabCreateDoubleMatrixOneDim(String name, 514 double[] a, int length); 515 516 private native long ptmatlabCreateDoubleMatrix(String name, double[][] a, 517 int n, int m); 518 519 private native long ptmatlabCreateComplexMatrixOneDim(String name, 520 Complex[] a, int length); 521 522 private native long ptmatlabCreateComplexMatrix(String name, Complex[][] a, 523 int n, int m); 524 525 private native long ptmatlabCreateStructMatrix(String name, 526 Object[] fieldNames, int n, int m); 527 528 private native void ptmatlabDestroy(long mxArray, String name); 529 530 private native long ptmatlabGetCell(long mxArray, int n, int m); 531 532 private native String ptmatlabGetClassName(long mxArray); 533 534 private native int[] ptmatlabGetDimensions(long mxArray); 535 536 private native Complex[][] ptmatlabGetComplexMatrix(long mxArray, int n, 537 int m); 538 539 private native double[][] ptmatlabGetDoubleMatrix(long mxArray, int n, 540 int m); 541 542 private native int[][] ptmatlabGetLogicalMatrix(long mxArray, int nRows, 543 int nCols); 544 545 private native String ptmatlabGetFieldNameByNumber(long mxArray, int k); 546 547 private native long ptmatlabGetFieldByNumber(long mxArray, int k, int n, 548 int m); 549 550 private native int ptmatlabGetNumberOfFields(long mxArray); 551 552 private native String ptmatlabGetString(long mxArray, int n); 553 554 private native String ptmatlabGetOutput(long outputBuffer, int n); 555 556 private native boolean ptmatlabIsComplex(long mxArray); 557 558 private native void ptmatlabSetCell(String name, long mxArray, int n, int m, 559 long valueMxArray); 560 561 private native void ptmatlabSetString(String name, long mxArray, int n, 562 String s, int slen); 563 564 private native void ptmatlabSetStructField(String name, long mxArray, 565 String fieldName, int n, int m, long valueMxArray); 566 567 // Converts a matlab engine mxArray (ma) variable to a Ptolemy II Token. 568 // @param ma Pointer to the matlab engine variable's mxArray 569 // structure as a java long. 570 // @return Ptolemy II Token of type that corresponds to ma's type. 571 // @exception IllegalActionException If ma cannot be obtained from 572 // the matlab engine, or if the mxArray type is not one of 573 // 'double', 'struct', 'char' or 'cell', or if not all elements of 574 // an ArrayToken to be created are of the same type. 575 // @see Engine 576 private Token _convertMxArrayToToken(long ma, ConversionParameters par) 577 throws IllegalActionException { 578 String maClassStr = ptmatlabGetClassName(ma); 579 int[] dims = ptmatlabGetDimensions(ma); 580 int nRows = dims[0]; 581 int nCols = dims[1]; 582 boolean scalarStructs = nCols == 1 && nRows == 1; 583 boolean scalarMatrices = nCols == 1 && nRows == 1 584 && par.getScalarMatrices; 585 Token retval = null; 586 587 if (maClassStr.equals("double")) { 588 if (ptmatlabIsComplex(ma)) { 589 Complex[][] a = ptmatlabGetComplexMatrix(ma, nRows, nCols); 590 591 if (a == null) { 592 throw new IllegalActionException( 593 "can't get complex matrix from matlab engine."); 594 } 595 596 if (scalarMatrices) { 597 retval = new ComplexToken(a[0][0]); 598 } else { 599 retval = new ComplexMatrixToken(a); 600 } 601 } else { 602 double[][] a = ptmatlabGetDoubleMatrix(ma, nRows, nCols); 603 604 if (a == null) { 605 throw new IllegalActionException( 606 "can't get double matrix from matlab engine."); 607 } 608 609 if (scalarMatrices) { 610 double tmp = a[0][0]; 611 612 if (_doubleIsInteger(tmp)) { 613 retval = new IntToken((int) tmp); 614 } else { 615 retval = new DoubleToken(tmp); 616 } 617 } else { 618 boolean allIntegers = par.getIntMatrices; 619 620 for (int i = 0; allIntegers && i < a.length; i++) { 621 for (int j = 0; allIntegers && j < a[0].length; j++) { 622 allIntegers &= _doubleIsInteger(a[i][j]); 623 } 624 } 625 626 if (allIntegers) { 627 int[][] tmp = new int[a.length][a[0].length]; 628 629 for (int i = 0; i < a.length; i++) { 630 for (int j = 0; j < a[0].length; j++) { 631 tmp[i][j] = (int) a[i][j]; 632 } 633 } 634 635 retval = new IntMatrixToken(tmp); 636 } else { 637 retval = new DoubleMatrixToken(a); 638 } 639 } 640 } 641 } else if (maClassStr.equals("logical")) { 642 int[][] a = ptmatlabGetLogicalMatrix(ma, nRows, nCols); 643 644 if (a == null) { 645 throw new IllegalActionException( 646 "can't get logical matrix from matlab engine."); 647 } 648 649 if (scalarMatrices) { 650 retval = new IntToken(a[0][0]); 651 } else { 652 retval = new IntMatrixToken(a); 653 } 654 } else if (maClassStr.equals("struct")) { 655 int nfields = ptmatlabGetNumberOfFields(ma); 656 Token[] ta = new Token[nCols]; 657 Token[] tr = new Token[nRows]; 658 String[] fieldNames = new String[nfields]; 659 660 for (int k = 0; k < nfields; k++) { 661 fieldNames[k] = ptmatlabGetFieldNameByNumber(ma, k); 662 } 663 664 Token[] fieldValues = new Token[nfields]; 665 666 for (int n = 0; n < nRows; n++) { 667 for (int m = 0; m < nCols; m++) { 668 for (int k = 0; k < nfields; k++) { 669 long fma = ptmatlabGetFieldByNumber(ma, k, n, m); 670 671 if (fma != 0) { 672 fieldValues[k] = _convertMxArrayToToken(fma, par); 673 } else { 674 throw new IllegalActionException("can't get field " 675 + fieldNames[k] + "from matlab " + "struct " 676 + nRows + "x" + nCols); 677 } 678 } 679 680 ta[m] = new RecordToken(fieldNames, fieldValues); 681 } 682 683 tr[n] = new ArrayToken(ta); 684 } 685 686 if (scalarStructs) { 687 retval = ((ArrayToken) tr[0]).getElement(0); 688 } else { 689 retval = new ArrayToken(tr); 690 } 691 } else if (maClassStr.equals("cell")) { 692 Token[] ta = new Token[nCols]; 693 Token[] tr = new Token[nRows]; 694 695 for (int n = 0; n < nRows; n++) { 696 boolean anyIntegers = false; 697 boolean anyDoubles = false; 698 699 for (int m = 0; m < nCols; m++) { 700 long cma = ptmatlabGetCell(ma, n, m); 701 702 if (cma != 0) { 703 ta[m] = _convertMxArrayToToken(cma, par); 704 705 // Track whether we get mixed types back 706 if (ta[m] instanceof IntToken) { 707 anyIntegers = true; 708 } else if (ta[m] instanceof DoubleToken) { 709 anyDoubles = true; 710 } 711 } // else - throw exception? 712 } 713 714 if (anyIntegers && anyDoubles) { 715 for (int m = 0; m < ta.length; m++) { 716 if (ta[m] instanceof IntToken) { 717 ta[m] = DoubleToken.convert(ta[m]); 718 } 719 } 720 } 721 722 tr[n] = new ArrayToken(ta); 723 724 // If not all tokens are of the same, this will throw 725 // an exception. 726 } 727 728 if (nRows == 1) { 729 retval = tr[0]; 730 } else { 731 retval = new ArrayToken(tr); 732 } 733 } else if (maClassStr.equals("char")) { 734 if (nRows == 1) { 735 retval = new StringToken(ptmatlabGetString(ma, 0)); 736 } else { 737 Token[] ta = new Token[nRows]; 738 739 for (int n = 0; n < nRows; n++) { 740 ta[n] = new StringToken(ptmatlabGetString(ma, n)); 741 } 742 743 retval = new ArrayToken(ta); 744 } 745 } else { 746 throw new IllegalActionException("no support for mxArray class " 747 + maClassStr + " " + dims[0] + " x " + dims[1]); 748 } 749 750 return retval; 751 } 752 753 // Creates (recursively) a matlab engine mxArray given a Ptolemy II Token. 754 // @param name Matlab variable name to be created. 755 // @param t PtolemyII Token providing the value for the variable. 756 // @return Matlab engine mxArray pointer cast to java long. 757 // @exception IllegalActionException If array creation failed, or if the 758 // Token was not one of the types supported by _createMxArray(). 759 // @see Engine 760 private long _createMxArray(String name, Token t) 761 throws IllegalActionException { 762 long ma = 0; 763 764 if (t instanceof ArrayToken) { 765 Token[] ta = ((ArrayToken) t).arrayValue(); 766 767 if (!(ta[0] instanceof StringToken)) { 768 ma = ptmatlabCreateCellMatrix(name, 1, ta.length); 769 770 if (ma == 0) { 771 throw new IllegalActionException( 772 "couldn't create cell " + "array " + name); 773 } 774 775 for (int n = 0; n < ta.length; n++) { 776 long fma = _createMxArray("(" + n + ")", ta[n]); 777 778 if (fma == 0) { 779 throw new IllegalActionException( 780 "couldn't create array for index " + n 781 + " in cell array " + name); 782 } 783 784 ptmatlabSetCell(name, ma, 0, n, fma); 785 } 786 } else { 787 String s = ((StringToken) ta[0]).stringValue(); 788 ma = ptmatlabCreateString(name, s, ta.length, s.length()); 789 790 for (int n = 1; n < ta.length; n++) { 791 s = ((StringToken) ta[n]).stringValue(); 792 ptmatlabSetString(name, ma, n, s, s.length()); 793 } 794 } 795 } else if (t instanceof RecordToken) { 796 Object[] fieldNames = ((RecordToken) t).labelSet().toArray(); 797 ma = ptmatlabCreateStructMatrix(name, fieldNames, 1, 1); 798 799 if (ma == 0) { 800 throw new IllegalActionException( 801 "couldn't create struct " + "array " + name); 802 } 803 804 for (Object fieldName : fieldNames) { 805 Token f = ((RecordToken) t).get((String) fieldName); 806 long fma = _createMxArray((String) fieldName, f); 807 808 if (fma == 0) { 809 throw new IllegalActionException( 810 "couldn't create array for field " + fieldName 811 + " in struct " + name); 812 } 813 814 ptmatlabSetStructField(name, ma, (String) fieldName, 0, 0, fma); 815 } 816 } else if (t instanceof StringToken) { 817 String s = ((StringToken) t).stringValue(); 818 ma = ptmatlabCreateString(name, s, 1, s.length()); 819 } else if (t instanceof ComplexMatrixToken) { 820 Complex[][] a = ((ComplexMatrixToken) t).complexMatrix(); 821 ma = ptmatlabCreateComplexMatrix(name, a, a.length, a[0].length); 822 } else if (t instanceof MatrixToken) { 823 double[][] a = ((MatrixToken) t).doubleMatrix(); 824 ma = ptmatlabCreateDoubleMatrix(name, a, a.length, a[0].length); 825 } else if (t instanceof ComplexToken) { 826 Complex[] a = { ((ComplexToken) t).complexValue() }; 827 ma = ptmatlabCreateComplexMatrixOneDim(name, a, a.length); 828 } else { 829 double[] a = new double[1]; 830 831 if (t instanceof BooleanToken) { 832 a[0] = ((BooleanToken) t).booleanValue() ? 1.0 : 0.0; 833 } else if (t instanceof DoubleToken) { 834 a[0] = ((ScalarToken) t).doubleValue(); 835 } else if (t instanceof IntToken) { 836 a[0] = ((ScalarToken) t).intValue(); 837 } else { 838 throw new IllegalActionException("Token " + t + " is of type " 839 + t.getType() + ", it should be one of " 840 + "ArrayToken, RecordToken, StringToken, ComplexMatrixToken, " 841 + "MatrixToken, ComplexToken, BooleanToken, DoubleToken or IntToken."); 842 } 843 844 ma = ptmatlabCreateDoubleMatrixOneDim(name, a, 1); 845 } 846 847 if (ma == 0) { 848 throw new IllegalActionException( 849 "couldn't create array for " + name); 850 } 851 852 return ma; 853 } 854 855 private boolean _doubleIsInteger(double d) { 856 // FindBugs reports "Test for floating point equality", which 857 // may be ignored here because we really want to know if 858 // the double is equal to the floor of the double. 859 return d == Math.floor(d) && d <= Integer.MAX_VALUE 860 && d >= Integer.MIN_VALUE; 861 } 862 863 /////////////////////////////////////////////////////////////////// 864 //// private variables //// 865 // Debug statements are sent to stdout if non-zero. If 1, only 866 // this class sends debug statements, if 2 then ptmatlab.dll also 867 // sends debug statements. 868 private byte debug = 0; 869}