├── .gitattributes ├── matconsolectl.png ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ └── matlabcontrol │ │ │ └── demo │ │ │ └── logo_128.png │ └── java │ │ ├── matlabcontrol │ │ ├── package-info.java │ │ ├── extensions │ │ │ ├── package-info.java │ │ │ └── MatlabTypeConverter.java │ │ ├── link │ │ │ ├── UnsupportedReturnException.java │ │ │ ├── LinkingException.java │ │ │ ├── MatlabNonNumericMatrix.java │ │ │ ├── MatlabInt8.java │ │ │ ├── MatlabInt32.java │ │ │ ├── MatlabInt64.java │ │ │ ├── MatlabInt16.java │ │ │ ├── MatlabSingle.java │ │ │ ├── MatlabDouble.java │ │ │ ├── UnassignableReturnException.java │ │ │ ├── MatlabLogicalMatrix.java │ │ │ ├── MatlabLogicalFullMatrix.java │ │ │ ├── MatlabMatrix.java │ │ │ ├── MatlabDoubleMatrix.java │ │ │ ├── LinkedOperations.java │ │ │ ├── MatlabLogicalSparseMatrix.java │ │ │ ├── MatlabFunction.java │ │ │ ├── MatlabInt32Array.java │ │ │ ├── MatlabInt8Array.java │ │ │ ├── MatlabInt64Array.java │ │ │ ├── MatlabInt16Array.java │ │ │ ├── MatlabSingleArray.java │ │ │ ├── MatlabDoubleArray.java │ │ │ ├── MatlabType.java │ │ │ ├── MatlabNumericMatrix.java │ │ │ ├── BaseArray.java │ │ │ ├── ClassInfo.java │ │ │ ├── MatlabDoubleFullMatrix.java │ │ │ ├── MatlabVariable.java │ │ │ ├── MatlabDoubleSparseMatrix.java │ │ │ ├── MatlabFunctionHandle.java │ │ │ ├── MatlabReturns.java │ │ │ └── MatlabNumber.java │ │ ├── MatlabConnectionException.java │ │ ├── demo │ │ │ ├── RemoteMain.java │ │ │ ├── DemoMain.java │ │ │ ├── InsideMatlab.java │ │ │ ├── ArrayPanel.java │ │ │ ├── ReturnFormatter.java │ │ │ └── ProxyMethodDescriptor.java │ │ ├── MatlabSession.java │ │ ├── RequestReceiver.java │ │ ├── ProxyFactory.java │ │ ├── ThrowableWrapper.java │ │ ├── JMIWrapperRemote.java │ │ ├── MatlabInvocationException.java │ │ ├── MatlabSessionImpl.java │ │ ├── JMIWrapperRemoteImpl.java │ │ ├── PermissiveSecurityManager.java │ │ ├── LocalMatlabProxyFactory.java │ │ ├── LocalHostRMIHelper.java │ │ ├── LocalMatlabProxy.java │ │ ├── internal │ │ │ └── MatlabRMIClassLoaderSpi.java │ │ ├── MatlabProxyFactory.java │ │ ├── MatlabBroadcaster.java │ │ ├── MatlabOperations.java │ │ ├── MatlabClassLoaderHelper.java │ │ ├── JMIValidator.java │ │ └── RemoteMatlabProxy.java │ │ └── com │ │ └── mathworks │ │ └── jmi │ │ ├── NativeMatlab.java │ │ └── Matlab.java └── test │ └── java │ └── matlabcontrol │ ├── CopyPasteTest.java │ ├── GetAndSetTest.java │ ├── link │ └── ArrayUtilsTest.java │ └── MatlabRequired.java ├── PULL_REQUEST_TEMPLATE.md ├── .github └── workflows │ ├── changelog-print.yml │ ├── gradle-wrapper-validation.yml │ ├── gradle-build.yml │ └── deploy.yml ├── gradle.properties ├── settings.gradle ├── LICENSE ├── gradlew.bat ├── CODE_OF_CONDUCT.md ├── CHANGES.md ├── CONTRIBUTING.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.png binary 3 | *.jar binary 4 | -------------------------------------------------------------------------------- /matconsolectl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diffplug/matconsolectl/HEAD/matconsolectl.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | .gradle/ 4 | *.DS_Store 5 | 6 | .project 7 | .classpath 8 | .settings/ 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diffplug/matconsolectl/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/matlabcontrol/demo/logo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diffplug/matconsolectl/HEAD/src/main/resources/matlabcontrol/demo/logo_128.png -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2015, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/extensions/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2015, DiffPlug 5 | */ 6 | package matlabcontrol.extensions; 7 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | After creating the PR, please add a commit that adds a bullet-point under the `-SNAPSHOT` section of [CHANGES.md](https://github.com/diffplug/matconsolectl/blob/master/CHANGES.md) that includes: 2 | 3 | - [ ] a summary of the change 4 | - [ ] a link to the newly created PR 5 | 6 | This makes it easier for the maintainers to quickly release your changes. 7 | -------------------------------------------------------------------------------- /.github/workflows/changelog-print.yml: -------------------------------------------------------------------------------- 1 | name: changelogPrint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | name: changelogPrint 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: jdk 11 12 | uses: actions/setup-java@v3 13 | with: 14 | java-version: 11 15 | distribution: 'temurin' 16 | - name: gradle caching 17 | uses: gradle/gradle-build-action@v2 18 | - run: ./gradlew changelogPrint 19 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | stable=4.6.0 2 | version=4.7.0-SNAPSHOT 3 | name=matconsolectl 4 | group=com.diffplug.matsim 5 | description=MatConsoleCtl - control MATLAB from Java 6 | org=diffplug 7 | 8 | osgi_manifest_ver=2 9 | 10 | git_url=github.com/diffplug/matconsolectl 11 | maven_group=com.diffplug.matsim 12 | maven_name=matconsolectl 13 | maven_desc=MatConsoleCtl - control MATLAB from Java 14 | javadoc_links= 15 | 16 | license=matconsolectl 17 | 18 | # Build requirements 19 | VER_JAVA=1.6 20 | VER_FINDBUGS=3.0.1 21 | 22 | # Testing 23 | VER_JUNIT=4.12 24 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: "Validate Gradle Wrapper" 2 | on: 3 | push: 4 | paths: 5 | - 'gradlew' 6 | - 'gradlew.bat' 7 | - 'gradle/wrapper/' 8 | pull_request: 9 | paths: 10 | - 'gradlew' 11 | - 'gradlew.bat' 12 | - 'gradle/wrapper/' 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | validation: 19 | name: "Validation" 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | - uses: gradle/wrapper-validation-action@v1 24 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/UnsupportedReturnException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * Thrown if a MATLAB type cannot be returned from MATLAB to Java because it is not supported. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public class UnsupportedReturnException extends RuntimeException { 15 | private static final long serialVersionUID = 0xF500L; 16 | 17 | UnsupportedReturnException(String msg) { 18 | super(msg); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/LinkingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * Issue linking a Java method to a MATLAB function. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public class LinkingException extends RuntimeException { 15 | private static final long serialVersionUID = 0xD500L; 16 | 17 | LinkingException(String msg) { 18 | super(msg); 19 | } 20 | 21 | LinkingException(String msg, Throwable cause) { 22 | super(msg, cause); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabConnectionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | /** 9 | * Represents a failure to connect to MATLAB or make MATLAB available for a connection. 10 | * 11 | * @since 2.0.0 12 | * 13 | * @author Joshua Kaplan 14 | */ 15 | public class MatlabConnectionException extends Exception { 16 | private static final long serialVersionUID = 0xA400L; 17 | 18 | MatlabConnectionException(String msg) { 19 | super(msg); 20 | } 21 | 22 | MatlabConnectionException(String msg, Throwable cause) { 23 | super(msg, cause); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabNonNumericMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | abstract class MatlabNonNumericMatrix extends MatlabMatrix { 15 | /** 16 | * Returns an array that holds the values from the MATLAB matrix. Each call returns a new copy which may be used in 17 | * any manner; modifications to it will have no effect on this instance. 18 | * 19 | * @return array 20 | */ 21 | public T toArray() { 22 | return getBaseArray().toRealArray(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/jmi/NativeMatlab.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package com.mathworks.jmi; 7 | 8 | /** 9 | * A partial stubbed out implementation of the NativeMatlab class with the method's used by matlabcontrol. This stub 10 | * exists so that matlabcontrol can compile against this library. At runtime this library is not referenced, instead the 11 | * jmi.jar on MATLAB's classpath is used. The build script for matlabcontrol intentionally does not include this 12 | * library in the distribution folder it generates. 13 | * 14 | * @author Joshua Kaplan 15 | */ 16 | public class NativeMatlab { 17 | public static boolean nativeIsMatlabThread() { 18 | throw new UnsupportedOperationException("stub"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt8.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * MATLAB {@code int8} with real and imaginary components. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public final class MatlabInt8 extends MatlabNumber { 15 | private static final Byte DEFAULT = 0; 16 | 17 | public MatlabInt8(byte real, byte imag) { 18 | super(DEFAULT, real, imag); 19 | } 20 | 21 | /** 22 | * Returns the real value. 23 | * 24 | * @return 25 | */ 26 | public byte toReal() { 27 | return _real; 28 | } 29 | 30 | /** 31 | * Returns the imaginary value. 32 | * 33 | * @return 34 | */ 35 | public byte toImaginary() { 36 | return _imag; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt32.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * MATLAB {@code int32} with real and imaginary components. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public final class MatlabInt32 extends MatlabNumber { 15 | private static final Integer DEFAULT = 0; 16 | 17 | public MatlabInt32(int real, int imag) { 18 | super(DEFAULT, real, imag); 19 | } 20 | 21 | /** 22 | * Returns the real value. 23 | * 24 | * @return 25 | */ 26 | public int toReal() { 27 | return _real; 28 | } 29 | 30 | /** 31 | * Returns the imaginary value. 32 | * 33 | * @return 34 | */ 35 | public int toImaginary() { 36 | return _imag; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt64.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * MATLAB {@code int64} with real and imaginary components. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public final class MatlabInt64 extends MatlabNumber { 15 | private static final Long DEFAULT = 0L; 16 | 17 | public MatlabInt64(long real, long imag) { 18 | super(DEFAULT, real, imag); 19 | } 20 | 21 | /** 22 | * Returns the real value. 23 | * 24 | * @return 25 | */ 26 | public long toReal() { 27 | return _real; 28 | } 29 | 30 | /** 31 | * Returns the imaginary value. 32 | * 33 | * @return 34 | */ 35 | public long toImaginary() { 36 | return _imag; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt16.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * MATLAB {@code int16} with real and imaginary components. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public final class MatlabInt16 extends MatlabNumber { 15 | private static final Short DEFAULT = 0; 16 | 17 | public MatlabInt16(short real, short imag) { 18 | super(DEFAULT, real, imag); 19 | } 20 | 21 | /** 22 | * Returns the real value. 23 | * 24 | * @return 25 | */ 26 | public short toReal() { 27 | return _real; 28 | } 29 | 30 | /** 31 | * Returns the imaginary value. 32 | * 33 | * @return 34 | */ 35 | public short toImaginary() { 36 | return _imag; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabSingle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * MATLAB {@code single} with real and imaginary components. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public final class MatlabSingle extends MatlabNumber { 15 | private static final Float DEFAULT = 0.0f; 16 | 17 | public MatlabSingle(float real, float imag) { 18 | super(DEFAULT, real, imag); 19 | } 20 | 21 | /** 22 | * Returns the real value. 23 | * 24 | * @return 25 | */ 26 | public float toReal() { 27 | return _real; 28 | } 29 | 30 | /** 31 | * Returns the imaginary value. 32 | * 33 | * @return 34 | */ 35 | public float toImaginary() { 36 | return _imag; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabDouble.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * MATLAB {@code double} with real and imaginary components. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | public final class MatlabDouble extends MatlabNumber { 15 | private static final Double DEFAULT = 0.0d; 16 | 17 | public MatlabDouble(double real, double imag) { 18 | super(DEFAULT, real, imag); 19 | } 20 | 21 | /** 22 | * Returns the real value. 23 | * 24 | * @return 25 | */ 26 | public double toReal() { 27 | return _real; 28 | } 29 | 30 | /** 31 | * Returns the imaginary value. 32 | * 33 | * @return 34 | */ 35 | public double toImaginary() { 36 | return _imag; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/demo/RemoteMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.demo; 7 | 8 | import java.awt.EventQueue; 9 | 10 | import javax.swing.WindowConstants; 11 | 12 | /** 13 | * Launches the demo when running outside MATLAB. 14 | * 15 | * @author Joshua Kaplan 16 | */ 17 | public class RemoteMain { 18 | public static void main(String[] args) { 19 | final String matlabLocation = (args.length == 1 ? args[0] : null); 20 | EventQueue.invokeLater(new Runnable() { 21 | @Override 22 | public void run() { 23 | DemoFrame frame = new DemoFrame("matlabcontrol demo - Running Outside MATLAB", matlabLocation); 24 | frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 25 | frame.setVisible(true); 26 | } 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/UnassignableReturnException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * The type to be returned is not assignable to the return type. This exception is conceptually similar to a 10 | * {@link ClassCastException} except that it is thrown before the cast would ever occur. For primitive return types, 11 | * this is similar to a {@link NullPointerException} being thrown when the return value is {@code null} and therefore 12 | * cannot be un-boxed. 13 | * 14 | * @since 4.2.0 15 | * @author Joshua Kaplan 16 | */ 17 | public class UnassignableReturnException extends RuntimeException { 18 | private static final long serialVersionUID = 0xE500L; 19 | 20 | UnassignableReturnException(String msg) { 21 | super(msg); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/jmi/Matlab.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package com.mathworks.jmi; 7 | 8 | /** 9 | * A partial stubbed out implementation of the Matlab class with the method's used by matlabcontrol. This stub exists 10 | * so that matlabcontrol can compile against this library. At runtime this library is not referenced, instead the 11 | * jmi.jar on MATLAB's classpath is used. The build script for matlabcontrol intentionally does not include this 12 | * library in the distribution folder it generates. 13 | * 14 | * @author Joshua Kaplan 15 | */ 16 | public class Matlab { 17 | public static Object mtFevalConsoleOutput(String function, Object[] args, int nargout) throws Exception { 18 | throw new UnsupportedOperationException("stub"); 19 | } 20 | 21 | public static void whenMatlabIdle(Runnable runnable) { 22 | throw new UnsupportedOperationException("stub"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.rmi.Remote; 9 | import java.rmi.RemoteException; 10 | 11 | /** 12 | * An implementation of this interface is bound to a RMI registry, representing this session of MATLAB. 13 | * 14 | * @since 4.0.0 15 | * 16 | * @author Joshua Kaplan 17 | */ 18 | interface MatlabSession extends Remote { 19 | /** 20 | * Attempts a connection to this session of MATLAB. If this session is available for connection it will send a 21 | * {@link JMIWrapperRemote} to the receiver and {@code true} will be returned. Otherwise {@code false} will be 22 | * returned and no other action will be taken. 23 | * 24 | * @param receiverID 25 | * @param port 26 | * @throws RemoteException 27 | * @return if connection was established 28 | */ 29 | public boolean connectFromRMI(String receiverID, int port) throws RemoteException; 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/gradle-build.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # GitHub recommends pinning actions to a commit SHA. 7 | # To get a newer version, you will need to update the SHA. 8 | # You can also reference a tag or branch, but the action may change without warning. 9 | 10 | name: ci 11 | 12 | on: [push] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | java: ['11'] 20 | name: Java ${{ matrix.java }} 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: jdk ${{ matrix.java }} 24 | uses: actions/setup-java@v3 25 | with: 26 | java-version: ${{ matrix.java }} 27 | distribution: 'temurin' 28 | - name: git fetch origin main 29 | run: git fetch origin main 30 | - name: gradle wrapper 31 | uses: gradle/wrapper-validation-action@v1 32 | - name: gradle caching 33 | uses: gradle/gradle-build-action@v2 34 | - run: ./gradlew check 35 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | plugins { 8 | // https://github.com/diffplug/blowdryer/blob/main/CHANGELOG.md 9 | id 'com.diffplug.blowdryerSetup' version '1.7.0' 10 | // https://github.com/diffplug/spotless/blob/main/plugin-gradle/CHANGES.md 11 | id 'com.diffplug.spotless' version '6.19.0' apply false 12 | // https://github.com/diffplug/spotless-changelog/blob/main/CHANGELOG.md 13 | id 'com.diffplug.spotless-changelog' version '3.0.2' apply false 14 | // https://plugins.gradle.org/plugin/com.gradle.plugin-publish 15 | id 'com.gradle.plugin-publish' version '1.2.0' apply false 16 | // https://github.com/equodev/equo-ide/blob/main/plugin-gradle/CHANGELOG.md 17 | id 'dev.equo.ide' version '1.3.0' apply false 18 | // https://github.com/gradle-nexus/publish-plugin/releases 19 | id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' apply false 20 | } 21 | 22 | blowdryerSetup { 23 | github 'diffplug/blowdryer-diffplug', 'tag', '7.1.0' 24 | //devLocal '../blowdryer-diffplug' 25 | setPluginsBlockTo { 26 | it.file 'plugin.versions' 27 | } 28 | } 29 | 30 | rootProject.name = 'matlabcontrol' -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabLogicalMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | */ 13 | public abstract class MatlabLogicalMatrix extends MatlabNonNumericMatrix { 14 | MatlabLogicalMatrix() {} 15 | 16 | public static MatlabLogicalMatrix getFull(T values) { 17 | return new MatlabLogicalFullMatrix(values); 18 | } 19 | 20 | public static MatlabLogicalMatrix getSparse(int[] rowIndices, int[] colIndices, boolean[] values, 21 | int numRows, int numCols) { 22 | return new MatlabLogicalSparseMatrix(rowIndices, colIndices, values, numRows, numCols); 23 | } 24 | 25 | public abstract boolean getElementAtLinearIndex(int linearIndex); 26 | 27 | public abstract boolean getElementAtIndices(int row, int column); 28 | 29 | public abstract boolean getElementAtIndices(int row, int column, int page); 30 | 31 | public abstract boolean getElementAtIndices(int row, int column, int[] pages); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/demo/DemoMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.demo; 7 | 8 | import java.awt.EventQueue; 9 | import java.net.URL; 10 | import java.net.URLClassLoader; 11 | 12 | import javax.swing.WindowConstants; 13 | 14 | public class DemoMain { 15 | public static void main(String[] args) { 16 | new DemoMain(); 17 | } 18 | 19 | public DemoMain() { 20 | final int closeOperation = isRunningInsideMATLAB() ? WindowConstants.HIDE_ON_CLOSE : WindowConstants.EXIT_ON_CLOSE; 21 | EventQueue.invokeLater(new Runnable() { 22 | @Override 23 | public void run() { 24 | DemoFrame frame = new DemoFrame("matlabcontrol demo - Running Inside MATLAB", null); 25 | frame.setDefaultCloseOperation(closeOperation); 26 | frame.setVisible(true); 27 | } 28 | }); 29 | } 30 | 31 | private static boolean isRunningInsideMATLAB() { 32 | ClassLoader cl = ClassLoader.getSystemClassLoader(); 33 | URL[] urls = ((URLClassLoader) cl).getURLs(); 34 | for (URL url : urls) { 35 | if (url.toExternalForm().contains("matlab")) { 36 | return true; 37 | } 38 | } 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabLogicalFullMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | */ 13 | class MatlabLogicalFullMatrix extends MatlabLogicalMatrix { 14 | private final FullArray _array; 15 | 16 | MatlabLogicalFullMatrix(boolean[] values, int[] dimensions) { 17 | _array = new FullArray(boolean[].class, values, null, dimensions); 18 | } 19 | 20 | MatlabLogicalFullMatrix(T values) { 21 | _array = new FullArray(boolean[].class, values, null); 22 | } 23 | 24 | @Override 25 | BaseArray getBaseArray() { 26 | return _array; 27 | } 28 | 29 | @Override 30 | public boolean getElementAtLinearIndex(int linearIndex) { 31 | return _array._real[linearIndex]; 32 | } 33 | 34 | @Override 35 | public boolean getElementAtIndices(int row, int column) { 36 | return _array._real[_array.getLinearIndex(row, column)]; 37 | } 38 | 39 | @Override 40 | public boolean getElementAtIndices(int row, int column, int page) { 41 | return _array._real[_array.getLinearIndex(row, column, page)]; 42 | } 43 | 44 | @Override 45 | public boolean getElementAtIndices(int row, int column, int[] pages) { 46 | return _array._real[_array.getLinearIndex(row, column, pages)]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * The base class for all MATLAB matrices. 10 | * 11 | * @since 4.2.0 12 | * @author Joshua Kaplan 13 | */ 14 | abstract class MatlabMatrix { 15 | abstract BaseArray getBaseArray(); 16 | 17 | /** 18 | * The number of elements in the array. The real and imaginary components of a number are together considered one 19 | * element. This is equivalent to MATLAB's {@code numel} function. 20 | * 21 | * @return number of elements 22 | */ 23 | public int getNumberOfElements() { 24 | return getBaseArray().getNumberOfDimensions(); 25 | } 26 | 27 | /** 28 | * Returns the length of the dimension specified by {@code dimension}. Dimensions use 0-based indexing. So the 29 | * first dimension, which is dimension 0, is the row length. The second dimension is the column length. The third 30 | * dimension and beyond are pages. 31 | * 32 | * @param dimension 33 | * @return length of {@code dimension} 34 | * @throws IllegalArgumentException if {@code dimension} is not a dimension of the array 35 | */ 36 | public int getLengthOfDimension(int dimension) { 37 | return getBaseArray().getLengthOfDimension(dimension); 38 | } 39 | 40 | /** 41 | * Returns the number of dimensions of the array. 42 | * 43 | * @return number of dimensions 44 | */ 45 | public int getNumberOfDimensions() { 46 | return getBaseArray().getNumberOfDimensions(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/matlabcontrol/CopyPasteTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import matlabcontrol.MatlabProxyFactory.CopyPasteCallback; 9 | 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import org.junit.experimental.categories.Category; 13 | 14 | @Category(MatlabRequired.Interactive.class) 15 | public class CopyPasteTest { 16 | private Runnable runnable; 17 | 18 | @Test 19 | public void testCopyPaste() throws MatlabConnectionException, MatlabInvocationException { 20 | MatlabProxyFactoryOptions.Builder builder = new MatlabProxyFactoryOptions.Builder(); 21 | builder.setCopyPasteCallback(new CopyPasteCallback() { 22 | @Override 23 | public void copyPaste(String matlabCmdsToConnect) { 24 | StringBuilder builder = new StringBuilder(); 25 | builder.append("Copy-paste the following lines into a MATLAB:\n"); 26 | String[] pieces = matlabCmdsToConnect.split(";"); 27 | for (String piece : pieces) { 28 | builder.append(" " + piece.trim() + ";\n"); 29 | } 30 | builder.append("\nWaiting for you to paste."); 31 | runnable = MatlabRequired.Interactive.prompt(builder.toString()); 32 | } 33 | }); 34 | MatlabProxyFactory factory = new MatlabProxyFactory(builder.build()); 35 | MatlabProxy proxy = factory.getProxy(); 36 | try { 37 | runnable.run(); 38 | proxy.eval("disp('connection established')"); 39 | proxy.setVariable("test", "abc"); 40 | Assert.assertEquals("abc", proxy.getVariable("test")); 41 | } finally { 42 | runnable.run(); 43 | proxy.disconnect(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # GH_TOKEN 2 | # NEXUS_USER 3 | # NEXUS_PASS64 (base64 NOTE: `base64` and `openssl base64` failed, had to use Java 4 | # byte[] data = "{{password}}".getBytes(StandardCharsets.UTF_8); 5 | # String encoded = new String(Base64.getEncoder().encode(data), StandardCharsets.UTF_8); 6 | # System.out.println(encoded); 7 | # GPG_PASSPHRASE 8 | # GPG_KEY64 (base64) 9 | # gpg --export-secret-keys --armor KEY_ID | openssl base64 | pbcopy 10 | 11 | name: deploy 12 | on: 13 | workflow_dispatch: 14 | inputs: 15 | to_publish: 16 | description: 'What to publish' 17 | required: true 18 | default: 'all' 19 | type: choice 20 | options: 21 | - all 22 | 23 | jobs: 24 | build: 25 | runs-on: ubuntu-latest 26 | name: deploy 27 | env: 28 | gh_token: ${{ secrets.GH_TOKEN }} 29 | ORG_GRADLE_PROJECT_nexus_user: ${{ secrets.NEXUS_USER }} 30 | ORG_GRADLE_PROJECT_nexus_pass64: ${{ secrets.NEXUS_PASS64 }} 31 | ORG_GRADLE_PROJECT_gpg_passphrase: ${{ secrets.GPG_PASSPHRASE }} 32 | ORG_GRADLE_PROJECT_gpg_key64: ${{ secrets.GPG_KEY64 }} 33 | steps: 34 | - uses: actions/checkout@v3 35 | - name: jdk 11 36 | uses: actions/setup-java@v3 37 | with: 38 | java-version: 11 39 | distribution: 'temurin' 40 | - name: gradle caching 41 | uses: gradle/gradle-build-action@v2 42 | with: 43 | gradle-home-cache-cleanup: true 44 | - name: git fetch origin main 45 | run: git fetch origin main 46 | - name: publish all 47 | if: "${{ github.event.inputs.to_publish == 'all' }}" 48 | run: | 49 | ./gradlew :changelogPush -Prelease=true -Penable_publishing=true --stacktrace --warning-mode all 50 | -------------------------------------------------------------------------------- /src/test/java/matlabcontrol/GetAndSetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import matlabcontrol.link.ArrayUtilsTest; 9 | 10 | import org.junit.AfterClass; 11 | import org.junit.Before; 12 | import org.junit.BeforeClass; 13 | import org.junit.Test; 14 | import org.junit.experimental.categories.Category; 15 | 16 | /** 17 | * 18 | * @author Joshua Kaplan 19 | */ 20 | @Category(MatlabRequired.Headless.class) 21 | public class GetAndSetTest { 22 | private static MatlabProxy proxy; 23 | 24 | @BeforeClass 25 | public static void createProxy() throws MatlabConnectionException { 26 | MatlabProxyFactoryOptions.Builder builder = new MatlabProxyFactoryOptions.Builder(); 27 | builder.setUsePreviouslyControlledSession(true); 28 | MatlabProxyFactory factory = new MatlabProxyFactory(builder.build()); 29 | proxy = factory.getProxy(); 30 | } 31 | 32 | @AfterClass 33 | public static void exitMatlab() throws MatlabInvocationException { 34 | if (proxy != null) { 35 | proxy.disconnect(); 36 | } 37 | } 38 | 39 | @Before 40 | public void clear() throws MatlabInvocationException { 41 | proxy.eval("clear"); 42 | } 43 | 44 | @Test 45 | public void testSetGet() throws MatlabInvocationException { 46 | testCaseSetGet(new boolean[]{true}); 47 | testCaseSetGet(new boolean[]{false}); 48 | 49 | testCaseSetGet(new double[]{1.5}); 50 | 51 | testCaseSetGet("string"); 52 | } 53 | 54 | private void testCaseSetGet(Object set) throws MatlabInvocationException { 55 | proxy.setVariable("a", set); 56 | Object get = proxy.getVariable("a"); 57 | ArrayUtilsTest.assertArraysEqual(set, get); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/RequestReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.rmi.Remote; 9 | import java.rmi.RemoteException; 10 | 11 | /** 12 | * Represents a receiver for a request to create a proxy. The receiver must be bound to an RMI registry, it will be 13 | * bound with the RMI identifier {@link #getReceiverID()}. Necessary to have this interface for RMI. 14 | * 15 | * @since 4.0.0 16 | * 17 | * @author Joshua Kaplan 18 | */ 19 | interface RequestReceiver extends Remote { 20 | /** 21 | * Receives an incoming wrapper around the JMI functionality inside of MATLAB. 22 | *

23 | * This method is to be called by {@link MatlabConnector} running inside of MATLAB's JVM. 24 | * 25 | * @param jmiWrapper 26 | * @param existingSession if the session sending the jmiWrapper was running prior to the request to create the proxy 27 | * @throws RemoteException 28 | */ 29 | public void receiveJMIWrapper(JMIWrapperRemote jmiWrapper, boolean existingSession) throws RemoteException; 30 | 31 | /** 32 | * The identifier of the receiver. 33 | * 34 | * @return 35 | * @throws RemoteException 36 | */ 37 | public String getReceiverID() throws RemoteException; 38 | 39 | /** 40 | * The classpath of the VM the receiver was created in encoded as an RMI codebase. 41 | * 42 | * @return 43 | * @throws RemoteException 44 | */ 45 | public String getClassPathAsRMICodebase() throws RemoteException; 46 | 47 | /** 48 | * The classpath of the VM the receiver was created in encoded as canonical paths. 49 | * 50 | * @return 51 | * @throws RemoteException 52 | */ 53 | public String[] getClassPathAsCanonicalPaths() throws RemoteException; 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/ProxyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import matlabcontrol.MatlabProxyFactory.Request; 9 | import matlabcontrol.MatlabProxyFactory.RequestCallback; 10 | 11 | /** 12 | * A factory which creates instances of {@link MatlabProxy}. 13 | * 14 | * @since 4.0.0 15 | * 16 | * @author Joshua Kaplan 17 | */ 18 | interface ProxyFactory { 19 | /** 20 | * Returns a {@link MatlabProxy}. If a connection cannot be established before the timeout then this method will end 21 | * execution and an exception will be thrown. A timeout can be specified with the options provided to this factory. 22 | * If no timeout was specified, then a default of 180 seconds will be used. 23 | *

24 | * While this method blocks the calling thread until a proxy is created (or the timeout is reached), any number of 25 | * threads may call {@code getProxy()} simultaneously. 26 | * 27 | * @throws MatlabConnectionException 28 | * @return proxy 29 | */ 30 | public MatlabProxy getProxy() throws MatlabConnectionException; 31 | 32 | /** 33 | * Requests a {@link MatlabProxy}. When the proxy has been created it will be provided to the {@code callback}. The 34 | * proxy may be provided to the callback before this method returns. There is no timeout. The returned 35 | * {@link Request} instance provides information about the request and can be used to cancel the request. 36 | *

37 | * This method is non-blocking. Any number of requests may be made simultaneously from the same thread or different 38 | * threads. 39 | * 40 | * @throws MatlabConnectionException 41 | * @return request 42 | */ 43 | public Request requestProxy(RequestCallback callback) throws MatlabConnectionException; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/demo/InsideMatlab.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.demo; 7 | 8 | import java.awt.EventQueue; 9 | import java.net.URL; 10 | import java.net.URLClassLoader; 11 | 12 | import javax.swing.WindowConstants; 13 | 14 | /** 15 | * This class exists solely as a entry point to the demo when running it from inside of MATLAB. By placing it in the 16 | * default package and giving it the name that it has, it means that once the code is added to MATLAB's Java classpath 17 | * then the demo can be launched just by typing {@code matlabcontroldemo}. Typing that will cause the constructor of 18 | * this class to be called. 19 | * 20 | * @author Joshua Kaplan 21 | */ 22 | class InsideMatlab { 23 | public static void main(String[] args) { 24 | ClassLoader cl = ClassLoader.getSystemClassLoader(); 25 | URL[] urls = ((URLClassLoader) cl).getURLs(); 26 | for (URL url : urls) { 27 | System.out.println(url.getFile()); 28 | } 29 | 30 | EventQueue.invokeLater(new Runnable() { 31 | @Override 32 | public void run() { 33 | DemoFrame frame = new DemoFrame("matlabcontrol demo - Running Inside MATLAB", null); 34 | frame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); 35 | frame.setVisible(true); 36 | } 37 | }); 38 | } 39 | 40 | /** 41 | * This method will be called by MATLAB to provide the text for the {@code ans} value. By overriding this method 42 | * in this manner it will cause this method's return value to be used as a status message: 43 | *
44 | 	 * {@code
45 | 	 * >> matlabcontroldemo
46 | 	 * 
47 | 	 * ans =
48 | 	 * 
49 | 	 * matlabcontrol demo launching...
50 | 	 * }
51 | 	 * 
52 | * 53 | * @return 54 | */ 55 | @Override 56 | public String toString() { 57 | return "matlabcontrol demo launching..."; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabDoubleMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | */ 13 | public abstract class MatlabDoubleMatrix extends MatlabNumericMatrix { 14 | MatlabDoubleMatrix() {} 15 | 16 | public static MatlabDoubleMatrix getFull(T real, T imag) { 17 | return new MatlabDoubleFullMatrix(real, imag); 18 | } 19 | 20 | public static MatlabDoubleMatrix getSparse(int[] rowIndices, int[] colIndices, 21 | double[] real, double[] imag, int numRows, int numCols) { 22 | return new MatlabDoubleSparseMatrix(rowIndices, colIndices, real, imag, numRows, numCols); 23 | } 24 | 25 | public abstract double getRealElementAtLinearIndex(int linearIndex); 26 | 27 | public abstract double getImaginaryElementAtLinearIndex(int linearIndex); 28 | 29 | public abstract double getRealElementAtIndices(int row, int column); 30 | 31 | public abstract double getRealElementAtIndices(int row, int column, int page); 32 | 33 | public abstract double getRealElementAtIndices(int row, int column, int[] pages); 34 | 35 | public abstract double getImaginaryElementAtIndices(int row, int column); 36 | 37 | public abstract double getImaginaryElementAtIndices(int row, int column, int page); 38 | 39 | public abstract double getImaginaryElementAtIndices(int row, int column, int[] pages); 40 | 41 | @Override 42 | public abstract MatlabDouble getElementAtLinearIndex(int linearIndex); 43 | 44 | @Override 45 | public abstract MatlabDouble getElementAtIndices(int row, int column); 46 | 47 | @Override 48 | public abstract MatlabDouble getElementAtIndices(int row, int column, int page); 49 | 50 | @Override 51 | public abstract MatlabDouble getElementAtIndices(int row, int column, int... pages); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/LinkedOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import matlabcontrol.MatlabInvocationException; 9 | import matlabcontrol.MatlabOperations; 10 | import matlabcontrol.MatlabProxy; 11 | 12 | /** 13 | * 14 | * @since 4.2.0 15 | * @author Joshua Kaplan 16 | */ 17 | public final class LinkedOperations implements MatlabOperations { 18 | private final MatlabProxy _delegateProxy; 19 | private final MatlabOperations _delegateOperations; 20 | 21 | public LinkedOperations(MatlabProxy proxy) { 22 | _delegateProxy = proxy; 23 | _delegateOperations = Linker.getLinkedMatlabOperations(proxy); 24 | } 25 | 26 | @Override 27 | public void eval(String command) throws MatlabInvocationException { 28 | _delegateOperations.eval(command); 29 | } 30 | 31 | @Override 32 | public Object[] returningEval(String command, int nargout) throws MatlabInvocationException { 33 | return _delegateOperations.returningEval(command, nargout); 34 | } 35 | 36 | @Override 37 | public void feval(String functionName, Object... args) throws MatlabInvocationException { 38 | _delegateOperations.feval(functionName, args); 39 | } 40 | 41 | @Override 42 | public Object[] returningFeval(String functionName, int nargout, Object... args) throws MatlabInvocationException { 43 | return _delegateOperations.returningFeval(functionName, nargout, args); 44 | } 45 | 46 | @Override 47 | public void setVariable(String variableName, Object value) throws MatlabInvocationException { 48 | _delegateOperations.setVariable(variableName, value); 49 | } 50 | 51 | @Override 52 | public Object getVariable(String variableName) throws MatlabInvocationException { 53 | return _delegateOperations.getVariable(variableName); 54 | } 55 | 56 | /** 57 | * The proxy used to communicate with MATLAB. 58 | * 59 | * @return proxy 60 | */ 61 | public MatlabProxy getDelegateProxy() { 62 | return _delegateProxy; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabLogicalSparseMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | */ 13 | class MatlabLogicalSparseMatrix extends MatlabLogicalMatrix { 14 | private final SparseArray _array; 15 | 16 | MatlabLogicalSparseMatrix(int[] linearIndices, int[] rowIndices, int[] colIndices, boolean[] values, 17 | int numRows, int numCols) { 18 | _array = new SparseArray(boolean[].class, linearIndices, rowIndices, colIndices, values, null, 19 | numRows, numCols); 20 | } 21 | 22 | MatlabLogicalSparseMatrix(int[] rowIndices, int[] colIndices, boolean[] values, int numRows, int numCols) { 23 | _array = new SparseArray(boolean[].class, rowIndices, colIndices, values, null, numRows, numCols); 24 | } 25 | 26 | @Override 27 | BaseArray getBaseArray() { 28 | return _array; 29 | } 30 | 31 | private boolean getElementAtSparseIndex(int sparseIndex) { 32 | boolean val = false; 33 | if (sparseIndex >= 0) { 34 | val = _array._realValues[sparseIndex]; 35 | } 36 | 37 | return val; 38 | } 39 | 40 | @Override 41 | public boolean getElementAtLinearIndex(int linearIndex) { 42 | return getElementAtSparseIndex(_array.getSparseIndexForLinearIndex(linearIndex)); 43 | } 44 | 45 | @Override 46 | public boolean getElementAtIndices(int row, int column) { 47 | return getElementAtSparseIndex(_array.getSparseIndexForIndices(row, column)); 48 | } 49 | 50 | @Override 51 | public boolean getElementAtIndices(int row, int column, int page) { 52 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using 3 indices"); 53 | } 54 | 55 | @Override 56 | public boolean getElementAtIndices(int row, int column, int[] pages) { 57 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using " + 58 | (2 + pages.length) + " indices"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/ThrowableWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | /** 9 | * A wrapper around any {@link Throwable} so that it can be sent over RMI without needing the class to be defined in 10 | * the receiving JVM. The stack trace will print as if it were the original throwable. 11 | * 12 | * @since 4.0.0 13 | * 14 | * @author Joshua Kaplan 15 | */ 16 | class ThrowableWrapper extends Throwable { 17 | private static final long serialVersionUID = 0xC500L; 18 | 19 | /** 20 | * The {@code String} representation of the {@code MatlabException} so that this exception can pretend to be a 21 | * {@code MatlabException}. 22 | */ 23 | private final String _toString; 24 | 25 | /** 26 | * Creates a wrapper around {@code innerThrowable} so that when the stack trace is printed it is the same to the 27 | * developer, but can be sent over RMI without the throwable being defined in the other JVM. 28 | * 29 | * @param innerThrowable 30 | */ 31 | ThrowableWrapper(Throwable innerThrowable) { 32 | super(msgFromToString(innerThrowable)); 33 | //Store innerThrowable's toString() value 34 | _toString = innerThrowable.toString(); 35 | 36 | //Set this stack trace to that of the innerThrowable 37 | this.setStackTrace(innerThrowable.getStackTrace()); 38 | 39 | //Store the cause, wrapping it 40 | if (innerThrowable.getCause() != null) { 41 | this.initCause(new ThrowableWrapper(innerThrowable.getCause())); 42 | } 43 | } 44 | 45 | /** 46 | * The innerThrowable seems to not implement getMessage() properly. 47 | * 48 | * So we extract the message from the exception's toString() method. 49 | */ 50 | static String msgFromToString(Throwable e) { 51 | String msg = e.toString(); 52 | int idx = msg.indexOf(':'); 53 | if (idx >= 0 && idx < msg.length() - 1) { 54 | return msg.substring(idx + 1); 55 | } else { 56 | return msg; 57 | } 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return _toString; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | /** 14 | * Information about a MATLAB function. 15 | * 16 | * @since 4.2.0 17 | * @author Joshua Kaplan 18 | */ 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @Target(ElementType.METHOD) 21 | public @interface MatlabFunction { 22 | /** 23 | * Either the name of a MATLAB function or a path to a MATLAB function. The value provided to this element is 24 | * resolved in the following order: 25 | *
    26 | *
  1. Valid MATLAB function name
    27 | * The value will be treated as a function on MATLAB's path. Whether a function with the specified name is 28 | * actually on MATLAB's path will not be confirmed.
  2. 29 | *
  3. Absolute path to an m-file
    30 | * The value will be treated as the location of an m-file. The file's existence will be confirmed.
  4. 31 | *
  5. Relative path to an m-file
    32 | * The value will be treated as the location of an m-file relative to the root directory of the interface which 33 | * declared the method being annotated. For example if the interface is {@code com.example.MyInterface} located 34 | * at {@code /projects/code/numera/com/example/MyInterface.java} then path will be resolved relative to 35 | * {@code /projects/code/numera/}. This path can be resolved properly when both the interface and m-file are 36 | * inside of a zip file such as a jar or war file. The file's existence will be confirmed.
  6. 37 | *
38 | * The validity of this element's value will be determined when the interface containing the method being annotated 39 | * is provided to {@link MatlabFunctionLinker#link(java.lang.Class, matlabcontrol.MatlabProxy)}. 40 | * 41 | * @return 42 | */ 43 | String value(); 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/JMIWrapperRemote.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.rmi.Remote; 9 | import java.rmi.RemoteException; 10 | 11 | /** 12 | * Methods that can be called to control MATLAB except for {@link #checkConnection()}. 13 | *

14 | * All of these methods throw RemoteException. RemoteException will be thrown if something occurs to disrupt the 15 | * communication between this JVM and the one MATLAB is running in. For instance, closing MATLAB will terminate its 16 | * JVM and then all method calls on this proxy will throw exceptions. 17 | *

18 | * For descriptions of what these methods do see the corresponding methods in {@link MatlabProxy}. 19 | * 20 | * @since 4.0.0 21 | * 22 | * @author Joshua Kaplan 23 | */ 24 | interface JMIWrapperRemote extends Remote { 25 | public void exit() throws RemoteException; 26 | 27 | public void setVariable(String variableName, Object value) throws RemoteException, MatlabInvocationException; 28 | 29 | public Object getVariable(String variableName) throws RemoteException, MatlabInvocationException; 30 | 31 | public void eval(String command) throws RemoteException, MatlabInvocationException; 32 | 33 | public Object[] returningEval(String command, int nargout) throws RemoteException, MatlabInvocationException; 34 | 35 | public void feval(String command, Object... args) throws RemoteException, MatlabInvocationException; 36 | 37 | public Object[] returningFeval(String command, int nargout, Object... args) throws RemoteException, MatlabInvocationException; 38 | 39 | public U invokeAndWait(MatlabProxy.MatlabThreadCallable callable) throws RemoteException, MatlabInvocationException; 40 | 41 | /** 42 | * This method does nothing. It is used internally to check if a connection is still active via calling this method 43 | * and seeing if it throws a {@code RemoteException} (if it does, the connection is no longer active). 44 | * 45 | * @throws RemoteException 46 | */ 47 | public void checkConnection() throws RemoteException; 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabInvocationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | /** 9 | * Represents a failure to invoke a method on the MATLAB session. 10 | * 11 | * @since 3.0.0 12 | * 13 | * @author Joshua Kaplan 14 | */ 15 | public class MatlabInvocationException extends Exception { 16 | private static final long serialVersionUID = 0xB400L; 17 | 18 | static enum Reason { 19 | INTERRRUPTED("Method could not be completed because the MATLAB thread was interrupted before MATLAB returned"), PROXY_NOT_CONNECTED("The proxy is not connected to MATLAB"), UNMARSHAL("Object attempting to be received cannot be transferred between Java Virtual Machines"), MARSHAL("Object attempting to be sent cannot be transferred between Java Virtual Machines"), INTERNAL_EXCEPTION("Method did not return properly because of an internal MATLAB exception"), NARGOUT_MISMATCH("Number of arguments returned did not match excepted"), EVENT_DISPATCH_THREAD("Issue pumping Event Dispatch Thread"), RUNTIME_EXCEPTION("RuntimeException occurred in MatlabThreadCallable, see cause for more information"), UNKNOWN("Method could not be invoked for an unknown reason, see cause for more information"); 20 | 21 | private final String _message; 22 | 23 | private Reason(String msg) { 24 | _message = msg; 25 | } 26 | 27 | MatlabInvocationException asException() { 28 | return new MatlabInvocationException(_message); 29 | } 30 | 31 | MatlabInvocationException asException(Throwable cause) { 32 | return new MatlabInvocationException(_message, cause); 33 | } 34 | 35 | MatlabInvocationException asException(String additionalInfo) { 36 | return new MatlabInvocationException(_message + ": " + additionalInfo); 37 | } 38 | 39 | MatlabInvocationException asException(String additionalInfo, Throwable cause) { 40 | return new MatlabInvocationException(_message + ": " + additionalInfo, cause); 41 | } 42 | } 43 | 44 | private MatlabInvocationException(String msg) { 45 | super(msg); 46 | } 47 | 48 | private MatlabInvocationException(String msg, Throwable cause) { 49 | super(msg, cause); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt32Array.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 12 | * @since 4.2.0 13 | * @author Joshua Kaplan 14 | * @param {@code int} array type, ex. {@code int[]}, {@code int[][]}, {@code int[][][]}, ... 15 | */ 16 | public class MatlabInt32Array extends MatlabNumberArray { 17 | MatlabInt32Array(int[] real, int[] imag, int[] lengths) { 18 | super(int[].class, real, imag, lengths); 19 | } 20 | 21 | public static MatlabInt32Array getInstance(T real, T imaginary) { 22 | return new MatlabInt32Array(real, imaginary); 23 | } 24 | 25 | private MatlabInt32Array(T real, T imaginary) { 26 | super(int[].class, real, imaginary); 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 33 | */ 34 | @Override 35 | public MatlabInt32 getElementAtLinearIndex(int index) { 36 | return new MatlabInt32(_real[index], (_imag == null ? 0 : _imag[index])); 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | * 42 | * @throws IllegalArgumentException {@inheritDoc} 43 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 44 | */ 45 | @Override 46 | public MatlabInt32 getElementAtIndices(int row, int column, int... pages) { 47 | int linearIndex = getLinearIndex(row, column, pages); 48 | 49 | return new MatlabInt32(_real[linearIndex], (_imag == null ? 0 : _imag[linearIndex])); 50 | } 51 | 52 | @Override 53 | boolean equalsRealArray(int[] other) { 54 | return Arrays.equals(_real, other); 55 | } 56 | 57 | @Override 58 | boolean equalsImaginaryArray(int[] other) { 59 | return Arrays.equals(_imag, other); 60 | } 61 | 62 | @Override 63 | int hashReal() { 64 | return Arrays.hashCode(_real); 65 | } 66 | 67 | @Override 68 | int hashImaginary() { 69 | return Arrays.hashCode(_imag); 70 | } 71 | 72 | @Override 73 | boolean containsNonZero(int[] array) { 74 | boolean contained = false; 75 | 76 | for (int val : array) { 77 | if (val != 0) { 78 | contained = true; 79 | break; 80 | } 81 | } 82 | 83 | return contained; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt8Array.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 12 | * @since 4.2.0 13 | * @author Joshua Kaplan 14 | * @param {@code byte} array type, ex. {@code byte[]}, {@code byte[][]}, {@code byte[][][]}, ... 15 | */ 16 | public class MatlabInt8Array extends MatlabNumberArray { 17 | MatlabInt8Array(byte[] real, byte[] imag, int[] lengths) { 18 | super(byte[].class, real, imag, lengths); 19 | } 20 | 21 | public static MatlabInt8Array getInstance(T real, T imaginary) { 22 | return new MatlabInt8Array(real, imaginary); 23 | } 24 | 25 | private MatlabInt8Array(T real, T imaginary) { 26 | super(byte[].class, real, imaginary); 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 33 | */ 34 | @Override 35 | public MatlabInt8 getElementAtLinearIndex(int index) { 36 | return new MatlabInt8(_real[index], (_imag == null ? 0 : _imag[index])); 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | * 42 | * @throws IllegalArgumentException {@inheritDoc} 43 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 44 | */ 45 | @Override 46 | public MatlabInt8 getElementAtIndices(int row, int column, int... pages) { 47 | int linearIndex = getLinearIndex(row, column, pages); 48 | 49 | return new MatlabInt8(_real[linearIndex], (_imag == null ? 0 : _imag[linearIndex])); 50 | } 51 | 52 | @Override 53 | boolean equalsRealArray(byte[] other) { 54 | return Arrays.equals(_real, other); 55 | } 56 | 57 | @Override 58 | boolean equalsImaginaryArray(byte[] other) { 59 | return Arrays.equals(_imag, other); 60 | } 61 | 62 | @Override 63 | int hashReal() { 64 | return Arrays.hashCode(_real); 65 | } 66 | 67 | @Override 68 | int hashImaginary() { 69 | return Arrays.hashCode(_imag); 70 | } 71 | 72 | @Override 73 | boolean containsNonZero(byte[] array) { 74 | boolean contained = false; 75 | 76 | for (byte val : array) { 77 | if (val != 0) { 78 | contained = true; 79 | break; 80 | } 81 | } 82 | 83 | return contained; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabSessionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.rmi.registry.Registry; 9 | import java.util.UUID; 10 | 11 | /** 12 | * Implementation of {@link MatlabSession}. Split into interface and implementation to work properly with RMI. 13 | * 14 | * @since 4.0.0 15 | * 16 | * @author Joshua Kaplan 17 | */ 18 | class MatlabSessionImpl implements MatlabSession { 19 | /** 20 | * The prefix for all RMI names of bound instances of {@link MatlabSession}. 21 | */ 22 | private static final String MATLAB_SESSION_PREFIX = "MATLAB_SESSION_"; 23 | 24 | /** 25 | * The bound name in the RMI registry for this instance. 26 | */ 27 | private final String SESSION_ID = MATLAB_SESSION_PREFIX + UUID.randomUUID().toString(); 28 | 29 | @Override 30 | public synchronized boolean connectFromRMI(String receiverID, int port) { 31 | boolean success = false; 32 | if (MatlabConnector.isAvailableForConnection()) { 33 | MatlabConnector.connect(receiverID, port, true); 34 | success = true; 35 | } 36 | 37 | return success; 38 | } 39 | 40 | /** 41 | * The unique identifier for this session. 42 | * 43 | * @return 44 | */ 45 | String getSessionID() { 46 | return SESSION_ID; 47 | } 48 | 49 | /** 50 | * Attempts to connect to a running instance of MATLAB. Returns {@code true} if a connection was made, 51 | * {@code false} otherwise. 52 | * 53 | * @param receiverID 54 | * @param port 55 | * @return if connection was made 56 | */ 57 | static boolean connectToRunningSession(String receiverID, int port) { 58 | boolean establishedConnection = false; 59 | 60 | try { 61 | Registry registry = LocalHostRMIHelper.getRegistry(port); 62 | 63 | String[] remoteNames = registry.list(); 64 | for (String name : remoteNames) { 65 | if (name.startsWith(MATLAB_SESSION_PREFIX)) { 66 | MatlabSession session = (MatlabSession) registry.lookup(name); 67 | if (session.connectFromRMI(receiverID, port)) { 68 | establishedConnection = true; 69 | break; 70 | } 71 | } 72 | } 73 | } catch (Exception e) {} 74 | 75 | return establishedConnection; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt64Array.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 12 | * @since 4.2.0 13 | * @author Joshua Kaplan 14 | * @param {@code long} array type, ex. {@code long[]}, {@code long[][]}, {@code long[][][]}, ... 15 | */ 16 | public class MatlabInt64Array extends MatlabNumberArray { 17 | MatlabInt64Array(long[] real, long[] imag, int[] lengths) { 18 | super(long[].class, real, imag, lengths); 19 | } 20 | 21 | public static MatlabInt64Array getInstance(T real, T imaginary) { 22 | return new MatlabInt64Array(real, imaginary); 23 | } 24 | 25 | private MatlabInt64Array(T real, T imaginary) { 26 | super(long[].class, real, imaginary); 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 33 | */ 34 | @Override 35 | public MatlabInt64 getElementAtLinearIndex(int index) { 36 | return new MatlabInt64(_real[index], (_imag == null ? 0 : _imag[index])); 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | * 42 | * @throws IllegalArgumentException {@inheritDoc} 43 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 44 | */ 45 | @Override 46 | public MatlabInt64 getElementAtIndices(int row, int column, int... pages) { 47 | int linearIndex = getLinearIndex(row, column, pages); 48 | 49 | return new MatlabInt64(_real[linearIndex], (_imag == null ? 0 : _imag[linearIndex])); 50 | } 51 | 52 | @Override 53 | boolean equalsRealArray(long[] other) { 54 | return Arrays.equals(_real, other); 55 | } 56 | 57 | @Override 58 | boolean equalsImaginaryArray(long[] other) { 59 | return Arrays.equals(_imag, other); 60 | } 61 | 62 | @Override 63 | int hashReal() { 64 | return Arrays.hashCode(_real); 65 | } 66 | 67 | @Override 68 | int hashImaginary() { 69 | return Arrays.hashCode(_imag); 70 | } 71 | 72 | @Override 73 | boolean containsNonZero(long[] array) { 74 | boolean contained = false; 75 | 76 | for (long val : array) { 77 | if (val != 0L) { 78 | contained = true; 79 | break; 80 | } 81 | } 82 | 83 | return contained; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabInt16Array.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 12 | * @since 4.2.0 13 | * @author Joshua Kaplan 14 | * @param {@code short} array type, ex. {@code short[]}, {@code short[][]}, {@code short[][][]}, ... 15 | */ 16 | public class MatlabInt16Array extends MatlabNumberArray { 17 | MatlabInt16Array(short[] real, short[] imag, int[] lengths) { 18 | super(short[].class, real, imag, lengths); 19 | } 20 | 21 | public static MatlabInt16Array getInstance(T real, T imaginary) { 22 | return new MatlabInt16Array(real, imaginary); 23 | } 24 | 25 | private MatlabInt16Array(T real, T imaginary) { 26 | super(short[].class, real, imaginary); 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 33 | */ 34 | @Override 35 | public MatlabInt16 getElementAtLinearIndex(int index) { 36 | return new MatlabInt16(_real[index], (_imag == null ? 0 : _imag[index])); 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | * 42 | * @throws IllegalArgumentException {@inheritDoc} 43 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 44 | */ 45 | @Override 46 | public MatlabInt16 getElementAtIndices(int row, int column, int... pages) { 47 | int linearIndex = getLinearIndex(row, column, pages); 48 | 49 | return new MatlabInt16(_real[linearIndex], (_imag == null ? 0 : _imag[linearIndex])); 50 | } 51 | 52 | @Override 53 | boolean equalsRealArray(short[] other) { 54 | return Arrays.equals(_real, other); 55 | } 56 | 57 | @Override 58 | boolean equalsImaginaryArray(short[] other) { 59 | return Arrays.equals(_imag, other); 60 | } 61 | 62 | @Override 63 | int hashReal() { 64 | return Arrays.hashCode(_real); 65 | } 66 | 67 | @Override 68 | int hashImaginary() { 69 | return Arrays.hashCode(_imag); 70 | } 71 | 72 | @Override 73 | boolean containsNonZero(short[] array) { 74 | boolean contained = false; 75 | 76 | for (short val : array) { 77 | if (val != 0) { 78 | contained = true; 79 | break; 80 | } 81 | } 82 | 83 | return contained; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabSingleArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 12 | * @since 4.2.0 13 | * @author Joshua Kaplan 14 | * @param {@code float} array type, ex. {@code float[]}, {@code float[][]}, {@code float[][][]}, ... 15 | */ 16 | public class MatlabSingleArray extends MatlabNumberArray { 17 | MatlabSingleArray(float[] real, float[] imag, int[] lengths) { 18 | super(float[].class, real, imag, lengths); 19 | } 20 | 21 | public static MatlabSingleArray getInstance(T real, T imaginary) { 22 | return new MatlabSingleArray(real, imaginary); 23 | } 24 | 25 | private MatlabSingleArray(T real, T imaginary) { 26 | super(float[].class, real, imaginary); 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 33 | */ 34 | @Override 35 | public MatlabSingle getElementAtLinearIndex(int index) { 36 | return new MatlabSingle(_real[index], (_imag == null ? 0 : _imag[index])); 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | * 42 | * @throws IllegalArgumentException {@inheritDoc} 43 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 44 | */ 45 | @Override 46 | public MatlabSingle getElementAtIndices(int row, int column, int... pages) { 47 | int linearIndex = getLinearIndex(row, column, pages); 48 | 49 | return new MatlabSingle(_real[linearIndex], (_imag == null ? 0 : _imag[linearIndex])); 50 | } 51 | 52 | @Override 53 | boolean equalsRealArray(float[] other) { 54 | return Arrays.equals(_real, other); 55 | } 56 | 57 | @Override 58 | boolean equalsImaginaryArray(float[] other) { 59 | return Arrays.equals(_imag, other); 60 | } 61 | 62 | @Override 63 | int hashReal() { 64 | return Arrays.hashCode(_real); 65 | } 66 | 67 | @Override 68 | int hashImaginary() { 69 | return Arrays.hashCode(_imag); 70 | } 71 | 72 | @Override 73 | boolean containsNonZero(float[] array) { 74 | boolean contained = false; 75 | 76 | for (float val : array) { 77 | if (val != 0.0f) { 78 | contained = true; 79 | break; 80 | } 81 | } 82 | 83 | return contained; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabDoubleArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 12 | * @since 4.2.0 13 | * @author Joshua Kaplan 14 | * @param {@code double} array type, ex. {@code double[]}, {@code double[][]}, {@code double[][][]}, ... 15 | */ 16 | public class MatlabDoubleArray extends MatlabNumberArray { 17 | MatlabDoubleArray(double[] real, double[] imag, int[] lengths) { 18 | super(double[].class, real, imag, lengths); 19 | } 20 | 21 | public static MatlabDoubleArray getInstance(T real, T imaginary) { 22 | return new MatlabDoubleArray(real, imaginary); 23 | } 24 | 25 | private MatlabDoubleArray(T real, T imaginary) { 26 | super(double[].class, real, imaginary); 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 33 | */ 34 | @Override 35 | public MatlabDouble getElementAtLinearIndex(int index) { 36 | return new MatlabDouble(_real[index], (_imag == null ? 0 : _imag[index])); 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | * 42 | * @throws IllegalArgumentException {@inheritDoc} 43 | * @throws ArrayIndexOutOfBoundsException {@inheritDoc} 44 | */ 45 | @Override 46 | public MatlabDouble getElementAtIndices(int row, int column, int... pages) { 47 | int linearIndex = getLinearIndex(row, column, pages); 48 | 49 | return new MatlabDouble(_real[linearIndex], (_imag == null ? 0 : _imag[linearIndex])); 50 | } 51 | 52 | @Override 53 | boolean equalsRealArray(double[] other) { 54 | return Arrays.equals(_real, other); 55 | } 56 | 57 | @Override 58 | boolean equalsImaginaryArray(double[] other) { 59 | return Arrays.equals(_imag, other); 60 | } 61 | 62 | @Override 63 | int hashReal() { 64 | return Arrays.hashCode(_real); 65 | } 66 | 67 | @Override 68 | int hashImaginary() { 69 | return Arrays.hashCode(_imag); 70 | } 71 | 72 | @Override 73 | boolean containsNonZero(double[] array) { 74 | boolean contained = false; 75 | 76 | for (double val : array) { 77 | if (val != 0.0d) { 78 | contained = true; 79 | break; 80 | } 81 | } 82 | 83 | return contained; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/JMIWrapperRemoteImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.rmi.RemoteException; 9 | 10 | /** 11 | * Passes method calls off to {@link JMIWrapper}. 12 | *

13 | * Methods called on this proxy will be performed inside of the JVM that created this object. This class is only created 14 | * inside of the MATLAB's JVM and so {@code JMIWrapper}'s calls will be able to communicate with MATLAB. 15 | *

16 | * These methods are documented in {@link MatlabProxy}. 17 | * 18 | * @since 4.0.0 19 | * 20 | * @author Joshua Kaplan 21 | */ 22 | class JMIWrapperRemoteImpl extends LocalHostRMIHelper.LocalHostRemoteObject implements JMIWrapperRemote { 23 | private static final long serialVersionUID = 6263244863419922018L; 24 | 25 | public JMIWrapperRemoteImpl() throws RemoteException {} 26 | 27 | @Override 28 | public void exit() { 29 | JMIWrapper.exit(); 30 | } 31 | 32 | @Override 33 | public void eval(String command) throws MatlabInvocationException { 34 | JMIWrapper.eval(command); 35 | } 36 | 37 | @Override 38 | public Object[] returningEval(String command, int nargout) throws MatlabInvocationException { 39 | return JMIWrapper.returningEval(command, nargout); 40 | } 41 | 42 | @Override 43 | public void feval(String command, Object... args) throws MatlabInvocationException { 44 | JMIWrapper.feval(command, args); 45 | } 46 | 47 | @Override 48 | public Object[] returningFeval(String command, int nargout, Object... args) throws MatlabInvocationException { 49 | return JMIWrapper.returningFeval(command, nargout, args); 50 | } 51 | 52 | @Override 53 | public void setVariable(String variableName, Object value) throws MatlabInvocationException { 54 | JMIWrapper.setVariable(variableName, value); 55 | } 56 | 57 | @Override 58 | public Object getVariable(String variableName) throws MatlabInvocationException { 59 | return JMIWrapper.getVariable(variableName); 60 | } 61 | 62 | @Override 63 | public T invokeAndWait(MatlabProxy.MatlabThreadCallable callable) throws MatlabInvocationException { 64 | return JMIWrapper.invokeAndWait(callable); 65 | } 66 | 67 | @Override 68 | public void checkConnection() {} 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/PermissiveSecurityManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.security.Permission; 9 | 10 | /** 11 | * A {@code SecurityManager} that always permits an action to take place. By default a Java application has no 12 | * {@code SecurityManager} set (although Java applets do). This security manager acts as if there was no security 13 | * manager in place. matlabcontrol uses Remote Method Invocation (RMI) to communicate with MATLAB when it is used in an 14 | * application that is not running inside MATLAB. RMI requires a security manager be set in order to allow loading 15 | * classes that are defined in the other Java Virtual Machine, but not its own. This is for good reason, because 16 | * allowing arbitrary code to be loaded into an application has the potential for a security exploit. By default RMI 17 | * allows connections from any external machine unless otherwise configured (or blocked by a firewall). matlabcontrol 18 | * is configured to prohibit any external connections on the port it is using. 19 | *

20 | * When matlabcontrol launches a session of MATLAB it installs this security manager so that MATLAB may load classes 21 | * defined in your application. matlabcontrol does not install this security manager in your program. Installing any 22 | * security manager will allow your application to receive objects from MATLAB that are of classes defined in MATLAB, 23 | * but not in your application. Using this security manager is convenient when your application does not need any 24 | * security beyond the default of having no security manager installed. 25 | *

26 | * To install this security manager: 27 | *
28 |  * {@code
29 |  * System.setSecurityManager(new PermissiveSecurityManager());
30 |  * }
31 |  * 
32 | * 33 | * @since 4.0.0 34 | * 35 | * @author Joshua Kaplan 36 | */ 37 | public class PermissiveSecurityManager extends SecurityManager { 38 | /** 39 | * Always accepts permission request. 40 | * 41 | * @param perm 42 | */ 43 | @Override 44 | public void checkPermission(Permission perm) {} 45 | 46 | /** 47 | * Always accepts permission request. 48 | * 49 | * @param perm 50 | * @param context 51 | */ 52 | @Override 53 | public void checkPermission(Permission perm, Object context) {} 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.io.Serializable; 9 | 10 | import matlabcontrol.MatlabInvocationException; 11 | import matlabcontrol.MatlabOperations; 12 | 13 | /** 14 | * Superclass of all Java classes which represent MATLAB types. 15 | *

16 | * Subclasses of this either final or abstract and non-extenable with final subclasses of their own. Being final makes 17 | * it easier to ensure appropriate behavior when transforming types automatically. Subclasses are not 18 | * {@link Serializable} to reduce the publicly exposed API and reduce the need to maintain serializable compatibility. 19 | * Instead, transferring occurs by use of {@link MatlabTypeSetter} and {@link MatlabTypeGetter}. A getter is typically 20 | * associated with a class as an inner static class. 21 | * 22 | * @since 4.2.0 23 | * @author Joshua Kaplan 24 | */ 25 | abstract class MatlabType { 26 | MatlabType() {} 27 | 28 | abstract MatlabTypeSetter getSetter(); 29 | 30 | /** 31 | * Retrieves in MATLAB the information necessary to create the associated {@code MatlabType} from a given MATLAB 32 | * variable. 33 | *

34 | * Must have an accessible no argument constructor. 35 | * 36 | * @param 37 | */ 38 | static interface MatlabTypeGetter extends Serializable { 39 | /** 40 | * Takes the information retrieved by the 41 | * {@link #getInMatlab(matlabcontrol.MatlabOperations, java.lang.String)} and creates the 42 | * associated {@code MatlabType}. 43 | * 44 | * @return 45 | */ 46 | public T retrieve(); 47 | 48 | /** 49 | * Retrieves the data it needs from the variable in MATLAB. So that after retrieving this information 50 | * {@link #retrieve()} can be called to create the appropriate {@code MatlabType}. 51 | * 52 | * @param ops 53 | * @param variableName 54 | */ 55 | public void getInMatlab(MatlabOperations ops, String variableName) throws MatlabInvocationException; 56 | } 57 | 58 | /** 59 | * Sets in MATLAB the equivalent of the data represented by the {@code MatlabType} that provides an instance of 60 | * an implementation of this class. 61 | */ 62 | static interface MatlabTypeSetter extends Serializable { 63 | public void setInMatlab(MatlabOperations ops, String variableName) throws MatlabInvocationException; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/LocalMatlabProxyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | import matlabcontrol.MatlabProxy.Identifier; 11 | import matlabcontrol.MatlabProxyFactory.Request; 12 | import matlabcontrol.MatlabProxyFactory.RequestCallback; 13 | 14 | /** 15 | * Creates local instances of {@link MatlabProxy}. 16 | * 17 | * @since 4.0.0 18 | * 19 | * @author Joshua Kaplan 20 | */ 21 | class LocalMatlabProxyFactory implements ProxyFactory { 22 | public LocalMatlabProxyFactory(MatlabProxyFactoryOptions options) {} 23 | 24 | @Override 25 | public LocalMatlabProxy getProxy() throws MatlabConnectionException { 26 | JMIValidator.validateJMIMethods(); 27 | 28 | return new LocalMatlabProxy(new LocalIdentifier()); 29 | } 30 | 31 | @Override 32 | public Request requestProxy(RequestCallback requestCallback) throws MatlabConnectionException { 33 | LocalMatlabProxy proxy = getProxy(); 34 | requestCallback.proxyCreated(proxy); 35 | 36 | return new LocalRequest(proxy.getIdentifier()); 37 | } 38 | 39 | private static final class LocalIdentifier implements Identifier { 40 | private static final AtomicInteger PROXY_CREATION_COUNTER = new AtomicInteger(); 41 | 42 | private final int _id = PROXY_CREATION_COUNTER.getAndIncrement(); 43 | 44 | @Override 45 | public boolean equals(Object other) { 46 | boolean equals; 47 | 48 | if (other instanceof LocalIdentifier) { 49 | equals = (((LocalIdentifier) other)._id == _id); 50 | } else { 51 | equals = false; 52 | } 53 | 54 | return equals; 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return _id; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "PROXY_LOCAL_" + _id; 65 | } 66 | } 67 | 68 | private static final class LocalRequest implements Request { 69 | private final Identifier _proxyID; 70 | 71 | private LocalRequest(Identifier proxyID) { 72 | _proxyID = proxyID; 73 | } 74 | 75 | @Override 76 | public Identifier getProxyIdentifer() { 77 | return _proxyID; 78 | } 79 | 80 | @Override 81 | public boolean cancel() { 82 | return false; 83 | } 84 | 85 | @Override 86 | public boolean isCancelled() { 87 | return false; 88 | } 89 | 90 | @Override 91 | public boolean isCompleted() { 92 | return true; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabNumericMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | */ 13 | abstract class MatlabNumericMatrix extends MatlabMatrix { 14 | /** 15 | * Returns {@code true} if the array has no imaginary values, {@code false} otherwise. Equivalent to the MATLAB 16 | * {@code isreal} function. 17 | * 18 | * @return 19 | */ 20 | public boolean isReal() { 21 | return getBaseArray().isReal(); 22 | } 23 | 24 | /** 25 | * Returns an array that holds the real values from the MATLAB array. Each call returns a new copy which may be used 26 | * in any manner; modifications to it will have no effect on this instance. 27 | * 28 | * @return real array 29 | */ 30 | public T toRealArray() { 31 | return getBaseArray().toRealArray(); 32 | } 33 | 34 | /** 35 | * Returns an array that holds the imaginary values from the MATLAB array. Each call returns a new copy which may be 36 | * used in any manner; modifications to it will have no effect on this instance. If this array is real then the 37 | * returned array will be have {@code 0} as all of its base elements. 38 | * 39 | * @return imaginary array 40 | */ 41 | public T toImaginaryArray() { 42 | return getBaseArray().toImaginaryArray(); 43 | } 44 | 45 | /** 46 | * Gets the element at {@code index} treating this array as a MATLAB column vector. This is equivalent to indexing 47 | * into a MATLAB array with just one subscript. 48 | * 49 | * @param index 50 | * @return element at {@code index} 51 | * @throws ArrayIndexOutOfBoundsException if {@code index} is out of bounds 52 | */ 53 | public abstract MatlabNumber getElementAtLinearIndex(int index); 54 | 55 | public abstract MatlabNumber getElementAtIndices(int row, int column); 56 | 57 | public abstract MatlabNumber getElementAtIndices(int row, int column, int page); 58 | 59 | /** 60 | * Gets the element at the specified {@code row}, {@code column}, and {@code pages}. 61 | * 62 | * @param row 63 | * @param column 64 | * @param pages 65 | * @return element at {@code row}, {@code column}, and {@code pages} 66 | * @throws IllegalArgumentException if number of indices does not equal this array's number of dimensions 67 | * @throws ArrayIndexOutOfBoundsException if the indices are out of bound 68 | */ 69 | public abstract MatlabNumber getElementAtIndices(int row, int column, int... pages); 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/matlabcontrol/link/ArrayUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import org.junit.Assert; 9 | import org.junit.ComparisonFailure; 10 | import org.junit.Test; 11 | 12 | public class ArrayUtilsTest { 13 | @Test 14 | public void testDeepCopyOnPrimitives() { 15 | byte[] bytes = new byte[]{1, 2, 3, 4, 5}; 16 | byte[] bytesCopied = ArrayUtils.deepCopy(bytes); 17 | Assert.assertTrue(bytes != bytesCopied); 18 | Assert.assertArrayEquals(bytes, bytesCopied); 19 | 20 | short[] shorts = new short[]{1, 2, 3, 4, 5}; 21 | short[] shortsCopied = ArrayUtils.deepCopy(shorts); 22 | Assert.assertTrue(shorts != shortsCopied); 23 | Assert.assertArrayEquals(shorts, shortsCopied); 24 | 25 | int[] ints = new int[]{1, 2, 3, 4, 5}; 26 | int[] intsCopied = ArrayUtils.deepCopy(ints); 27 | Assert.assertTrue(ints != intsCopied); 28 | Assert.assertArrayEquals(ints, intsCopied); 29 | 30 | long[] longs = new long[]{1, 2, 3, 4, 5}; 31 | long[] longsCopied = ArrayUtils.deepCopy(longs); 32 | Assert.assertTrue(longs != longsCopied); 33 | Assert.assertArrayEquals(longs, longsCopied); 34 | 35 | float[] floats = new float[]{1, 2, 3, 4, 5}; 36 | float[] floatsCopied = ArrayUtils.deepCopy(floats); 37 | Assert.assertTrue(floats != floatsCopied); 38 | Assert.assertArrayEquals(floats, floatsCopied, 0.01f); 39 | 40 | double[] doubles = new double[]{1, 2, 3, 4, 5}; 41 | double[] doublesCopied = ArrayUtils.deepCopy(doubles); 42 | Assert.assertTrue(doubles != doublesCopied); 43 | Assert.assertArrayEquals(doubles, doublesCopied, 0.01); 44 | 45 | boolean[] bools = new boolean[]{true, true, false, true}; 46 | boolean[] boolsCopied = ArrayUtils.deepCopy(bools); 47 | Assert.assertTrue(bools != boolsCopied); 48 | Assert.assertArrayEquals(bools, boolsCopied); 49 | 50 | char[] chars = new char[]{1, 2, 3, 4, 5}; 51 | char[] charsCopied = ArrayUtils.deepCopy(chars); 52 | Assert.assertTrue(chars != charsCopied); 53 | Assert.assertArrayEquals(chars, charsCopied); 54 | } 55 | 56 | public static void assertArraysEqual(Object expected, Object actual) { 57 | if (expected.getClass().isArray()) { 58 | if (!ArrayUtils.equals(expected, actual)) { 59 | throw new ComparisonFailure("test failed", toString(expected), toString(actual)); 60 | } 61 | } else { 62 | if (!expected.equals(actual)) { 63 | throw new ComparisonFailure("test failed", toString(expected), toString(actual)); 64 | } 65 | } 66 | } 67 | 68 | private static String toString(Object any) { 69 | return "class: " + any.getClass() + "\ntoString: " + any.toString(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/BaseArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | * 13 | * @param underlying linear array - single dimensional array type, ex. {@code byte[]} 14 | * @param output array - primitive numeric array type, ex. {@code byte[][][]} 15 | * (1 or more dimensions is acceptable, including for example {@code byte[]}) 16 | */ 17 | abstract class BaseArray { 18 | /** 19 | * Returns {@code true} if the array has no imaginary values, {@code false} otherwise. Equivalent to the MATLAB 20 | * {@code isreal} function. 21 | * 22 | * @return 23 | */ 24 | abstract boolean isReal(); 25 | 26 | /** 27 | * The number of elements in the array. Zero elements are counted. The real and imaginary components of a number are 28 | * together considered one element. This is equivalent to MATLAB's {@code numel} function. 29 | * 30 | * @return number of elements 31 | */ 32 | abstract int getNumberOfElements(); 33 | 34 | /** 35 | * Returns the length of the dimension specified by {@code dimension}. Dimensions use 0-based indexing. So the 36 | * first dimension, which is dimension 0, is the row length. The second dimension is the column length. The third 37 | * dimension and beyond are pages. 38 | * 39 | * @param dimension 40 | * @return length of {@code dimension} 41 | * @throws IllegalArgumentException if {@code dimension} is not a dimension of the array 42 | */ 43 | abstract int getLengthOfDimension(int dimension); 44 | 45 | /** 46 | * Returns the number of dimensions of the array. 47 | * 48 | * @return number of dimensions 49 | */ 50 | abstract int getNumberOfDimensions(); 51 | 52 | /** 53 | * Returns an array that holds the real values from the MATLAB array. Each call returns a new copy which may be used 54 | * in any manner; modifications to it will have no effect on this instance. 55 | * 56 | * @return real array 57 | */ 58 | abstract T toRealArray(); 59 | 60 | /** 61 | * Returns an array that holds the imaginary values from the MATLAB array. Each call returns a new copy which may be 62 | * used in any manner; modifications to it will have no effect on this instance. If this array is real then the 63 | * returned array will be have the default value as all of its base elements. 64 | * 65 | * @return imaginary array 66 | */ 67 | abstract T toImaginaryArray(); 68 | 69 | /** 70 | * If this array has a sparse representation. 71 | * 72 | * @return 73 | */ 74 | abstract boolean isSparse(); 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/LocalHostRMIHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.io.IOException; 9 | import java.io.Serializable; 10 | import java.net.InetAddress; 11 | import java.net.ServerSocket; 12 | import java.net.Socket; 13 | import java.rmi.Remote; 14 | import java.rmi.RemoteException; 15 | import java.rmi.registry.LocateRegistry; 16 | import java.rmi.registry.Registry; 17 | import java.rmi.server.RMIClientSocketFactory; 18 | import java.rmi.server.RMIServerSocketFactory; 19 | import java.rmi.server.UnicastRemoteObject; 20 | 21 | import javax.net.ServerSocketFactory; 22 | import javax.net.SocketFactory; 23 | 24 | /** 25 | * Handles creation of RMI objects, making sure they only operate on localhost. 26 | * 27 | * @since 4.0.0 28 | * 29 | * @author Joshua Kaplan 30 | */ 31 | class LocalHostRMIHelper { 32 | private static final LocalHostRMISocketFactory SOCKET_FACTORY = new LocalHostRMISocketFactory(); 33 | 34 | public static Registry getRegistry(int port) throws RemoteException { 35 | return LocateRegistry.getRegistry("localhost", port, SOCKET_FACTORY); 36 | } 37 | 38 | public static Registry createRegistry(int port) throws RemoteException { 39 | return LocateRegistry.createRegistry(port, SOCKET_FACTORY, SOCKET_FACTORY); 40 | } 41 | 42 | public static Remote exportObject(Remote object) throws RemoteException { 43 | return UnicastRemoteObject.exportObject(object, 0, SOCKET_FACTORY, SOCKET_FACTORY); 44 | } 45 | 46 | private static class LocalHostRMISocketFactory implements RMIClientSocketFactory, RMIServerSocketFactory, Serializable { 47 | private static final long serialVersionUID = 2973279795727940224L; 48 | 49 | @Override 50 | public Socket createSocket(String host, int port) throws IOException { 51 | return SocketFactory.getDefault().createSocket(InetAddress.getByName("localhost"), port); 52 | } 53 | 54 | @Override 55 | public ServerSocket createServerSocket(int port) throws IOException { 56 | return ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost")); 57 | } 58 | 59 | @Override 60 | public boolean equals(Object o) { 61 | return (o instanceof LocalHostRMISocketFactory); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | return 5; 67 | } 68 | 69 | /** 70 | * Overridden to provide a better name for the RMI RenewClean thread. 71 | * 72 | * @return 73 | */ 74 | @Override 75 | public String toString() { 76 | return "MLC localhost Socket Factory"; 77 | } 78 | } 79 | 80 | static class LocalHostRemoteObject extends UnicastRemoteObject { 81 | private static final long serialVersionUID = -7160502485279570444L; 82 | 83 | LocalHostRemoteObject() throws RemoteException { 84 | super(0, SOCKET_FACTORY, SOCKET_FACTORY); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | All code up to tags/original: 2 | Copyright (c) 2013, Joshua Kaplan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 6 | following conditions are met: 7 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 8 | disclaimer. 9 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 10 | following disclaimer in the documentation and/or other materials provided with the distribution. 11 | - Neither the name of matlabcontrol nor the names of its contributors may be used to endorse or promote products 12 | derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 15 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 17 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 19 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 20 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 | 22 | 23 | All code after tags/original: 24 | Copyright (c) 2016, DiffPlug 25 | All rights reserved. 26 | 27 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 28 | following conditions are met: 29 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 30 | disclaimer. 31 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 32 | following disclaimer in the documentation and/or other materials provided with the distribution. 33 | - Neither the name of MatConsoleCtl nor the names of its contributors may be used to endorse or promote products 34 | derived from this software without specific prior written permission. 35 | 36 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 37 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 38 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 39 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 40 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 41 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 42 | -------------------------------------------------------------------------------- /src/test/java/matlabcontrol/MatlabRequired.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.awt.BorderLayout; 9 | import java.awt.Container; 10 | import java.awt.event.WindowEvent; 11 | import java.util.concurrent.atomic.AtomicReference; 12 | 13 | import javax.swing.JButton; 14 | import javax.swing.JFrame; 15 | import javax.swing.JTextArea; 16 | import javax.swing.SwingUtilities; 17 | 18 | import org.junit.Assert; 19 | 20 | public class MatlabRequired { 21 | /** Marks that this test requires user-in-the-loop interaction with MATLAB. */ 22 | public static class Interactive { 23 | /** Opens a dialog with the given instructions. Returns a runnable which closes the dialog. */ 24 | public static Runnable prompt(final String instructions) { 25 | // figure out the name of the test from the stack 26 | StackTraceElement[] elements = new Throwable().getStackTrace(); 27 | String testName = "unknown"; 28 | for (int i = 1; i < elements.length; ++i) { 29 | if (!elements[i].getClassName().startsWith("matlabcontrol.")) { 30 | StackTraceElement element = elements[i - 1]; 31 | testName = element.getClassName() + "::" + element.getMethodName(); 32 | break; 33 | } 34 | } 35 | final String finalTestName = testName; 36 | 37 | // open a dialog and save it 38 | final AtomicReference box = new AtomicReference(); 39 | SwingUtilities.invokeLater(new Runnable() { 40 | @Override 41 | public void run() { 42 | JFrame frame = new JFrame(); 43 | box.set(frame); 44 | frame.setTitle(finalTestName); 45 | 46 | Container contentPane = frame.getContentPane(); 47 | contentPane.setLayout(new BorderLayout()); 48 | 49 | JTextArea label = new JTextArea(); 50 | label.setText(instructions); 51 | contentPane.add(label, BorderLayout.CENTER); 52 | 53 | JButton button = new JButton(); 54 | button.setText("Fail"); 55 | contentPane.add(button, BorderLayout.SOUTH); 56 | 57 | frame.pack(); 58 | frame.setVisible(true); 59 | 60 | button.addActionListener(new java.awt.event.ActionListener() { 61 | @Override 62 | public void actionPerformed(java.awt.event.ActionEvent evt) { 63 | Assert.fail("User clicked fail"); 64 | } 65 | }); 66 | } 67 | }); 68 | 69 | // return a runnable which will close the dialog 70 | return new Runnable() { 71 | @Override 72 | public void run() { 73 | SwingUtilities.invokeLater(new Runnable() { 74 | @Override 75 | public void run() { 76 | JFrame frame = box.get(); 77 | if (frame.isVisible()) { 78 | frame.setVisible(false); 79 | ; 80 | frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); 81 | } 82 | } 83 | }); 84 | } 85 | }; 86 | } 87 | } 88 | 89 | /** Marks that this test requires MATLAB but no user. */ 90 | public static class Headless {} 91 | } 92 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/ClassInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.concurrent.ConcurrentHashMap; 9 | import java.util.concurrent.ConcurrentMap; 10 | 11 | /** 12 | * 13 | * @since 4.2.0 14 | * @author Joshua Kaplan 15 | */ 16 | class ClassInfo { 17 | private static ConcurrentMap, ClassInfo> CACHE = new ConcurrentHashMap, ClassInfo>(); 18 | 19 | static ClassInfo getInfo(Class clazz) { 20 | ClassInfo info = CACHE.get(clazz); 21 | if (info == null) { 22 | info = new ClassInfo(clazz); 23 | CACHE.put(clazz, info); 24 | } 25 | 26 | return info; 27 | } 28 | 29 | /** 30 | * The class this information is about 31 | */ 32 | final Class describedClass; 33 | 34 | /** 35 | * If the class is either {@code void} or {@code java.lang.Void} 36 | */ 37 | final boolean isVoid; 38 | 39 | /** 40 | * If the class is primitive 41 | */ 42 | final boolean isPrimitive; 43 | 44 | /** 45 | * If an array type 46 | */ 47 | final boolean isArray; 48 | 49 | /** 50 | * If the array's base component type is a primitive 51 | */ 52 | final boolean isPrimitiveArray; 53 | 54 | /** 55 | * If the base component type of an array, {@code null} if not an array 56 | */ 57 | final Class baseComponentType; 58 | 59 | /** 60 | * The number of array dimensions, {@code 0} if not an array 61 | */ 62 | final int arrayDimensions; 63 | 64 | /** 65 | * If the class is one of: {@code byte}, {@code Byte}, {@code short}, {@code Short}, {@code int}, 66 | * {@code Integer}, {@code long}, {@code Long}, {@code float}, {@code Float}, {@code double}, {@code Double} 67 | */ 68 | final boolean isBuiltinNumeric; 69 | 70 | /** 71 | * If the class inherits from {@code MatlabType} 72 | */ 73 | final boolean isMatlabType; 74 | 75 | private ClassInfo(Class clazz) { 76 | describedClass = clazz; 77 | 78 | isPrimitive = clazz.isPrimitive(); 79 | 80 | if (clazz.isArray()) { 81 | isArray = true; 82 | 83 | int dim = 0; 84 | Class type = clazz; 85 | while (type.isArray()) { 86 | dim++; 87 | type = type.getComponentType(); 88 | } 89 | 90 | arrayDimensions = dim; 91 | baseComponentType = type; 92 | isPrimitiveArray = type.isPrimitive(); 93 | } else { 94 | isArray = false; 95 | baseComponentType = null; 96 | isPrimitiveArray = false; 97 | arrayDimensions = 0; 98 | } 99 | 100 | isVoid = clazz.equals(Void.class) || clazz.equals(void.class); 101 | isMatlabType = MatlabType.class.isAssignableFrom(clazz); 102 | 103 | isBuiltinNumeric = clazz.equals(Byte.class) || clazz.equals(byte.class) || 104 | clazz.equals(Short.class) || clazz.equals(short.class) || 105 | clazz.equals(Integer.class) || clazz.equals(int.class) || 106 | clazz.equals(Long.class) || clazz.equals(long.class) || 107 | clazz.equals(Float.class) || clazz.equals(float.class) || 108 | clazz.equals(Double.class) || clazz.equals(double.class); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at community@diffplug.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabDoubleFullMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | */ 13 | class MatlabDoubleFullMatrix extends MatlabDoubleMatrix { 14 | private final FullArray _array; 15 | 16 | MatlabDoubleFullMatrix(double[] real, double[] imag, int[] dimensions) { 17 | _array = new FullArray(double[].class, real, imag, dimensions); 18 | } 19 | 20 | MatlabDoubleFullMatrix(T real, T imag) { 21 | _array = new FullArray(double[].class, real, imag); 22 | } 23 | 24 | @Override 25 | BaseArray getBaseArray() { 26 | return _array; 27 | } 28 | 29 | @Override 30 | public double getRealElementAtLinearIndex(int linearIndex) { 31 | return _array._real[linearIndex]; 32 | } 33 | 34 | @Override 35 | public double getImaginaryElementAtLinearIndex(int linearIndex) { 36 | return _array._imag == null ? 0 : _array._imag[linearIndex]; 37 | } 38 | 39 | @Override 40 | public double getRealElementAtIndices(int row, int column) { 41 | return _array._real[_array.getLinearIndex(row, column)]; 42 | } 43 | 44 | @Override 45 | public double getRealElementAtIndices(int row, int column, int page) { 46 | return _array._real[_array.getLinearIndex(row, column, page)]; 47 | } 48 | 49 | @Override 50 | public double getRealElementAtIndices(int row, int column, int[] pages) { 51 | return _array._real[_array.getLinearIndex(row, column, pages)]; 52 | } 53 | 54 | @Override 55 | public double getImaginaryElementAtIndices(int row, int column) { 56 | return _array._imag == null ? 0 : _array._imag[_array.getLinearIndex(row, column)]; 57 | } 58 | 59 | @Override 60 | public double getImaginaryElementAtIndices(int row, int column, int page) { 61 | return _array._imag == null ? 0 : _array._imag[_array.getLinearIndex(row, column, page)]; 62 | } 63 | 64 | @Override 65 | public double getImaginaryElementAtIndices(int row, int column, int[] pages) { 66 | return _array._imag == null ? 0 : _array._imag[_array.getLinearIndex(row, column, pages)]; 67 | } 68 | 69 | @Override 70 | public MatlabDouble getElementAtLinearIndex(int linearIndex) { 71 | return new MatlabDouble(_array._real[linearIndex], _array._imag == null ? 0 : _array._imag[linearIndex]); 72 | } 73 | 74 | @Override 75 | public MatlabDouble getElementAtIndices(int row, int column) { 76 | int linearIndex = _array.getLinearIndex(row, column); 77 | 78 | return new MatlabDouble(_array._real[linearIndex], _array._imag == null ? 0 : _array._imag[linearIndex]); 79 | } 80 | 81 | @Override 82 | public MatlabDouble getElementAtIndices(int row, int column, int page) { 83 | int linearIndex = _array.getLinearIndex(row, column, page); 84 | 85 | return new MatlabDouble(_array._real[linearIndex], _array._imag == null ? 0 : _array._imag[linearIndex]); 86 | } 87 | 88 | @Override 89 | public MatlabDouble getElementAtIndices(int row, int column, int... pages) { 90 | int linearIndex = _array.getLinearIndex(row, column, pages); 91 | 92 | return new MatlabDouble(_array._real[linearIndex], _array._imag == null ? 0 : _array._imag[linearIndex]); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # MatConsoleCtl releases 2 | 3 | ## [Unreleased] 4 | 5 | ## [4.6.1] - 2023-07-11 6 | - Support passing custom MATLAB environment & modify build gradle to OSGIfy(add the MANIFEST headers) the generated jar. 7 | - https://github.com/diffplug/matconsolectl/pull/27 8 | 9 | ## [4.6.0] - 2020-11-12 10 | - Added checks for directories and non-exisiting files in classpath converters: In Configuration.getClassPathAsRMICodebase() and Configuration.getClassPathAsCanonicalPaths(). This prevents unnecessary IOExceptions for invalid classpaths. 11 | - https://github.com/diffplug/matconsolectl/pull/21 12 | 13 | ## [4.5.0] - 2017-07-18 14 | - Added `Builder.setOutputWriter` and `Builder.setErrorWriter` for capturing `stdout` and `stderr` from MATLAB. ([#20](https://github.com/diffplug/matconsolectl/pull/20)) 15 | 16 | ## [4.4.4] - 2016-12-15 17 | - Yet a better fix to a bug where MatConsoleCtl would not run if there were [spaces in the path](https://github.com/diffplug/matconsolectl/issues/15) to the MatConsoleCtl jar. 18 | 19 | ## [4.4.3] - 2016-12-15 20 | - Better fix to a bug where MatConsoleCtl would not run if there were [spaces in the path](https://github.com/diffplug/matconsolectl/issues/15) to the MatConsoleCtl jar. 21 | 22 | ## [4.4.2] - 2015-07-05 23 | - Fixed a bug where MatConsoleCtl would not run if there were [spaces in the path](https://github.com/diffplug/matconsolectl/issues/11) to the MatConsoleCtl jar. 24 | 25 | ## [4.4.1] - 2015-10-16 26 | - Corrected the license in the maven metadata. 27 | 28 | ## [4.4.0] - 2015-10-16 29 | - `MatlabType.MatlabTypeGetter` is now generic. 30 | - Fixed lots of compiler warnings. 31 | - Removed some [unused code](https://github.com/diffplug/matconsolectl/commit/c514188e55880528268dd3314f7347d95d00b7b6), and carefully marked code which [appears unused](https://github.com/diffplug/matconsolectl/commit/60564f2e8a80494b443d7da31c01d2e55c6d72c2) but is actually needed for internal MATLAB scripts. 32 | - Applied DiffPlug's standard formatting and code-quality plugins, FindBugs found several bugs. 33 | - [Fixed bug in ArrayUtils.equals when applied to arrays of long.](https://github.com/diffplug/matconsolectl/commit/088b954551392dc7b24142fd7f1cbcdc6a4005bf) 34 | - [Fixed a serialization bug.](https://github.com/diffplug/matconsolectl/commit/d6bc07adca74f0bb3ae91c1009222eff6b975774) 35 | - Broke up the test suite into `test`, `testMatlabHeadless`, and `testMatlabInteractive` 36 | - Moved the demo code into the main library. It's a very small demo with no dependencies, makes life easier to manage one jar rather than two. 37 | 38 | ## [4.3.0] - 1015-02-23 39 | - Added OSGi compatibility. 40 | 41 | ## [4.2.1] - 2015-02-22 42 | - ThrowableWrapper now initializes the `getMessage()` field with MATLAB's raw error text. 43 | 44 | ## [4.2.0] - 2015-02-20 45 | - Switched to gradle, which makes the jmistub subproject unnecessary. 46 | - Added `CopyPasteCallback` to the set of options for creating a `MatlabProxyFactory`. The factory sends a chunk of code to the callback, and the user copy-pastes this code into a MATLAB terminal to initiate a connection. 47 | + At first, I got a bunch of Serialization errors. There were a bunch of `Serializable` classes that didn't specify a `serialVersionUID`. Specifying these seemed to fix the problem. 48 | + You can connect over and over this way, and the MATLAB instance stays happy. 49 | 50 | ## Versions up to 4.1.0 are from the original matlabcontrol project on [Google code page](https://code.google.com/p/matlabcontrol/wiki/VersionHistory). 51 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/demo/ArrayPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.demo; 7 | 8 | import java.awt.Color; 9 | import java.awt.GridLayout; 10 | import java.util.ArrayList; 11 | 12 | import javax.swing.JComboBox; 13 | import javax.swing.JPanel; 14 | import javax.swing.JTextField; 15 | 16 | /** 17 | * The panel that contains the options to select elements of the array. 18 | * 19 | * @author Joshua Kaplan 20 | */ 21 | @SuppressWarnings("serial") 22 | class ArrayPanel extends JPanel { 23 | //Input options: String or Double 24 | private static final int DOUBLE_INDEX = 0, STRING_INDEX = 1; 25 | 26 | private static final String[] OPTIONS = new String[2]; 27 | 28 | static { 29 | OPTIONS[DOUBLE_INDEX] = "Double"; 30 | OPTIONS[STRING_INDEX] = "String"; 31 | } 32 | 33 | /** 34 | * Number of fields and drop down lists. 35 | */ 36 | public static final int NUM_ENTRIES = 3; 37 | 38 | /** 39 | * Drop down lists to choose between object types. 40 | */ 41 | private final JComboBox[] _optionBoxes; 42 | 43 | /** 44 | * Fields for inputting values. 45 | */ 46 | private final JTextField[] _entryFields; 47 | 48 | public ArrayPanel() { 49 | super(new GridLayout(NUM_ENTRIES, 2)); 50 | this.setBackground(Color.WHITE); 51 | 52 | //Drop down lists and input fields 53 | _optionBoxes = new JComboBox[NUM_ENTRIES]; 54 | _entryFields = new JTextField[NUM_ENTRIES]; 55 | 56 | for (int i = 0; i < NUM_ENTRIES; i++) { 57 | _optionBoxes[i] = new JComboBox(OPTIONS); 58 | _entryFields[i] = new JTextField(8); 59 | this.add(_optionBoxes[i]); 60 | this.add(_entryFields[i]); 61 | } 62 | } 63 | 64 | /** 65 | * Take the elements of the fields and put them into an array. 66 | * 67 | * @return 68 | */ 69 | public Object[] getArray() { 70 | ArrayList entries = new ArrayList(); 71 | for (int i = 0; i < NUM_ENTRIES; i++) { 72 | if (!_entryFields[i].getText().isEmpty()) { 73 | if (_optionBoxes[i].getSelectedIndex() == DOUBLE_INDEX) { 74 | try { 75 | entries.add(Double.parseDouble(_entryFields[i].getText())); 76 | } catch (Exception e) { 77 | entries.add(0); 78 | } 79 | } 80 | if (_optionBoxes[i].getSelectedIndex() == STRING_INDEX) { 81 | entries.add(_entryFields[i].getText()); 82 | } 83 | } 84 | } 85 | 86 | return entries.toArray(); 87 | } 88 | 89 | /** 90 | * Return the first entry of the fields. 91 | * 92 | * @return 93 | */ 94 | public Object getFirstEntry() { 95 | if (!_entryFields[0].getText().isEmpty()) { 96 | if (_optionBoxes[0].getSelectedIndex() == DOUBLE_INDEX) { 97 | try { 98 | return Double.parseDouble(_entryFields[0].getText()); 99 | } catch (Exception e) { 100 | return 0; 101 | } 102 | } 103 | if (_optionBoxes[0].getSelectedIndex() == STRING_INDEX) { 104 | return _entryFields[0].getText(); 105 | } 106 | } 107 | 108 | return null; 109 | } 110 | 111 | /** 112 | * Enable the first {@code n} fields for input. The others are disabled. 113 | * 114 | * @param n 115 | */ 116 | public void enableInputFields(int n) { 117 | for (int i = 0; i < NUM_ENTRIES; i++) { 118 | _optionBoxes[i].setEnabled(i < n); 119 | _entryFields[i].setEnabled(i < n); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/demo/ReturnFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.demo; 7 | 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.io.StringWriter; 11 | import java.lang.reflect.Array; 12 | 13 | /** 14 | * Formats returned {@code Object}s and {@code Exception}s from MATLAB. 15 | * 16 | * @author Joshua Kaplan 17 | */ 18 | class ReturnFormatter { 19 | private ReturnFormatter() {} 20 | 21 | /** 22 | * Format the exception into a string that can be displayed. 23 | * 24 | * @param exc the exception 25 | * @return the exception as a string 26 | */ 27 | public static String formatException(Exception exc) { 28 | StringWriter stringWriter = new StringWriter(); 29 | PrintWriter printWriter = new PrintWriter(stringWriter); 30 | exc.printStackTrace(printWriter); 31 | try { 32 | stringWriter.close(); 33 | } catch (IOException ex) {} 34 | 35 | return stringWriter.toString(); 36 | } 37 | 38 | /** 39 | * Takes in the result from MATLAB and turns it into an easily readable format. 40 | * 41 | * @param result 42 | * @return description 43 | */ 44 | public static String formatResult(Object result) { 45 | return formatResult(result, 0); 46 | } 47 | 48 | /** 49 | * Takes in the result from MATLAB and turns it into an easily readable format. 50 | * 51 | * @param result 52 | * @param level, pass in 0 to initialize, used recursively 53 | * @return description 54 | */ 55 | private static String formatResult(Object result, int level) { 56 | //Message builder 57 | StringBuilder builder = new StringBuilder(); 58 | 59 | //Tab offset for levels 60 | String tab = ""; 61 | for (int i = 0; i < level + 1; i++) { 62 | tab += " "; 63 | } 64 | 65 | //If the result is null 66 | if (result == null) { 67 | builder.append("null encountered\n"); 68 | } 69 | //If the result is an array 70 | else if (result.getClass().isArray()) { 71 | Class componentClass = result.getClass().getComponentType(); 72 | 73 | //Primitive array 74 | if (componentClass.isPrimitive()) { 75 | String componentName = componentClass.toString(); 76 | int length = Array.getLength(result); 77 | 78 | builder.append(componentName); 79 | builder.append(" array, length = "); 80 | builder.append(length); 81 | builder.append("\n"); 82 | 83 | for (int i = 0; i < length; i++) { 84 | builder.append(tab); 85 | builder.append("index "); 86 | builder.append(i); 87 | builder.append(", "); 88 | builder.append(componentName); 89 | builder.append(": "); 90 | builder.append(Array.get(result, i)); 91 | builder.append("\n"); 92 | } 93 | } 94 | //Object array 95 | else { 96 | Object[] array = (Object[]) result; 97 | 98 | builder.append(array.getClass().getComponentType().getName()); 99 | builder.append(" array, length = "); 100 | builder.append(array.length); 101 | builder.append("\n"); 102 | 103 | for (int i = 0; i < array.length; i++) { 104 | builder.append(tab); 105 | builder.append("index "); 106 | builder.append(i); 107 | builder.append(", "); 108 | builder.append(formatResult(array[i], level + 1)); 109 | } 110 | } 111 | } 112 | //If an Object and not an array 113 | else { 114 | builder.append(result.getClass().getCanonicalName()); 115 | builder.append(": "); 116 | builder.append(result); 117 | builder.append("\n"); 118 | } 119 | 120 | return builder.toString(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/LocalMatlabProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | /** 9 | * Allows for calling MATLAB from inside of MATLAB. 10 | * 11 | * @since 3.1.0 12 | * 13 | * @author Joshua Kaplan 14 | */ 15 | class LocalMatlabProxy extends MatlabProxy { 16 | /** 17 | * If connected to MATLAB. 18 | * 19 | * This notion of connection exists to make it consistent with {@link RemoteMatlabProxy}, but is not actually 20 | * necessary. Unless a user calls {@link #disconnect()} this proxy cannot become disconnected. 21 | */ 22 | private volatile boolean _isConnected = true; 23 | 24 | LocalMatlabProxy(Identifier id) { 25 | super(id, true); 26 | } 27 | 28 | @Override 29 | public boolean isRunningInsideMatlab() { 30 | return true; 31 | } 32 | 33 | @Override 34 | public boolean isConnected() { 35 | return _isConnected; 36 | } 37 | 38 | @Override 39 | public boolean disconnect() { 40 | _isConnected = false; 41 | 42 | //Notify listeners 43 | notifyDisconnectionListeners(); 44 | 45 | return true; 46 | } 47 | 48 | // Methods which interact with MATLAB 49 | 50 | @Override 51 | public void exit() throws MatlabInvocationException { 52 | if (this.isConnected()) { 53 | JMIWrapper.exit(); 54 | } else { 55 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 56 | } 57 | } 58 | 59 | @Override 60 | public void eval(String command) throws MatlabInvocationException { 61 | if (this.isConnected()) { 62 | JMIWrapper.eval(command); 63 | } else { 64 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 65 | } 66 | } 67 | 68 | @Override 69 | public Object[] returningEval(String command, int nargout) throws MatlabInvocationException { 70 | if (this.isConnected()) { 71 | return JMIWrapper.returningEval(command, nargout); 72 | } else { 73 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 74 | } 75 | } 76 | 77 | @Override 78 | public void feval(String functionName, Object... args) throws MatlabInvocationException { 79 | if (this.isConnected()) { 80 | JMIWrapper.feval(functionName, args); 81 | } else { 82 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 83 | } 84 | } 85 | 86 | @Override 87 | public Object[] returningFeval(String functionName, int nargout, Object... args) throws MatlabInvocationException { 88 | if (this.isConnected()) { 89 | return JMIWrapper.returningFeval(functionName, nargout, args); 90 | } else { 91 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 92 | } 93 | } 94 | 95 | @Override 96 | public void setVariable(String variableName, Object value) throws MatlabInvocationException { 97 | if (this.isConnected()) { 98 | JMIWrapper.setVariable(variableName, value); 99 | } else { 100 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 101 | } 102 | } 103 | 104 | @Override 105 | public Object getVariable(String variableName) throws MatlabInvocationException { 106 | if (this.isConnected()) { 107 | return JMIWrapper.getVariable(variableName); 108 | } else { 109 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 110 | } 111 | } 112 | 113 | @Override 114 | public T invokeAndWait(MatlabThreadCallable callable) throws MatlabInvocationException { 115 | if (this.isConnected()) { 116 | return JMIWrapper.invokeAndWait(callable); 117 | } else { 118 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabVariable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import matlabcontrol.MatlabInvocationException; 9 | import matlabcontrol.MatlabOperations; 10 | 11 | /** 12 | * Represents a variable in MATLAB. The representation is not associated with a given session of MATLAB. An instance of 13 | * this class with a given name does not mean that a variable with that name exists in any session of MATLAB. 14 | * 15 | * @since 4.2.0 16 | * @author Joshua Kaplan 17 | */ 18 | public final class MatlabVariable extends MatlabType { 19 | private final String _name; 20 | 21 | /** 22 | * Constructs a representation of a MATLAB variable with the name specified by {@code name}. 23 | * 24 | * @param name 25 | * @throws IllegalArgumentException if {@code name} is not a valid MATLAB variable name 26 | */ 27 | public MatlabVariable(String name) { 28 | //Validate variable name 29 | if (name.isEmpty()) { 30 | throw new IllegalArgumentException("Invalid MATLAB variable name: " + name); 31 | } 32 | char[] nameChars = name.toCharArray(); 33 | if (!Character.isLetter(nameChars[0])) { 34 | throw new IllegalArgumentException("Invalid MATLAB variable name: " + name); 35 | } 36 | for (char element : nameChars) { 37 | if (!(Character.isLetter(element) || Character.isDigit(element) || element == '_')) { 38 | throw new IllegalArgumentException("Invalid MATLAB variable name: " + name); 39 | } 40 | } 41 | _name = name; 42 | } 43 | 44 | /** 45 | * The name of this variable. 46 | * 47 | * @return 48 | */ 49 | public String getName() { 50 | return _name; 51 | } 52 | 53 | /** 54 | * Returns a brief description of this variable. The exact details of this representation are unspecified and are 55 | * subject to change. 56 | * 57 | * @return 58 | */ 59 | @Override 60 | public String toString() { 61 | return "[" + this.getClass().getName() + " name=" + _name + "]"; 62 | } 63 | 64 | /** 65 | * Returns {@code true} if and only if {@code obj} is a {@code MatlabVariable} and has the same name as this 66 | * variable. 67 | * 68 | * @param obj 69 | * @return 70 | */ 71 | @Override 72 | public boolean equals(Object obj) { 73 | boolean equal = false; 74 | 75 | if (obj instanceof MatlabVariable) { 76 | MatlabVariable other = (MatlabVariable) obj; 77 | equal = other._name.equals(_name); 78 | } 79 | 80 | return equal; 81 | } 82 | 83 | /** 84 | * Returns a hash code consistent with {@link #equals(java.lang.Object)}. 85 | * 86 | * @return 87 | */ 88 | @Override 89 | public int hashCode() { 90 | return _name.hashCode(); 91 | } 92 | 93 | static class MatlabVariableGetter implements MatlabTypeGetter { 94 | private static final long serialVersionUID = 7724337165919355824L; 95 | private String _name; 96 | private boolean _retrieved; 97 | 98 | @Override 99 | public MatlabVariable retrieve() { 100 | if (!_retrieved) { 101 | throw new IllegalStateException("variable not retrieved"); 102 | } 103 | 104 | return new MatlabVariable(_name); 105 | } 106 | 107 | @Override 108 | public void getInMatlab(MatlabOperations ops, String variableName) throws MatlabInvocationException { 109 | _name = variableName; 110 | _retrieved = true; 111 | } 112 | } 113 | 114 | @Override 115 | MatlabTypeSetter getSetter() { 116 | return new MatlabVariableSetter(_name); 117 | } 118 | 119 | private static class MatlabVariableSetter implements MatlabTypeSetter { 120 | private static final long serialVersionUID = -2208485477826441076L; 121 | private final String _name; 122 | 123 | private MatlabVariableSetter(String name) { 124 | _name = name; 125 | } 126 | 127 | @Override 128 | public void setInMatlab(MatlabOperations ops, String variableName) throws MatlabInvocationException { 129 | ops.eval(variableName + " = " + _name + ";"); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to MatConsoleCtl 2 | 3 | Pull requests are welcome, preferably against `master`. 4 | 5 | Every successful Travis CI build on branch `master` is automatically published to [`https://oss.sonatype.org/content/repositories/snapshots`](https://oss.sonatype.org/content/repositories/snapshots/com/diffplug/), and its javadoc are published [here](http://diffplug.github.io/matsessionctl/javadoc/snapshot/). 6 | 7 | ## Build instructions 8 | 9 | It's a bog-standard gradle build. 10 | 11 | `gradlew eclipse` 12 | * creates an Eclipse project file for you. 13 | 14 | `gradlew build` 15 | * builds the jar 16 | * runs FindBugs 17 | * checks the formatting 18 | * runs the tests 19 | 20 | If you're getting style warnings, `gradlew spotlessApply` will apply anything necessary to fix formatting. For more info on the formatter, check out [spotless](https://github.com/diffplug/spotless). 21 | 22 | ## Testing MATLAB stuff 23 | 24 | In order to keep CI happy, there are three kinds of tests: 25 | 26 | * `gradlew test` doesn't need MATLAB, automatically called by `gradlew build` 27 | * `gradlew testMatlabHeadless` needs MATLAB but not a human, see [GetAndSetTest.java](test/matlabcontrol/GetAndSetTest.java?ts=4) 28 | * `gradlew testMatlabInteractive` needs MATLAB and a human, see [CopyPasteTest.java](test/matlabcontrol/CopyPasteTest.java?ts=4) 29 | * `gradlew testMatlabAll` runs all of the above 30 | 31 | ## License 32 | 33 | By contributing your code, you agree to license your contribution under the terms of the New BSD license as such: 34 | 35 | ``` 36 | All code up to tags/original: 37 | Copyright (c) 2013, Joshua Kaplan 38 | All rights reserved. 39 | 40 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 41 | following conditions are met: 42 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 43 | disclaimer. 44 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 45 | following disclaimer in the documentation and/or other materials provided with the distribution. 46 | - Neither the name of matlabcontrol nor the names of its contributors may be used to endorse or promote products 47 | derived from this software without specific prior written permission. 48 | 49 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 50 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 51 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 52 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 53 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 54 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 55 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | 58 | All code after tags/original: 59 | Copyright (c) 2015, DiffPlug 60 | All rights reserved. 61 | 62 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 63 | following conditions are met: 64 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 65 | disclaimer. 66 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 67 | following disclaimer in the documentation and/or other materials provided with the distribution. 68 | - Neither the name of MatConsoleCtl nor the names of its contributors may be used to endorse or promote products 69 | derived from this software without specific prior written permission. 70 | 71 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 72 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 73 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 74 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 75 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 76 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 77 | ``` 78 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabDoubleSparseMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | /** 9 | * 10 | * @since 4.2.0 11 | * @author Joshua Kaplan 12 | */ 13 | class MatlabDoubleSparseMatrix extends MatlabDoubleMatrix { 14 | private final SparseArray _array; 15 | 16 | MatlabDoubleSparseMatrix(int[] linearIndices, int[] rowIndices, int[] colIndices, double[] real, double[] imag, 17 | int numRows, int numCols) { 18 | _array = new SparseArray(double[].class, linearIndices, rowIndices, colIndices, real, imag, 19 | numRows, numCols); 20 | } 21 | 22 | MatlabDoubleSparseMatrix(int[] rowIndices, int[] colIndices, double[] real, double[] imag, int numRows, int numCols) { 23 | _array = new SparseArray(double[].class, rowIndices, colIndices, real, imag, numRows, numCols); 24 | } 25 | 26 | @Override 27 | BaseArray getBaseArray() { 28 | return _array; 29 | } 30 | 31 | private double getRealElementAtSparseIndex(int sparseIndex) { 32 | double val = 0; 33 | if (sparseIndex >= 0) { 34 | val = _array._realValues[sparseIndex]; 35 | } 36 | 37 | return val; 38 | } 39 | 40 | private double getImaginaryElementAtSparseIndex(int sparseIndex) { 41 | double val = 0; 42 | if (!_array.isReal() && sparseIndex >= 0) { 43 | val = _array._imagValues[sparseIndex]; 44 | } 45 | 46 | return val; 47 | } 48 | 49 | @Override 50 | public double getRealElementAtLinearIndex(int linearIndex) { 51 | return this.getRealElementAtSparseIndex(_array.getSparseIndexForLinearIndex(linearIndex)); 52 | } 53 | 54 | @Override 55 | public double getImaginaryElementAtLinearIndex(int linearIndex) { 56 | return this.getImaginaryElementAtSparseIndex(_array.getSparseIndexForLinearIndex(linearIndex)); 57 | } 58 | 59 | @Override 60 | public double getRealElementAtIndices(int row, int column) { 61 | return this.getRealElementAtSparseIndex(_array.getSparseIndexForIndices(row, column)); 62 | } 63 | 64 | @Override 65 | public double getRealElementAtIndices(int row, int column, int page) { 66 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using 3 indices"); 67 | } 68 | 69 | @Override 70 | public double getRealElementAtIndices(int row, int column, int[] pages) { 71 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using " + 72 | (2 + pages.length) + " indices"); 73 | } 74 | 75 | @Override 76 | public double getImaginaryElementAtIndices(int row, int column) { 77 | return this.getImaginaryElementAtSparseIndex(_array.getSparseIndexForIndices(row, column)); 78 | } 79 | 80 | @Override 81 | public double getImaginaryElementAtIndices(int row, int column, int page) { 82 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using 3 indices"); 83 | } 84 | 85 | @Override 86 | public double getImaginaryElementAtIndices(int row, int column, int[] pages) { 87 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using " + 88 | (2 + pages.length) + " indices"); 89 | } 90 | 91 | @Override 92 | public MatlabDouble getElementAtLinearIndex(int linearIndex) { 93 | int sparseIndex = _array.getSparseIndexForLinearIndex(linearIndex); 94 | 95 | return new MatlabDouble(this.getRealElementAtSparseIndex(sparseIndex), 96 | this.getImaginaryElementAtSparseIndex(sparseIndex)); 97 | } 98 | 99 | @Override 100 | public MatlabDouble getElementAtIndices(int row, int column) { 101 | int sparseIndex = _array.getSparseIndexForIndices(row, column); 102 | 103 | return new MatlabDouble(this.getRealElementAtSparseIndex(sparseIndex), 104 | this.getImaginaryElementAtSparseIndex(sparseIndex)); 105 | } 106 | 107 | @Override 108 | public MatlabDouble getElementAtIndices(int row, int column, int page) { 109 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using 3 indices"); 110 | } 111 | 112 | @Override 113 | public MatlabDouble getElementAtIndices(int row, int column, int... pages) { 114 | throw new IllegalArgumentException("Array has 2 dimensions, it cannot be indexed into using " + 115 | (2 + pages.length) + " indices"); 116 | } 117 | 118 | @Override 119 | public int hashCode() { 120 | return _array.hashCode(); 121 | } 122 | 123 | @Override 124 | public boolean equals(Object obj) { 125 | boolean equal = false; 126 | if (this == obj) { 127 | equal = true; 128 | } else if (obj != null && this.getClass().equals(obj.getClass())) { 129 | MatlabDoubleSparseMatrix other = (MatlabDoubleSparseMatrix) obj; 130 | equal = _array.equals(other._array); 131 | } 132 | 133 | return equal; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/internal/MatlabRMIClassLoaderSpi.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.internal; 7 | 8 | import java.io.File; 9 | import java.net.MalformedURLException; 10 | import java.rmi.server.RMIClassLoader; 11 | import java.rmi.server.RMIClassLoaderSpi; 12 | 13 | /** 14 | * Internal Use Only 15 | *

16 | * This class must be public so that it can be created via reflection by {@link RemoteClassLoader}. If it were package 17 | * private it would result in an {@link IllegalAccessError} because only classes in the same package as a package 18 | * private class may construct it (even via reflection). It has been placed in the {@code matlabcontrol.internal} 19 | * package to make it clear it is not intended for use by users of matlabcontrol. 20 | *

21 | * A custom service provider for the RMI class loader. Allows for loading classes sent from the external JVM and 22 | * providing annotations so that the external JVM may load classes defined only in the MATLAB JVM. Loading classes from 23 | * the external JVM could be accomplished by setting {@code java.rmi.server.codebase} property in the external JVM but 24 | * that could interfere with other uses of RMI in the application. There is no way to always sending the correct 25 | * annotations without this custom rmi class loader spi. While the {@code java.rmi.server.codebase} property could be 26 | * set in the MATLAB JVM, the property is checked only at load time. This would mean that class definitions added 27 | * dynamically with {@code javaaddpath} could not be sent. 28 | * 29 | * @since 4.0.0 30 | * 31 | * @author Joshua Kaplan 32 | * 33 | */ 34 | public class MatlabRMIClassLoaderSpi extends RMIClassLoaderSpi { 35 | /** 36 | * Loading of classes is delegated to the default {@link RMIClassLoaderSpi}. 37 | */ 38 | private final RMIClassLoaderSpi _delegateLoaderSpi = RMIClassLoader.getDefaultProviderInstance(); 39 | 40 | /** 41 | * The codebase of the external virtual machine which has a proxy that can interact with this session of MATLAB. 42 | * This is done instead of setting the {@code java.rmi.server.codebase} property so that matlabcontrol does not 43 | * interfere with any other uses of RMI in the application. 44 | */ 45 | private static volatile String _remoteCodebase = null; 46 | 47 | /** 48 | * Sets the codebase of the currently connected external JVM. This should be called only once per connection to an 49 | * external JVM and should occur before users of the API can send objects over RMI. 50 | * 51 | * @param remoteCodebase 52 | */ 53 | public static void setCodebase(String remoteCodebase) { 54 | _remoteCodebase = remoteCodebase; 55 | } 56 | 57 | @Override 58 | public Class loadClass(String codebase, String name, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { 59 | return _delegateLoaderSpi.loadClass(_remoteCodebase, name, defaultLoader); 60 | } 61 | 62 | @Override 63 | public Class loadProxyClass(String codebase, String[] interfaces, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { 64 | return _delegateLoaderSpi.loadProxyClass(_remoteCodebase, interfaces, defaultLoader); 65 | } 66 | 67 | @Override 68 | public ClassLoader getClassLoader(String codebase) throws MalformedURLException { 69 | return _delegateLoaderSpi.getClassLoader(_remoteCodebase); 70 | } 71 | 72 | /** 73 | * {@inheritDoc} 74 | *

75 | * The returned annotation becomes the {@code codebase} argument in 76 | * {@link #loadClass(java.lang.String, java.lang.String, java.lang.ClassLoader)} when the {@code RMIClassLoaderSpi} 77 | * in the receiving JVM attempts to load {@code clazz}. This allows for classes defined in MATLAB but not in the 78 | * receiving JVM to find and load the class definition. 79 | * 80 | * @param clazz 81 | * @return 82 | */ 83 | @Override 84 | public String getClassAnnotation(Class clazz) { 85 | if (clazz == null) { 86 | throw new NullPointerException("class may not be null"); 87 | } 88 | 89 | String annotation = null; 90 | 91 | //If the class has a code source, meaning it is not part of the Java Runtime Environment 92 | if (clazz.getProtectionDomain().getCodeSource() != null) { 93 | //This convoluted way of determining the code source location is necessary due to a bug in early versions of 94 | //Java 6 on Windows (such as what is used by MATLAB R2007b) which puts a space in the code source's URL. 95 | //A space in the URL will cause the receiver of this annotation to treat it as a path separator, which would 96 | //be very problematic and likely cause invalid protocol exceptions. 97 | try { 98 | File file = new File(clazz.getProtectionDomain().getCodeSource().getLocation().getPath()); 99 | annotation = file.toURI().toURL().toString(); 100 | } catch (MalformedURLException e) {} 101 | } 102 | 103 | return annotation; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabProxyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import matlabcontrol.MatlabProxy.Identifier; 9 | 10 | /** 11 | * Creates instances of {@link MatlabProxy}. Any number of proxies may be created with a factory. 12 | *

13 | * How the proxies will connect to a session of MATLAB depends on whether the factory is running inside or outside 14 | * MATLAB: 15 | *

16 | * Running inside MATLAB
17 | * The proxy will connect to the session of MATLAB this factory is running in. 18 | *

19 | * Running outside MATLAB
20 | * By default a new session of MATLAB will be started and connected to, but the factory may be configured via the 21 | * options provided to this factory to connect to a previously controlled session. 22 | *

23 | * This class is unconditionally thread-safe. Any number of proxies may be created simultaneously. 24 | * 25 | * @since 4.0.0 26 | * 27 | * @author Joshua Kaplan 28 | */ 29 | public class MatlabProxyFactory implements ProxyFactory { 30 | private final ProxyFactory _delegateFactory; 31 | 32 | /** 33 | * Constructs the factory using default options. 34 | * 35 | * @throws MatlabConnectionException 36 | */ 37 | public MatlabProxyFactory() { 38 | this(new MatlabProxyFactoryOptions.Builder().build()); 39 | } 40 | 41 | /** 42 | * Constructs the factory with the specified {@code options}. Depending on the whether the factory is running inside 43 | * MATLAB or outside MATLAB will determine if a given option is used. 44 | * 45 | * @param options 46 | */ 47 | public MatlabProxyFactory(MatlabProxyFactoryOptions options) { 48 | if (Configuration.isRunningInsideMatlab()) { 49 | _delegateFactory = new LocalMatlabProxyFactory(options); 50 | } else { 51 | _delegateFactory = new RemoteMatlabProxyFactory(options); 52 | } 53 | } 54 | 55 | @Override 56 | public MatlabProxy getProxy() throws MatlabConnectionException { 57 | return _delegateFactory.getProxy(); 58 | } 59 | 60 | @Override 61 | public Request requestProxy(RequestCallback callback) throws MatlabConnectionException { 62 | if (callback == null) { 63 | throw new NullPointerException("The request callback may not be null"); 64 | } 65 | 66 | return _delegateFactory.requestProxy(callback); 67 | } 68 | 69 | /** 70 | * Provides the requested proxy. 71 | * 72 | * @since 4.0.0 73 | * @author Joshua Kaplan 74 | */ 75 | public static interface RequestCallback { 76 | /** 77 | * Called when the proxy has been created. Because requests have no timeout, there is no guarantee that this 78 | * method will ever be called. 79 | * 80 | * @param proxy 81 | */ 82 | public void proxyCreated(MatlabProxy proxy); 83 | } 84 | 85 | /** 86 | * A request for a proxy. Because requests have no timeout, a {@code Request} has no concept of 87 | * failure. 88 | *

89 | * Implementations of this class are unconditionally thread-safe. 90 | *

91 | * WARNING: This interface is not intended to be implemented by users of matlabcontrol. Methods may be added 92 | * to this interface, and these additions will not be considered breaking binary compatibility. 93 | * 94 | * @since 4.0.0 95 | * @author Joshua Kaplan 96 | */ 97 | public static interface Request { 98 | /** 99 | * The identifier of the proxy associated with this request. If the proxy is created, then its identifier 100 | * accessible via {@link MatlabProxy#getIdentifier()} will return {@code true} when tested for equivalence with 101 | * the identifier returned by this method using {@link Identifier#equals(java.lang.Object)}. 102 | * 103 | * @return proxy's identifier 104 | */ 105 | public Identifier getProxyIdentifer(); 106 | 107 | /** 108 | * Attempts to cancel the request. If the request has already been completed or cannot successfully be canceled 109 | * then {@code false} will be returned, otherwise {@code true} will be returned. If the request has already been 110 | * successfully canceled then this method will have no effect and {@code true} will be returned. 111 | * 112 | * @return if successfully cancelled 113 | */ 114 | public boolean cancel(); 115 | 116 | /** 117 | * If the request has been successfully cancelled. 118 | * 119 | * @return if successfully cancelled 120 | */ 121 | public boolean isCancelled(); 122 | 123 | /** 124 | * Returns {@code true} if the proxy has been created. 125 | * 126 | * @return if the proxy has been created 127 | */ 128 | public boolean isCompleted(); 129 | } 130 | 131 | /** 132 | * A callback interface for receiving the commands which must be 133 | * copy-pasted into MATLAB to initiate a connection. 134 | */ 135 | public interface CopyPasteCallback { 136 | /** 137 | * The given code should be copy-pasted into MATLAB. 138 | */ 139 | void copyPaste(String matlabCmdsToConnect); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabBroadcaster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.rmi.NoSuchObjectException; 9 | import java.rmi.NotBoundException; 10 | import java.rmi.RemoteException; 11 | import java.rmi.registry.Registry; 12 | import java.rmi.server.UnicastRemoteObject; 13 | import java.util.Timer; 14 | import java.util.TimerTask; 15 | 16 | /** 17 | * Enables a session of MATLAB to be connected to by matlabcontrol running outside MATLAB. 18 | * 19 | * @since 4.0.0 20 | * 21 | * @author Joshua Kaplan 22 | */ 23 | class MatlabBroadcaster { 24 | /** 25 | * A reference to the RMI registry which holds {@code MatlabSession}s. 26 | */ 27 | private static Registry _registry = null; 28 | 29 | /** 30 | * Represents this session of MATLAB. 31 | */ 32 | private static final MatlabSessionImpl _session = new MatlabSessionImpl(); 33 | 34 | /** 35 | * The frequency (in milliseconds) with which to check if the connection to the registry still exists. 36 | */ 37 | private static final int BROADCAST_CHECK_PERIOD = 1000; 38 | 39 | /** 40 | * The timer used to check if still connected to the registry. 41 | */ 42 | private static final Timer _broadcastTimer = new Timer("MLC Broadcast Maintainer"); 43 | 44 | /** 45 | * Private constructor so this class cannot be constructed. 46 | */ 47 | private MatlabBroadcaster() {} 48 | 49 | /** 50 | * Returns the session object bound to the RMI registry by this broadcaster. 51 | * 52 | * @return 53 | */ 54 | static MatlabSessionImpl getSession() { 55 | return _session; 56 | } 57 | 58 | /** 59 | * Makes this session of MATLAB visible to matlabcontrol. Once broadcasting, matlabcontrol running outside MATLAB 60 | * will be able to connect to this session of MATLAB. 61 | * 62 | * @throws MatlabConnectionException thrown if not running inside MATLAB or unable to broadcast 63 | */ 64 | synchronized static void broadcast(int broadcastPort) throws MatlabConnectionException { 65 | //If the registry hasn't been created 66 | if (_registry == null) { 67 | //Create or retrieve an RMI registry 68 | setupRegistry(broadcastPort); 69 | 70 | //Register this session so that it can be reconnected to 71 | bindSession(); 72 | 73 | //If the registry becomes disconnected, either create a new one or locate a new one 74 | maintainRegistryConnection(broadcastPort); 75 | } 76 | } 77 | 78 | /** 79 | * Attempts to create a registry, and if that cannot be done, then attempts to get an existing registry. 80 | * 81 | * @throws MatlabConnectionException if a registry can neither be created nor retrieved 82 | */ 83 | private static void setupRegistry(int broadcastPort) throws MatlabConnectionException { 84 | try { 85 | _registry = LocalHostRMIHelper.createRegistry(broadcastPort); 86 | } 87 | //If we can't create one, try to retrieve an existing one 88 | catch (Exception e) { 89 | try { 90 | _registry = LocalHostRMIHelper.getRegistry(broadcastPort); 91 | } catch (Exception ex) { 92 | throw new MatlabConnectionException("Could not create or connect to the RMI registry", ex); 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * Binds the session object, an instance of {@link MatlabSession} to the registry with {@link #SESSION_ID}. 99 | * 100 | * @throws MatlabConnectionException 101 | */ 102 | private static void bindSession() throws MatlabConnectionException { 103 | //Unexport the object, it will throw an exception if it is not bound - so ignore that 104 | try { 105 | UnicastRemoteObject.unexportObject(_session, true); 106 | } catch (NoSuchObjectException e) {} 107 | 108 | try { 109 | _registry.bind(_session.getSessionID(), LocalHostRMIHelper.exportObject(_session)); 110 | } catch (Exception e) { 111 | throw new MatlabConnectionException("Could not register this session of MATLAB", e); 112 | } 113 | } 114 | 115 | /** 116 | * Checks with a timer that the registry still exists and that the session object is exported to it. If either 117 | * stop being the case then an attempt is made to re-establish. 118 | */ 119 | private static void maintainRegistryConnection(final int broadcastPort) { 120 | //Configure the a timer to monitor the broadcast 121 | _broadcastTimer.schedule(new TimerTask() { 122 | @Override 123 | public void run() { 124 | //Check if the registry is connected 125 | try { 126 | //Will succeed if connected and the session object is still exported 127 | _registry.lookup(_session.getSessionID()); 128 | } 129 | //Session object is no longer exported 130 | catch (NotBoundException e) { 131 | try { 132 | bindSession(); 133 | } 134 | //Nothing more can be done if this fails 135 | catch (MatlabConnectionException ex) {} 136 | } 137 | //Registry is no longer connected 138 | catch (RemoteException e) { 139 | try { 140 | setupRegistry(broadcastPort); 141 | bindSession(); 142 | } 143 | //Nothing more can be done if this fails 144 | catch (MatlabConnectionException ex) {} 145 | } 146 | } 147 | }, BROADCAST_CHECK_PERIOD, BROADCAST_CHECK_PERIOD); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | /** 9 | * Operations which interact with a session of MATLAB. 10 | *

11 | * WARNING: This interface is not intended to be implemented by users of matlabcontrol. Methods may be added to 12 | * this interface, and these additions will not be considered breaking binary compatability. 13 | * 14 | * @since 4.1.0 15 | * 16 | * @author Joshua Kaplan 17 | */ 18 | public interface MatlabOperations { 19 | /** 20 | * Evaluates a command in MATLAB. This is equivalent to MATLAB's {@code eval('command')}. 21 | * 22 | * @param command the command to be evaluated in MATLAB 23 | * @throws MatlabInvocationException 24 | */ 25 | public void eval(String command) throws MatlabInvocationException; 26 | 27 | /** 28 | * Evaluates a command in MATLAB, returning the result. This is equivalent to MATLAB's {@code eval('command')}. 29 | *

30 | * In order for the result of this command to be returned the number of arguments to be returned must be specified 31 | * by {@code nargout}. This is equivalent in MATLAB to the number of variables placed on the left hand side of an 32 | * expression. For example, in MATLAB the {@code inmem} function may be used with either 1, 2, or 3 return values 33 | * each resulting in a different behavior: 34 | *
 35 | 	 * {@code
 36 | 	 * M = inmem;
 37 | 	 * [M, X] = inmem;
 38 | 	 * [M, X, J] = inmem;
 39 | 	 * }
 40 | 	 * 
41 | * The returned {@code Object} array will be of length {@code nargout} with each return argument placed into the 42 | * corresponding array position. 43 | *

44 | * If the command cannot return the number of arguments specified by {@code nargout} then an exception will be 45 | * thrown. 46 | * 47 | * @param command the command to be evaluated in MATLAB 48 | * @param nargout the number of arguments that will be returned from evaluating {@code command} 49 | * @return result of MATLAB command, the length of the array will be {@code nargout} 50 | * @throws MatlabInvocationException 51 | */ 52 | public Object[] returningEval(String command, int nargout) throws MatlabInvocationException; 53 | 54 | /** 55 | * Calls a MATLAB function with the name {@code functionName}, returning the result. Arguments to the function may 56 | * be provided as {@code args}, but are not required if the function needs no arguments. 57 | *

58 | * The function arguments will be converted into MATLAB equivalents as appropriate. Importantly, this means that a 59 | * {@code String} will be converted to a MATLAB {@code char} array, not a variable name. 60 | * 61 | * @param functionName the name of the MATLAB function to call 62 | * @param args the arguments to the function 63 | * @throws MatlabInvocationException 64 | */ 65 | public void feval(String functionName, Object... args) throws MatlabInvocationException; 66 | 67 | /** 68 | * Calls a MATLAB function with the name {@code functionName}, returning the result. Arguments to the function may 69 | * be provided as {@code args}, but are not required if the function needs no arguments. 70 | *

71 | * The function arguments will be converted into MATLAB equivalents as appropriate. Importantly, this means that a 72 | * {@code String} will be converted to a MATLAB {@code char} array, not a variable name. 73 | *

74 | * In order for the result of this function to be returned the number of arguments to be returned must be specified 75 | * by {@code nargout}. This is equivalent in MATLAB to the number of variables placed on the left hand side of an 76 | * expression. For example, in MATLAB the {@code inmem} function may be used with either 1, 2, or 3 return values 77 | * each resulting in a different behavior: 78 | *
 79 | 	 * {@code
 80 | 	 * M = inmem;
 81 | 	 * [M, X] = inmem;
 82 | 	 * [M, X, J] = inmem;
 83 | 	 * }
 84 | 	 * 
85 | * The returned {@code Object} array will be of length {@code nargout} with each return argument placed into the 86 | * corresponding array position. 87 | *

88 | * If the function is not capable of returning the number of arguments specified by {@code nargout} then an 89 | * exception will be thrown. 90 | * 91 | * @param functionName the name of the MATLAB function to call 92 | * @param nargout the number of arguments that will be returned by {@code functionName} 93 | * @param args the arguments to the function 94 | * @return result of MATLAB function, the length of the array will be {@code nargout} 95 | * @throws MatlabInvocationException 96 | */ 97 | public Object[] returningFeval(String functionName, int nargout, Object... args) throws MatlabInvocationException; 98 | 99 | /** 100 | * Sets {@code variableName} to {@code value} in MATLAB, creating the variable if it does not yet exist. 101 | * 102 | * @param variableName 103 | * @param value 104 | * @throws MatlabInvocationException 105 | */ 106 | public void setVariable(String variableName, Object value) throws MatlabInvocationException; 107 | 108 | /** 109 | * Gets the value of {@code variableName} in MATLAB. 110 | * 111 | * @param variableName 112 | * @return value 113 | * @throws MatlabInvocationException 114 | */ 115 | public Object getVariable(String variableName) throws MatlabInvocationException; 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabFunctionHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | import matlabcontrol.MatlabInvocationException; 12 | import matlabcontrol.MatlabOperations; 13 | 14 | /** 15 | * 16 | * @since 4.2.0 17 | * @author Joshua Kaplan 18 | */ 19 | public final class MatlabFunctionHandle extends MatlabType { 20 | private final String _function; 21 | 22 | public MatlabFunctionHandle(String functionName) { 23 | if (functionName == null) { 24 | throw new NullPointerException("MATLAB function name may not be null"); 25 | } 26 | 27 | if (functionName.isEmpty()) { 28 | throw new IllegalArgumentException("Invalid MATLAB function name; may not be an empty String"); 29 | } 30 | 31 | //Anonymous function 32 | if (functionName.startsWith("@")) { 33 | checkAnonymousFunction(functionName); 34 | } 35 | //Regular function 36 | else { 37 | checkFunctionName(functionName); 38 | } 39 | 40 | _function = functionName; 41 | } 42 | 43 | private static void checkAnonymousFunction(String function) { 44 | char[] functionChars = function.toCharArray(); 45 | 46 | //Validate that the the anonymous function is of the form @(something) 47 | if (functionChars.length < 2 || functionChars[1] != '(') { 48 | throw new IllegalArgumentException("Invalid anonymous MATLAB function: " + function + "\n" + 49 | "@ must be followed with ("); 50 | } 51 | int closingParenIndex = function.indexOf(")"); 52 | if (closingParenIndex == -1) { 53 | throw new IllegalArgumentException("Invalid anonymous MATLAB function: " + function + "\n" + 54 | "Must terminate the argument list with )"); 55 | } 56 | 57 | //Validate all of the arguments of the anonymous function 58 | String argsString = function.substring(2, closingParenIndex); 59 | String[] args; 60 | args = argsString.isEmpty() ? new String[0] : argsString.split(", "); 61 | Set seenArgs = new HashSet(); 62 | for (String arg : args) { 63 | arg = arg.trim(); 64 | 65 | if (seenArgs.contains(arg)) { 66 | throw new IllegalArgumentException("Invalid anonymous MATLAB function: " + function + "\n" + 67 | "Invalid argument name: " + arg + "\n" + 68 | "Argument names must be unique"); 69 | } 70 | seenArgs.add(arg); 71 | 72 | char[] argsChars = arg.toCharArray(); 73 | 74 | if (!Character.isLetter(argsChars[0])) { 75 | throw new IllegalArgumentException("Invalid anonymous MATLAB function: " + function + "\n" + 76 | "Invalid argument name: " + arg + "\n" + 77 | "Argument must begin with a letter"); 78 | } 79 | 80 | for (char element : argsChars) { 81 | if (!(Character.isLetter(element) || Character.isDigit(element) || element == '_')) { 82 | throw new IllegalArgumentException("Invalid anonymous MATLAB function: " + function + "\n" + 83 | "Invalid argument name: " + arg + "\n" + 84 | "Argument must consist only of letters, numbers, and underscores"); 85 | } 86 | } 87 | } 88 | 89 | //Validate the function has a body, but don't actually validate the body 90 | String body = function.substring(closingParenIndex + 1, function.length()); 91 | if (body.trim().isEmpty()) { 92 | throw new IllegalArgumentException("Invalid anonymous MATLAB function: " + function + "\n" + 93 | "Anonymous function must have a body"); 94 | } 95 | } 96 | 97 | private static void checkFunctionName(String functionName) { 98 | char[] nameChars = functionName.toCharArray(); 99 | 100 | if (!Character.isLetter(nameChars[0])) { 101 | throw new IllegalArgumentException("Invalid MATLAB function name: " + functionName + "\n" + 102 | "Function name must begin with a letter"); 103 | } 104 | 105 | for (char element : nameChars) { 106 | if (!(Character.isLetter(element) || Character.isDigit(element) || element == '_')) { 107 | throw new IllegalArgumentException("Invalid MATLAB function name: " + functionName + "\n" + 108 | "Function name must consist only of letters, numbers, and underscores"); 109 | } 110 | } 111 | } 112 | 113 | @Override 114 | public String toString() { 115 | return "[" + this.getClass().getName() + " functionName=" + _function + "]"; 116 | } 117 | 118 | @Override 119 | MatlabTypeSetter getSetter() { 120 | return new MatlabFunctionHandlerSetter(_function); 121 | } 122 | 123 | private static class MatlabFunctionHandlerSetter implements MatlabTypeSetter { 124 | private static final long serialVersionUID = 1909686398012439080L; 125 | private final String _function; 126 | 127 | public MatlabFunctionHandlerSetter(String function) { 128 | _function = function; 129 | } 130 | 131 | @Override 132 | public void setInMatlab(MatlabOperations ops, String variableName) throws MatlabInvocationException { 133 | if (_function.startsWith("@")) { 134 | ops.eval(variableName + " = " + _function + ";"); 135 | } else { 136 | ops.eval(variableName + " = @" + _function + ";"); 137 | } 138 | } 139 | } 140 | 141 | static class MatlabFunctionHandleGetter implements MatlabTypeGetter { 142 | private static final long serialVersionUID = 4448554689248088229L; 143 | private String _function; 144 | private boolean _retrieved = false; 145 | 146 | @Override 147 | public MatlabFunctionHandle retrieve() { 148 | if (_retrieved) { 149 | return new MatlabFunctionHandle(_function); 150 | } else { 151 | throw new IllegalStateException("MatlabFunctionHandle cannot be deserialized until the data has been " + 152 | "retrieved from MATLAB"); 153 | } 154 | } 155 | 156 | @Override 157 | public void getInMatlab(MatlabOperations ops, String variableName) throws MatlabInvocationException { 158 | _function = (String) ops.returningEval("func2str(" + variableName + ");", 1)[0]; 159 | 160 | _retrieved = true; 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/extensions/MatlabTypeConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.extensions; 7 | 8 | import java.io.Serializable; 9 | 10 | import matlabcontrol.MatlabInvocationException; 11 | import matlabcontrol.MatlabProxy; 12 | import matlabcontrol.MatlabProxy.MatlabThreadCallable; 13 | import matlabcontrol.MatlabProxy.MatlabThreadProxy; 14 | 15 | /** 16 | * Converts between MATLAB and Java types. Currently only supports numeric arrays. 17 | *

18 | * This class is unconditionally thread-safe. 19 | * 20 | * @since 4.0.0 21 | * 22 | * @author Joshua Kaplan 23 | */ 24 | public class MatlabTypeConverter { 25 | private final MatlabProxy _proxy; 26 | 27 | /** 28 | * Constructs the converter. 29 | * 30 | * @param proxy 31 | */ 32 | public MatlabTypeConverter(MatlabProxy proxy) { 33 | _proxy = proxy; 34 | } 35 | 36 | /** 37 | * Retrieves the MATLAB numeric array with the variable name {@code arrayName}. 38 | * 39 | * @param arrayName 40 | * @return the retrieved numeric array 41 | * @throws matlabcontrol.MatlabInvocationException if thrown by the proxy 42 | */ 43 | public MatlabNumericArray getNumericArray(String arrayName) throws MatlabInvocationException { 44 | ArrayInfo info = _proxy.invokeAndWait(new GetArrayCallable(arrayName)); 45 | 46 | return new MatlabNumericArray(info.real, info.imaginary, info.lengths); 47 | } 48 | 49 | private static class GetArrayCallable implements MatlabThreadCallable, Serializable { 50 | private static final long serialVersionUID = -1269094288040717603L; 51 | private final String _arrayName; 52 | 53 | public GetArrayCallable(String arrayName) { 54 | _arrayName = arrayName; 55 | } 56 | 57 | @Override 58 | public ArrayInfo call(MatlabThreadProxy proxy) throws MatlabInvocationException { 59 | //Retrieve real values 60 | Object realObject = proxy.returningEval("real(" + _arrayName + ");", 1)[0]; 61 | double[] realValues = (double[]) realObject; 62 | 63 | //Retrieve imaginary values if present 64 | boolean isReal = ((boolean[]) proxy.returningEval("isreal(" + _arrayName + ");", 1)[0])[0]; 65 | double[] imaginaryValues = null; 66 | if (!isReal) { 67 | Object imaginaryObject = proxy.returningEval("imag(" + _arrayName + ");", 1)[0]; 68 | imaginaryValues = (double[]) imaginaryObject; 69 | } 70 | 71 | //Retrieve lengths of array 72 | double[] size = (double[]) proxy.returningEval("size(" + _arrayName + ");", 1)[0]; 73 | int[] lengths = new int[size.length]; 74 | for (int i = 0; i < size.length; i++) { 75 | lengths[i] = (int) size[i]; 76 | } 77 | 78 | return new ArrayInfo(realValues, imaginaryValues, lengths); 79 | } 80 | } 81 | 82 | private static class ArrayInfo implements Serializable { 83 | private static final long serialVersionUID = 4464014916120235711L; 84 | private final double[] real, imaginary; 85 | private final int[] lengths; 86 | 87 | public ArrayInfo(double[] real, double[] imaginary, int[] lengths) { 88 | this.real = real; 89 | this.imaginary = imaginary; 90 | this.lengths = lengths; 91 | } 92 | } 93 | 94 | /** 95 | * Stores the {@code array} in MATLAB with the variable name {@code arrayName}. 96 | * 97 | * @param arrayName the variable name 98 | * @param array 99 | * @throws matlabcontrol.MatlabInvocationException if thrown by the proxy 100 | */ 101 | public void setNumericArray(String arrayName, MatlabNumericArray array) throws MatlabInvocationException { 102 | _proxy.invokeAndWait(new SetArrayCallable(arrayName, array)); 103 | } 104 | 105 | private static class SetArrayCallable implements MatlabThreadCallable, Serializable { 106 | private static final long serialVersionUID = -7403498224028558628L; 107 | private final String _arrayName; 108 | private final double[] _realArray, _imaginaryArray; 109 | private final int[] _lengths; 110 | 111 | private SetArrayCallable(String arrayName, MatlabNumericArray array) { 112 | _arrayName = arrayName; 113 | _realArray = array.getRealLinearArray(); 114 | _imaginaryArray = array.getImaginaryLinearArray(); 115 | _lengths = array.getLengths(); 116 | } 117 | 118 | @Override 119 | public Object call(MatlabThreadProxy proxy) throws MatlabInvocationException { 120 | //Store real array in the MATLAB environment 121 | String realArray = (String) proxy.returningEval("genvarname('" + _arrayName + "_real', who);", 1)[0]; 122 | proxy.setVariable(realArray, _realArray); 123 | 124 | //If present, store the imaginary array in the MATLAB environment 125 | String imagArray = null; 126 | if (_imaginaryArray != null) { 127 | imagArray = (String) proxy.returningEval("genvarname('" + _arrayName + "_imag', who);", 1)[0]; 128 | proxy.setVariable(imagArray, _imaginaryArray); 129 | } 130 | 131 | //Build a statement to eval 132 | // - If imaginary array exists, combine the real and imaginary arrays 133 | // - Set the proper dimension length metadata 134 | // - Store as arrayName 135 | StringBuilder evalStatement = new StringBuilder(_arrayName + " = reshape(" + realArray); 136 | if (_imaginaryArray != null) { 137 | evalStatement.append(" + " + imagArray + " * i"); 138 | } 139 | for (int length : _lengths) { 140 | evalStatement.append(", " + length); 141 | } 142 | evalStatement.append(");"); 143 | proxy.eval(evalStatement.toString()); 144 | 145 | //Clear variables holding separate real and imaginary arrays 146 | proxy.eval("clear " + realArray + ";"); 147 | proxy.eval("clear " + imagArray + ";"); 148 | 149 | return null; 150 | } 151 | } 152 | 153 | /** 154 | * Returns a brief description of this converter. The exact details of this representation are unspecified and are 155 | * subject to change. 156 | * 157 | * @return 158 | */ 159 | @Override 160 | public String toString() { 161 | return "[" + this.getClass().getName() + " proxy=" + _proxy + "]"; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MatConsoleCtl: Control MATLAB from Java 2 | 3 | 14 | [![Maven artifact](https://img.shields.io/badge/mavenCentral-com.diffplug.matsim%3Amatconsolectl-blue.svg)](https://bintray.com/diffplug/opensource/matconsolectl/view) 15 | [![Latest version](https://img.shields.io/badge/latest-4.6.0-blue.svg)](https://github.com/diffplug/matconsolectl/releases/latest) 16 | [![Javadoc](https://img.shields.io/badge/javadoc-OK-blue.svg)](https://diffplug.github.io/matconsolectl/javadoc/4.6.0/) 17 | [![License Apache](https://img.shields.io/badge/license-BSD-blue.svg)](https://tldrlegal.com/license/bsd-3-clause-license-(revised)) 18 | 19 | [![Changelog](https://img.shields.io/badge/changelog-4.7.0--SNAPSHOT-brightgreen.svg)](CHANGES.md) 20 | [![Travis CI](https://travis-ci.org/diffplug/matconsolectl.svg?branch=master)](https://travis-ci.org/diffplug/matconsolectl) 21 | 22 | 23 | MatConsoleCtl is a Java API that allows calling MATLAB from Java. You can `eval`, `feval`, as well as `get` and `set` variables. Interaction can be performed from either inside MATLAB or outside MATLAB (both by starting a new MATLAB or by connecting to an existing MATLAB). 24 | 25 | 30 | 31 | ```java 32 | MatlabProxyFactoryOptions.Builder builder = new MatlabProxyFactoryOptions.Builder(); 33 | // setup the factory 34 | // setCopyPasteCallback() connects to an existing MATLAB by copy-pasting a few lines into the command window 35 | // setUsePreviouslyControlledSession() starts a new MATLAB or connects to a previously started MATLAB without any user intervention 36 | 37 | MatlabProxyFactory factory = new MatlabProxyFactory(builder.build()); 38 | // get the proxy 39 | MatlabProxy proxy = factory.getProxy(); 40 | // do stuff over the proxy 41 | proxy.eval("disp('hello world!)") 42 | proxy.setVariable("a", 5.0); 43 | Object a = proxy.getVariable("a"); 44 | double actual = ((double[]) result)[0]; 45 | assert(actual == 5.0) 46 | // disconnect the proxy 47 | proxy.disconnect(); 48 | ``` 49 | 50 | Javadoc links for [MatlabProxyFactoryOptions.Builder](http://diffplug.github.io/matconsolectl/javadoc/snapshot/matlabcontrol/MatlabProxyFactoryOptions.Builder.html) and [MatlabProxy](http://diffplug.github.io/matconsolectl/javadoc/snapshot/matlabcontrol/MatlabProxy.html). 51 | 52 | Contributions are welcome, see the [contributing guide](CONTRIBUTING.md) for development info. 53 | 54 | ## Demo 55 | 56 | MatConsoleCtl includes a demo GUI. Below is a script you can use to run the demo inside of MATLAB: 57 | 58 | ```matlab 59 | version = '4.6.0'; 60 | tempdir = 'matconsolectl_demo'; 61 | 62 | % make a directory to copy the jar into 63 | mkdir(tempdir); 64 | % download the jar 65 | URL = ['https://repo1.maven.org/maven2/com/diffplug/matsim/matconsolectl/' version '/matconsolectl-' version '.jar']; 66 | filename = [tempdir '/matconsolectl-' version '.jar']; 67 | websave(filename,URL); 68 | % add it to the path 69 | javaaddpath([pwd '\' filename]); 70 | 71 | % run it 72 | matlabcontrol.demo.DemoMain 73 | ``` 74 | 75 | You can also run the demo outside of MATLAB by downloading the jar, then running `java -jar matconsolectl-4.6.0.jar` at a console. 76 | 77 | ## Compatibility 78 | 79 | MatConsoleCtl works on Win/Mac/Linux, MATLAB R2007b through R2016b, and it will continue to work so long as MATLAB maintains the Java MATLAB Interface.\* 80 | 81 | \* On OS X 10.5, R2009a and earlier, you will need to do some hacking. matlabcontrol requires Java 6, and Apple only released 64-bit Java 6 for OS X 10.5, while MATLAB only released 32-bit MATLAB for R2009a and earlier. There are unofficial ways to run 32-bit Java 6 on OS X 10.5. 82 | 83 | 84 | 85 | ## Acknowledgements 86 | 87 | This is forked from the matlabcontrol project originally maintained on the now-defunct [Google Code](https://code.google.com/p/matlabcontrol/). The name was changed to ensure that we don't infringe the original project's license, but we did not change the package names, so this project is binary compatible with the original matlabcontrol. We are very thankful to Joshua Kaplan for creating matlabcontrol, but this fork is in no way associated with or endorsed by any authors of the original project. 88 | 89 | We have fixed some bugs and added some features (see the [changelog](CHANGES.md)), and we will maintain this library into the future. We're happy to accept [pull requests](CONTRIBUTING.md) too! 90 | 91 | * Formatted by [spotless](https://github.com/diffplug/spotless), [as such](https://github.com/diffplug/matconsolectl/blob/v4.4.1/build.gradle?ts=4#L169-L189). 92 | * Bugs found by [findbugs](http://findbugs.sourceforge.net/), [as such](https://github.com/diffplug/matconsolectl/blob/v4.4.1/build.gradle?ts=4#L191-L215). 93 | * OSGi metadata generated by JRuyi's [osgibnd-gradle-plugin] (https://github.com/jruyi/osgibnd-gradle-plugin), which leverages Peter Kriens' [bnd](http://www.aqute.biz/Bnd/Bnd). 94 | * Scripts in the `.ci` folder are inspired by [Ben Limmer's work](http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/). 95 | * Built by [gradle](http://gradle.org/). 96 | * Tested by [junit](http://junit.org/). 97 | * Maintained by [DiffPlug](http://www.diffplug.com/). 98 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/demo/ProxyMethodDescriptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.demo; 7 | 8 | /** 9 | * Represents a method in the matlabcontrol API along with information to adjust the GUI. 10 | * 11 | * @author Joshua Kaplan 12 | */ 13 | enum ProxyMethodDescriptor { 14 | EVAL("void eval(String command)", 15 | Documentation.EVAL, "command", "args (disabled)", false, 0), RETURNING_EVAL("Object[] returningEval(String command, int nargout)", 16 | Documentation.RETURNING_EVAL, "comamnd", "args (disabled)", true, 0), FEVAL("void feval(String functionName, Object... args)", 17 | Documentation.FEVAL, "functionName", "args", false, ArrayPanel.NUM_ENTRIES), RETURNING_FEVAL("Object[] returningFeval(String functionName, int nargout, Object... args)", 18 | Documentation.RETURNING_FEVAL, "functionName", "args", true, ArrayPanel.NUM_ENTRIES), SET_VARIABLE("void setVariable(String variableName, Object value)", 19 | Documentation.SET_VARIABLE, "variableName", "value", false, 1), GET_VARIABLE("Object getVariable(String variableName)", 20 | Documentation.GET_VARIABLE, "variableName", "args (disabled)", false, 0); 21 | 22 | private ProxyMethodDescriptor(String signature, String message, String stringInputName, 23 | String argsInputName, boolean returnCountEnabled, int argsInputNumberEnabled) { 24 | this.signature = signature; 25 | this.message = message; 26 | this.stringInputName = stringInputName; 27 | this.argsInputName = argsInputName; 28 | this.returnCountEnabled = returnCountEnabled; 29 | this.argsInputNumberEnabled = argsInputNumberEnabled; 30 | } 31 | 32 | /** 33 | * Method signature 34 | */ 35 | public final String signature; 36 | 37 | /** 38 | * Description of method. 39 | */ 40 | public final String message; 41 | 42 | /** 43 | * Text that surrounds the border of the text input field. 44 | */ 45 | public final String stringInputName; 46 | 47 | /** 48 | * Text that surrounds the border of the arguments input field. 49 | */ 50 | public final String argsInputName; 51 | 52 | /** 53 | * Whether the return count field is enabled. 54 | */ 55 | public final boolean returnCountEnabled; 56 | 57 | /** 58 | * Number of entries enabled in the argument input field. 59 | */ 60 | public final int argsInputNumberEnabled; 61 | 62 | @Override 63 | public String toString() { 64 | return signature; 65 | } 66 | 67 | private static class Documentation { 68 | private static final String EVAL = "Evaluates a command in MATLAB. This is equivalent to MATLAB's eval('command').", 69 | 70 | RETURNING_EVAL = "Evaluates a command in MATLAB, returning the result. This is equivalent to MATLAB's " + 71 | "eval('command')." + 72 | "

" + 73 | "In order for the result of this command to be returned the number of arguments to be returned must be " + 74 | "specified by nargout. This is equivalent in MATLAB to the number of variables placed on the " + 75 | "left hand side of an expression. For example, in MATLAB the inmem function may be used with " + 76 | "either 1, 2, or 3 return values each resulting in a different behavior:" + 77 | "
" +
 78 | 						"M = inmem;\n" +
 79 | 						"[M, X] = inmem;\n" +
 80 | 						"[M, X, J] = inmem;\n" +
 81 | 						"
" + 82 | "The returned Object array will be of length nargout with each return argument " + 83 | "placed into the corresponding array position." + 84 | "

" + 85 | "If the command cannot return the number of arguments specified by nargout then an exception " + 86 | "will be thrown.", 87 | 88 | FEVAL = "Calls a MATLAB function with the name functionName. Arguments to the function may be " + 89 | "provided as args, but are not required if the function needs no arguments." + 90 | "

" + 91 | "The function arguments will be converted into MATLAB equivalents as appropriate. Importantly, this means " + 92 | "that a String will be converted to a MATLAB char array, not a variable name.", 93 | 94 | RETURNING_FEVAL = "Calls a MATLAB function with the name functionName, returning the result. Arguments to " + 95 | "the function may be provided as args, but are not required if the function needs no arguments." + 96 | "

" + 97 | "The function arguments will be converted into MATLAB equivalents as appropriate. Importantly, this means " + 98 | "that a String will be converted to a MATLAB char array, not a variable name." + 99 | "

" + 100 | "In order for the result of this function to be returned the number of arguments to be returned must be " + 101 | "specified by nargout. This is equivalent in MATLAB to the number of variables placed on the " + 102 | "left hand side of an expression. For example, in MATLAB the inmem function may be used with " + 103 | "either 1, 2, or 3 return values each resulting in a different behavior:" + 104 | "
" +
105 | 						"M = inmem;\n" +
106 | 						"[M, X] = inmem;\n" +
107 | 						"[M, X, J] = inmem;\n" +
108 | 						"
" + 109 | "The returned Object array will be of length nargout with each return argument " + 110 | "placed into the corresponding array position." + 111 | "

" + 112 | "If the function is not capable of returning the number of arguments specified by nargout then " + 113 | "an exception will be thrown.", 114 | 115 | SET_VARIABLE = "Sets variableName to value in MATLAB, creating the variable if it does not " + 116 | "yet exist.", 117 | 118 | GET_VARIABLE = "Gets the value of variableName in MATLAB."; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/MatlabClassLoaderHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.lang.reflect.Method; 9 | import java.net.URI; 10 | import java.net.URISyntaxException; 11 | import java.net.URL; 12 | import java.net.URLClassLoader; 13 | 14 | /** 15 | * Configures class loading inside of MATLAB such that it will work properly with RMI. 16 | *

17 | * MATLAB uses {@code com.mathworks.jmi.CustomURLClassLoader} to load classes. Class loaders first ask their parent 18 | * class loader to attempt to load the class and so on up the chain all the way to the system class loader. (This is by 19 | * convention, one which {@code CustomURLClassLoader} follows). All classes listed on MATLAB's static class path 20 | * (defined in the {@code matlabroot/toolbox/local/classpath.txt} file where {@code matlabroot} is the location of 21 | * MATLAB) will be known to the system class loader. As such, all Java classes listed on the static class path will 22 | * be loaded by the system class loader. Classes listed on MATLAB's dynamic class path via the {@code javaaddpath} 23 | * function will be loaded by {@code CustomURLClassLoader}. 24 | *

25 | * When matlabcontrol is started outside MATLAB and launches a session of MATLAB it adds itself to MATLAB's dynamic 26 | * classpath. This allows matlabcontrol's code to run inside of MATLAB without needing to find and then modify the 27 | * classpath.txt file (which also very well might not be desired by the user). When matlabcontrol is started inside 28 | * MATLAB the user may have placed it on either the static or dynamic class path (the user could do both, but the 29 | * static class path would be used and the dynamic would be ignored). matlabcontrol cannot initially control which 30 | * class loader knows about matlabcontrol. 31 | *

32 | * The reason all of this matters is because of how RMI loads classes. When RMI attempts to load a class it starts by 33 | * asking the current thread for its class loader (@code Thread#getContextClassLoader}. (If that does not work it can 34 | * then attempt to load the class from a remote location, but this feature is not used by matlabcontrol and so this 35 | * step does not occur.) The class loader that is returned by the thread RMI is running on will not be MATLAB's 36 | * {@code CustomURLClassLoader}. As such, RMI will not operate properly if matlabcontrol was added to the dynamic class 37 | * path. 38 | *

39 | * When a class loader is attempting to load a class, it starts with its parent class loader and so on up the chain. By 40 | * informing the system class loader of the location of matlabcontrol, then RMI's class loader will be able to find the 41 | * matlabcontrol classes. This can be done via reflection (see {@link #addToSystemClassLoader()}. Once the 42 | * system class loader knows about the classes in matlabcontrol it will load the classes. If classes have been loaded 43 | * by the {@code CustomURLClassLoader} then problems can arise. The classes loaded by the system class loader and those 44 | * loaded by {@code CustomURLClassLoader} will exist in separate runtime packages, meaning they can 45 | * only access public classes, methods, and fields of one another. 46 | *

47 | * {@code MatlabClassLoaderHelper} manipulates class loading. If the system class loader knows about matlabcontrol 48 | * then no action is taken. Otherwise, the system class loader is told about matlabcontrol. When matlabcontrol launches 49 | * a session of MATLAB this class is called before any other class is loaded, and this class is never used again. As 50 | * such, if this class is loaded by the {@code CustomURLClassLoader}, it will not be a problem. 51 | * 52 | * @since 4.0.0 53 | * 54 | * @author Joshua Kaplan 55 | */ 56 | class MatlabClassLoaderHelper { 57 | /** 58 | * Configures class loading to work properly with RMI inside MATLAB. See the class description for more detail. 59 | * Called from within MATLAB when {@link RemoteMatlabProxyFactory} launches a session of MATLAB. 60 | * 61 | * @throws MatlabConnectionException 62 | */ 63 | public static void configureClassLoading() throws MatlabConnectionException { 64 | if (!isOnSystemClassLoader()) { 65 | addToSystemClassLoader(); 66 | } 67 | } 68 | 69 | /** 70 | * Determines if matlabcontrol is on the system class loader's class path. 71 | * 72 | * @return 73 | * @throws MatlabConnectionException 74 | */ 75 | private static boolean isOnSystemClassLoader() throws MatlabConnectionException { 76 | try { 77 | URI matlabcontrolLocation = MatlabClassLoaderHelper.class.getProtectionDomain().getCodeSource().getLocation().toURI(); 78 | 79 | URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 80 | URL[] urls = systemClassLoader.getURLs(); 81 | 82 | for (URL url : urls) { 83 | if (url.toURI().equals(matlabcontrolLocation)) { 84 | return true; 85 | } 86 | } 87 | return false; 88 | } catch (ClassCastException e) { 89 | throw new MatlabConnectionException("Unable to determine if matlabcontrol is on the system class loader's classpath", e); 90 | } catch (URISyntaxException e) { 91 | throw new MatlabConnectionException("Unable to determine if matlabcontrol is on the system class loader's classpath", e); 92 | } 93 | } 94 | 95 | /** 96 | * Adds the location where matlabcontrol exists to the system class loader. 97 | * 98 | * @throws MatlabConnectionException 99 | */ 100 | private static void addToSystemClassLoader() throws MatlabConnectionException { 101 | URL matlabcontrolLocation = MatlabClassLoaderHelper.class.getProtectionDomain().getCodeSource().getLocation(); 102 | 103 | try { 104 | URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 105 | Class classLoaderClass = URLClassLoader.class; 106 | 107 | Method method = classLoaderClass.getDeclaredMethod("addURL", URL.class); 108 | method.setAccessible(true); 109 | method.invoke(systemClassLoader, matlabcontrolLocation); 110 | } catch (Exception e) { 111 | throw new MatlabConnectionException("Unable to add matlabcontrol to system class loader", e); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabReturns.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import java.util.Arrays; 9 | 10 | /** 11 | * MATLAB return containers. 12 | * 13 | * @since 4.2.0 14 | * @author Joshua Kaplan 15 | */ 16 | @SuppressWarnings("unchecked") 17 | public final class MatlabReturns { 18 | private MatlabReturns() {} 19 | 20 | /** 21 | * Hidden super class of all of the {@code Return}X classes. This class is hidden because it is not a valid return 22 | * type from a method declared in an interface provided to {@link MatlabFunctionLinker} and there is no need for a 23 | * user to make use of this class. 24 | */ 25 | static class ReturnN { 26 | private final Object[] _values; 27 | 28 | ReturnN(Object[] values) { 29 | _values = values; 30 | } 31 | 32 | Object get(int i) { 33 | return _values[i]; 34 | } 35 | 36 | /** 37 | * Returns a brief description of this container. The exact details of this representation are unspecified and 38 | * are subject to change. 39 | * 40 | * @return 41 | */ 42 | @Override 43 | public String toString() { 44 | return "[" + this.getClass().getCanonicalName() + 45 | " size=" + _values.length + "," + 46 | " values=" + Arrays.toString(_values) + "]"; 47 | } 48 | } 49 | 50 | /** 51 | * Container for two MATLAB return values. 52 | * 53 | * @param first return type 54 | * @param second return type 55 | */ 56 | public static class Return2 extends ReturnN { 57 | Return2(Object[] values) { 58 | super(values); 59 | } 60 | 61 | /** 62 | * The first return argument. 63 | * 64 | * @return 65 | */ 66 | public A getFirst() { 67 | return (A) get(0); 68 | } 69 | 70 | /** 71 | * The second return argument. 72 | * 73 | * @return 74 | */ 75 | public B getSecond() { 76 | return (B) get(1); 77 | } 78 | } 79 | 80 | /** 81 | * Container for three MATLAB return values. 82 | * 83 | * @param first return type 84 | * @param second return type 85 | * @param third return type 86 | */ 87 | public static class Return3 extends Return2 { 88 | Return3(Object[] values) { 89 | super(values); 90 | } 91 | 92 | /** 93 | * The third return argument. 94 | * 95 | * @return 96 | */ 97 | public C getThird() { 98 | return (C) get(2); 99 | } 100 | } 101 | 102 | /** 103 | * Container for four MATLAB return values. 104 | * 105 | * @param first return type 106 | * @param second return type 107 | * @param third return type 108 | * @param fourth return type 109 | */ 110 | public static class Return4 extends Return3 { 111 | Return4(Object[] values) { 112 | super(values); 113 | } 114 | 115 | /** 116 | * The fourth return argument. 117 | * 118 | * @return 119 | */ 120 | public D getFourth() { 121 | return (D) get(3); 122 | } 123 | } 124 | 125 | /** 126 | * Container for five MATLAB return values. 127 | * 128 | * @param first return type 129 | * @param second return type 130 | * @param third return type 131 | * @param fourth return type 132 | * @param fifth return type 133 | */ 134 | public static class Return5 extends Return4 { 135 | Return5(Object[] values) { 136 | super(values); 137 | } 138 | 139 | /** 140 | * The fifth return argument. 141 | * 142 | * @return 143 | */ 144 | public E getFifth() { 145 | return (E) get(4); 146 | } 147 | } 148 | 149 | /** 150 | * Container for six MATLAB return values. 151 | * 152 | * @param first return type 153 | * @param second return type 154 | * @param third return type 155 | * @param fourth return type 156 | * @param fifth return type 157 | * @param sixth return type 158 | */ 159 | public static class Return6 extends Return5 { 160 | Return6(Object[] values) { 161 | super(values); 162 | } 163 | 164 | /** 165 | * The sixth return argument. 166 | * 167 | * @return 168 | */ 169 | public F getSixth() { 170 | return (F) get(5); 171 | } 172 | } 173 | 174 | /** 175 | * Container for seven MATLAB return values. 176 | * 177 | * @param first return type 178 | * @param second return type 179 | * @param third return type 180 | * @param fourth return type 181 | * @param fifth return type 182 | * @param sixth return type 183 | * @param seventh return type 184 | */ 185 | public static class Return7 extends Return6 { 186 | Return7(Object[] values) { 187 | super(values); 188 | } 189 | 190 | /** 191 | * The seventh return argument. 192 | * 193 | * @return 194 | */ 195 | public G getSeventh() { 196 | return (G) get(6); 197 | } 198 | } 199 | 200 | /** 201 | * Container for eight MATLAB return values. 202 | * 203 | * @param first return type 204 | * @param second return type 205 | * @param third return type 206 | * @param fourth return type 207 | * @param fifth return type 208 | * @param sixth return type 209 | * @param seventh return type 210 | * @param eight return type 211 | */ 212 | public static class Return8 extends Return7 { 213 | Return8(Object[] values) { 214 | super(values); 215 | } 216 | 217 | /** 218 | * The eight return argument. 219 | * 220 | * @return 221 | */ 222 | public H getEighth() { 223 | return (H) get(7); 224 | } 225 | } 226 | 227 | /** 228 | * Container for nine MATLAB return values. 229 | * 230 | * @param first return type 231 | * @param second return type 232 | * @param third return type 233 | * @param fourth return type 234 | * @param fifth return type 235 | * @param sixth return type 236 | * @param seventh return type 237 | * @param eight return type 238 | * @param ninth return type 239 | */ 240 | public static class Return9 extends Return8 { 241 | Return9(Object[] values) { 242 | super(values); 243 | } 244 | 245 | /** 246 | * The ninth argument. 247 | * 248 | * @return 249 | */ 250 | public I getNinth() { 251 | return (I) get(8); 252 | } 253 | } 254 | 255 | @SuppressWarnings("rawtypes") 256 | static ReturnN getMaxReturn(Object[] values) { 257 | return new Return9(values); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/JMIValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.lang.reflect.Method; 9 | import java.lang.reflect.Modifier; 10 | import java.util.HashSet; 11 | 12 | /** 13 | * Validates that the methods used by {@link JMIWrapper} are present in the current Java Virtual Machine, which should 14 | * always be MATLAB's JVM when this class is used. This is done because {@code jmi.jar} is entirely undocumented and 15 | * could change in any future release without notice. If that occurred it could result in a number of exceptions that 16 | * could be insufficiently informative to resolve the issue. This class throws detailed exceptions when an expected 17 | * method (or class the method belongs to) is not found. 18 | * 19 | * @since 4.0.0 20 | * 21 | * @author Joshua Kaplan 22 | */ 23 | class JMIValidator { 24 | private JMIValidator() {} 25 | 26 | /** 27 | * Checks that all of the methods matlabcontrol uses are present. If they are all present then nothing will happen. 28 | * If not, then an informative exception is thrown. 29 | * 30 | * @throws MatlabConnectionException 31 | */ 32 | static void validateJMIMethods() throws MatlabConnectionException { 33 | //Class: com.mathworks.jmi.Matlab 34 | Class matlabClass = getAndCheckClass("com.mathworks.jmi.Matlab"); 35 | 36 | //Method: public static Object mtFevalConsoleOutput(String, Object[], int) throws Exception 37 | checkMethod(matlabClass, Object.class, "mtFevalConsoleOutput", 38 | new Class[]{String.class, Object[].class, int.class}, 39 | new Class[]{Exception.class}); 40 | 41 | //Method: public static void whenMatlabIdle(Runnable) 42 | checkMethod(matlabClass, Void.TYPE, "whenMatlabIdle", 43 | new Class[]{Runnable.class}, 44 | new Class[0]); 45 | 46 | //Class: com.mathworks.jmi.NativeMatlab 47 | Class nativeMatlabClass = getAndCheckClass("com.mathworks.jmi.NativeMatlab"); 48 | 49 | //Method: public static boolean nativeIsMatlabThread() 50 | checkMethod(nativeMatlabClass, boolean.class, "nativeIsMatlabThread", 51 | new Class[0], 52 | new Class[0]); 53 | } 54 | 55 | private static Class getAndCheckClass(String className) throws MatlabConnectionException { 56 | try { 57 | return Class.forName(className, false, JMIValidator.class.getClassLoader()); 58 | } catch (ClassNotFoundException e) { 59 | throw new MatlabConnectionException("This version of MATLAB is missing a class required by matlabcontrol\n" + 60 | "Required: " + className, e); 61 | } 62 | //Should not occur: MATLAB by default has no SecurityManager installed and PermissiveSecurityManager permits this 63 | catch (SecurityException e) { 64 | throw new MatlabConnectionException("Unable to verify if MATLAB has the method required by matlabcontrol", e); 65 | } 66 | } 67 | 68 | /** 69 | * Checks that there exists a method named {@code methodName} for class {@code clazz} that is {@code public static}, 70 | * return {@code requiredReturn}, has parameters {@code reqiredParameters} (in the specified order), and throws 71 | * {@code requiredExceptions}. Exceptions that extend {@link RuntimeException} are ignored. 72 | * 73 | * 74 | * @param clazz 75 | * @param requiredReturn 76 | * @param methodName 77 | * @param requiredParameters 78 | * @param requiredExceptions 79 | * @throws MatlabConnectionException 80 | */ 81 | private static void checkMethod(Class clazz, Class requiredReturn, String methodName, 82 | Class[] requiredParameters, Class[] requiredExceptions) throws MatlabConnectionException { 83 | try { 84 | Method method = clazz.getDeclaredMethod(methodName, requiredParameters); 85 | int actualModifiers = method.getModifiers(); 86 | Class actualReturn = method.getReturnType(); 87 | Class[] actualExceptions = method.getExceptionTypes(); 88 | 89 | //Determine if the exceptions are equivalent 90 | boolean exceptionsEqual = doExceptionsMatch(requiredExceptions, actualExceptions); 91 | 92 | if (!Modifier.isPublic(actualModifiers) || !Modifier.isStatic(actualModifiers) || 93 | !actualReturn.equals(requiredReturn) || !exceptionsEqual) { 94 | String required = buildMethodDescription(clazz, requiredReturn, methodName, requiredParameters, requiredExceptions); 95 | 96 | throw new MatlabConnectionException("This version of MATLAB is missing a method required by matlabcontrol\n" + 97 | "Required: " + required + "\n" + 98 | "Found: " + method.toString()); 99 | } 100 | } catch (NoSuchMethodException e) { 101 | String required = buildMethodDescription(clazz, requiredReturn, methodName, requiredParameters, requiredExceptions); 102 | 103 | throw new MatlabConnectionException("This version of MATLAB is missing a method required by matlabcontrol\n" + 104 | "Required: " + required); 105 | } 106 | } 107 | 108 | /** 109 | * Determine if the {@link Exception} classes are equivalent, ignoring {@link RuntimeException}s. 110 | * 111 | * @param requiredExceptions 112 | * @param actualExceptions 113 | * @return 114 | */ 115 | private static boolean doExceptionsMatch(Class[] requiredExceptions, Class[] actualExceptions) { 116 | HashSet> requiredSet = new HashSet>(); 117 | for (Class excClass : requiredExceptions) { 118 | if (!RuntimeException.class.isAssignableFrom(excClass)) { 119 | requiredSet.add(excClass); 120 | } 121 | } 122 | 123 | HashSet> actualSet = new HashSet>(); 124 | for (Class excClass : actualExceptions) { 125 | if (!RuntimeException.class.isAssignableFrom(excClass)) { 126 | actualSet.add(excClass); 127 | } 128 | } 129 | 130 | return requiredSet.equals(actualSet); 131 | 132 | } 133 | 134 | /** 135 | * Builds a String representation of the form: 136 | * {@code 137 | * public static [requiredReturn] [methodName]([requiredParameters]) throws [requiredExceptions] 138 | * } 139 | * If no exceptions are thrown then the "throws" part will not occur. 140 | * 141 | * @param clazz 142 | * @param requiredReturn 143 | * @param methodName 144 | * @param requiredParameters 145 | * @param requiredExceptions 146 | * @return 147 | */ 148 | private static String buildMethodDescription(Class clazz, Class requiredReturn, String methodName, 149 | Class[] requiredParameters, Class[] requiredExceptions) { 150 | StringBuilder paramString = new StringBuilder(); 151 | for (int i = 0; i < requiredParameters.length; i++) { 152 | paramString.append(requiredParameters[i].getCanonicalName()); 153 | 154 | if (i < requiredParameters.length - 1) { 155 | paramString.append(","); 156 | } 157 | } 158 | 159 | StringBuilder throwsString = new StringBuilder(); 160 | if (requiredExceptions.length > 0) { 161 | throwsString.append(" throws "); 162 | for (int i = 0; i < requiredExceptions.length; i++) { 163 | throwsString.append(requiredExceptions[i].getCanonicalName()); 164 | 165 | if (i < requiredExceptions.length - 1) { 166 | throwsString.append(","); 167 | } 168 | } 169 | } 170 | 171 | String desc = "public static " + requiredReturn.getCanonicalName() + " " + 172 | clazz.getCanonicalName() + "." + methodName + "(" + paramString + ")" + throwsString; 173 | 174 | return desc; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/link/MatlabNumber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol.link; 7 | 8 | import matlabcontrol.MatlabInvocationException; 9 | import matlabcontrol.MatlabOperations; 10 | 11 | /** 12 | * Corresponds to a MATLAB number with real and imaginary components. 13 | * 14 | * @since 4.2.0 15 | * @author Joshua Kaplan 16 | */ 17 | abstract class MatlabNumber extends MatlabType { 18 | final T _real; 19 | final T _imag; 20 | 21 | /** 22 | * The default value for {@code T}. 23 | */ 24 | private final T _default; 25 | 26 | MatlabNumber(T defaultValue, T real, T imag) { 27 | _default = defaultValue; 28 | 29 | _real = real; 30 | _imag = imag; 31 | } 32 | 33 | /** 34 | * Returns {@code true} if the imaginary value is equivalent to the default value for the numeric type, 35 | * {@code false} otherwise. Equivalent to the MATLAB {@code isreal} function. 36 | * 37 | * @return 38 | */ 39 | public boolean isReal() { 40 | return _default.equals(_imag); 41 | } 42 | 43 | /** 44 | * Returns a {@code String} representation of the number. If the number is real the String representation is 45 | * real. If an imaginary component is present, the format is real + imagi if the 46 | * imaginary component is non-negative and real - imagi if the imaginary component is 47 | * negative. 48 | * 49 | * @return 50 | */ 51 | @Override 52 | public String toString() { 53 | String fullStr; 54 | String realStr = _real.toString(); 55 | 56 | if (this.isReal()) { 57 | fullStr = realStr; 58 | } else { 59 | String imagStr = _imag.toString(); 60 | if (imagStr.startsWith("-")) { 61 | fullStr = realStr + " - " + imagStr.substring(1, imagStr.length()) + "i"; 62 | } else { 63 | fullStr = realStr + " + " + imagStr + "i"; 64 | } 65 | } 66 | 67 | return fullStr; 68 | } 69 | 70 | /** 71 | * Returns {@code true} if and only if the other object is of the same class and its component real and imaginary 72 | * parts are both equal. The definition of equality for the component parts is defined by the {@code equals(...)} 73 | * method of the boxed numeric class of its components. 74 | * 75 | * @param obj 76 | * @return 77 | */ 78 | @Override 79 | public boolean equals(Object obj) { 80 | boolean equal = false; 81 | 82 | if (obj != null && this.getClass().equals(obj.getClass())) { 83 | MatlabNumber other = (MatlabNumber) obj; 84 | equal = _real.equals(other._real) && _imag.equals(other._imag); 85 | } 86 | 87 | return equal; 88 | } 89 | 90 | /** 91 | * Returns a hash code compatible with {@code equals(...)}. 92 | * 93 | * @return 94 | */ 95 | @Override 96 | public int hashCode() { 97 | int hash = 5; 98 | hash = 79 * hash + _real.hashCode(); 99 | hash = 79 * hash + _imag.hashCode(); 100 | 101 | return hash; 102 | } 103 | 104 | @Override 105 | MatlabTypeSetter getSetter() { 106 | return new MatlabNumberSetter(_real, _imag); 107 | } 108 | 109 | //This is very non-object oriented to have this here instead of each subclass, but it saves a lot of boilerplate 110 | private static class MatlabNumberSetter implements MatlabTypeSetter { 111 | private static final long serialVersionUID = -8427476815084218279L; 112 | private final Number _real; 113 | private final Number _imag; 114 | 115 | private MatlabNumberSetter(Number real, Number imag) { 116 | _real = real; 117 | _imag = imag; 118 | } 119 | 120 | @Override 121 | public void setInMatlab(MatlabOperations ops, String name) throws MatlabInvocationException { 122 | //Avoids limitation that MATLAB cannot multiply an integer stored in a variable by i 123 | 124 | if (_real instanceof Byte) { 125 | ops.eval(name + "=int8(" + _real.byteValue() + "+" + _imag.byteValue() + "i);"); 126 | } else if (_real instanceof Short) { 127 | ops.eval(name + "=int16(" + _real.shortValue() + "+" + _imag.shortValue() + "i);"); 128 | } else if (_real instanceof Integer) { 129 | ops.eval(name + "=int32(" + _real.intValue() + "+" + _imag.intValue() + "i);"); 130 | } else if (_real instanceof Long) { 131 | ops.eval(name + "=int64(" + _real.longValue() + "+" + _imag.longValue() + "i);"); 132 | } 133 | 134 | // Set value through an array to avoid values being converted to MATLAB double 135 | 136 | else if (_real instanceof Float) { 137 | ops.setVariable(name, new float[]{_real.floatValue(), _imag.floatValue()}); 138 | ops.eval(name + "=" + name + "(1)+" + name + "(2)*i;"); 139 | } else if (_real instanceof Double) { 140 | ops.setVariable(name, new double[]{_real.doubleValue(), _imag.doubleValue()}); 141 | ops.eval(name + "=" + name + "(1)+" + name + "(2)*i;"); 142 | } 143 | } 144 | } 145 | 146 | static class MatlabNumberGetter implements MatlabTypeGetter> { 147 | private static final long serialVersionUID = 1339080882185682568L; 148 | private Object _real; 149 | private Object _imag; 150 | private boolean _retrieved; 151 | 152 | @Override 153 | public MatlabNumber retrieve() { 154 | if (!_retrieved) { 155 | throw new IllegalStateException("complex number has not been retrieved"); 156 | } 157 | 158 | MatlabNumber num; 159 | if (_real.getClass().equals(Byte.class)) { 160 | num = new MatlabInt8((Byte) _real, (Byte) _imag); 161 | } else if (_real.getClass().equals(Short.class)) { 162 | num = new MatlabInt16((Short) _real, (Short) _imag); 163 | } else if (_real.getClass().equals(Integer.class)) { 164 | num = new MatlabInt32((Integer) _real, (Integer) _imag); 165 | } else if (_real.getClass().equals(Long.class)) { 166 | num = new MatlabInt64((Long) _real, (Long) _imag); 167 | } else if (_real.getClass().equals(Float.class)) { 168 | num = new MatlabSingle((Float) _real, (Float) _imag); 169 | } else if (_real.getClass().equals(Double.class)) { 170 | num = new MatlabDouble((Double) _real, (Double) _imag); 171 | } else { 172 | throw new IllegalStateException("unsupported numeric type: " + _real.getClass().getCanonicalName()); 173 | } 174 | 175 | return num; 176 | } 177 | 178 | @Override 179 | public void getInMatlab(MatlabOperations ops, String variableName) throws MatlabInvocationException { 180 | String receiverName = (String) ops.returningEval("genvarname('complex_receiver_', who);", 1)[0]; 181 | try { 182 | ops.setVariable(receiverName, this); 183 | ops.eval(receiverName + ".setReal(real(" + variableName + "));"); 184 | ops.eval(receiverName + ".setImaginary(imag(" + variableName + "));"); 185 | } finally { 186 | ops.eval("clear " + receiverName); 187 | } 188 | 189 | _retrieved = true; 190 | } 191 | 192 | /** 193 | * Sets the real value. Only meant to be called from MATLAB. In order for MATLAB to pass in the number, this 194 | * method must have {@code Object} as its parameter, not {@code Number}. 195 | * 196 | * @param real 197 | */ 198 | public void setReal(Object real) { 199 | _real = real; 200 | } 201 | 202 | /** 203 | * Sets the imaginary value. Only meant to be called from MATLAB. In order for MATLAB to pass in the number, 204 | * this method must have {@code Object} as its parameter, not {@code Number}. 205 | * 206 | * @param imag 207 | */ 208 | public void setImaginary(Object imag) { 209 | _imag = imag; 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/matlabcontrol/RemoteMatlabProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Code licensed under new-style BSD (see LICENSE). 3 | * All code up to tags/original: Copyright (c) 2013, Joshua Kaplan 4 | * All code after tags/original: Copyright (c) 2016, DiffPlug 5 | */ 6 | package matlabcontrol; 7 | 8 | import java.rmi.MarshalException; 9 | import java.rmi.NoSuchObjectException; 10 | import java.rmi.RemoteException; 11 | import java.rmi.UnmarshalException; 12 | import java.rmi.server.UnicastRemoteObject; 13 | import java.util.Timer; 14 | import java.util.TimerTask; 15 | 16 | /** 17 | * Allows for calling MATLAB from outside of MATLAB. 18 | * 19 | * @since 3.0.0 20 | * 21 | * @author Joshua Kaplan 22 | */ 23 | class RemoteMatlabProxy extends MatlabProxy { 24 | /** 25 | * The remote JMI wrapper which is a remote object connected over RMI. 26 | */ 27 | private final JMIWrapperRemote _jmiWrapper; 28 | 29 | /** 30 | * The receiver for the proxy. While the receiver is bound to the RMI registry and a reference is maintained 31 | * (the RMI registry uses weak references), the connection to MATLAB's JVM will remain active. The JMI wrapper, 32 | * while a remote object, is not bound to the registry, and will not keep the RMI thread running. 33 | */ 34 | private final RequestReceiver _receiver; 35 | 36 | /** 37 | * A timer that periodically checks if still connected. 38 | */ 39 | private final Timer _connectionTimer; 40 | 41 | /** 42 | * Whether the proxy is connected. If the value is {@code false} the proxy is definitely disconnected. If the value 43 | * is {@code true} then the proxy may be disconnected. The accuracy of this will be checked then the next 44 | * time {@link #isConnected()} is called and this value will be updated if necessary. 45 | */ 46 | private volatile boolean _isConnected = true; 47 | 48 | /** 49 | * The duration (in milliseconds) between checks to determine if still connected. 50 | */ 51 | private static final int CONNECTION_CHECK_PERIOD = 1000; 52 | 53 | /** 54 | * The proxy is never to be created outside of this package, it is to be constructed after a 55 | * {@link JMIWrapperRemote} has been received via RMI. 56 | * 57 | * @param internalProxy 58 | * @param receiver 59 | * @param id 60 | * @param existingSession 61 | */ 62 | RemoteMatlabProxy(JMIWrapperRemote internalProxy, RequestReceiver receiver, Identifier id, boolean existingSession) { 63 | super(id, existingSession); 64 | 65 | _connectionTimer = new Timer("MLC Connection Listener " + id); 66 | _jmiWrapper = internalProxy; 67 | _receiver = receiver; 68 | } 69 | 70 | /** 71 | * Initializes aspects of the proxy that cannot be done safely in the constructor without leaking a reference to 72 | * {@code this}. 73 | */ 74 | void init() { 75 | _connectionTimer.schedule(new CheckConnectionTask(), CONNECTION_CHECK_PERIOD, CONNECTION_CHECK_PERIOD); 76 | } 77 | 78 | private class CheckConnectionTask extends TimerTask { 79 | @Override 80 | public void run() { 81 | if (!RemoteMatlabProxy.this.isConnected()) { 82 | //If not connected, perform disconnection so RMI thread can terminate 83 | RemoteMatlabProxy.this.disconnect(); 84 | 85 | //Notify listeners 86 | notifyDisconnectionListeners(); 87 | 88 | //Cancel timer, which will terminate the timer's thread 89 | _connectionTimer.cancel(); 90 | } 91 | } 92 | } 93 | 94 | @Override 95 | public boolean isRunningInsideMatlab() { 96 | return false; 97 | } 98 | 99 | @Override 100 | public boolean isConnected() { 101 | //If believed to be connected, verify this is up to date information 102 | if (_isConnected) { 103 | boolean connected; 104 | //Call a remote method, if it throws a RemoteException then it is no longer connected 105 | try { 106 | _jmiWrapper.checkConnection(); 107 | connected = true; 108 | } catch (RemoteException e) { 109 | connected = false; 110 | } 111 | 112 | _isConnected = connected; 113 | } 114 | 115 | return _isConnected; 116 | } 117 | 118 | @Override 119 | public boolean disconnect() { 120 | _connectionTimer.cancel(); 121 | 122 | //Unexport the receiver so that the RMI threads can shut down 123 | try { 124 | //If succesfully exported, then definitely not connected 125 | //If the export failed, we still might be connected, isConnected() will check 126 | _isConnected = !UnicastRemoteObject.unexportObject(_receiver, true); 127 | } 128 | //If it is not exported, that's ok because we were trying to unexport it 129 | catch (NoSuchObjectException e) {} 130 | 131 | return this.isConnected(); 132 | } 133 | 134 | // Methods which interact with MATLAB (and helper methods and interfaces) 135 | 136 | private static interface RemoteInvocation { 137 | public T invoke() throws RemoteException, MatlabInvocationException; 138 | } 139 | 140 | private T invoke(RemoteInvocation invocation) throws MatlabInvocationException { 141 | if (!_isConnected) { 142 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(); 143 | } else { 144 | try { 145 | return invocation.invoke(); 146 | } catch (UnmarshalException e) { 147 | throw MatlabInvocationException.Reason.UNMARSHAL.asException(e); 148 | } catch (MarshalException e) { 149 | throw MatlabInvocationException.Reason.MARSHAL.asException(e); 150 | } catch (RemoteException e) { 151 | if (this.isConnected()) { 152 | throw MatlabInvocationException.Reason.UNKNOWN.asException(e); 153 | } else { 154 | throw MatlabInvocationException.Reason.PROXY_NOT_CONNECTED.asException(e); 155 | } 156 | } 157 | } 158 | } 159 | 160 | @Override 161 | public void setVariable(final String variableName, final Object value) throws MatlabInvocationException { 162 | this.invoke(new RemoteInvocation() { 163 | @Override 164 | public Void invoke() throws RemoteException, MatlabInvocationException { 165 | _jmiWrapper.setVariable(variableName, value); 166 | 167 | return null; 168 | } 169 | }); 170 | } 171 | 172 | @Override 173 | public Object getVariable(final String variableName) throws MatlabInvocationException { 174 | return this.invoke(new RemoteInvocation() { 175 | @Override 176 | public Object invoke() throws RemoteException, MatlabInvocationException { 177 | return _jmiWrapper.getVariable(variableName); 178 | } 179 | }); 180 | } 181 | 182 | @Override 183 | public void exit() throws MatlabInvocationException { 184 | this.invoke(new RemoteInvocation() { 185 | @Override 186 | public Void invoke() throws RemoteException, MatlabInvocationException { 187 | _jmiWrapper.exit(); 188 | 189 | return null; 190 | } 191 | }); 192 | } 193 | 194 | @Override 195 | public void eval(final String command) throws MatlabInvocationException { 196 | this.invoke(new RemoteInvocation() { 197 | @Override 198 | public Void invoke() throws RemoteException, MatlabInvocationException { 199 | _jmiWrapper.eval(command); 200 | 201 | return null; 202 | } 203 | }); 204 | } 205 | 206 | @Override 207 | public Object[] returningEval(final String command, final int nargout) throws MatlabInvocationException { 208 | return this.invoke(new RemoteInvocation() { 209 | @Override 210 | public Object[] invoke() throws RemoteException, MatlabInvocationException { 211 | return _jmiWrapper.returningEval(command, nargout); 212 | } 213 | }); 214 | } 215 | 216 | @Override 217 | public void feval(final String functionName, final Object... args) throws MatlabInvocationException { 218 | this.invoke(new RemoteInvocation() { 219 | @Override 220 | public Void invoke() throws RemoteException, MatlabInvocationException { 221 | _jmiWrapper.feval(functionName, args); 222 | 223 | return null; 224 | } 225 | }); 226 | } 227 | 228 | @Override 229 | public Object[] returningFeval(final String functionName, final int nargout, final Object... args) 230 | throws MatlabInvocationException { 231 | return this.invoke(new RemoteInvocation() { 232 | @Override 233 | public Object[] invoke() throws RemoteException, MatlabInvocationException { 234 | return _jmiWrapper.returningFeval(functionName, nargout, args); 235 | } 236 | }); 237 | } 238 | 239 | @Override 240 | public T invokeAndWait(final MatlabThreadCallable callable) throws MatlabInvocationException { 241 | return this.invoke(new RemoteInvocation() { 242 | @Override 243 | public T invoke() throws RemoteException, MatlabInvocationException { 244 | return _jmiWrapper.invokeAndWait(callable); 245 | } 246 | }); 247 | } 248 | } 249 | --------------------------------------------------------------------------------