diff --git a/.ci/ci.sh b/.ci/ci.sh index 88021f5..8fbbba9 100755 --- a/.ci/ci.sh +++ b/.ci/ci.sh @@ -7,5 +7,5 @@ if [ "$TRAVIS_REPO_SLUG" == "diffplug/matfilerw" ] && [ "$TRAVIS_PULL_REQUEST" = # Publish the artifacts ./gradlew publish || exit 1 # Push the javadoc - ./.ci/push-javadoc.sh + ./gradlew publishGhPages || exit 1 fi diff --git a/.ci/push-javadoc.sh b/.ci/push-javadoc.sh deleted file mode 100755 index 4a3a307..0000000 --- a/.ci/push-javadoc.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -echo "Pushing javadoc to gh-pages..." - -# save the version -VERSION=$(./gradlew -q printVersion) - -# copy javadoc from the build to ~/javadoc-temp -cp -R build/docs/javadoc $HOME/javadoc-temp - -# clone a new copy jmatio to gh-pages -cd $HOME -git config --global user.email "travis@travis-ci.org" -git config --global user.name "travis-ci" -rm -rf $HOME/gh-pages -git clone --quiet --branch=gh-pages https://${gh_token}@github.com/diffplug/matfilerw gh-pages > /dev/null - -# copy the javadoc into the build -cd gh-pages -mkdir -p javadoc -if [[ "$VERSION" != *SNAPSHOT* ]]; then - git rm -rf javadoc/${VERSION} - cp -Rf $HOME/javadoc-temp/ ./javadoc/${VERSION}/ -else - git rm -rf javadoc/snapshot - cp -Rf $HOME/javadoc-temp/ ./javadoc/snapshot/ -fi - -# add all of the stuff and commit it -git add -f -A -git commit -m "Lastest javadoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages" -if ! git push -fq origin gh-pages &> /dev/null; then - echo "Error pushing javadoc to origin. Bad gh_token? GitHub down?" - exit 1 -else - echo "Pushed javadoc to gh-pages." -fi diff --git a/.travis.yml b/.travis.yml index 4048052..a2a7818 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,9 @@ jdk: - oraclejdk8 script: "./.ci/ci.sh" before_cache: -- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ cache: directories: - - "$HOME/.gradle/caches/" - - "$HOME/.gradle/wrapper/" - - "build/jdk" + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ diff --git a/CHANGES.md b/CHANGES.md index bef0168..f4b2591 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,61 @@ # MatFileRW releases -### Version 1.4.0-SNAPSHOT - TBD ([javadoc](http://diffplug.github.io/matfilerw/javadoc/snapshot/), [jcenter](https://oss.sonatype.org/content/repositories/snapshots/com/diffplug/matsim/matfilerw/)) +### Version 3.2.0-SNAPSHOT - TBD ([javadoc](http://diffplug.github.io/matfilerw/javadoc/snapshot/), [snapshot](https://oss.sonatype.org/content/repositories/snapshots/com/diffplug/matsim/matfilerw/)) + +### Version 3.1.1 - December 28th 2018 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/3.1.1/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/3.1.1/view)) + +* Fixed MatFileReader.read(File file) to allow multiple calls(see [#20](https://github.com/diffplug/matfilerw/issues/20)). + +### Version 3.1.0 - November 13th 2018 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/3.1.0/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/3.1.0/view)) + +* Added support for Jigsaw and Java 9+ (see [#16](https://github.com/diffplug/matfilerw/issues/16)). + +### Version 3.0.1 - June 28th 2017 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/3.0.1/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/3.0.1/view)) + +* Fixed second-level MCOS property dereferencing (see [#13](https://github.com/diffplug/matfilerw/issues/13)). + +### Version 3.0.0 - November 22nd 2016 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/3.0.0/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/3.0.0/view)) + +* Fixed multidimensional array indexing (see [#10](https://github.com/diffplug/matfilerw/issues/10)). + + For arrays with dimension 1 or 2, there is no change. + + For arrays with dimension 3 and up, matfilerw 2.x did not order the dimensions in the column-major format used by MATLAB. In 3.x forward, matfilerw uses the same column-major format as MATLAB. +* `getImaginary` will always return zero for real arrays. + +### Version 2.3.0 - August 18th 2016 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/2.3.0/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/2.3.0/view)) + +* Fixed problem where a handle class is encoded within an MLObject. +* MCOS variables can now be parsed from anywhere within the MAT-File (previously could only be a root variable). +* Arrays of structs and fields are now enforced to have their fields be in a consistent order element to element. + +### Version 2.2.0 - February 9th 2016 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/2.2.0/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/2.2.0/view)) + +* Added `MatFile.readFull(ByteBuffer buf, MatFileType type)` +* Added `MLArray.getIndex(int...)` and `MLNumericArray.set(T value, int...)` and `T get(int...)` for N-dimensional indices. +* Fixed the JRE6 download in the build file. + +### Version 2.1.0 - December 10th 2015 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/2.1.0/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/2.1.0/view)) + +* Fixed support for reading UTF fields (closes [#2](https://github.com/diffplug/matfilerw/issues/2)) + +### Version 2.0.0 - October 25th 2015 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/2.0.0/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/2.0.0/view)) + +* Incorporated all changes from `codesourcery/JMatIO`. + + Adds support for MCOS Objects. + + Adds a Simulink decoder for reading files from Simulink MDL files. + + Improved performance. +* `MLObject` and `MLStructure` now share `MLStructureObjectBase` as a base class. This caused a small breaking change to `MLObject`s API. +* `MatFileHeader` now wraps up the whole endianness mess, which also required a small breaking change. + +### Version 2.0.0.TRANSITION - October 25th 2015 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/2.0.TRANSITION/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/2.0.TRANSITION/view)) + +Only pertinent for people who are migrating from a JMatIO fork whose packages were renamed to `ca.mjdsystems.jmatio`. + +* `ca.mjdsystems.jmatio` code is included umodified, but marked as deprecated. +* `com.jmatio` code is identitical to `2.0.0`. ### Version 1.3.1 - October 16th 2015 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/1.3.1/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/1.3.1/view)) -- Corrected the license in the maven metadata. +* Corrected the license in the maven metadata. ### Version 1.3.0 - October 16th 2015 ([javadoc](http://diffplug.github.io/matfilerw/javadoc/1.3.0/), [jcenter](https://bintray.com/diffplug/opensource/matfilerw/1.3.0/view)) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..4be4ecf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +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. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +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. + +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. + +## Scope + +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. + +## Enforcement + +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. + +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. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF index c8a1d5e..bc1ccab 100644 --- a/META-INF/MANIFEST.MF +++ b/META-INF/MANIFEST.MF @@ -1,16 +1,17 @@ Manifest-Version: 1.0 Bundle-Description: MatFileRW - Read and write .mat files Bundle-DocURL: https://github.com/diffplug/matfilerw -Bundle-License: https://github.com/diffplug/matfilerw/blob/v1.3.0/LICENS - E +Bundle-License: https://github.com/diffplug/matfilerw/blob/v3.1.0-SNAP + SHOT/LICENSE Bundle-ManifestVersion: 2 Bundle-SymbolicName: com.diffplug.matsim.matfilerw Bundle-Vendor: DiffPlug -Bundle-Version: 1.3.0 -Export-Package: com.jmatio.common;version="1.3.0",com.jmatio.common.util - ;uses:="com.jmatio.types";version="1.3.0",com.jmatio.io;uses:="com.jmat - io.types";version="1.3.0",com.jmatio.io.stream;uses:="com.jmatio.types" - ;version="1.3.0",com.jmatio.types;version="1.3.0" -Import-Package: com.jmatio.common,com.jmatio.io.stream,com.jmatio.types, - sun.misc +Bundle-Version: 3.1.0.I201706282450 +Export-Package: com.jmatio.common;version="3.1.0",com.jmatio.common.ut + il;uses:="com.jmatio.types";version="3.1.0",com.jmatio.io;uses:="com. + jmatio.types";version="3.1.0",com.jmatio.io.stream;uses:="com.jmatio. + types";version="3.1.0",com.jmatio.types;uses:="com.jmatio.common";ver + sion="3.1.0" +Import-Package: sun.misc;resolution:=optional,com.jmatio.common,com.jm + atio.io.stream,com.jmatio.types Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.6))" diff --git a/MatlabClasses/HandleClass.m b/MatlabClasses/HandleClass.m new file mode 100644 index 0000000..7501426 --- /dev/null +++ b/MatlabClasses/HandleClass.m @@ -0,0 +1,6 @@ +% http://stackoverflow.com/questions/36025747/matlab-documentation-on-handle-variables-and-mat-files +classdef HandleClass < handle +properties + myPropA +end % properties +end % classdef diff --git a/MatlabClasses/OtherClass.m b/MatlabClasses/OtherClass.m new file mode 100644 index 0000000..44016cf --- /dev/null +++ b/MatlabClasses/OtherClass.m @@ -0,0 +1,6 @@ +% http://stackoverflow.com/questions/36025747/matlab-documentation-on-handle-variables-and-mat-files +classdef OtherClass +properties + myObjA +end % properties +end % classdef diff --git a/MatlabClasses/SimpleEmpty.m b/MatlabClasses/SimpleEmpty.m index 3cc91c1..eb0685e 100644 --- a/MatlabClasses/SimpleEmpty.m +++ b/MatlabClasses/SimpleEmpty.m @@ -1,11 +1,10 @@ classdef SimpleEmpty - %SIMPLEEMPTY A simple empty class definition. - - properties - end - - methods - end - -end + %SIMPLEEMPTY A simple empty class definition. + + properties + end + methods + end + +end diff --git a/MatlabClasses/SimpleSingleText.m b/MatlabClasses/SimpleSingleText.m index fa5168b..6f46b74 100644 --- a/MatlabClasses/SimpleSingleText.m +++ b/MatlabClasses/SimpleSingleText.m @@ -1,13 +1,12 @@ classdef SimpleSingleText - %SIMPLESINGLETEXT Summary of this class goes here - % Detailed explanation goes here - - properties - test_text = 'Default text' - end - - methods - end - -end + %SIMPLESINGLETEXT Summary of this class goes here + % Detailed explanation goes here + + properties + test_text = 'Default text' + end + methods + end + +end diff --git a/MatlabClasses/TestHandleClass.m b/MatlabClasses/TestHandleClass.m new file mode 100644 index 0000000..1b38647 --- /dev/null +++ b/MatlabClasses/TestHandleClass.m @@ -0,0 +1,18 @@ +% http://stackoverflow.com/questions/36025747/matlab-documentation-on-handle-variables-and-mat-files + +%% create and save the objects +% Create 2 object variables. 2nd +% one has a property that refers to +% same object as the first one +objA = HandleClass; +objA.myPropA = 5; +objB = OtherClass; +objB.myObjA = objA; +objC = HandleClass; +objC.myPropA = objA; + +save('../src/test/resources/handles.mat', 'objA', 'objB', 'objC'); + +%% clear and load them back +clear +load('../src/test/resources/handles.mat') diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..89d5062 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +After creating the PR, please add a commit that adds a bullet-point under the `-SNAPSHOT` section of [CHANGES.md](https://github.com/diffplug/matfilerw/blob/master/CHANGES.md) that includes: + +- [ ] a summary of the change +- [ ] a link to the newly created PR + +This makes it easier for the maintainers to quickly release your changes. diff --git a/README.md b/README.md index 5034456..d6fd5cb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +## MatFileRW is not abandoned, but we recommend migrating to [HebiRobotics/MFL](https://github.com/HebiRobotics/MFL) + +MatFileRW will continue to accept bug reports and PRs. However, we recommend starting new projects with [HebiRobotics/MFL](https://github.com/HebiRobotics/MFL) because it: + +- has every testcase from MatFileRW and more +- has a cleaner and more modern codebase +- supports concurrent compression and decompression +- is designed for interoperability with native-Java matrix manipulation +- has optional [EJML](https://ejml.org) integration + +Besides the effort to learn the new API, there are no downsides and quite a few upsides. Migrating a project is a big job, so we will continue to accept bug reports and PR's for MatFileRW to support those who don't choose to migrate. + # MatFileRW: Read and write MATLAB MAT-files from Java [![Maven artifact](https://img.shields.io/badge/mavenCentral-com.diffplug.matsim%3Amatfilerw-blue.svg)](https://bintray.com/diffplug/opensource/matfilerw/view) -[![Latest version](https://img.shields.io/badge/latest-1.3.1-blue.svg)](https://github.com/diffplug/matfilerw/releases/latest) -[![Javadoc](https://img.shields.io/badge/javadoc-OK-blue.svg)](https://diffplug.github.io/matfilerw/javadoc/1.3.1/) +[![Latest version](https://img.shields.io/badge/latest-3.1.1-blue.svg)](https://github.com/diffplug/matfilerw/releases/latest) +[![Javadoc](https://img.shields.io/badge/javadoc-OK-blue.svg)](https://diffplug.github.io/matfilerw/javadoc/3.1.1/) [![License Apache](https://img.shields.io/badge/license-BSD-blue.svg)](https://tldrlegal.com/license/bsd-3-clause-license-(revised)) -[![Changelog](https://img.shields.io/badge/changelog-1.4.0--SNAPSHOT-brightgreen.svg)](CHANGES.md) +[![Changelog](https://img.shields.io/badge/changelog-3.2.0--SNAPSHOT-brightgreen.svg)](CHANGES.md) [![Travis CI](https://travis-ci.org/diffplug/matfilerw.svg?branch=master)](https://travis-ci.org/diffplug/matfilerw) -MatFileRW is a library which allows reading and writing MAT files. Have a look at [MatIOTest.java](src/test/java/com/jmatio/test/MatIOTest.java?ts=4) to see each part in use. +MatFileRW is a library which allows reading and writing MAT files. Have a look at [MatIOTest.java](https://github.com/diffplug/matfilerw/blob/master/src/test/java/com/jmatio/io/MatIOTest.java) to see each part in use. As far as compatibility, the TL;DR is that it will work with any MAT-File with default settings. The dirty details are that this library works with `v6` and `v7`, but not `v4` or `v7.3`. @@ -30,9 +42,11 @@ As far as compatibility, the TL;DR is that it will work with any MAT-File with d * MATLAB does not export to v7.3 by default. * The [Mathworks website](http://www.mathworks.com/help/matlab/import_export/mat-file-versions.html?refresh=true) has more details. -## codemercenary/jmatio and the status of ca.mjdsystems +## codemercenary/jmatio and ca.mjdsystems.jmatio + +Since JMatIO wasn't updated for a while, lots of people made forks. One of the most prominent was Jason Lokerson's, hosted on GitHub as codemercenary/JMatIO. It included several improvements, but all the packages were renamed to `ca.mjdsystems.jmatio`. Starting with 2.0.0, all of the improvements from MatFileRW and `ca.mjdsystems.jmatio` have been merged into the `com.jmatio` packages. -Since JMatIO wasn't updated for a while, lots of people made forks. One of the most prominent was Jason Lokerson's, hosted on GitHub as codemercenary/JMatIO. It included several improvements, but all the packages were renamed to "ca.mjdsystems.jmatio". In an effort to bring all JMatIO's under one roof, we have included its source code, unchanged, into MatFileRW. We are working on merging all of its improvements into the main "com.jmatio" classes, and the "ca.mjdsystems" namespace is deprecated, and will be removed in 2.0. +If you are a user of the `ca.mjdsystems` packages, you should download `com.diffplug.matsim:matfilerw:2.0.0.TRANSITION` from mavenCentral. This contains the `ca.mjdsystems.jmatio` packages unchanged, but marked as deprecated. After you have removed all dependencies on the `ca.mjdsystems.jmatio` packages, you will be able to use the regular `2.0.0` version, and its descendants. ## Acknowledgements @@ -40,10 +54,23 @@ This project is forked from the JMatIO project originally maintained on [SourceF 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! +People whose commits are included in this project +* Original JMatIO project credit to Wojciech Gradkowski +* Thanks to Tim Ryan for [finding and fixing](https://github.com/diffplug/matfilerw/pull/9) a multidimensional indexing bug. +* MLSparse improvements credit to Sina Samangooei +* Reading from streams credit to Jonathan Hare +* MCOS and Simulink MAT-File parsing by Matthew Dawson +* Further MCOS fixes thanks to Piotr Smolinski +* int32 writing by Gabriel Shubiner +* ZLIP EOF Exception fix by David Williams +* Integration work by Jason Lokerson +* Multidimensional array indexing by Mikael Grev +* AbstractIterator taken from Google's Guava + +Tools used by this project * Formatted by [spotless](https://github.com/diffplug/spotless), [as such](https://github.com/diffplug/matfilerw/blob/v1.3.1/build.gradle?ts=4#L129-L149). * Bugs found by [findbugs](http://findbugs.sourceforge.net/), [as such](https://github.com/diffplug/matfilerw/blob/v1.3.1/build.gradle?ts=4#L151-L175). -* 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). -* 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/). +* OSGi metadata generated by [Goomph](https://github.com/diffplug/goomph), which leverages Peter Kriens' [bnd](http://www.aqute.biz/Bnd/Bnd). * Built by [gradle](http://gradle.org/). * Tested by [junit](http://junit.org/). * Maintained by [DiffPlug](http://www.diffplug.com/). diff --git a/build.gradle b/build.gradle index b79d581..24dbaa3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,14 @@ plugins { // osgi - id "org.jruyi.osgibnd" version "0.4.0" + id "com.diffplug.gradle.osgi.bndmanifest" version "3.4.0" // code formatting - id "com.diffplug.gradle.spotless" version "1.3.1" + id "com.diffplug.gradle.spotless" version "3.0.0" // bintray uploading id "com.jfrog.bintray" version "1.3.1" // for downloading JRE6 id "de.undercouch.download" version "2.0.0" + // github pages + id "org.ajoberstar.github-pages" version "1.4.2" } repositories { @@ -28,31 +30,20 @@ dependencies { ////////// // OSGI // ////////// - -// delete the old manifest to ensure there's no caching or merging going on -jar.doFirst { - project.file('META-INF/MANIFEST.MF').delete() -} - -jar.manifest { - attributes ( - 'Export-Package': 'com.jmatio.*', - 'Bundle-SymbolicName': "${project.group}.${project.name}", - 'Bundle-Vendor': 'DiffPlug', - 'Bundle-DocURL': "https://github.com/${project.org}/${project.name}", - 'Bundle-License': "https://github.com/${project.org}/${project.name}/blob/v${project.version}/LICENSE", - '-removeheaders': 'Bnd-LastModified,Bundle-Name,Created-By,Tool', - '-savemanifest': project.file('META-INF/MANIFEST.MF'), - ) -} - -// manifests are only generated when the java files change - not if manifest properties change -// this task forces the manifests to regenerate -task refreshManifest(dependsOn: jar) -gradle.taskGraph.whenReady { taskGraph -> - if (taskGraph.hasTask(refreshManifest)) { - jar.outputs.upToDateWhen { false } - } +jar.manifest.attributes ( + 'Export-Package': 'com.jmatio.*', + 'Import-Package': 'sun.misc;resolution:=optional,com.jmatio.*', + 'Bundle-SymbolicName': "${project.group}.${project.name}", + 'Bundle-Description': "${project.description}", + 'Bundle-Vendor': 'DiffPlug', + 'Bundle-DocURL': "https://github.com/${project.org}/${project.name}", + 'Bundle-License': "https://github.com/${project.org}/${project.name}/blob/v${project.version}/LICENSE", + '-removeheaders': 'Bnd-LastModified,Bundle-Name,Created-By,Tool', + 'Automatic-Module-Name': 'com.jmatio', // Jigsaw module name +) +// copy the manifest into the WC +osgiBndManifest { + copyTo 'META-INF/MANIFEST.MF' } //////////// @@ -67,9 +58,10 @@ gradle.taskGraph.whenReady { taskGraph -> def JDK6_URL = 'https://bitbucket.org/alexkasko/openjdk-unofficial-builds/downloads/openjdk-1.6.0-unofficial-b30-windows-i586-image.zip' def JDK6_SHA_256 = '8a119a8c9b2ecc48e8457738f57b251e6265b2c0fb232de0ff6fddce0f127045' // place where the JRE6 will end up -def JDK6_DIR = "${buildDir}/jdk" -def JDK6_DST = "${JDK6_DIR}/jdk.zip" -def JRE6_HOME = "${buildDir}/jdk/openjdk-1.6.0-unofficial-b30-windows-i586-image/jre" +def JDK6_ROOT_DIR = "${buildDir}/jdk" +def JDK6_DST = "${JDK6_ROOT_DIR}/jdk.zip" +def JDK6_DIR = "${JDK6_ROOT_DIR}/unzipped" +def JRE6_HOME = "${JDK6_DIR}/openjdk-1.6.0-unofficial-b30-windows-i586-image/jre" import de.undercouch.gradle.tasks.download.Download; import de.undercouch.gradle.tasks.download.Verify; @@ -131,21 +123,21 @@ tasks.eclipse.dependsOn(cleanEclipse) //////////// spotless { java { - licenseHeaderFile 'spotless.license.java' // License header file - importOrderFile 'spotless.importorder' // An import ordering file, exported from Eclipse - eclipseFormatFile 'spotless.eclipseformat.xml' // XML file dumped out by the Eclipse formatter - // Eclipse formatter puts excess whitespace after lambda blocks - // funcThatTakesLambdas(x -> {} , y -> {} ) // what Eclipse does - // funcThatTakesLambdas(x -> {}, y -> {}) // what I wish Eclipse did - custom 'Lambda fix', { it.replace('} )', '})').replace('} ,', '},') } + licenseHeaderFile 'gradle/spotless.license.java' // License header file + importOrderFile 'gradle/spotless.importorder' // An import ordering file, exported from Eclipse + eclipseFormatFile 'gradle/spotless.eclipseformat.xml' // XML file dumped out by the Eclipse formatter + removeUnusedImports() } format 'misc', { - target '**/.gitignore', '**/*.gradle', '**/*.md', '**/*.sh' + target '.gitignore', '*.gradle', '*.md', '.ci/*.sh' indentWithTabs() trimTrailingWhitespace() endWithNewline() } - freshmark {} + freshmark { + target '*.md' + propertiesFile('gradle.properties') + } } ////////////// @@ -155,7 +147,7 @@ apply plugin: 'findbugs' findbugs { toolVersion = VER_FINDBUGS sourceSets = [sourceSets.main] // don't check the test code - ignoreFailures = false // bug free or it doesn't ship! + ignoreFailures = false // no bugs or it doesn't ship reportsDir = file('build/findbugs') effort = 'max' // min|default|max reportLevel = 'medium' // low|medium|high (low = sensitive to even minor mistakes) @@ -184,6 +176,7 @@ task sourcesJar(type: Jar) { from sourceSets.main.allJava } +def verSnapshot = { it.endsWith('-SNAPSHOT') ? 'snapshot' : it } // Where it's possible to name parameters and methods clearly enough // that javadoc is not necessary, why make the code bigger? // @@ -293,6 +286,23 @@ if (!isSnapshot) { bintrayUpload.dependsOn(['generatePomFileForMavenJavaPublication', jar, sourcesJar, javadocJar]) } +////////////////// +// GITHUB PAGES // +////////////////// +githubPages { + repoUri = "https://github.com/${project.org}/${project.name}" + deleteExistingFiles = false + pages { + from javadoc.destinationDir + into "javadoc/${verSnapshot(version)}" + } + credentials { + username = cred('gh_token') + password = '' + } +} +tasks.prepareGhPages.dependsOn(":javadoc") + // helps external scripts detect version task printVersion << { println version diff --git a/doc/matfile_format.pdf b/doc/matfile_format.pdf index 8252fa2..77be1d7 100644 Binary files a/doc/matfile_format.pdf and b/doc/matfile_format.pdf differ diff --git a/gradle.properties b/gradle.properties index b521196..176e626 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -stable=1.3.1 -version=1.4.0-SNAPSHOT +stable=3.1.1 +version=3.2.0-SNAPSHOT name=matfilerw group=com.diffplug.matsim description=MatFileRW - Read and write .mat files diff --git a/spotless.eclipseformat.xml b/gradle/spotless.eclipseformat.xml similarity index 93% rename from spotless.eclipseformat.xml rename to gradle/spotless.eclipseformat.xml index 613e633..8dceaed 100644 --- a/spotless.eclipseformat.xml +++ b/gradle/spotless.eclipseformat.xml @@ -6,19 +6,23 @@ + + - + + + @@ -31,6 +35,7 @@ + @@ -52,8 +57,10 @@ + + @@ -72,7 +79,7 @@ - + @@ -82,6 +89,7 @@ + @@ -100,6 +108,7 @@ + @@ -115,6 +124,7 @@ + @@ -124,7 +134,7 @@ - + @@ -153,14 +163,17 @@ + + + @@ -183,6 +196,7 @@ + @@ -194,8 +208,10 @@ + + @@ -232,7 +248,7 @@ - + @@ -256,6 +272,7 @@ + @@ -271,6 +288,7 @@ + @@ -280,7 +298,7 @@ - + diff --git a/spotless.importorder b/gradle/spotless.importorder similarity index 100% rename from spotless.importorder rename to gradle/spotless.importorder diff --git a/spotless.license.java b/gradle/spotless.license.java similarity index 100% rename from spotless.license.java rename to gradle/spotless.license.java diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index fd7e590..3baa851 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 437f556..0fa9b6a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Oct 06 21:43:04 PDT 2015 +#Sat Nov 12 18:45:38 PST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip diff --git a/gradlew b/gradlew index 91a7e26..27309d9 100755 --- a/gradlew +++ b/gradlew @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..832fdb6 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,7 +46,7 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args diff --git a/license.txt b/license.txt deleted file mode 100644 index 753fa5c..0000000 --- a/license.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2006, Wojciech Gradkowski -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name of the JMatIO nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 0c86b73..0000000 --- a/pom.xml +++ /dev/null @@ -1,176 +0,0 @@ - - - 4.0.0 - - 2.2.1 - - ca.mjdsystems.jmatio - jmatio - 1.2-SNAPSHOT - - Matlab's MAT-file I/O API in JAVA. Supports Matlab 5 MAT-flie format reading and writing. Written in pure JAVA. - - jmatio - https://github.com/MJDSys/JMatIO - - - BSD - Free for open or commercial use, with credit line - http://www.linfo.org/bsdlicense.html - repo - - - - https://github.com/MJDSys/JMatIO - scm:git:https://github.com/MJDSys/jmatio.git - scm:git:https://github.com/MJDSys/jmatio.git - HEAD - - - - cbwatcham - Craig Watcham - http://sourceforge.net/users/cbwatcham/ - Sourceforge - http://sourceforge.net/ - - architect - developer - - - - ss - Sina Samangooei - ss@ecs.soton.ac.uk - http://www.ecs.soton.ac.uk/people/ss - The University of Southampton - http://www.soton.ac.uk - - developer - - 0 - - - mjdsys - Matthew Dawson - matthew@mjdsystems.ca - - developer - - - - - - junit - junit - 4.11 - test - - - - 1.7 - 1.7 - UTF-8 - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - true - 1.6 - 1.6 - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.8.1 - - - org.apache.maven.plugins - maven-install-plugin - 2.5.1 - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - org.apache.maven.plugins - maven-resources-plugin - 2.6 - - - org.apache.maven.plugins - maven-site-plugin - 3.3 - - - org.apache.maven.plugins - maven-surefire-plugin - 2.17 - - - org.apache.maven.plugins - maven-clean-plugin - 2.5 - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - verify - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - verify - - javadoc - jar - - - - - - maven-assembly-plugin - 2.4 - - rs.jar - - src/main/assembly/bundle.xml - - - - - verify - - single - - - - - - - - org.sonatype.oss - oss-parent - 9 - - diff --git a/project.clj b/project.clj deleted file mode 100644 index 166f835..0000000 --- a/project.clj +++ /dev/null @@ -1,9 +0,0 @@ -(defproject com.callisto/JMatIO "1.1" - - :description "Matlab IO" - - :license "Copyright (c) 2006, Wojciech Gradkowski. See license.txt" - - :plugins [[s3-wagon-private "1.1.2"]] - - :java-source-paths ["src/main/java"]) diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 25a1d35..0000000 --- a/readme.txt +++ /dev/null @@ -1,65 +0,0 @@ -JMatIO is a JAVA library to read/write/manipulate with Matlab binary -MAT-files. - -If you would like to comment, improve, critisize the project please -email me: wgradkowski@gmail.com - -or visit JMatIO project page at Sourceforge: -http://www.sourceforge.net/projects/jmatio - -Subversion Access - -This project's SourceForge.net Subversion repository can be checked out through -SVN with the following instruction set: - -svn co https://jmatio.svn.sourceforge.net/svnroot/jmatio/trunk jmatio - -Have fun :) - -Wojciech Gradkowski - -CHANGE LOG: -[09.05.2013] -+ added read/write support for input/output streams (ss, jsh) - -[04.12.2012] -+ adding various fixes (thanks to: Kristofer Sandlund) -+ adding read support for objects, java objects, other types -+ performance enhancements - -[05.10.2007] -+ Sparse matrix bugfixes by Jonas Pettersson (LU/EAB) -+ MatFileReader performance enhancements by Eugene Rudoy -+ new MatFileReader methods added - -[02.03.2007] -+ Regression bug fixed: Double arrays created natively in Matlab are read - incorrectly (reversed byte ordering) - -[22.02.2007] -+ Added support:UInt8 array -+ MAJOR reading performance enhancement - reading is as fast as in Matlab now -+ Removed Log4j references - -TODO: -- Other array types (serialized objects (OPAQUE) is done partially) -- Writer performance enhancement -- Documentation and examples -- Organize JUnit tests -- Refactor exceptions -- Make structures and cell arrays more user friendly - -NOTE: -Numerical arrays (MLDouble, MLUint8) are now backed by direct ByteBuffers. For -really BIG arrays the maximum heap size for direct buffers may be modified by --XX:MaxDirectMemorySize= - - -[some.time.2006] -Currently supproted data types are: -+ Double array -+ Char array -+ Structure -+ Cell array -+ Sparase array - diff --git a/src/main/java/ca/mjdsystems/jmatio/common/MatDataTypes.java b/src/main/java/ca/mjdsystems/jmatio/common/MatDataTypes.java deleted file mode 100755 index 9918f72..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/common/MatDataTypes.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.common; - -/** - * MAT-file data types - * - * @author Wojciech Gradkowski - */ -public class MatDataTypes { - /* MAT-File Data Types */ - public static final int miUNKNOWN = 0; - public static final int miINT8 = 1; - public static final int miUINT8 = 2; - public static final int miINT16 = 3; - public static final int miUINT16 = 4; - public static final int miINT32 = 5; - public static final int miUINT32 = 6; - public static final int miSINGLE = 7; - public static final int miDOUBLE = 9; - public static final int miINT64 = 12; - public static final int miUINT64 = 13; - public static final int miMATRIX = 14; - public static final int miCOMPRESSED = 15; - public static final int miUTF8 = 16; - public static final int miUTF16 = 17; - public static final int miUTF32 = 18; - - public static final int miSIZE_INT64 = 8; - public static final int miSIZE_INT32 = 4; - public static final int miSIZE_INT16 = 2; - public static final int miSIZE_INT8 = 1; - public static final int miSIZE_UINT64 = 8; - public static final int miSIZE_UINT32 = 4; - public static final int miSIZE_UINT16 = 2; - public static final int miSIZE_UINT8 = 1; - public static final int miSIZE_DOUBLE = 8; - public static final int miSIZE_CHAR = 1; - - /** - * Return number of bytes for given type. - * - * @param type - MatDataTypes - * @return - */ - public static int sizeOf(int type) { - switch (type) { - case MatDataTypes.miINT8: - return miSIZE_INT8; - case MatDataTypes.miUINT8: - return miSIZE_UINT8; - case MatDataTypes.miINT16: - return miSIZE_INT16; - case MatDataTypes.miUINT16: - return miSIZE_UINT16; - case MatDataTypes.miINT32: - return miSIZE_INT32; - case MatDataTypes.miUINT32: - return miSIZE_UINT32; - case MatDataTypes.miINT64: - return miSIZE_INT64; - case MatDataTypes.miUINT64: - return miSIZE_UINT64; - case MatDataTypes.miDOUBLE: - return miSIZE_DOUBLE; - default: - return 1; - } - } - - /** - * Get String representation of a data type - * - * @param type - data type - * @return - String representation - */ - public static String typeToString(int type) { - String s; - switch (type) { - case MatDataTypes.miUNKNOWN: - s = "unknown"; - break; - case MatDataTypes.miINT8: - s = "int8"; - break; - case MatDataTypes.miUINT8: - s = "uint8"; - break; - case MatDataTypes.miINT16: - s = "int16"; - break; - case MatDataTypes.miUINT16: - s = "uint16"; - break; - case MatDataTypes.miINT32: - s = "int32"; - break; - case MatDataTypes.miUINT32: - s = "uint32"; - break; - case MatDataTypes.miSINGLE: - s = "single"; - break; - case MatDataTypes.miDOUBLE: - s = "double"; - break; - case MatDataTypes.miINT64: - s = "int64"; - break; - case MatDataTypes.miUINT64: - s = "uint64"; - break; - case MatDataTypes.miMATRIX: - s = "matrix"; - break; - case MatDataTypes.miCOMPRESSED: - s = "compressed"; - break; - case MatDataTypes.miUTF8: - s = "uft8"; - break; - case MatDataTypes.miUTF16: - s = "utf16"; - break; - case MatDataTypes.miUTF32: - s = "utf32"; - break; - default: - s = "unknown"; - } - return s; - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/ByteBufferInputStream.java b/src/main/java/ca/mjdsystems/jmatio/io/ByteBufferInputStream.java deleted file mode 100644 index 200046d..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/ByteBufferInputStream.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; - -class ByteBufferInputStream extends InputStream { - private ByteBuffer buf; - - private int limit; - - public ByteBufferInputStream(final ByteBuffer buf, final int limit) { - this.buf = buf; - this.limit = limit; - } - - @Override - public synchronized int read() throws IOException { - if (!(limit > 0)) { - return -1; - } - limit--; - return buf.get() & 0xFF; - } - - @Override - public synchronized int read(byte[] bytes, int off, int len) - throws IOException { - if (!(limit > 0)) { - return -1; - } - len = Math.min(len, limit); - // Read only what's left - buf.get(bytes, off, len); - limit -= len; - return len; - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/DataOutputStream.java b/src/main/java/ca/mjdsystems/jmatio/io/DataOutputStream.java deleted file mode 100644 index b89584e..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/DataOutputStream.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.IOException; -import java.nio.ByteBuffer; - -interface DataOutputStream { - /** - * Returns the current size of this stream. - * - * @return the current size of this stream. - * @throws IOException - */ - public abstract int size() throws IOException; - - /** - * Returns the current {@link ByteBuffer} mapped on the target file. - *

- * Note: the {@link ByteBuffer} has READ ONLY access. - * - * @return the {@link ByteBuffer} - * @throws IOException - */ - public abstract ByteBuffer getByteBuffer() throws IOException; - - /** - * Writes a sequence of bytes to this stream from the given buffer. - * - * @param byteBuffer - * the source {@link ByteBuffer} - * @throws IOException - */ - public abstract void write(ByteBuffer byteBuffer) throws IOException; - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/FileBufferedDataOutputStream.java b/src/main/java/ca/mjdsystems/jmatio/io/FileBufferedDataOutputStream.java deleted file mode 100644 index c4783eb..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/FileBufferedDataOutputStream.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; - -import ca.mjdsystems.jmatio.types.MLArray; - -/** - * This is an {@link OutputStream} that is backed by a {@link RandomAccessFile} - * and accessed with buffered access. - * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - * - */ -class FileBufferedDataOutputStream extends OutputStream implements DataOutputStream { - private static final int BUFFER_SIZE = 1024; - private ByteBuffer buf; - private final FileChannel rwChannel; - private final RandomAccessFile raFile; - private final File file; - - public FileBufferedDataOutputStream() throws IOException { - file = File.createTempFile("jmatio-", null); - file.deleteOnExit(); - raFile = new RandomAccessFile(file, "rw"); - rwChannel = raFile.getChannel(); - buf = ByteBuffer.allocate(BUFFER_SIZE); - } - - public FileBufferedDataOutputStream(MLArray array) throws IOException { - file = File.createTempFile("jmatio-" + array.getName() + "-", null); - file.deleteOnExit(); - raFile = new RandomAccessFile(file, "rw"); - rwChannel = raFile.getChannel(); - buf = ByteBuffer.allocate(BUFFER_SIZE); - } - - @Override - public void write(int b) throws IOException { - if (buf.position() >= buf.capacity()) { - flush(); - } - - buf.put((byte) (b & 0xff)); - } - - /* (non-Javadoc) - * @see java.io.OutputStream#write(byte[]) - */ - @Override - public void write(byte[] b) throws IOException { - write(b, 0, b.length); - } - - /* (non-Javadoc) - * @see java.io.OutputStream#write(byte[], int, int) - */ - @Override - public void write(byte[] b, int off, int len) throws IOException { - int wbytes = len; - int offset = off; - - while (wbytes > 0) { - if (buf.position() >= buf.capacity()) { - flush(); - } - - int length = Math.min(wbytes, buf.limit() - buf.position()); - - buf.put(b, offset, length); - - offset += length; - wbytes -= length; - } - } - - /* (non-Javadoc) - * @see java.io.OutputStream#close() - */ - @Override - public void close() throws IOException { - flush(); - - buf = null; - - if (rwChannel.isOpen()) { - - rwChannel.close(); - } - - raFile.close(); - } - - /* (non-Javadoc) - * @see java.io.OutputStream#flush() - */ - @Override - public void flush() throws IOException { - if (buf != null && buf.position() > 0) { - buf.flip(); - rwChannel.write(buf); - buf.clear(); - } - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.io.DataOutputStream#size() - */ - public int size() throws IOException { - flush(); - - return (int) file.length(); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.io.DataOutputStream#getByteBuffer() - */ - public ByteBuffer getByteBuffer() throws IOException { - return rwChannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.io.DataOutputStream#write(java.nio.ByteBuffer) - */ - public void write(ByteBuffer byteBuffer) throws IOException { - byte[] tmp = new byte[BUFFER_SIZE]; - - while (byteBuffer.hasRemaining()) { - int length = Math.min(byteBuffer.remaining(), tmp.length); - byteBuffer.get(tmp, 0, length); - write(tmp, 0, length); - } - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatFileFilter.java b/src/main/java/ca/mjdsystems/jmatio/io/MatFileFilter.java deleted file mode 100644 index afaca43..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatFileFilter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.util.HashSet; -import java.util.Set; - -/** - * File filter. - * - * This class is used to tell MatFileReader which matrices - * should be processed. This is useful when operating on big MAT-files, - * when there's no need to load all arrays into memory. - * - * Usage: - *


- * //create new filter instance
- * MatFileFilter filter = new MatFileFilter();
- * //add a needle
- * filter.addArrayName( "your_array_name" );
- * 
- * //read array form file (haystack) looking _only_ for pecified array (needle)
- * MatFileReader mfr = new MatFileReader( fileName, filter );
- * 
- * - * @see ca.mjdsystems.jmatio.io.MatFileReader - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - */ -public class MatFileFilter { - private Set filter; - - /** - * Creates empty filter intance. - * - * Note: empty filter acceps all results. - */ - public MatFileFilter() { - filter = new HashSet(); - } - - /** - * Create filter intance and add array names. - * - * @param names - array of names (needles) - */ - public MatFileFilter(String[] names) { - this(); - - for (String name : names) { - addArrayName(name); - } - } - - /** - * Add array name to the filter. This array will be processed - * while crawling thourg the MAT-file - * - * @param name - array name (needle) - */ - public void addArrayName(String name) { - filter.add(name); - } - - /** - * Test if given name matches the filter. - * - * @param name - array name to be tested - * @return - true if array (matrix) of this name should be processed - */ - public boolean matches(String name) { - if (filter.size() == 0) { - return true; - } - return filter.contains(name); - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatFileHeader.java b/src/main/java/ca/mjdsystems/jmatio/io/MatFileHeader.java deleted file mode 100755 index 0bfcb00..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatFileHeader.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.nio.ByteOrder; -import java.util.Date; - -/** - * MAT-file header - * - * Level 5 MAT-files begin with a 128-byte header made up of a 124 byte text field - * and two, 16-bit flag fields - * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - */ -public class MatFileHeader { - private static String DEFAULT_DESCRIPTIVE_TEXT = "MATLAB 5.0 MAT-file, Platform: " - + System.getProperty("os.name") - + ", CREATED on: "; - private static int DEFAULT_VERSION = 0x0100; - private static byte[] DEFAULT_ENDIAN_INDICATOR = new byte[]{(byte) 'M', (byte) 'I'}; - private final ByteOrder byteOrder; - - private int version; - private String description; - private byte[] endianIndicator; - - /** - * New MAT-file header - * - * @param description - descriptive text (no longer than 116 characters) - * @param version - by default is set to 0x0100 - * @param endianIndicator - byte array size of 2 indicating byte-swapping requirement - */ - public MatFileHeader(String description, int version, byte[] endianIndicator, ByteOrder byteOrder) { - this.description = description; - this.version = version; - this.endianIndicator = endianIndicator; - this.byteOrder = byteOrder; - } - - /** - * Gets descriptive text - * - * @return - */ - public String getDescription() { - return description; - } - - /** - * Gets endian indicator. Bytes written as "MI" suggest that byte-swapping operation is required - * in order to interpret data correctly. If value is set to "IM" byte-swapping is not needed. - * - * @return - a byte array size of 2 - */ - public byte[] getEndianIndicator() { - return endianIndicator; - } - - /** - * When creating a MAT-file, set version to 0x0100 - * - * @return - */ - public int getVersion() { - return version; - } - - //@facotry - /** - * A factory. Creates new MatFileHeader instance with default header values: - *
    - *
  • MAT-file is 5.0 version
  • - *
  • version is set to 0x0100
  • - *
  • no byte-swapping ("IM")
  • - *
- * - * @return - new MatFileHeader instance - */ - public static MatFileHeader createHeader() { - return new MatFileHeader(DEFAULT_DESCRIPTIVE_TEXT + (new Date()).toString(), - DEFAULT_VERSION, - DEFAULT_ENDIAN_INDICATOR, - ByteOrder.BIG_ENDIAN); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("["); - sb.append("desriptive text: " + description); - sb.append(", version: " + version); - sb.append(", endianIndicator: " + new String(endianIndicator)); - sb.append("]"); - - return sb.toString(); - } - - public ByteOrder getByteOrder() { - assert((byteOrder != ByteOrder.LITTLE_ENDIAN || endianIndicator[0] == 'I') && (byteOrder != ByteOrder.BIG_ENDIAN || endianIndicator[0] == 'M')); - return byteOrder; - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatFileIncrementalWriter.java b/src/main/java/ca/mjdsystems/jmatio/io/MatFileIncrementalWriter.java deleted file mode 100755 index 624b060..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatFileIncrementalWriter.java +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.WritableByteChannel; -import java.util.Collection; -import java.util.Set; -import java.util.TreeSet; -import java.util.zip.DataFormatException; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; - -import ca.mjdsystems.jmatio.common.MatDataTypes; -import ca.mjdsystems.jmatio.types.MLArray; -import ca.mjdsystems.jmatio.types.MLCell; -import ca.mjdsystems.jmatio.types.MLChar; -import ca.mjdsystems.jmatio.types.MLNumericArray; -import ca.mjdsystems.jmatio.types.MLSparse; -import ca.mjdsystems.jmatio.types.MLStructure; - -/** - * MAT-file Incremental writer. - * - * An updated writer which allows adding variables incrementally - * for the life of the writer. This is necessary to allow large - * variables to be written without having to hold onto then longer - * than is necessary. - * - * The writer internally maintains a list of the variable names - * it has written so far, and will throw an exception if the same - * variable name is submitted more than once to the same reader. - * - * Usage: - *

- * //1. First create example arrays
- * double[] src = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
- * MLDouble mlDouble = new MLDouble( "double_arr", src, 3 );
- * MLChar mlChar = new MLChar( "char_arr", "I am dummy" );
- *         
- * //2. write arrays to file
- * MatFileIncrementalWriter writer = new MatFileIncrementalWriter( new File("mat_file.mat"));
- * writer.write(mlDouble);
- * writer.write(mlChar);
- * 
- * writer.close();
- * 
- * 
- * - * this is "equal" to Matlab commands: - *

- * >> double_arr = [ 1 2; 3 4; 5 6];
- * >> char_arr = 'I am dummy';
- * >>
- * >> save('mat_file.mat', 'double_arr');
- * >> save('mat_file.mat', 'char_arr', '-append');
- * 
- * - * @author - */ -public class MatFileIncrementalWriter { - // private static final Logger logger = Logger.getLogger(MatFileWriter.class); - private WritableByteChannel channel = null; - - private boolean headerWritten = false; - private boolean isStillValid = false; - private Set varNames = new TreeSet(); - - /** - * Creates a writer to a file given the filename. - * - * @param fileName - name of ouput file - * @throws IOException - * @throws DataFormatException - */ - public MatFileIncrementalWriter(String fileName) throws IOException { - this(new File(fileName)); - } - - /** - * Creats a writer to a file given the File object. - * - * @param file - an output File - * @throws IOException - * @throws DataFormatException - */ - public MatFileIncrementalWriter(File file) throws IOException { - this((new FileOutputStream(file)).getChannel()); - } - - /** - * Creates a writer for a file, given an output channel to the file.. - * - * Writes MAT-file header and compressed data (miCOMPRESSED). - * - * @param chan - WritableByteChannel - * @param data - Collection of MLArray elements - * @throws IOException - */ - public MatFileIncrementalWriter(WritableByteChannel chan) throws IOException { - this.channel = chan; - isStillValid = true; - } - - public synchronized void write(MLArray data) - throws IOException { - String vName = data.getName(); - if (varNames.contains(vName)) { - throw new IllegalArgumentException("Error: variable " + vName + " specified more than once for file input."); - } - try { - //write the header, but only once. - if (!headerWritten) { - writeHeader(channel); - } - - //prepare buffer for MATRIX data - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - //write MATRIX bytes into buffer - writeMatrix(dos, data); - - //compress data to save storage - Deflater compresser = new Deflater(); - - byte[] input = baos.toByteArray(); - - ByteArrayOutputStream compressed = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(new DeflaterOutputStream(compressed, compresser)); - - dout.write(input); - - dout.close(); - compressed.close(); - - //write COMPRESSED tag and compressed data into output channel - byte[] compressedBytes = compressed.toByteArray(); - ByteBuffer buf = ByteBuffer.allocateDirect(2 * 4 /* Int size */ + compressedBytes.length); - buf.putInt(MatDataTypes.miCOMPRESSED); - buf.putInt(compressedBytes.length); - buf.put(compressedBytes); - - buf.flip(); - channel.write(buf); - } catch (IOException e) { - throw e; - } finally {} - } - - /** - * Writes MLArrays into WritableByteChannel. - * - * @param channel - * the channel to write to - * @param data - * the collection of {@link MLArray} objects - * @throws IOException - * if writing fails - */ - public synchronized void write(Collection data) throws IOException { - try { - - //write data - for (MLArray matrix : data) { - write(matrix); - } - } catch (IllegalArgumentException iae) { - isStillValid = false; - throw iae; - } catch (IOException e) { - throw e; - } - } - - public synchronized void close() throws IOException { - channel.close(); - } - - /** - * Writes MAT-file header into OutputStream - * @param os OutputStream - * @throws IOException - */ - private void writeHeader(WritableByteChannel channel) throws IOException { - //write descriptive text - MatFileHeader header = MatFileHeader.createHeader(); - char[] dest = new char[116]; - char[] src = header.getDescription().toCharArray(); - System.arraycopy(src, 0, dest, 0, src.length); - - byte[] endianIndicator = header.getEndianIndicator(); - - ByteBuffer buf = ByteBuffer.allocateDirect(dest.length * 2 /* Char size */ + 2 + endianIndicator.length); - - for (int i = 0; i < dest.length; i++) { - buf.put((byte) dest[i]); - } - //write subsyst data offset - buf.position(buf.position() + 8); - - //write version - int version = header.getVersion(); - buf.put((byte) (version >> 8)); - buf.put((byte) version); - - buf.put(endianIndicator); - - buf.flip(); - channel.write(buf); - - headerWritten = true; - } - - /** - * Writes MATRIX into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeMatrix(DataOutputStream output, MLArray array) throws IOException { - OSArrayTag tag; - ByteArrayOutputStream buffer; - DataOutputStream bufferDOS; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - - //flags - writeFlags(dos, array); - - //dimensions - writeDimensions(dos, array); - - //array name - writeName(dos, array); - - switch (array.getType()) { - case MLArray.mxCHAR_CLASS: - //write char data - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - Character[] ac = ((MLChar) array).exportChar(); - for (int i = 0; i < ac.length; i++) { - bufferDOS.writeByte((byte) ac[i].charValue()); - } - tag = new OSArrayTag(MatDataTypes.miUTF8, buffer.toByteArray()); - tag.writeTo(dos); - - break; - case MLArray.mxDOUBLE_CLASS: - - tag = new OSArrayTag(MatDataTypes.miDOUBLE, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miDOUBLE, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxUINT8_CLASS: - - tag = new OSArrayTag(MatDataTypes.miUINT8, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miUINT8, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxINT8_CLASS: - - tag = new OSArrayTag(MatDataTypes.miINT8, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miINT8, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxINT16_CLASS: - - tag = new OSArrayTag(MatDataTypes.miINT16, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miINT16, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxINT64_CLASS: - - tag = new OSArrayTag(MatDataTypes.miINT64, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miINT64, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxUINT64_CLASS: - - tag = new OSArrayTag(MatDataTypes.miUINT64, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miUINT64, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxSTRUCT_CLASS: - //field name length - int itag = 4 << 16 | MatDataTypes.miINT32 & 0xffff; - dos.writeInt(itag); - dos.writeInt(((MLStructure) array).getMaxFieldLenth()); - - //get field names - tag = new OSArrayTag(MatDataTypes.miINT8, ((MLStructure) array).getKeySetToByteArray()); - tag.writeTo(dos); - - for (MLArray a : ((MLStructure) array).getAllFields()) { - writeMatrix(dos, a); - } - break; - case MLArray.mxCELL_CLASS: - for (MLArray a : ((MLCell) array).cells()) { - writeMatrix(dos, a); - } - break; - case MLArray.mxSPARSE_CLASS: - int[] ai; - //write ir - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - ai = ((MLSparse) array).getIR(); - for (int i : ai) { - bufferDOS.writeInt(i); - } - tag = new OSArrayTag(MatDataTypes.miINT32, buffer.toByteArray()); - tag.writeTo(dos); - //write jc - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - ai = ((MLSparse) array).getJC(); - for (int i : ai) { - bufferDOS.writeInt(i); - } - tag = new OSArrayTag(MatDataTypes.miINT32, buffer.toByteArray()); - tag.writeTo(dos); - //write real - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - - Double[] ad = ((MLSparse) array).exportReal(); - - for (int i = 0; i < ad.length; i++) { - bufferDOS.writeDouble(ad[i].doubleValue()); - } - - tag = new OSArrayTag(MatDataTypes.miDOUBLE, buffer.toByteArray()); - tag.writeTo(dos); - //write real imaginary - if (array.isComplex()) { - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - ad = ((MLSparse) array).exportImaginary(); - for (int i = 0; i < ad.length; i++) { - bufferDOS.writeDouble(ad[i].doubleValue()); - } - tag = new OSArrayTag(MatDataTypes.miDOUBLE, buffer.toByteArray()); - tag.writeTo(dos); - } - break; - default: - throw new MatlabIOException("Cannot write matrix of type: " + MLArray.typeToString(array.getType())); - - } - - //write matrix - output.writeInt(MatDataTypes.miMATRIX); //matrix tag - output.writeInt(baos.size()); //size of matrix - output.write(baos.toByteArray()); //matrix data - } - - /** - * Writes MATRIX flags into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeFlags(DataOutputStream os, MLArray array) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - DataOutputStream bufferDOS = new DataOutputStream(buffer); - - bufferDOS.writeInt(array.getFlags()); - - if (array.isSparse()) { - bufferDOS.writeInt(((MLSparse) array).getMaxNZ()); - } else { - bufferDOS.writeInt(0); - } - OSArrayTag tag = new OSArrayTag(MatDataTypes.miUINT32, buffer.toByteArray()); - tag.writeTo(os); - - } - - /** - * Writes MATRIX dimensions into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeDimensions(DataOutputStream os, MLArray array) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - DataOutputStream bufferDOS = new DataOutputStream(buffer); - - int[] dims = array.getDimensions(); - for (int i = 0; i < dims.length; i++) { - bufferDOS.writeInt(dims[i]); - } - OSArrayTag tag = new OSArrayTag(MatDataTypes.miINT32, buffer.toByteArray()); - tag.writeTo(os); - - } - - /** - * Writes MATRIX name into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeName(DataOutputStream os, MLArray array) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - DataOutputStream bufferDOS = new DataOutputStream(buffer); - - byte[] nameByteArray = array.getNameToByteArray(); - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - bufferDOS.write(nameByteArray); - OSArrayTag tag = new OSArrayTag(MatDataTypes.miINT8, buffer.toByteArray()); - tag.writeTo(os); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatFileInputStream.java b/src/main/java/ca/mjdsystems/jmatio/io/MatFileInputStream.java deleted file mode 100644 index a591ef4..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatFileInputStream.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import ca.mjdsystems.jmatio.common.MatDataTypes; -import ca.mjdsystems.jmatio.types.ByteStorageSupport; - -/** - * MAT-file input stream class. - * - * @author Wojciech Gradkowski - */ -class MatFileInputStream { - private final int type; - private final ByteBuffer buf; - - /** - * Attach MAT-file input stream to InputStream - * - * @param is - input stream - * @param type - type of data in the stream - * @see ca.mjdsystems.jmatio.common.MatDataTypes - */ - public MatFileInputStream(ByteBuffer buf, int type) { - this.type = type; - this.buf = buf; - } - - /** - * Reads data (number of bytes red is determined by data type) - * from the stream to int. - * - * @return - * @throws IOException - */ - public int readInt() throws IOException { - switch (type) { - case MatDataTypes.miUINT8: - return (int) (buf.get() & 0xFF); - case MatDataTypes.miINT8: - return (int) buf.get(); - case MatDataTypes.miUINT16: - return (int) (buf.getShort() & 0xFFFF); - case MatDataTypes.miINT16: - return (int) buf.getShort(); - case MatDataTypes.miUINT32: - return (int) (buf.getInt() & 0xFFFFFFFF); - case MatDataTypes.miINT32: - return (int) buf.getInt(); - case MatDataTypes.miUINT64: - return (int) buf.getLong(); - case MatDataTypes.miINT64: - return (int) buf.getLong(); - case MatDataTypes.miDOUBLE: - return (int) buf.getDouble(); - default: - throw new IllegalArgumentException("Unknown data type: " + type); - } - } - - /** - * Reads data (number of bytes red is determined by data type) - * from the stream to char. - * - * @return - char - * @throws IOException - */ - public char readChar() throws IOException { - switch (type) { - case MatDataTypes.miUINT8: - return (char) (buf.get() & 0xFF); - case MatDataTypes.miINT8: - return (char) buf.get(); - case MatDataTypes.miUINT16: - return (char) (buf.getShort() & 0xFFFF); - case MatDataTypes.miINT16: - return (char) buf.getShort(); - case MatDataTypes.miUINT32: - return (char) (buf.getInt() & 0xFFFFFFFF); - case MatDataTypes.miINT32: - return (char) buf.getInt(); - case MatDataTypes.miDOUBLE: - return (char) buf.getDouble(); - case MatDataTypes.miUTF8: - return (char) buf.get(); - default: - throw new IllegalArgumentException("Unknown data type: " + type); - } - } - - /** - * Reads data (number of bytes red is determined by data type) - * from the stream to double. - * - * @return - double - * @throws IOException - */ - public double readDouble() throws IOException { - switch (type) { - case MatDataTypes.miUINT8: - return (double) (buf.get() & 0xFF); - case MatDataTypes.miINT8: - return (double) buf.get(); - case MatDataTypes.miUINT16: - return (double) (buf.getShort() & 0xFFFF); - case MatDataTypes.miINT16: - return (double) buf.getShort(); - case MatDataTypes.miUINT32: - return (double) (buf.getInt() & 0xFFFFFFFF); - case MatDataTypes.miINT32: - return (double) buf.getInt(); - case MatDataTypes.miDOUBLE: - return (double) buf.getDouble(); - default: - throw new IllegalArgumentException("Unknown data type: " + type); - } - } - - public byte readByte() { - switch (type) { - case MatDataTypes.miUINT8: - return (byte) (buf.get() & 0xFF); - case MatDataTypes.miINT8: - return (byte) buf.get(); - case MatDataTypes.miUINT16: - return (byte) (buf.getShort() & 0xFFFF); - case MatDataTypes.miINT16: - return (byte) buf.getShort(); - case MatDataTypes.miUINT32: - return (byte) (buf.getInt() & 0xFFFFFFFF); - case MatDataTypes.miINT32: - return (byte) buf.getInt(); - case MatDataTypes.miDOUBLE: - return (byte) buf.getDouble(); - case MatDataTypes.miUTF8: - return (byte) buf.get(); - default: - throw new IllegalArgumentException("Unknown data type: " + type); - } - } - - /** - * Reads the data into a {@link ByteBuffer}. This method is - * only supported for arrays with backing ByteBuffer ({@link ByteStorageSupport}). - * - * @param dest - * the destination {@link ByteBuffer} - * @param elements - * the number of elements to read into a buffer - * @param storage - * the backing {@link ByteStorageSupport} that - * gives information how data should be interpreted - * @return reference to the destination {@link ByteBuffer} - * @throws IOException - * if buffer is under-fed, or another IO problem occurs - */ - public ByteBuffer readToByteBuffer(ByteBuffer dest, int elements, - ByteStorageSupport storage) throws IOException { - - int bytesAllocated = storage.getBytesAllocated(); - int size = elements * storage.getBytesAllocated(); - - //direct buffer copy - if (MatDataTypes.sizeOf(type) == bytesAllocated && buf.order().equals(dest.order())) { - int bufMaxSize = 1024; - int bufSize = Math.min(buf.remaining(), bufMaxSize); - int bufPos = buf.position(); - - byte[] tmp = new byte[bufSize]; - - while (dest.remaining() > 0) { - int length = Math.min(dest.remaining(), tmp.length); - buf.get(tmp, 0, length); - dest.put(tmp, 0, length); - } - buf.position(bufPos + size); - } else { - //because Matlab writes data not respectively to the declared - //matrix type, the reading is not straight forward (as above) - Class clazz = storage.getStorageClazz(); - while (dest.remaining() > 0) { - if (clazz.equals(Double.class)) { - dest.putDouble(readDouble()); - } else if (clazz.equals(Byte.class)) { - dest.put(readByte()); - } else if (clazz.equals(Integer.class)) { - dest.putInt(readInt()); - } else if (clazz.equals(Long.class)) { - dest.putLong(readLong()); - } else if (clazz.equals(Float.class)) { - dest.putFloat(readFloat()); - } else if (clazz.equals(Short.class)) { - dest.putShort(readShort()); - } else { - throw new RuntimeException("Not supported buffer reader for " + clazz); - } - } - } - dest.rewind(); - return dest; - } - - private float readFloat() { - switch (type) { - case MatDataTypes.miUINT8: - return (float) (buf.get() & 0xFF); - case MatDataTypes.miINT8: - return (float) buf.get(); - case MatDataTypes.miUINT16: - return (float) (buf.getShort() & 0xFFFF); - case MatDataTypes.miINT16: - return (float) buf.getShort(); - case MatDataTypes.miUINT32: - return (float) (buf.getInt() & 0xFFFFFFFF); - case MatDataTypes.miINT32: - return (float) buf.getInt(); - case MatDataTypes.miSINGLE: - return (float) buf.getFloat(); - case MatDataTypes.miDOUBLE: - return (float) buf.getDouble(); - default: - throw new IllegalArgumentException("Unknown data type: " + type); - } - } - - private short readShort() { - switch (type) { - case MatDataTypes.miUINT8: - return (short) (buf.get() & 0xFF); - case MatDataTypes.miINT8: - return (short) buf.get(); - case MatDataTypes.miUINT16: - return (short) (buf.getShort() & 0xFFFF); - case MatDataTypes.miINT16: - return (short) buf.getShort(); - case MatDataTypes.miUINT32: - return (short) (buf.getInt() & 0xFFFFFFFF); - case MatDataTypes.miINT32: - return (short) buf.getInt(); - case MatDataTypes.miUINT64: - return (short) buf.getLong(); - case MatDataTypes.miINT64: - return (short) buf.getLong(); - case MatDataTypes.miDOUBLE: - return (short) buf.getDouble(); - default: - throw new IllegalArgumentException("Unknown data type: " + type); - } - } - - private long readLong() { - switch (type) { - case MatDataTypes.miUINT8: - return (long) (buf.get() & 0xFF); - case MatDataTypes.miINT8: - return (long) buf.get(); - case MatDataTypes.miUINT16: - return (long) (buf.getShort() & 0xFFFF); - case MatDataTypes.miINT16: - return (long) buf.getShort(); - case MatDataTypes.miUINT32: - return (long) (buf.getInt() & 0xFFFFFFFF); - case MatDataTypes.miINT32: - return (long) buf.getInt(); - case MatDataTypes.miUINT64: - return (long) buf.getLong(); - case MatDataTypes.miINT64: - return (long) buf.getLong(); - case MatDataTypes.miDOUBLE: - return (long) buf.getDouble(); - default: - throw new IllegalArgumentException("Unknown data type: " + type); - } - } - - public void skip(int padding) { - buf.position(buf.position() + padding); - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatFileReader.java b/src/main/java/ca/mjdsystems/jmatio/io/MatFileReader.java deleted file mode 100755 index 935dcf9..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatFileReader.java +++ /dev/null @@ -1,1441 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.EOFException; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.RandomAccessFile; -import java.lang.ref.WeakReference; -import java.lang.reflect.Method; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.*; -import java.util.zip.InflaterInputStream; - -import ca.mjdsystems.jmatio.common.MatDataTypes; -import ca.mjdsystems.jmatio.io.MatFileWriter.ByteArrayOutputStream2; -import ca.mjdsystems.jmatio.types.ByteStorageSupport; -import ca.mjdsystems.jmatio.types.MLArray; -import ca.mjdsystems.jmatio.types.MLCell; -import ca.mjdsystems.jmatio.types.MLChar; -import ca.mjdsystems.jmatio.types.MLDouble; -import ca.mjdsystems.jmatio.types.MLEmptyArray; -import ca.mjdsystems.jmatio.types.MLInt16; -import ca.mjdsystems.jmatio.types.MLInt32; -import ca.mjdsystems.jmatio.types.MLInt64; -import ca.mjdsystems.jmatio.types.MLInt8; -import ca.mjdsystems.jmatio.types.MLJavaObject; -import ca.mjdsystems.jmatio.types.MLNumericArray; -import ca.mjdsystems.jmatio.types.MLObject; -import ca.mjdsystems.jmatio.types.MLSingle; -import ca.mjdsystems.jmatio.types.MLSparse; -import ca.mjdsystems.jmatio.types.MLStructure; -import ca.mjdsystems.jmatio.types.MLUInt32; -import ca.mjdsystems.jmatio.types.MLUInt64; -import ca.mjdsystems.jmatio.types.MLUInt8; - -/** - * MAT-file reader. Reads MAT-file into MLArray objects. - * - * Usage: - *

- * //read in the file
- * MatFileReader mfr = new MatFileReader( "mat_file.mat" );
- * 
- * //get array of a name "my_array" from file
- * MLArray mlArrayRetrived = mfr.getMLArray( "my_array" );
- * 
- * //or get the collection of all arrays that were stored in the file
- * Map content = mfr.getContent();
- * 
- * - * @see ca.mjdsystems.jmatio.io.MatFileFilter - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - */ -/** - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - * - */ -public class MatFileReader { - public static final int MEMORY_MAPPED_FILE = 1; - public static final int DIRECT_BYTE_BUFFER = 2; - public static final int HEAP_BYTE_BUFFER = 4; - - /** - * Type of matlab mat file. - */ - private final MatFileType matType; - /** - * MAT-file header - */ - private MatFileHeader matFileHeader; - /** - * Container for red MLArrays - */ - private Map data; - /** - * Tells how bytes are organized in the buffer. - */ - private ByteOrder byteOrder; - /** - * Array name filter - */ - private MatFileFilter filter; - /** - * Whether or not we have found an MCOS type variable. Needed to know if further processing is needed. - */ - private boolean haveMCOS = false; - /** - * Holds the likely candidate for the MCOS extra data at the end of a MAT file. - */ - private MLUInt8 mcosData; - - /** - * Creates instance of MatFileReader and reads MAT-file - * from location given as fileName. - * - * This method reads MAT-file without filtering. - * - * @param fileName the MAT-file path String - * @throws IOException when error occurred while processing the file. - */ - public MatFileReader(String fileName) throws FileNotFoundException, IOException { - this(new File(fileName), new MatFileFilter(), MatFileType.Regular); - } - - /** - * Creates instance of MatFileReader and reads MAT-file - * from location given as fileName. - * - * Results are filtered by MatFileFilter. Arrays that do not meet - * filter match condition will not be available in results. - * - * @param fileName the MAT-file path String - * @param MatFileFilter array name filter. - * @throws IOException when error occurred while processing the file. - */ - public MatFileReader(String fileName, MatFileFilter filter) throws IOException { - this(new File(fileName), filter, MatFileType.Regular); - } - - /** - * Creates instance of MatFileReader and reads MAT-file - * from file. - * - * This method reads MAT-file without filtering. - * - * @param file the MAT-file - * @throws IOException when error occurred while processing the file. - */ - public MatFileReader(File file) throws IOException { - this(file, new MatFileFilter(), MatFileType.Regular); - - } - - /** - * Creates instance of MatFileReader and reads MAT-file from - * file. - *

- * Results are filtered by MatFileFilter. Arrays that do not - * meet filter match condition will not be available in results. - *

- * Note: this method reads file using the memory mapped file policy, see - * notes to {@link #read(File, MatFileFilter, ca.mjdsystems.jmatio.io.MatFileReader.MallocPolicy)} - * - * @param file - * the MAT-file - * @param MatFileFilter - * array name filter. - * @throws IOException - * when error occurred while processing the file. - */ - public MatFileReader(File file, MatFileFilter filter, MatFileType matType) throws IOException { - this(matType); - - read(file, filter, MEMORY_MAPPED_FILE); - } - - public MatFileReader(MatFileType matType) { - this.matType = matType; - filter = new MatFileFilter(); - data = new LinkedHashMap(); - } - - /** - * Creates instance of MatFileReader and reads MAT-file from - * file. - * - * This method reads MAT-file without filtering. - * - * @param stream - * the MAT-file stream - * @throws IOException - * when error occurred while processing the file. - */ - public MatFileReader(InputStream stream, MatFileType type) throws IOException { - this(stream, new MatFileFilter(), type); - } - - /** - * Creates instance of MatFileReader and reads MAT-file from - * file. - *

- * Results are filtered by MatFileFilter. Arrays that do not - * meet filter match condition will not be available in results. - *

- * Note: this method reads file using the memory mapped file policy, see - * notes to - * {@link #read(File, MatFileFilter, ca.mjdsystems.jmatio.io.MatFileReader.MallocPolicy)} - * - * - * @param stream - * the MAT-file stream - * @param MatFileFilter - * array name filter. - * @throws IOException - * when error occurred while processing the file. - */ - public MatFileReader(InputStream stream, MatFileFilter filter, MatFileType type) throws IOException { - this(type); - - read(stream, filter); - } - - /** - * Reads the content of a MAT-file and returns the mapped content. - *

- * This method calls - * read(file, new MatFileFilter(), MallocPolicy.MEMORY_MAPPED_FILE). - * - * @param file - * a valid MAT-file file to be read - * @return the same as {@link #getContent()} - * @throws IOException - * if error occurs during file processing - */ - public synchronized Map read(File file) throws IOException { - return read(file, new MatFileFilter(), MEMORY_MAPPED_FILE); - } - - /** - * Reads the content of a MAT-file and returns the mapped content. - *

- * This method calls read(stream, new MatFileFilter()). - * - * @param stream - * a valid MAT-file stream to be read - * @return the same as {@link #getContent()} - * @throws IOException - * if error occurs during file processing - */ - public synchronized Map read(InputStream stream) throws IOException { - return read(stream, new MatFileFilter()); - } - - /** - * Reads the content of a MAT-file and returns the mapped content. - *

- * This method calls - * read(file, new MatFileFilter(), policy). - * - * @param file - * a valid MAT-file file to be read - * @param policy - * the file memory allocation policy - * @return the same as {@link #getContent()} - * @throws IOException - * if error occurs during file processing - */ - public synchronized Map read(File file, int policy) throws IOException { - return read(file, new MatFileFilter(), policy); - } - - /** - * Reads the content of a MAT-file and returns the mapped content. - *

- * Because of java bug #4724038 - * which disables releasing the memory mapped resource, additional different - * allocation modes are available. - *

    - *
  • {@link #MEMORY_MAPPED_FILE} - a memory mapped file
  • - *
  • {@link #DIRECT_BYTE_BUFFER} - a uses - * {@link ByteBuffer#allocateDirect(int)} method to read in - * the file contents
  • - *
  • {@link #HEAP_BYTE_BUFFER} - a uses - * {@link ByteBuffer#allocate(int)} method to read in the - * file contents
  • - *
- * Note: memory mapped file will try to invoke a nasty code to relase - * it's resources - * - * @param file - * a valid MAT-file file to be read - * @param filter - * the array filter applied during reading - * @param policy - * the file memory allocation policy - * @return the same as {@link #getContent()} - * @see MatFileFilter - * @throws IOException - * if error occurs during file processing - */ - private static final int DIRECT_BUFFER_LIMIT = 1 << 25; - - public synchronized Map read(File file, MatFileFilter filter, - int policy) throws IOException { - this.filter = filter; - - //clear the results - for (String key : data.keySet()) { - data.remove(key); - } - - FileChannel roChannel = null; - RandomAccessFile raFile = null; - ByteBuffer buf = null; - WeakReference bufferWeakRef = null; - try { - //Create a read-only memory-mapped file - raFile = new RandomAccessFile(file, "r"); - roChannel = raFile.getChannel(); - // until java bug #4715154 is fixed I am not using memory mapped files - // The bug disables re-opening the memory mapped files for writing - // or deleting until the VM stops working. In real life I need to open - // and update files - switch (policy) { - case DIRECT_BYTE_BUFFER: - buf = ByteBuffer.allocateDirect((int) roChannel.size()); - roChannel.read(buf, 0); - buf.rewind(); - break; - case HEAP_BYTE_BUFFER: - int filesize = (int) roChannel.size(); - System.gc(); - buf = ByteBuffer.allocate(filesize); - - // The following two methods couldn't be used (at least under MS Windows) - // since they are implemented in a suboptimal way. Each of them - // allocates its own _direct_ buffer of exactly the same size, - // the buffer passed as parameter has, reads data into it and - // only afterwards moves data into the buffer passed as parameter. - // roChannel.read(buf, 0); // ends up in outOfMemory - // raFile.readFully(buf.array()); // ends up in outOfMemory - int numberOfBlocks = filesize / DIRECT_BUFFER_LIMIT + ((filesize % DIRECT_BUFFER_LIMIT) > 0 ? 1 : 0); - if (numberOfBlocks > 1) { - ByteBuffer tempByteBuffer = ByteBuffer.allocateDirect(DIRECT_BUFFER_LIMIT); - for (int block = 0; block < numberOfBlocks; block++) { - tempByteBuffer.clear(); - roChannel.read(tempByteBuffer, block * DIRECT_BUFFER_LIMIT); - tempByteBuffer.flip(); - buf.put(tempByteBuffer); - } - tempByteBuffer = null; - } else - roChannel.read(buf, 0); - - buf.rewind(); - break; - case MEMORY_MAPPED_FILE: - buf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int) roChannel.size()); - bufferWeakRef = new WeakReference((MappedByteBuffer) buf); - break; - default: - throw new IllegalArgumentException("Unknown file allocation policy"); - } - // Do the actual work. - parseData(buf); - - return getContent(); - } catch (IOException e) { - throw e; - } finally { - if (roChannel != null) { - roChannel.close(); - } - if (raFile != null) { - raFile.close(); - } - if (buf != null && bufferWeakRef != null && policy == MEMORY_MAPPED_FILE) { - try { - clean(buf); - } catch (Exception e) { - int GC_TIMEOUT_MS = 1000; - buf = null; - long start = System.currentTimeMillis(); - while (bufferWeakRef.get() != null) { - if (System.currentTimeMillis() - start > GC_TIMEOUT_MS) { - break; //a hell cannot be unmapped - hopefully GC will - //do it's job later - } - System.gc(); - Thread.yield(); - } - } - } - } - - } - - private void parseData(ByteBuffer buf) throws IOException { - //read in file header - readHeader(buf); - - while (buf.remaining() > 0) { - readData(buf); - } - if (haveMCOS) { - parseMCOS(mcosData); - if (data.get("@") == mcosData) { - data.remove("@"); - } - for (Map.Entry it : data.entrySet()) { - if (it.getValue() == mcosData) { - data.remove(it.getKey()); - break; - } - } - } - mcosData = null; - } - - private void parseMCOS(MLUInt8 mcosData) throws IOException { - // First, parse back out the mcosData. - ByteBuffer buffer = mcosData.getRealByteBuffer(); - ByteBufferInputStream dataStream = new ByteBufferInputStream(buffer, buffer.limit()); - - Map mcosContent; - - MatFileReader matFile = new MatFileReader(dataStream, MatFileType.ReducedHeader); - mcosContent = matFile.getContent(); - MLCell mcosInfo = (MLCell) ((MLStructure) mcosContent.get("@0")).getField("MCOS"); - ByteBuffer mcosDataBuf = ((MLUInt8) mcosInfo.get(0)).getRealByteBuffer(); - // This bytebuffer needs to be read in the byte order of the MAT file order. Thus fix. - mcosDataBuf.order(matFile.getMatFileHeader().getByteOrder()); - - // Parse out the data buffer. First get version information. Should always equal 2. - int version = mcosDataBuf.getInt(); - if (version != 2) { - throw new IllegalStateException("MAT file's MCOS data has a different version(?). Got: " + version + ", wanted 2."); - } - - // Get the string count + define the string array. - int strCount = mcosDataBuf.getInt(); - String[] strs = new String[strCount]; - - // Get the segment indexes. - int segmentIndexes[] = new int[6]; - for (int i = 0; i < segmentIndexes.length; ++i) { - segmentIndexes[i] = mcosDataBuf.getInt(); - } - - // There should now be 8 0 bytes. Make sure this is true to avoid object format changes. - if (mcosDataBuf.getLong() != 0) { - throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); - } - - // Finally, read in each string. Java doesn't provide an easy way to do this in bulk, so just use a stupid formula for now. - for (int i = 0; i < strCount; ++i) { - StringBuilder sb = new StringBuilder(); - for (char next = (char) mcosDataBuf.get(); next != '\0'; next = (char) mcosDataBuf.get()) { - sb.append(next); - } - strs[i] = sb.toString(); - } - - // Sanity check, next 8 byte aligned position in the buffer should equal the start of the first segment! - if (((mcosDataBuf.position() + 0x07) & ~0x07) != segmentIndexes[0]) { - throw new IllegalStateException("Data from the strings section was not all read!"); - } - - // First segment, class information. Really just need the class names. - List classNamesList = new ArrayList(); - mcosDataBuf.position(segmentIndexes[0]); - // There are 16 unknown bytes. Ensure they are 0. - if (mcosDataBuf.getLong() != 0 || mcosDataBuf.getLong() != 0) { - throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); - } - while (mcosDataBuf.position() < segmentIndexes[1]) { - int packageNameIndex = mcosDataBuf.getInt(); // Unused for now. - int classNameIndex = mcosDataBuf.getInt(); // Unused for now. - String className = strs[classNameIndex - 1]; - classNamesList.add(className); - if (mcosDataBuf.getLong() != 0) { - throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); - } - } - - // Sanity check, position in the buffer should equal the start of the second segment! - if (mcosDataBuf.position() != segmentIndexes[1]) { - throw new IllegalStateException("Data from the class section was not all read!"); - } - - // @todo: Second segment, Object properties containing other properties. Not used yet, thus ignored. - mcosDataBuf.position(segmentIndexes[2]); - - // Third segment. Contains all the useful per-object information. - Map objectInfoList = new HashMap(); - // There are 24 unknown bytes. Ensure they are 0. - if (mcosDataBuf.getLong() != 0 || mcosDataBuf.getLong() != 0 || mcosDataBuf.getLong() != 0) { - throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); - } - int objectCount = 1; - while (mcosDataBuf.position() < segmentIndexes[3]) { - // First fetch the data. - int classIndex = mcosDataBuf.getInt(); - if (mcosDataBuf.getLong() != 0) { - throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); - } - int segment2Index = mcosDataBuf.getInt(); - int segment4Index = mcosDataBuf.getInt(); - mcosDataBuf.getInt(); // This value is random. But we need to move the buffer forward, so read it without a check. - int objectId = objectCount++; // It would appear that the "objectId" is in fact some other MATLAB value. Thus ignore, - // and use the index into this segment as the id instead. - - // Then parse it into the form needed for the object. - objectInfoList.put(objectId - 1, new MatMCOSObjectInformation(classNamesList.get(classIndex - 1), classIndex, objectId, segment2Index, segment4Index)); - } - - // Sanity check, position in the buffer should equal the start of the fourth segment! - if (mcosDataBuf.position() != segmentIndexes[3]) { - throw new IllegalStateException("Data from the object section was not all read! At: " + mcosDataBuf.position() + ", wanted: " + segmentIndexes[3]); - } - - // Fourth segment. Contains the regular properties for objects. - // There are 8 unknown bytes. Ensure they are 0. - if (mcosDataBuf.getLong() != 0) { - throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); - } - List> segment4Properties = new ArrayList>(); - while (mcosDataBuf.position() < segmentIndexes[4]) { - Map properties = new HashMap(); - int propertiesCount = mcosDataBuf.getInt(); - for (int i = 0; i < propertiesCount; ++i) { - int nameIndex = mcosDataBuf.getInt(); - int flag = mcosDataBuf.getInt(); - int heapIndex = mcosDataBuf.getInt(); - - String propertyName = strs[nameIndex - 1]; - MLArray property; - switch (flag) { - case 0: - property = new MLChar(propertyName, strs[heapIndex - 1]); - break; - case 1: - property = mcosInfo.get(heapIndex + 2); - break; - case 2: - // @todo: Handle a boolean. - throw new UnsupportedOperationException("Mat file parsing does not yet support booleans!"); - default: - throw new UnsupportedOperationException("Don't yet support parameter type: " + flag + "!"); - } - if (property instanceof MLUInt32) { - int[][] data = ((MLUInt32) property).getArray(); - if (data[0][0] == 0xdd000000 && data[1][0] == 0x02) { - MLObjectPlaceholder objHolder = new MLObjectPlaceholder(propertyName, "", data); - property = processMCOS(objHolder, classNamesList, objectInfoList); - } - } - properties.put(propertyName, property); - } - segment4Properties.add(properties); - mcosDataBuf.position((mcosDataBuf.position() + 0x07) & ~0x07); - } - - // Sanity check, position in the buffer should equal the start of the fifth segment! - if (mcosDataBuf.position() != segmentIndexes[4]) { - throw new IllegalStateException("Data from the properties section (2) was not all read! At: " + mcosDataBuf.position() + ", wanted: " + segmentIndexes[4]); - } - - // Now merge in the properties from segment 4 into object. - for (MatMCOSObjectInformation it : objectInfoList.values()) { - Map objAttributes = it.structure; - if (it.segment4PropertiesIndex > 0) { - for (Map.Entry attribute : segment4Properties.get(it.segment4PropertiesIndex - 1).entrySet()) { - objAttributes.put(attribute.getKey(), attribute.getValue()); - } - } else { - throw new IllegalStateException("Properties are not found! Not sure where to look ..."); - } - } - - // Finally, merge in attributes from the global grab bag. - MLCell attribBag = (MLCell) mcosInfo.get(mcosInfo.getSize() - 1); // Get the grab bag. - for (MatMCOSObjectInformation it : objectInfoList.values()) { - MLStructure attributes = (MLStructure) attribBag.get(it.classId); - Collection attributeNames = attributes.getFieldNames(); - Map objAttributes = it.structure; - for (String attributeName : attributeNames) { - if (objAttributes.get(attributeName) == null) { - objAttributes.put(attributeName, attributes.getField(attributeName)); - } - } - } - - for (Map.Entry it : data.entrySet()) { - if (it.getValue() instanceof MLObjectPlaceholder) { - MLObjectPlaceholder objHolder = (MLObjectPlaceholder) it.getValue(); - it.setValue(processMCOS(objHolder, classNamesList, objectInfoList)); - } - } - } - - private MLObject processMCOS(MLObjectPlaceholder objHolder, List classNamesList, Map objectInfoList) { - int classId = objHolder.classId; - MLObject obj = new MLObject(objHolder.name, classNamesList.get(classId - 1), objHolder.getDimensions(), 0); - for (int i = 0; i < obj.getSize(); ++i) { - MatMCOSObjectInformation objectInformation = objectInfoList.get(objHolder.objectIds[i] - 1); - if (classId != objectInformation.classId) { - throw new IllegalStateException("Found an object in array with a different class id! Actual: " + objectInformation.classId + ", expected: " + classId + "!"); - } - obj.setFields(i, objectInformation.structure); - } - return obj; - } - - /** - * Read a mat file from a stream. Internally this will read the stream fully - * into memory before parsing it. - * - * @param stream - * a valid MAT-file stream to be read - * @param filter - * the array filter applied during reading - * - * @return the same as {@link #getContent()} - * @see MatFileFilter - * @throws IOException - * if error occurs during file processing - */ - public synchronized Map read(InputStream stream, MatFileFilter filter) throws IOException { - this.filter = filter; - - data.clear(); - - ByteBuffer buf = null; - - final ByteArrayOutputStream2 baos = new ByteArrayOutputStream2(); - copy(stream, baos); - buf = ByteBuffer.wrap(baos.getBuf(), 0, baos.getCount()); - - // Do the actual work - parseData(buf); - - return getContent(); - } - - private void copy(InputStream stream, ByteArrayOutputStream2 output) throws IOException { - final byte[] buffer = new byte[1024 * 4]; - int n = 0; - while (-1 != (n = stream.read(buffer))) { - output.write(buffer, 0, n); - } - } - - /** - * Workaround taken from bug #4724038 - * to release the memory mapped byte buffer. - *

- * Little quote from SUN: This is highly inadvisable, to put it mildly. - * It is exceedingly dangerous to forcibly unmap a mapped byte buffer that's - * visible to Java code. Doing so risks both the security and stability of - * the system - *

- * Since the memory byte buffer used to map the file is not exposed to the - * outside world, maybe it's save to use it without being cursed by the SUN. - * Since there is no other solution this will do (don't trust voodoo GC - * invocation) - * - * @param buffer - * the buffer to be unmapped - * @throws Exception - * all kind of evil stuff - */ - private void clean(final Object buffer) throws Exception { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - try { - Method getCleanerMethod = buffer.getClass().getMethod( - "cleaner", new Class[0]); - getCleanerMethod.setAccessible(true); - sun.misc.Cleaner cleaner = (sun.misc.Cleaner) getCleanerMethod - .invoke(buffer, new Object[0]); - cleaner.clean(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - }); - } - - /** - * Gets MAT-file header - * - * @return - a MatFileHeader object - */ - public MatFileHeader getMatFileHeader() { - return matFileHeader; - } - - /** - * Returns list of MLArray objects that were inside MAT-file - * - * @return - a ArrayList - * @deprecated use getContent which returns a Map to provide - * easier access to MLArrays contained in MAT-file - */ - public ArrayList getData() { - return new ArrayList(data.values()); - } - - /** - * Returns the value to which the red file maps the specified array name. - * - * Returns null if the file contains no content for this name. - * - * @param - array name - * @return - the MLArray to which this file maps the specified name, - * or null if the file contains no content for this name. - */ - public MLArray getMLArray(String name) { - return data.get(name); - } - - /** - * Returns a map of MLArray objects that were inside MAT-file. - * - * MLArrays are mapped with MLArrays' names - * - * @return - a Map of MLArrays mapped with their names. - */ - public Map getContent() { - return data; - } - - /** - * Reads data form byte buffer. Searches for either - * miCOMPRESSED data or miMATRIX data. - * - * Compressed data are inflated and the product is recursively passed back - * to this same method. - * - * Modifies buf position. - * - * @param buf - - * input byte buffer - * @throws IOException when error occurs while reading the buffer. - */ - private void readData(ByteBuffer buf) throws IOException { - //read data - ISMatTag tag = new ISMatTag(buf); - switch (tag.type) { - case MatDataTypes.miCOMPRESSED: - int numOfBytes = tag.size; - //inflate and recur - if (buf.remaining() < numOfBytes) { - throw new MatlabIOException("Compressed buffer length miscalculated!"); - } - //instead of standard Inlater class instance I use an inflater input - //stream... gives a great boost to the performance - InflaterInputStream iis = new InflaterInputStream(new ByteBufferInputStream(buf, numOfBytes)); - - //process data decompression - byte[] result = new byte[1024]; - - HeapBufferDataOutputStream dos = new HeapBufferDataOutputStream(); - int i; - try { - do { - i = iis.read(result, 0, result.length); - int len = Math.max(0, i); - dos.write(result, 0, len); - } while (i > 0); - } catch (EOFException eofe) { - System.out.println("EOFException detected!"); - } catch (IOException e) { - throw new MatlabIOException("Could not decompress data: " + e); - } finally { - iis.close(); - dos.flush(); - } - //create a ByteBuffer from the deflated data - ByteBuffer out = dos.getByteBuffer(); - - //with proper byte ordering - out.order(byteOrder); - - try { - readData(out); - - } catch (IOException e) { - throw e; - } finally { - dos.close(); - } - break; - case MatDataTypes.miMATRIX: - - //read in the matrix - int pos = buf.position(); - - MLArray element = readMatrix(buf, true); - - if (element != null) { - if (!data.containsKey(element.getName())) { - data.put(element.getName(), element); - } - if (element.getName() == "@") { - int nextIndex = 0; - for (; data.containsKey("@" + nextIndex); nextIndex++) {} - data.put("@" + nextIndex, element); - } - } else { - int red = buf.position() - pos; - int toread = tag.size - red; - buf.position(buf.position() + toread); - } - int red = buf.position() - pos; - - int toread = tag.size - red; - - if (toread != 0) { - throw new MatlabIOException("Matrix was not red fully! " + toread + " remaining in the buffer."); - } - break; - default: - throw new MatlabIOException("Incorrect data tag: " + tag); - - } - } - - /** - * Reads miMATRIX from from input stream. - * - * If reading was not finished (which is normal for filtered results) - * returns null. - * - * Modifies buf position to the position when reading - * finished. - * - * Uses recursive processing for some ML**** data types. - * - * @param buf - - * input byte buffer - * @param isRoot - - * when true informs that if this is a top level - * matrix - * @return - MLArray or null if matrix does - * not match filter - * @throws IOException when error occurs while reading the buffer. - */ - private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { - //result - MLArray mlArray; - ISMatTag tag; - - //read flags - int[] flags = readFlags(buf); - int attributes = (flags.length != 0) ? flags[0] : 0; - int nzmax = (flags.length != 0) ? flags[1] : 0; - int type = attributes & 0xff; - - //read Array dimension - int[] dims = readDimension(buf); - - //read array Name - String name = readName(buf); - - //if this array is filtered out return immediately - if (isRoot && !filter.matches(name)) { - return null; - } - - //read data >> consider changing it to stategy pattern - switch (type) { - case MLArray.mxSTRUCT_CLASS: - - MLStructure struct = new MLStructure(name, dims, type, attributes); - - // field name length - this subelement always uses the compressed data element format - tag = new ISMatTag(buf); - int maxlen = buf.getInt(); //maximum field length - - ////// read fields data as Int8 - tag = new ISMatTag(buf); - //calculate number of fields - int numOfFields = tag.size / maxlen; - - String[] fieldNames = new String[numOfFields]; - for (int i = 0; i < numOfFields; i++) { - byte[] names = new byte[maxlen]; - buf.get(names); - fieldNames[i] = zeroEndByteArrayToString(names); - } - - buf.position(buf.position() + tag.padding); - //read fields - for (int index = 0; index < struct.getM() * struct.getN(); index++) { - for (int i = 0; i < numOfFields; i++) { - //read matrix recursively - tag = new ISMatTag(buf); - - if (tag.size > 0) { - MLArray fieldValue = readMatrix(buf, false); - struct.setField(fieldNames[i], fieldValue, index); - } else { - struct.setField(fieldNames[i], new MLEmptyArray(), index); - } - } - } - mlArray = struct; - break; - case MLArray.mxCELL_CLASS: - MLCell cell = new MLCell(name, dims, type, attributes); - for (int i = 0; i < cell.getM() * cell.getN(); i++) { - tag = new ISMatTag(buf); - if (tag.size > 0) { - //read matrix recursively - MLArray cellmatrix = readMatrix(buf, false); - cell.set(cellmatrix, i); - } else { - cell.set(new MLEmptyArray(), i); - } - } - mlArray = cell; - break; - case MLArray.mxDOUBLE_CLASS: - mlArray = new MLDouble(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - case MLArray.mxSINGLE_CLASS: - mlArray = new MLSingle(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - case MLArray.mxUINT8_CLASS: - mlArray = new MLUInt8(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - - // This might be the MCOS extra data. If there is no name, set it as the current set of data. - if (name.equals("")) { - mcosData = (MLUInt8) mlArray; - } - - break; - case MLArray.mxINT8_CLASS: - mlArray = new MLInt8(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - - case MLArray.mxINT16_CLASS: - mlArray = new MLInt16(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - case MLArray.mxINT32_CLASS: - mlArray = new MLInt32(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - case MLArray.mxUINT32_CLASS: - mlArray = new MLUInt32(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - case MLArray.mxINT64_CLASS: - mlArray = new MLInt64(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - case MLArray.mxUINT64_CLASS: - mlArray = new MLUInt64(name, dims, type, attributes); - //read real - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), - (MLNumericArray) mlArray); - //read complex - if (mlArray.isComplex()) { - tag = new ISMatTag(buf); - tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), - (MLNumericArray) mlArray); - } - break; - case MLArray.mxCHAR_CLASS: - MLChar mlchar = new MLChar(name, dims, type, attributes); - - //read real - tag = new ISMatTag(buf); - // char[] ac = tag.readToCharArray(); - String str = tag.readToString(); - - for (int i = 0; i < str.length(); i++) { - mlchar.setChar(str.charAt(i), i); - } - mlArray = mlchar; - break; - case MLArray.mxSPARSE_CLASS: - MLSparse sparse = new MLSparse(name, dims, attributes, nzmax); - //read ir (row indices) - tag = new ISMatTag(buf); - int[] ir = tag.readToIntArray(); - //read jc (column count) - tag = new ISMatTag(buf); - int[] jc = tag.readToIntArray(); - - //read pr (real part) - tag = new ISMatTag(buf); - double[] ad1 = tag.readToDoubleArray(); - int count = 0; - for (int column = 0; column < sparse.getN(); column++) { - while (count < jc[column + 1]) { - sparse.setReal(ad1[count], ir[count], column); - count++; - } - } - - //read pi (imaginary part) - if (sparse.isComplex()) { - tag = new ISMatTag(buf); - double[] ad2 = tag.readToDoubleArray(); - - count = 0; - for (int column = 0; column < sparse.getN(); column++) { - while (count < jc[column + 1]) { - sparse.setImaginary(ad2[count], ir[count], column); - count++; - } - } - } - mlArray = sparse; - break; - - case MLArray.mxOPAQUE_CLASS: - //read class name - tag = new ISMatTag(buf); - // class name - String className = tag.readToString(); - // System.out.println( "Class name: " + className ); - // should be "java" - // System.out.println( "Array name: " + name ); - - // the stored array name - // read array name stored in dims (!) - byte[] nn = new byte[dims.length]; - for (int i = 0; i < dims.length; i++) { - nn[i] = (byte) dims[i]; - } - String arrName = new String(nn); - // System.out.println( "Array name: " + arrName ); - - // next tag should be miMatrix - ISMatTag contentTag = new ISMatTag(buf); - - if (contentTag.type == MatDataTypes.miMATRIX) { - if (name.equals("java")) { - // should return UInt8 - MLUInt8 content = (MLUInt8) readMatrix(buf, false); - - // de-serialize object - ObjectInputStream ois = new ObjectInputStream( - new ByteBufferInputStream(content.getRealByteBuffer(), - content.getRealByteBuffer().limit())); - try { - Object o = ois.readObject(); - mlArray = new MLJavaObject(arrName, className, o); - } catch (Exception e) { - throw new IOException(e); - } finally { - ois.close(); - } - } else if (name.equals("MCOS")) { - // FileWrapper__ is a special MATLAB internal name. Should never appear from users. - if (!className.equals("FileWrapper__")) { - MLUInt32 content = (MLUInt32) readMatrix(buf, false); - int[][] t = content.getArray(); - - // Check that the first four numbers are the same, as expected. - if (t[0][0] != 0xdd000000 || t[1][0] != 2) { - throw new IOException("MCOS per-object header was different then expected! Got: " + content.contentToString()); - } - - mlArray = new MLObjectPlaceholder(arrName, className, t); - haveMCOS = true; - } else { // This is where we get the useful MCOS data. Only used on FileWrapper__ classes. - mlArray = readMatrix(buf, false); - } - } else { - throw new IOException("Unknown object type (" + name + ") found."); - } - } else { - throw new IOException("Unexpected object content"); - } - break; - case MLArray.mxOBJECT_CLASS: - //read class name - tag = new ISMatTag(buf); - - // class name - className = tag.readToString(); - - // TODO: currently copy pasted from structure - - mlArray = new MLObject(name, className, dims, attributes); - - //field name lenght - this subelement always uses the compressed data element format - tag = new ISMatTag(buf); - maxlen = buf.getInt(); //maximum field length - - ////// read fields data as Int8 - tag = new ISMatTag(buf); - //calculate number of fields - numOfFields = tag.size / maxlen; - - fieldNames = new String[numOfFields]; - for (int i = 0; i < numOfFields; i++) { - byte[] names = new byte[maxlen]; - buf.get(names); - fieldNames[i] = zeroEndByteArrayToString(names); - } - - buf.position(buf.position() + tag.padding); - //read fields - for (int index = 0; index < 1; index++) { - Map fields = new HashMap(); - for (int i = 0; i < numOfFields; i++) { - //read matrix recursively - tag = new ISMatTag(buf); - - if (tag.size > 0) { - MLArray fieldValue = readMatrix(buf, false); - fields.put(fieldNames[i], fieldValue); - } else { - fields.put(fieldNames[i], new MLEmptyArray()); - } - } - ((MLObject) mlArray).setFields(index, fields); - } - break; - default: - throw new MatlabIOException("Incorrect matlab array class: " + MLArray.typeToString(type)); - - } - return mlArray; - } - - /** - * Converts byte array to String. - * - * It assumes that String ends with \0 value. - * - * @param bytes byte array containing the string. - * @return String retrieved from byte array. - * @throws IOException if reading error occurred. - */ - private String zeroEndByteArrayToString(byte[] bytes) throws IOException { - int i = 0; - - for (i = 0; i < bytes.length && bytes[i] != 0; i++) - ; - - return new String(bytes, 0, i); - - } - - /** - * Reads Matrix flags. - * - * Modifies buf position. - * - * @param buf ByteBuffer - * @return flags int array - * @throws IOException if reading from buffer fails - */ - private int[] readFlags(ByteBuffer buf) throws IOException { - ISMatTag tag = new ISMatTag(buf); - - int[] flags = tag.readToIntArray(); - - return flags; - } - - /** - * Reads Matrix dimensions. - * - * Modifies buf position. - * - * @param buf ByteBuffer - * @return dimensions int array - * @throws IOException if reading from buffer fails - */ - private int[] readDimension(ByteBuffer buf) throws IOException { - - ISMatTag tag = new ISMatTag(buf); - int[] dims = tag.readToIntArray(); - return dims; - - } - - /** - * Reads Matrix name. - * - * Modifies buf position. - * - * @param buf ByteBuffer - * @return name String - * @throws IOException if reading from buffer fails - */ - private String readName(ByteBuffer buf) throws IOException { - ISMatTag tag = new ISMatTag(buf); - - return tag.readToString(); - } - - /** - * Reads MAT-file header. - * - * Modifies buf position. - * - * @param buf - * ByteBuffer - * @throws IOException - * if reading from buffer fails or if this is not a valid - * MAT-file - */ - private void readHeader(ByteBuffer buf) throws IOException { - //header values - String description; - int version; - byte[] endianIndicator = new byte[2]; - - // This part of the header is missing if the file isn't a regular mat file. So ignore. - if (matType == MatFileType.Regular) { - //descriptive text 116 bytes - byte[] descriptionBuffer = new byte[116]; - buf.get(descriptionBuffer); - - description = zeroEndByteArrayToString(descriptionBuffer); - - if (!description.matches("MATLAB 5.0 MAT-file.*")) { - throw new MatlabIOException("This is not a valid MATLAB 5.0 MAT-file."); - } - - //subsyst data offset 8 bytes - buf.position(buf.position() + 8); - } else { - description = "Simulink generated MATLAB 5.0 MAT-file"; // Default simulink description. - } - - byte[] bversion = new byte[2]; - //version 2 bytes - buf.get(bversion); - - //endian indicator 2 bytes - buf.get(endianIndicator); - - //program reading the MAT-file must perform byte swapping to interpret the data - //in the MAT-file correctly - if ((char) endianIndicator[0] == 'I' && (char) endianIndicator[1] == 'M') { - byteOrder = ByteOrder.LITTLE_ENDIAN; - version = bversion[1] & 0xff | bversion[0] << 8; - } else { - byteOrder = ByteOrder.BIG_ENDIAN; - version = bversion[0] & 0xff | bversion[1] << 8; - } - - buf.order(byteOrder); - - matFileHeader = new MatFileHeader(description, version, endianIndicator, byteOrder); - - // After the header, the next read must be aligned. Thus force the alignment. Only matters with reduced header data, - // but apply it regardless for safety. - buf.position((buf.position() + 7) & 0xfffffff8); - } - - /** - * TAG operator. Facilitates reading operations. - * - * Note: reading from buffer modifies it's position - * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - */ - private static class ISMatTag extends MatTag { - private final MatFileInputStream mfis; - private final int padding; - private final boolean compressed; - - public ISMatTag(ByteBuffer buf) throws IOException { - //must call parent constructor - super(0, 0); - int tmp = buf.getInt(); - - //data not packed in the tag - if (tmp >> 16 == 0) { - type = tmp; - size = buf.getInt(); - compressed = false; - } else //data _packed_ in the tag (compressed) - { - size = tmp >> 16; // 2 more significant bytes - type = tmp & 0xffff; // 2 less significant bytes; - compressed = true; - } - padding = getPadding(size, compressed); - mfis = new MatFileInputStream(buf, type); - } - - public void readToByteBuffer(ByteBuffer buff, ByteStorageSupport storage) throws IOException { - int elements = size / sizeOf(); - mfis.readToByteBuffer(buff, elements, storage); - mfis.skip(padding); - } - - public byte[] readToByteArray() throws IOException { - //allocate memory for array elements - int elements = size / sizeOf(); - byte[] ab = new byte[elements]; - - for (int i = 0; i < elements; i++) { - ab[i] = mfis.readByte(); - } - - //skip padding - mfis.skip(padding); - - return ab; - } - - public double[] readToDoubleArray() throws IOException { - //allocate memory for array elements - int elements = size / sizeOf(); - double[] ad = new double[elements]; - - for (int i = 0; i < elements; i++) { - ad[i] = mfis.readDouble(); - } - - //skip padding - - mfis.skip(padding); - return ad; - } - - public int[] readToIntArray() throws IOException { - //allocate memory for array elements - int elements = size / sizeOf(); - int[] ai = new int[elements]; - - for (int i = 0; i < elements; i++) { - ai[i] = mfis.readInt(); - } - - //skip padding - mfis.skip(padding); - return ai; - } - - public String readToString() throws IOException { - // - byte[] bytes = readToByteArray(); - - return new String(bytes, "UTF-8"); - - } - - public char[] readToCharArray() throws IOException { - //allocate memory for array elements - int elements = size / sizeOf(); - char[] ac = new char[elements]; - - for (int i = 0; i < elements; i++) { - ac[i] = mfis.readChar(); - } - - //skip padding - mfis.skip(padding); - return ac; - } - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatFileWriter.java b/src/main/java/ca/mjdsystems/jmatio/io/MatFileWriter.java deleted file mode 100755 index 1e814ab..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatFileWriter.java +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.WritableByteChannel; -import java.util.Collection; -import java.util.zip.DataFormatException; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; - -import ca.mjdsystems.jmatio.common.MatDataTypes; -import ca.mjdsystems.jmatio.types.MLArray; -import ca.mjdsystems.jmatio.types.MLCell; -import ca.mjdsystems.jmatio.types.MLChar; -import ca.mjdsystems.jmatio.types.MLNumericArray; -import ca.mjdsystems.jmatio.types.MLSparse; -import ca.mjdsystems.jmatio.types.MLStructure; - -/** - * MAT-file writer. - * - * Usage: - * - *
- * 
- * //1. First create example arrays
- * double[] src = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
- * MLDouble mlDouble = new MLDouble( "double_arr", src, 3 );
- * MLChar mlChar = new MLChar( "char_arr", "I am dummy" );
- *
- * //2. write arrays to file
- * ArrayList list = new ArrayList();
- * list.add( mlDouble );
- * list.add( mlChar );
- *
- * new MatFileWriter( "mat_file.mat", list );
- * 
- * 
- * - * this is "equal" to Matlab commands: - * - *
- * 
- * >> double_arr = [ 1 2; 3 4; 5 6];
- * >> char_arr = 'I am dummy';
- * >>
- * >> save('mat_file.mat', 'double_arr', 'char_arr');
- * 
- * - * - * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - */ -public class MatFileWriter { - - /** - * A hack stolen from Greg Wilkins of Mortbay. - * A {@link ByteArrayOutputStream} with revealed internals so there is no more wasteful - * copying when calling {@link ByteArrayOutputStream#toByteArray()} - * @author ss - * - */ - static class ByteArrayOutputStream2 extends ByteArrayOutputStream { - public ByteArrayOutputStream2() { - super(); - } - - public byte[] getBuf() { - return buf; - } - - public int getCount() { - return count; - } - } - // private static final Logger logger = Logger.getLogger(MatFileWriter.class); - - /** - * Creates the new {@link MatFileWriter} instance - */ - public MatFileWriter() { - super(); - } - - /** - * Writes MLArrays into file given by fileName. - * - * @param fileName - name of ouput file - * @param data - Collection of MLArray elements - * @throws IOException - * @throws DataFormatException - */ - public MatFileWriter(String fileName, Collection data) throws IOException { - this(new File(fileName), data); - } - - /** - * Writes MLArrays into File. - * - * @param file - an output File - * @param data - Collection of MLArray elements - * @throws IOException - * @throws DataFormatException - */ - public MatFileWriter(File file, Collection data) throws IOException { - this((new FileOutputStream(file)).getChannel(), data); - } - - /** - * Writes MLArrays into OuputSteram. - * - * Writes MAT-file header and compressed data (miCOMPRESSED). - * - * @param output - OutputStream - * @param data - Collection of MLArray elements - * @throws IOException - */ - public MatFileWriter(WritableByteChannel channel, Collection data) throws IOException { - write(channel, data); - } - - /** - * Writes MLArrays into file created from - * filepath. - * - * @param filepath - * the absolute file path of a MAT-file to which data is written - * @param data - * the collection of {@link MLArray} objects - * @throws IOException - * if error occurred during MAT-file writing - */ - public synchronized void write(String filepath, Collection data) - throws IOException { - write(new File(filepath), data); - } - - /** - * Writes MLArrays into File - * - * @param file - * the MAT-file to which data is written - * @param data - * the collection of {@link MLArray} objects - * @throws IOException - * if error occurred during MAT-file writing - */ - public synchronized void write(File file, Collection data) - throws IOException { - FileOutputStream fos = new FileOutputStream(file); - - try { - write(fos.getChannel(), data); - } catch (IOException e) { - throw e; - } finally { - fos.close(); - } - } - - /** - * Writes MLArrays into WritableByteChannel. - * - * @param channel - * the channel to write to - * @param data - * the collection of {@link MLArray} objects - * @throws IOException - * if writing fails - */ - private synchronized void write(WritableByteChannel channel, - Collection data) throws IOException { - try { - //write header - writeHeader(channel); - - //write data - for (MLArray matrix : data) { - //compress data to save storage - Deflater compresser = new Deflater(); - - ByteArrayOutputStream2 compressed = new ByteArrayOutputStream2(); - DataOutputStream dout = new DataOutputStream(new DeflaterOutputStream(compressed, compresser)); - - writeMatrix(dout, matrix); - dout.flush(); - dout.close(); - - //write COMPRESSED tag and compressed data into output channel - - int compressedSize = compressed.getCount(); - ByteBuffer buf = ByteBuffer.allocateDirect(2 * 4 /* Int size */ + compressedSize); - buf.putInt(MatDataTypes.miCOMPRESSED); - - buf.putInt(compressedSize); - buf.put(compressed.getBuf(), 0, compressedSize); - - buf.flip(); - channel.write(buf); - } - } catch (IOException e) { - throw e; - } finally { - channel.close(); - } - } - - /** - * Writes MAT-file header into OutputStream - * @param os OutputStream - * @throws IOException - */ - private void writeHeader(WritableByteChannel channel) throws IOException { - //write descriptive text - MatFileHeader header = MatFileHeader.createHeader(); - char[] dest = new char[116]; - char[] src = header.getDescription().toCharArray(); - System.arraycopy(src, 0, dest, 0, src.length); - - byte[] endianIndicator = header.getEndianIndicator(); - - ByteBuffer buf = ByteBuffer.allocateDirect(dest.length * 2 /* Char size */ + 2 + endianIndicator.length); - - for (int i = 0; i < dest.length; i++) { - buf.put((byte) dest[i]); - } - //write subsyst data offset - buf.position(buf.position() + 8); - - //write version - int version = header.getVersion(); - buf.put((byte) (version >> 8)); - buf.put((byte) version); - - buf.put(endianIndicator); - - buf.flip(); - channel.write(buf); - } - - /** - * Writes MATRIX into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeMatrix(DataOutputStream output, MLArray array) throws IOException { - OSArrayTag tag; - ByteArrayOutputStream buffer; - DataOutputStream bufferDOS; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - - //flags - writeFlags(dos, array); - - //dimensions - writeDimensions(dos, array); - - //array name - writeName(dos, array); - - switch (array.getType()) { - case MLArray.mxCHAR_CLASS: - //write char data - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - Character[] ac = ((MLChar) array).exportChar(); - for (int i = 0; i < ac.length; i++) { - String temp = new StringBuffer().append(ac[i].charValue()).toString(); - bufferDOS.write(temp.getBytes("UTF-8")); - } - tag = new OSArrayTag(MatDataTypes.miUTF8, buffer.toByteArray()); - tag.writeTo(dos); - - break; - case MLArray.mxDOUBLE_CLASS: - - tag = new OSArrayTag(MatDataTypes.miDOUBLE, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miDOUBLE, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxSINGLE_CLASS: - - tag = new OSArrayTag(MatDataTypes.miSINGLE, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miSINGLE, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxUINT8_CLASS: - - tag = new OSArrayTag(MatDataTypes.miUINT8, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miUINT8, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxINT8_CLASS: - - tag = new OSArrayTag(MatDataTypes.miINT8, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miINT8, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxINT16_CLASS: - - tag = new OSArrayTag(MatDataTypes.miINT16, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miINT16, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxINT32_CLASS: - tag = new OSArrayTag(MatDataTypes.miINT32, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miINT32, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxINT64_CLASS: - - tag = new OSArrayTag(MatDataTypes.miINT64, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miINT64, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxUINT64_CLASS: - - tag = new OSArrayTag(MatDataTypes.miUINT64, - ((MLNumericArray) array).getRealByteBuffer()); - tag.writeTo(dos); - - //write real imaginary - if (array.isComplex()) { - tag = new OSArrayTag(MatDataTypes.miUINT64, - ((MLNumericArray) array).getImaginaryByteBuffer()); - tag.writeTo(dos); - } - break; - case MLArray.mxSTRUCT_CLASS: - //field name length - int itag = 4 << 16 | MatDataTypes.miINT32 & 0xffff; - dos.writeInt(itag); - dos.writeInt(((MLStructure) array).getMaxFieldLenth()); - - //get field names - tag = new OSArrayTag(MatDataTypes.miINT8, ((MLStructure) array).getKeySetToByteArray()); - tag.writeTo(dos); - - for (MLArray a : ((MLStructure) array).getAllFields()) { - writeMatrix(dos, a); - } - break; - case MLArray.mxCELL_CLASS: - for (MLArray a : ((MLCell) array).cells()) { - writeMatrix(dos, a); - } - break; - case MLArray.mxSPARSE_CLASS: - int[] ai; - //write ir - - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - ai = ((MLSparse) array).getIR(); - for (int i : ai) { - bufferDOS.writeInt(i); - } - tag = new OSArrayTag(MatDataTypes.miINT32, buffer.toByteArray()); - tag.writeTo(dos); - //write jc - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - ai = ((MLSparse) array).getJC(); - for (int i : ai) { - bufferDOS.writeInt(i); - } - tag = new OSArrayTag(MatDataTypes.miINT32, buffer.toByteArray()); - tag.writeTo(dos); - //write real - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - - Double[] ad = ((MLSparse) array).exportReal(); - - for (int i = 0; i < ad.length; i++) { - bufferDOS.writeDouble(ad[i].doubleValue()); - } - - tag = new OSArrayTag(MatDataTypes.miDOUBLE, buffer.toByteArray()); - tag.writeTo(dos); - //write real imaginary - if (array.isComplex()) { - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - ad = ((MLSparse) array).exportImaginary(); - for (int i = 0; i < ad.length; i++) { - bufferDOS.writeDouble(ad[i].doubleValue()); - } - tag = new OSArrayTag(MatDataTypes.miDOUBLE, buffer.toByteArray()); - tag.writeTo(dos); - } - break; - default: - throw new MatlabIOException("Cannot write matrix of type: " + MLArray.typeToString(array.getType())); - - } - - //write matrix - output.writeInt(MatDataTypes.miMATRIX); //matrix tag - output.writeInt(baos.size()); //size of matrix - output.write(baos.toByteArray()); //matrix data - } - - /** - * Writes MATRIX flags into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeFlags(DataOutputStream os, MLArray array) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - DataOutputStream bufferDOS = new DataOutputStream(buffer); - - bufferDOS.writeInt(array.getFlags()); - - if (array.isSparse()) { - bufferDOS.writeInt(((MLSparse) array).getMaxNZ()); - } else { - bufferDOS.writeInt(0); - } - OSArrayTag tag = new OSArrayTag(MatDataTypes.miUINT32, buffer.toByteArray()); - tag.writeTo(os); - - } - - /** - * Writes MATRIX dimensions into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeDimensions(DataOutputStream os, MLArray array) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - DataOutputStream bufferDOS = new DataOutputStream(buffer); - - int[] dims = array.getDimensions(); - for (int i = 0; i < dims.length; i++) { - bufferDOS.writeInt(dims[i]); - } - OSArrayTag tag = new OSArrayTag(MatDataTypes.miINT32, buffer.toByteArray()); - tag.writeTo(os); - - } - - /** - * Writes MATRIX name into OutputStream. - * - * @param os - OutputStream - * @param array - a MLArray - * @throws IOException - */ - private void writeName(DataOutputStream os, MLArray array) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - DataOutputStream bufferDOS = new DataOutputStream(buffer); - - byte[] nameByteArray = array.getNameToByteArray(); - buffer = new ByteArrayOutputStream(); - bufferDOS = new DataOutputStream(buffer); - bufferDOS.write(nameByteArray); - OSArrayTag tag = new OSArrayTag(MatDataTypes.miINT8, buffer.toByteArray()); - tag.writeTo(os); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatTag.java b/src/main/java/ca/mjdsystems/jmatio/io/MatTag.java deleted file mode 100755 index 209fd52..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatTag.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import ca.mjdsystems.jmatio.common.MatDataTypes; - -/** - * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - */ -class MatTag { - protected int type; - protected int size; - - /** - * @param type - * @param size - * @param compressed - */ - public MatTag(int type, int size) { - this.type = type; - this.size = size; - } - - /** - * Calculate padding - */ - protected int getPadding(int size, boolean compressed) { - int padding; - //data not packed in the tag - if (!compressed) { - int b; - padding = (b = (((size / sizeOf()) % (8 / sizeOf())) * sizeOf())) != 0 ? 8 - b : 0; - } else //data _packed_ in the tag (compressed) - { - int b; - padding = (b = (((size / sizeOf()) % (4 / sizeOf())) * sizeOf())) != 0 ? 4 - b : 0; - } - return padding; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - String s; - - s = "[tag: " + MatDataTypes.typeToString(type) + " size: " + size + "]"; - - return s; - } - - /** - * Get size of single data in this tag. - * - * @return - number of bytes for single data - */ - public int sizeOf() { - return MatDataTypes.sizeOf(type); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatlabIOException.java b/src/main/java/ca/mjdsystems/jmatio/io/MatlabIOException.java deleted file mode 100755 index d6b8161..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatlabIOException.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.IOException; - -/** - * MAT-file reader/writer exception - * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - */ -@SuppressWarnings("serial") -public class MatlabIOException extends IOException { - public MatlabIOException(String s) { - super(s); - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/OSArrayTag.java b/src/main/java/ca/mjdsystems/jmatio/io/OSArrayTag.java deleted file mode 100755 index 778675a..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/io/OSArrayTag.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.io; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * Tiny class that represents MAT-file TAG - * It simplifies writing data. Automates writing padding for instance. - */ -class OSArrayTag extends MatTag { - private ByteBuffer data; - - /** - * Creates TAG and stets its size as size of byte array - * - * @param type - * @param data - */ - public OSArrayTag(int type, byte[] data) { - this(type, ByteBuffer.wrap(data)); - } - - /** - * Creates TAG and stets its size as size of byte array - * - * @param type - * @param data - */ - public OSArrayTag(int type, ByteBuffer data) { - super(type, data.limit()); - this.data = data; - data.rewind(); - } - - /** - * Writes tag and data to DataOutputStream. Wites padding if neccesary. - * - * @param os - * @throws IOException - */ - public void writeTo(DataOutputStream os) throws IOException { - - int padding; - if (size <= 4 && size > 0) { - // Use small data element format (Page 1-10 in "MATLAB 7 MAT-File Format", September 2010 revision) - os.writeShort(size); - os.writeShort(type); - padding = getPadding(data.limit(), true); - } else { - os.writeInt(type); - os.writeInt(size); - padding = getPadding(data.limit(), false); - } - - int maxBuffSize = 1024; - int writeBuffSize = data.remaining() < maxBuffSize ? data.remaining() : maxBuffSize; - byte[] tmp = new byte[writeBuffSize]; - while (data.remaining() > 0) { - int length = data.remaining() > tmp.length ? tmp.length : data.remaining(); - data.get(tmp, 0, length); - os.write(tmp, 0, length); - } - - if (padding > 0) { - os.write(new byte[padding]); - } - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/ByteStorageSupport.java b/src/main/java/ca/mjdsystems/jmatio/types/ByteStorageSupport.java deleted file mode 100644 index f22dfc5..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/ByteStorageSupport.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -public interface ByteStorageSupport { - int getBytesAllocated(); - - T buldFromBytes(byte[] bytes); - - byte[] getByteArray(T value); - - Class getStorageClazz(); - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/GenericArrayCreator.java b/src/main/java/ca/mjdsystems/jmatio/types/GenericArrayCreator.java deleted file mode 100755 index 59023b5..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/GenericArrayCreator.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -public interface GenericArrayCreator { - T[] createArray(int m, int n); - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLArray.java b/src/main/java/ca/mjdsystems/jmatio/types/MLArray.java deleted file mode 100755 index 1b3fe58..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLArray.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -public class MLArray { - - /* Matlab Array Types (Classes) */ - public static final int mxUNKNOWN_CLASS = 0; - public static final int mxCELL_CLASS = 1; - public static final int mxSTRUCT_CLASS = 2; - public static final int mxOBJECT_CLASS = 3; - public static final int mxCHAR_CLASS = 4; - public static final int mxSPARSE_CLASS = 5; - public static final int mxDOUBLE_CLASS = 6; - public static final int mxSINGLE_CLASS = 7; - public static final int mxINT8_CLASS = 8; - public static final int mxUINT8_CLASS = 9; - public static final int mxINT16_CLASS = 10; - public static final int mxUINT16_CLASS = 11; - public static final int mxINT32_CLASS = 12; - public static final int mxUINT32_CLASS = 13; - public static final int mxINT64_CLASS = 14; - public static final int mxUINT64_CLASS = 15; - public static final int mxFUNCTION_CLASS = 16; - public static final int mxOPAQUE_CLASS = 17; - - public static final int mtFLAG_COMPLEX = 0x0800; - public static final int mtFLAG_GLOBAL = 0x0400; - public static final int mtFLAG_LOGICAL = 0x0200; - public static final int mtFLAG_TYPE = 0xff; - - protected int dims[]; - public String name; - protected int attributes; - protected int type; - - public MLArray(String name, int[] dims, int type, int attributes) { - this.dims = new int[dims.length]; - System.arraycopy(dims, 0, this.dims, 0, dims.length); - - if (name != null && !name.equals("")) { - this.name = name; - } else { - this.name = "@"; //default name - } - - this.type = type; - this.attributes = attributes; - } - - /** - * Gets array name - * - * @return - array name - */ - public String getName() { - return name; - } - - public int getFlags() { - int flags = type & mtFLAG_TYPE | attributes & 0xffffff00; - - return flags; - } - - public byte[] getNameToByteArray() { - return name.getBytes(); - } - - public int[] getDimensions() { - int ai[] = null; - if (dims != null) { - ai = new int[dims.length]; - System.arraycopy(dims, 0, ai, 0, dims.length); - } - return ai; - } - - public int getM() { - int i = 0; - if (dims != null) { - i = dims[0]; - } - return i; - } - - public int getN() { - int i = 0; - if (dims != null) { - if (dims.length > 2) { - i = 1; - for (int j = 1; j < dims.length; j++) { - i *= dims[j]; - } - } else { - i = dims[1]; - } - } - return i; - } - - public int getNDimensions() { - int i = 0; - if (dims != null) { - i = dims.length; - } - return i; - } - - public int getSize() { - return getM() * getN(); - } - - public int getType() { - return type; - } - - public boolean isEmpty() { - return getN() == 0; - } - - public static final String typeToString(int type) { - String s; - switch (type) { - case mxUNKNOWN_CLASS: - s = "unknown"; - break; - case mxCELL_CLASS: - s = "cell"; - break; - case mxSTRUCT_CLASS: - s = "struct"; - break; - case mxCHAR_CLASS: - s = "char"; - break; - case mxSPARSE_CLASS: - s = "sparse"; - break; - case mxDOUBLE_CLASS: - s = "double"; - break; - case mxSINGLE_CLASS: - s = "single"; - break; - case mxINT8_CLASS: - s = "int8"; - break; - case mxUINT8_CLASS: - s = "uint8"; - break; - case mxINT16_CLASS: - s = "int16"; - break; - case mxUINT16_CLASS: - s = "uint16"; - break; - case mxINT32_CLASS: - s = "int32"; - break; - case mxUINT32_CLASS: - s = "uint32"; - break; - case mxINT64_CLASS: - s = "int64"; - break; - case mxUINT64_CLASS: - s = "uint64"; - break; - case mxFUNCTION_CLASS: - s = "function_handle"; - break; - case mxOPAQUE_CLASS: - s = "opaque"; - break; - case mxOBJECT_CLASS: - s = "object"; - break; - default: - s = "unknown"; - break; - } - return s; - } - - public boolean isCell() { - return type == mxCELL_CLASS; - } - - public boolean isChar() { - return type == mxCHAR_CLASS; - } - - public boolean isComplex() { - return (attributes & mtFLAG_COMPLEX) != 0; - } - - public boolean isSparse() { - return type == mxSPARSE_CLASS; - } - - public boolean isStruct() { - return type == mxSTRUCT_CLASS; - } - - public boolean isDouble() { - return type == mxDOUBLE_CLASS; - } - - public boolean isSingle() { - return type == mxSINGLE_CLASS; - } - - public boolean isInt8() { - return type == mxINT8_CLASS; - } - - public boolean isUint8() { - return type == mxUINT8_CLASS; - } - - public boolean isInt16() { - return type == mxINT16_CLASS; - } - - public boolean isUint16() { - return type == mxUINT16_CLASS; - } - - public boolean isInt32() { - return type == mxINT32_CLASS; - } - - public boolean isUint32() { - return type == mxUINT32_CLASS; - } - - public boolean isInt64() { - return type == mxINT64_CLASS; - } - - public boolean isUint64() { - return type == mxUINT64_CLASS; - } - - public boolean isObject() { - return type == mxOBJECT_CLASS; - } - - public boolean isOpaque() { - return type == mxOPAQUE_CLASS; - } - - public boolean isLogical() { - return (attributes & mtFLAG_LOGICAL) != 0; - } - - public boolean isFunctionObject() { - return type == mxFUNCTION_CLASS; - } - - public boolean isUnknown() { - return type == mxUNKNOWN_CLASS; - } - - protected int getIndex(int m, int n) { - return m + n * getM(); - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - if (dims != null) { - sb.append('['); - if (dims.length > 3) { - sb.append(dims.length); - sb.append('D'); - } else { - sb.append(dims[0]); - sb.append('x'); - sb.append(dims[1]); - if (dims.length == 3) { - sb.append('x'); - sb.append(dims[2]); - } - } - sb.append(" "); - sb.append(typeToString(type)); - sb.append(" array"); - if (isSparse()) { - sb.append(" (sparse"); - if (isComplex()) { - sb.append(" complex"); - } - sb.append(")"); - } else if (isComplex()) { - sb.append(" (complex)"); - } - sb.append(']'); - } else { - sb.append("[invalid]"); - } - return sb.toString(); - } - - public String contentToString() { - return "content cannot be displayed"; - } - - public void dispose() { - - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLCell.java b/src/main/java/ca/mjdsystems/jmatio/types/MLCell.java deleted file mode 100755 index 92bad8d..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLCell.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.util.ArrayList; - -public class MLCell extends MLArray { - private ArrayList cells; - - public MLCell(String name, int[] dims) { - this(name, dims, MLArray.mxCELL_CLASS, 0); - } - - public MLCell(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - - cells = new ArrayList(getM() * getN()); - - for (int i = 0; i < getM() * getN(); i++) { - cells.add(new MLEmptyArray()); - } - } - - public void set(MLArray value, int m, int n) { - cells.set(getIndex(m, n), value); - } - - public void set(MLArray value, int index) { - cells.set(index, value); - } - - public MLArray get(int m, int n) { - return cells.get(getIndex(m, n)); - } - - public MLArray get(int index) { - return cells.get(index); - } - - public int getIndex(int m, int n) { - return m + n * getM(); - } - - public ArrayList cells() { - return cells; - } - - public String contentToString() { - StringBuffer sb = new StringBuffer(); - sb.append(name + " = \n"); - - for (int m = 0; m < getM(); m++) { - sb.append("\t"); - for (int n = 0; n < getN(); n++) { - sb.append(get(m, n)); - sb.append("\t"); - } - sb.append("\n"); - } - return sb.toString(); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLChar.java b/src/main/java/ca/mjdsystems/jmatio/types/MLChar.java deleted file mode 100755 index 471a6ae..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLChar.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.util.Arrays; - -public class MLChar extends MLArray implements GenericArrayCreator { - Character[] chars; - - /** - * Creates the 1 x {@link String#length()} {@link MLChar} from the given - * String. - * - * @param name the {@link MLArray} name - * @param value the String - */ - public MLChar(String name, String value) { - this(name, new int[]{1, value.length()}, MLArray.mxCHAR_CLASS, 0); - set(value); - } - - /** - * Create the {@link MLChar} from array of {@link String}s. - * - * @param name the {@link MLArray} name - * @param values the array of {@link String}s - */ - public MLChar(String name, String[] values) { - this(name, new int[]{values.length, values.length > 0 ? getMaxLength(values) : 0}, MLArray.mxCHAR_CLASS, 0); - - for (int i = 0; i < values.length; i++) { - set(values[i], i); - } - } - - /** - * Returns the maximum {@link String} length of array of {@link String}s. - * @param values the array of {@link String}s - * @return the maximum {@link String} length of array of {@link String}s - */ - private static int getMaxLength(String[] values) { - int result = 0; - - for (int i = 0, curr = 0; i < values.length; i++) { - if ((curr = values[i].length()) > result) { - result = curr; - } - } - return result; - } - - /** - * Added method to allow initialization of a char array representing - * an array of strings. - * - * @param name - * @param values - * @param maxlen - */ - public MLChar(String name, String[] values, int maxlen) { - this(name, new int[]{values.length, maxlen}, MLArray.mxCHAR_CLASS, 0); - int idx = 0; - for (String v : values) { - set(v, idx); - idx++; - } - } - - public MLChar(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - chars = createArray(getM(), getN()); - } - - public Character[] createArray(int m, int n) { - return new Character[m * n]; - } - - public void setChar(char ch, int index) { - chars[index] = ch; - } - - /** - * Populates the {@link MLChar} with the {@link String} value. - * @param value the String value - */ - public void set(String value) { - char[] cha = value.toCharArray(); - for (int i = 0; i < getN() && i < value.length(); i++) { - setChar(cha[i], i); - } - } - - /** - * Set one row, specifying the row. - * - * @param value - * @param idx - */ - public void set(String value, int idx) { - int rowOffset = getM(); - for (int i = 0; i < getN(); i++) { - if (i < value.length()) { - - setChar(value.charAt(i), idx + (rowOffset * i)); - } else { - setChar(' ', idx + (rowOffset * i)); - } - } - } - - public Character getChar(int m, int n) { - return chars[getIndex(m, n)]; - } - - public Character[] exportChar() { - return chars; - } - - @Override - public boolean equals(Object o) { - if (o instanceof MLChar) { - return Arrays.equals(chars, ((MLChar) o).chars); - } - return super.equals(o); - } - - /** - * Gets the m-th character matrix's row as String. - * - * @param m - row number - * @return - String - */ - public String getString(int m) { - StringBuffer charbuff = new StringBuffer(); - - for (int n = 0; n < getN(); n++) { - charbuff.append(getChar(m, n)); - } - - return charbuff.toString().trim(); - } - - public String contentToString() { - StringBuffer sb = new StringBuffer(); - sb.append(name + " = \n"); - - for (int m = 0; m < getM(); m++) { - sb.append("\t"); - StringBuffer charbuff = new StringBuffer(); - charbuff.append("'"); - for (int n = 0; n < getN(); n++) { - charbuff.append(getChar(m, n)); - } - charbuff.append("'"); - sb.append(charbuff); - sb.append("\n"); - } - return sb.toString(); - - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLDouble.java b/src/main/java/ca/mjdsystems/jmatio/types/MLDouble.java deleted file mode 100755 index 1477e91..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLDouble.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -/** - * Class represents Double array (matrix) - * - * @author Wojciech Gradkowski - */ -public class MLDouble extends MLNumericArray { - - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type: here mxDOUBLE_CLASS - * @param attributes - array flags - */ - public MLDouble(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - /** - * Create a MLDouble array with given name, - * and dimensions. - * - * @param name - array name - * @param dims - array dimensions - */ - public MLDouble(String name, int[] dims) { - super(name, dims, MLArray.mxDOUBLE_CLASS, 0); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLDouble(String name, Double[] vals, int m) { - super(name, MLArray.mxDOUBLE_CLASS, vals, m); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from double[][] - * - * Note: array is converted to Double[] - * - * @param name - array name - * @param vals - two-dimensional array of values - */ - public MLDouble(String name, double[][] vals) { - this(name, double2DToDouble(vals), vals.length); - } - - /** - * Jama [math.nist.gov] style: - * construct a matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLDouble(String name, double[] vals, int m) { - this(name, castToDouble(vals), m); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) - */ - public Double[] createArray(int m, int n) { - return new Double[m * n]; - } - - /** - * Gets two-dimensional real array. - * - * @return - 2D real array - */ - public double[][] getArray() { - double[][] result = new double[getM()][]; - - for (int m = 0; m < getM(); m++) { - result[m] = new double[getN()]; - - for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); - } - } - return result; - } - - /** - * Casts Double[] to double[] - * - * @param - source Double[] - * @return - result double[] - */ - private static Double[] castToDouble(double[] d) { - Double[] dest = new Double[d.length]; - for (int i = 0; i < d.length; i++) { - dest[i] = (Double) d[i]; - } - return dest; - } - - /** - * Converts double[][] to Double[] - * - * @param dd - * @return - */ - private static Double[] double2DToDouble(double[][] dd) { - Double[] d = new Double[dd.length * dd[0].length]; - for (int n = 0; n < dd[0].length; n++) { - for (int m = 0; m < dd.length; m++) { - d[m + n * dd.length] = dd[m][n]; - } - } - return d; - } - - public int getBytesAllocated() { - return Double.SIZE >> 3; - } - - public Double buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return ByteBuffer.wrap(bytes).getDouble(); - - } - - public byte[] getByteArray(Double value) { - int byteAllocated = getBytesAllocated(); - ByteBuffer buff = ByteBuffer.allocate(byteAllocated); - buff.putDouble(value); - return buff.array(); - } - - public Class getStorageClazz() { - return Double.class; - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLEmptyArray.java b/src/main/java/ca/mjdsystems/jmatio/types/MLEmptyArray.java deleted file mode 100755 index 4a9013c..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLEmptyArray.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -public class MLEmptyArray extends MLArray { - public MLEmptyArray() { - this(null); - } - - public MLEmptyArray(String name) { - this(name, new int[]{0, 0}, mxDOUBLE_CLASS, 0); - } - - public MLEmptyArray(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - // TODO Auto-generated constructor stub - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLInt16.java b/src/main/java/ca/mjdsystems/jmatio/types/MLInt16.java deleted file mode 100644 index 8110eea..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLInt16.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -/** - * Class represents Int16 array (matrix) - * - */ -public class MLInt16 extends MLNumericArray { - - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type: here mxINT16_CLASS - * @param attributes - array flags - */ - public MLInt16(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - /** - * Create a MLSingle array with given name, - * and dimensions. - * - * @param name - array name - * @param dims - array dimensions - */ - public MLInt16(String name, int[] dims) { - super(name, dims, MLArray.mxINT16_CLASS, 0); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of Short, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt16(String name, Short[] vals, int m) { - super(name, MLArray.mxINT16_CLASS, vals, m); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from double[][] - * - * Note: array is converted to Short[] - * - * @param name - array name - * @param vals - two-dimensional array of values - */ - public MLInt16(String name, short[][] vals) { - this(name, short2DToShort(vals), vals.length); - } - - /** - * Jama [math.nist.gov] style: - * construct a matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of short, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt16(String name, short[] vals, int m) { - this(name, castToShort(vals), m); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) - */ - public Short[] createArray(int m, int n) { - return new Short[m * n]; - } - - /** - * Gets two-dimensional real array. - * - * @return - 2D real array - */ - public short[][] getArray() { - short[][] result = new short[getM()][]; - - for (int m = 0; m < getM(); m++) { - result[m] = new short[getN()]; - - for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); - } - } - return result; - } - - /** - * Casts Short[] to short[] - * - * @param - source short[] - * @return - result Short[] - */ - private static Short[] castToShort(short[] d) { - Short[] dest = new Short[d.length]; - for (int i = 0; i < d.length; i++) { - dest[i] = (Short) d[i]; - } - return dest; - } - - /** - * Converts short[][] to Short[] - * - * @param dd - * @return - */ - private static Short[] short2DToShort(short[][] dd) { - Short[] d = new Short[dd.length * dd[0].length]; - for (int n = 0; n < dd[0].length; n++) { - for (int m = 0; m < dd.length; m++) { - d[m + n * dd.length] = dd[m][n]; - } - } - return d; - } - - public int getBytesAllocated() { - return Short.SIZE >> 3; - } - - public Short buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return ByteBuffer.wrap(bytes).getShort(); - - } - - public byte[] getByteArray(Short value) { - int byteAllocated = getBytesAllocated(); - ByteBuffer buff = ByteBuffer.allocate(byteAllocated); - buff.putShort(value); - return buff.array(); - } - - public Class getStorageClazz() { - return Short.class; - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLInt32.java b/src/main/java/ca/mjdsystems/jmatio/types/MLInt32.java deleted file mode 100644 index eb96bc9..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLInt32.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -public class MLInt32 extends MLNumericArray { - - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type: here mxDOUBLE_CLASS - * @param attributes - array flags - */ - public MLInt32(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - /** - * Create a {@link MLInt64} array with given name, - * and dimensions. - * - * @param name - array name - * @param dims - array dimensions - */ - public MLInt32(String name, int[] dims) { - super(name, dims, MLArray.mxINT32_CLASS, 0); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt32(String name, Integer[] vals, int m) { - super(name, MLArray.mxINT32_CLASS, vals, m); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from byte[][] - * - * Note: array is converted to Byte[] - * - * @param name - array name - * @param vals - two-dimensional array of values - */ - public MLInt32(String name, int[][] vals) { - this(name, int2DToInteger(vals), vals.length); - } - - /** - * Jama [math.nist.gov] style: - * construct a matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt32(String name, int[] vals, int m) { - this(name, castToInteger(vals), m); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) - */ - public Integer[] createArray(int m, int n) { - return new Integer[m * n]; - } - - /** - * Gets two-dimensional real array. - * - * @return - 2D real array - */ - public int[][] getArray() { - int[][] result = new int[getM()][]; - - for (int m = 0; m < getM(); m++) { - result[m] = new int[getN()]; - - for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); - } - } - return result; - } - - /** - * Casts Double[] to byte[] - * - * @param - source Long[] - * @return - result long[] - */ - private static Integer[] castToInteger(int[] d) { - Integer[] dest = new Integer[d.length]; - for (int i = 0; i < d.length; i++) { - dest[i] = d[i]; - } - return dest; - } - - /** - * Converts byte[][] to Long[] - * - * @param dd - * @return - */ - private static Integer[] int2DToInteger(int[][] dd) { - Integer[] d = new Integer[dd.length * dd[0].length]; - for (int n = 0; n < dd[0].length; n++) { - for (int m = 0; m < dd.length; m++) { - d[m + n * dd.length] = dd[m][n]; - } - } - return d; - } - - public Integer buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return ByteBuffer.wrap(bytes).getInt(); - } - - public int getBytesAllocated() { - return Integer.SIZE >> 3; - } - - public Class getStorageClazz() { - return Integer.class; - } - - public byte[] getByteArray(Integer value) { - int byteAllocated = getBytesAllocated(); - ByteBuffer buff = ByteBuffer.allocate(byteAllocated); - buff.putInt(value); - return buff.array(); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLInt64.java b/src/main/java/ca/mjdsystems/jmatio/types/MLInt64.java deleted file mode 100644 index 3cc6f52..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLInt64.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -/** - * Class represents Int64 (long) array (matrix) - * - * @author Wojciech Gradkowski - */ -public class MLInt64 extends MLNumericArray { - - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type: here mxDOUBLE_CLASS - * @param attributes - array flags - */ - public MLInt64(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - /** - * Create a {@link MLInt64} array with given name, - * and dimensions. - * - * @param name - array name - * @param dims - array dimensions - */ - public MLInt64(String name, int[] dims) { - super(name, dims, MLArray.mxINT64_CLASS, 0); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt64(String name, Long[] vals, int m) { - super(name, MLArray.mxINT64_CLASS, vals, m); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from byte[][] - * - * Note: array is converted to Byte[] - * - * @param name - array name - * @param vals - two-dimensional array of values - */ - public MLInt64(String name, long[][] vals) { - this(name, long2DToLong(vals), vals.length); - } - - /** - * Jama [math.nist.gov] style: - * construct a matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt64(String name, long[] vals, int m) { - this(name, castToLong(vals), m); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) - */ - public Long[] createArray(int m, int n) { - return new Long[m * n]; - } - - /** - * Gets two-dimensional real array. - * - * @return - 2D real array - */ - public long[][] getArray() { - long[][] result = new long[getM()][]; - - for (int m = 0; m < getM(); m++) { - result[m] = new long[getN()]; - - for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); - } - } - return result; - } - - /** - * Casts Double[] to byte[] - * - * @param - source Long[] - * @return - result long[] - */ - private static Long[] castToLong(long[] d) { - Long[] dest = new Long[d.length]; - for (int i = 0; i < d.length; i++) { - dest[i] = (long) d[i]; - } - return dest; - } - - /** - * Converts byte[][] to Long[] - * - * @param dd - * @return - */ - private static Long[] long2DToLong(long[][] dd) { - Long[] d = new Long[dd.length * dd[0].length]; - for (int n = 0; n < dd[0].length; n++) { - for (int m = 0; m < dd.length; m++) { - d[m + n * dd.length] = dd[m][n]; - } - } - return d; - } - - public Long buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return ByteBuffer.wrap(bytes).getLong(); - } - - public int getBytesAllocated() { - return Long.SIZE >> 3; - } - - public Class getStorageClazz() { - return Long.class; - } - - public byte[] getByteArray(Long value) { - int byteAllocated = getBytesAllocated(); - ByteBuffer buff = ByteBuffer.allocate(byteAllocated); - buff.putLong(value); - return buff.array(); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLInt8.java b/src/main/java/ca/mjdsystems/jmatio/types/MLInt8.java deleted file mode 100644 index 81f4485..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLInt8.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -public class MLInt8 extends MLNumericArray { - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type: here mxDOUBLE_CLASS - * @param attributes - array flags - */ - public MLInt8(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - /** - * Create a {@link MLUInt8} array with given name, - * and dimensions. - * - * @param name - array name - * @param dims - array dimensions - */ - public MLInt8(String name, int[] dims) { - super(name, dims, MLArray.mxINT8_CLASS, 0); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt8(String name, Byte[] vals, int m) { - super(name, MLArray.mxINT8_CLASS, vals, m); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from byte[][] - * - * Note: array is converted to Byte[] - * - * @param name - array name - * @param vals - two-dimensional array of values - */ - public MLInt8(String name, byte[][] vals) { - this(name, byte2DToByte(vals), vals.length); - } - - /** - * Jama [math.nist.gov] style: - * construct a matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLInt8(String name, byte[] vals, int m) { - this(name, castToByte(vals), m); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) - */ - public Byte[] createArray(int m, int n) { - return new Byte[m * n]; - } - - /** - * Gets two-dimensional real array. - * - * @return - 2D real array - */ - public byte[][] getArray() { - byte[][] result = new byte[getM()][]; - - for (int m = 0; m < getM(); m++) { - result[m] = new byte[getN()]; - - for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); - } - } - return result; - } - - /** - * Casts Double[] to byte[] - * - * @param - source Byte[] - * @return - result byte[] - */ - private static Byte[] castToByte(byte[] d) { - Byte[] dest = new Byte[d.length]; - for (int i = 0; i < d.length; i++) { - dest[i] = (byte) d[i]; - } - return dest; - } - - /** - * Converts byte[][] to Byte[] - * - * @param dd - * @return - */ - private static Byte[] byte2DToByte(byte[][] dd) { - Byte[] d = new Byte[dd.length * dd[0].length]; - for (int n = 0; n < dd[0].length; n++) { - for (int m = 0; m < dd.length; m++) { - d[m + n * dd.length] = dd[m][n]; - } - } - return d; - } - - public Byte buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return bytes[0]; - } - - public byte[] getByteArray(Byte value) { - return new byte[]{value}; - } - - public int getBytesAllocated() { - return Byte.SIZE >> 3; - } - - public Class getStorageClazz() { - return Byte.class; - } - - /** - * Override to accelerate the performance - * - * @see ca.mjdsystems.jmatio.types.MLNumericArray#_get(java.nio.ByteBuffer, int) - */ - @Override - protected Byte _get(ByteBuffer buffer, int index) { - return buffer.get(index); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLJavaObject.java b/src/main/java/ca/mjdsystems/jmatio/types/MLJavaObject.java deleted file mode 100644 index 968668c..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLJavaObject.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -public class MLJavaObject extends MLArray { - private final Object o; - private final String className; - - public MLJavaObject(String name, String className, Object o) { - super(name, new int[]{1, 1}, MLArray.mxOPAQUE_CLASS, 0); - this.o = o; - this.className = className; - } - - public String getClassName() { - return className; - } - - public Object getObject() { - return o; - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLNumericArray.java b/src/main/java/ca/mjdsystems/jmatio/types/MLNumericArray.java deleted file mode 100755 index f7cfd4a..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLNumericArray.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; -import java.util.Arrays; - -/** - * Abstract class for numeric arrays. - * - * @author Wojciech Gradkowski - * - * @param - */ -/** - * @author Wojciech Gradkowski - * - * @param - */ -public abstract class MLNumericArray extends MLArray - implements GenericArrayCreator, - ByteStorageSupport { - private ByteBuffer real; - private ByteBuffer imaginary; - /** The buffer for creating Number from bytes */ - private byte[] bytes; - - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type - * @param attributes - array flags - */ - public MLNumericArray(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - allocate(); - - } - - protected void allocate() { - real = ByteBuffer.allocate(getSize() * getBytesAllocated()); - if (isComplex()) { - imaginary = ByteBuffer.allocate(getSize() * getBytesAllocated()); - } - bytes = new byte[getBytesAllocated()]; - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param type - array type - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLNumericArray(String name, int type, T[] vals, int m) { - this(name, new int[]{m, vals.length / m}, type, 0); - //fill the array - for (int i = 0; i < vals.length; i++) { - set(vals[i], i); - } - } - - /** - * Gets single real array element of A(m,n). - * - * @param m - row index - * @param n - column index - * @return - array element - */ - public T getReal(int m, int n) { - return getReal(getIndex(m, n)); - } - - /** - * @param index - * @return - */ - public T getReal(int index) { - return _get(real, index); - } - - /** - * Sets single real array element. - * - * @param value - element value - * @param m - row index - * @param n - column index - */ - public void setReal(T value, int m, int n) { - setReal(value, getIndex(m, n)); - } - - /** - * Sets single real array element. - * - * @param value - element value - * @param index - column-packed vector index - */ - public void setReal(T value, int index) { - _set(real, value, index); - } - - /** - * Sets real part of matrix - * - * @param vector - column-packed vector of elements - */ - public void setReal(T[] vector) { - if (vector.length != getSize()) { - throw new IllegalArgumentException("Matrix dimensions do not match. " + getSize() + " not " + vector.length); - } - System.arraycopy(vector, 0, real, 0, vector.length); - } - - /** - * Sets single imaginary array element. - * - * @param value - element value - * @param m - row index - * @param n - column index - */ - public void setImaginary(T value, int m, int n) { - setImaginary(value, getIndex(m, n)); - } - - /** - * Sets single real array element. - * - * @param value - element value - * @param index - column-packed vector index - */ - public void setImaginary(T value, int index) { - if (isComplex()) { - _set(imaginary, value, index); - } - } - - /** - * Gets single imaginary array element of A(m,n). - * - * @param m - row index - * @param n - column index - * @return - array element - */ - public T getImaginary(int m, int n) { - return getImaginary(getIndex(m, n)); - } - - /** - * @param index - * @return - */ - public T getImaginary(int index) { - return _get(imaginary, index); - } - - /** - * Exports column-packed vector of real elements - * - * @return - column-packed vector of real elements - */ - // public T[] exportReal() - // { - // return real.clone(); - // } - /** - * Exports column-packed vector of imaginary elements - * - * @return - column-packed vector of imaginary elements - */ - - // public T[] exportImaginary() - // { - // return imaginary.clone(); - // } - /** - * Does the same as setReal. - * - * @param value - element value - * @param m - row index - * @param n - column index - */ - public void set(T value, int m, int n) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } - setReal(value, m, n); - } - - /** - * Does the same as setReal. - * - * @param value - element value - * @param index - column-packed vector index - */ - public void set(T value, int index) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } - setReal(value, index); - } - - /** - * Does the same as getReal. - * - * @param m - row index - * @param n - column index - * @return - array element - */ - public T get(int m, int n) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } - return getReal(m, n); - } - - /** - * @param index - * @return - */ - public T get(int index) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } - return _get(real, index); - } - - /** - * @param vector - */ - public void set(T[] vector) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } - setReal(vector); - } - - private int getByteOffset(int index) { - return index * getBytesAllocated(); - } - - protected T _get(ByteBuffer buffer, int index) { - buffer.position(getByteOffset(index)); - buffer.get(bytes, 0, bytes.length); - return buldFromBytes(bytes); - } - - protected void _set(ByteBuffer buffer, T value, int index) { - buffer.position(getByteOffset(index)); - buffer.put(getByteArray(value)); - } - - public void putImaginaryByteBuffer(ByteBuffer buff) { - if (!isComplex()) { - throw new RuntimeException("Array is not complex"); - } - imaginary.rewind(); - imaginary.put(buff); - } - - public ByteBuffer getImaginaryByteBuffer() { - return imaginary; - } - - public void putRealByteBuffer(ByteBuffer buff) { - real.rewind(); - real.put(buff); - } - - public ByteBuffer getRealByteBuffer() { - return real; - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.MLArray#contentToString() - */ - public String contentToString() { - StringBuffer sb = new StringBuffer(); - sb.append(name + " = \n"); - - if (getSize() > 1000) { - sb.append("Cannot display variables with more than 1000 elements."); - return sb.toString(); - } - for (int m = 0; m < getM(); m++) { - sb.append("\t"); - for (int n = 0; n < getN(); n++) { - sb.append(getReal(m, n)); - if (isComplex()) { - sb.append("+" + getImaginary(m, n)); - } - sb.append("\t"); - } - sb.append("\n"); - } - return sb.toString(); - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object o) { - if (o instanceof MLNumericArray) { - boolean result = directByteBufferEquals(real, ((MLNumericArray) o).real) - && Arrays.equals(dims, ((MLNumericArray) o).dims); - if (isComplex() && result) { - result &= directByteBufferEquals(imaginary, ((MLNumericArray) o).imaginary); - } - return result; - } - return super.equals(o); - } - - /** - * Equals implementation for direct ByteBuffer - * - * @param buffa the source buffer to be compared - * @param buffb the destination buffer to be compared - * @return true if buffers are equal in terms of content - */ - private static boolean directByteBufferEquals(ByteBuffer buffa, ByteBuffer buffb) { - if (buffa == buffb) { - return true; - } - - if (buffa == null || buffb == null) { - return false; - } - - buffa.rewind(); - buffb.rewind(); - - int length = buffa.remaining(); - - if (buffb.remaining() != length) { - return false; - } - - for (int i = 0; i < length; i++) { - if (buffa.get() != buffb.get()) { - return false; - } - } - - return true; - } - - public void dispose() { - if (real != null) { - real.clear(); - } - if (imaginary != null) { - real.clear(); - } - - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLObject.java b/src/main/java/ca/mjdsystems/jmatio/types/MLObject.java deleted file mode 100644 index 8492312..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLObject.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class MLObject extends MLArray { - private final List> objects = new ArrayList>(); - private final String className; - - public MLObject(String name, String className, int[] dimensions, int attributes) { - super(name, dimensions, MLArray.mxOBJECT_CLASS, 0); - for (int i = 0; i < getSize(); ++i) { - objects.add(null); - } - this.className = className; - } - - public String getClassName() { - return className; - } - - public Map getFields(int index) { - return objects.get(index); - } - - public MLObject setFields(int index, Map fields) { - if (index >= getSize()) { - throw new IndexOutOfBoundsException("Index: " + index + " Size: " + getSize()); - } - objects.set(index, fields); - return this; - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLSingle.java b/src/main/java/ca/mjdsystems/jmatio/types/MLSingle.java deleted file mode 100644 index 3d44247..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLSingle.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -public class MLSingle extends MLNumericArray { - - public MLSingle(String name, Float[] vals, int m) { - super(name, MLArray.mxSINGLE_CLASS, vals, m); - } - - public MLSingle(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - public Float[] createArray(int m, int n) { - return new Float[m * n]; - } - - public Float buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return ByteBuffer.wrap(bytes).getFloat(); - } - - public byte[] getByteArray(Float value) { - int byteAllocated = getBytesAllocated(); - ByteBuffer buff = ByteBuffer.allocate(byteAllocated); - buff.putFloat(value); - return buff.array(); - } - - public int getBytesAllocated() { - return Float.SIZE >> 3; - } - - public Class getStorageClazz() { - return Float.class; - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLSparse.java b/src/main/java/ca/mjdsystems/jmatio/types/MLSparse.java deleted file mode 100755 index 9bb8c07..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLSparse.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; - -public class MLSparse extends MLNumericArray { - int nzmax; - private SortedSet indexSet; - private SortedMap real; - private SortedMap imaginary; - - /** - * @param name - * @param dims - * @param attributes - * @param nzmax - */ - public MLSparse(String name, int[] dims, int attributes, int nzmax) { - super(name, dims, MLArray.mxSPARSE_CLASS, attributes); - this.nzmax = nzmax; - } - - protected void allocate() { - real = new TreeMap(); - if (isComplex()) { - imaginary = new TreeMap(); - } - indexSet = new TreeSet(); - } - - /** - * Gets maximum number of non-zero values - * - * @return - */ - public int getMaxNZ() { - return nzmax; - } - - /** - * Gets row indices - * - * ir points to an integer array of length nzmax containing the row indices of - * the corresponding elements in pr and pi. - */ - public int[] getIR() { - int[] ir = new int[nzmax]; - int i = 0; - for (IndexMN index : indexSet) { - ir[i++] = index.m; - } - return ir; - } - - /** - * Gets column indices - * - * ic points to an integer array of length nzmax containing the column indices of - * the corresponding elements in pr and pi. - */ - public int[] getIC() { - int[] ic = new int[nzmax]; - int i = 0; - for (IndexMN index : indexSet) { - ic[i++] = index.n; - } - return ic; - } - - /** - * Gets column indices. - * - * jc points to an integer array of length N+1 that contains column index information. - * For j, in the range 0<=j<=N, jc[j] is the index in ir and pr (and pi - * if it exists) of the first nonzero entry in the jth column and jc[j+1]�1 index - * of the last nonzero entry. As a result, jc[N] is also equal to nnz, the number - * of nonzero entries in the matrix. If nnz is less than nzmax, then more nonzero - * entries can be inserted in the array without allocating additional storage - * - * @return - */ - public int[] getJC() { - int[] jc = new int[getN() + 1]; - // jc[j] is the number of nonzero elements in all preceeding columns - for (IndexMN index : indexSet) { - for (int column = index.n + 1; column < jc.length; column++) { - jc[column]++; - } - } - return jc; - } - - /* (non-Javadoc) - * @see com.paradigmdesigner.matlab.types.GenericArrayCreator#createArray(int, int) - */ - public Double[] createArray(int m, int n) { - return null; - } - - /* (non-Javadoc) - * @see com.paradigmdesigner.matlab.types.MLNumericArray#getReal(int, int) - */ - public Double getReal(int m, int n) { - IndexMN i = new IndexMN(m, n); - if (real.containsKey(i)) { - return real.get(i); - } - return new Double(0); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.MLNumericArray#getReal(int) - */ - public Double getReal(int index) { - throw new IllegalArgumentException("Can't get Sparse array elements by index. " + - "Please use getReal(int index) instead."); - } - - /** - * @param value - * @param m - * @param n - */ - public void setReal(Double value, int m, int n) { - IndexMN i = new IndexMN(m, n); - indexSet.add(i); - real.put(i, value); - } - - /** - * @param value - * @param index - */ - public void setReal(Double value, int index) { - throw new IllegalArgumentException("Can't set Sparse array elements by index. " + - "Please use setReal(Double value, int m, int n) instead."); - } - - /** - * @param value - * @param m - * @param n - */ - public void setImaginary(Double value, int m, int n) { - IndexMN i = new IndexMN(m, n); - indexSet.add(i); - imaginary.put(i, value); - } - - /** - * @param value - * @param index - */ - public void setImaginary(Double value, int index) { - throw new IllegalArgumentException("Can't set Sparse array elements by index. " + - "Please use setImaginary(Double value, int m, int n) instead."); - } - - /* (non-Javadoc) - * @see com.paradigmdesigner.matlab.types.MLNumericArray#getImaginary(int, int) - */ - public Double getImaginary(int m, int n) { - IndexMN i = new IndexMN(m, n); - if (imaginary.containsKey(i)) { - return imaginary.get(i); - } - return new Double(0); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.MLNumericArray#getImaginary(int) - */ - public Double getImaginary(int index) { - throw new IllegalArgumentException("Can't get Sparse array elements by index. " + - "Please use getImaginary(int index) instead."); - } - - /** - * Returns the real part (PR) array. PR has length number-of-nonzero-values. - * - * @return real part - */ - public Double[] exportReal() { - Double[] ad = new Double[indexSet.size()]; - int i = 0; - for (IndexMN index : indexSet) { - if (real.containsKey(index)) { - ad[i] = real.get(index); - } else { - ad[i] = 0.0; - } - i++; - } - return ad; - } - - /** - * Returns the imaginary part (PI) array. PI has length number-of-nonzero-values. - * - * @return - */ - public Double[] exportImaginary() { - Double[] ad = new Double[indexSet.size()]; - int i = 0; - for (IndexMN index : indexSet) { - if (imaginary.containsKey(index)) { - ad[i] = imaginary.get(index); - } else { - ad[i] = 0.0; - } - i++; - } - return ad; - } - - /* (non-Javadoc) - * @see com.paradigmdesigner.matlab.types.MLArray#contentToString() - */ - public String contentToString() { - StringBuffer sb = new StringBuffer(); - sb.append(name + " = \n"); - - for (IndexMN i : indexSet) { - sb.append("\t("); - sb.append(i.m + "," + i.n); - sb.append(")"); - sb.append("\t" + getReal(i.m, i.n)); - if (isComplex()) { - sb.append("+" + getImaginary(i.m, i.n)); - } - sb.append("\n"); - - } - - return sb.toString(); - } - - /** - * Matrix index (m,n) - * - * @author Wojciech Gradkowski - */ - private class IndexMN implements Comparable { - int m; - int n; - - public IndexMN(int m, int n) { - this.m = m; - this.n = n; - } - - /* (non-Javadoc) - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - public int compareTo(IndexMN anOtherIndex) { - return getIndex(m, n) - getIndex(anOtherIndex.m, anOtherIndex.n); - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object o) { - if (o instanceof IndexMN) { - return m == ((IndexMN) o).m && n == ((IndexMN) o).n; - } - return super.equals(o); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("{"); - sb.append("m=" + m); - sb.append(", "); - sb.append("n=" + n); - sb.append("}"); - return sb.toString(); - } - } - - public int getBytesAllocated() { - return Double.SIZE << 3; - } - - public Double buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return ByteBuffer.wrap(bytes).getDouble(); - - } - - public byte[] getByteArray(Double value) { - int byteAllocated = getBytesAllocated(); - ByteBuffer buff = ByteBuffer.allocate(byteAllocated); - buff.putDouble(value); - return buff.array(); - } - - public Class getStorageClazz() { - return Double.class; - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLUInt32.java b/src/main/java/ca/mjdsystems/jmatio/types/MLUInt32.java deleted file mode 100644 index a095190..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLUInt32.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -public class MLUInt32 extends MLInt32 { - - public MLUInt32(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - public MLUInt32(String name, int[] vals, int m) { - super(name, vals, m); - } - - public MLUInt32(String name, int[] dims) { - super(name, dims); - } - - public MLUInt32(String name, int[][] vals) { - super(name, vals); - } - - public MLUInt32(String name, Integer[] vals, int m) { - super(name, vals, m); - } -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLUInt64.java b/src/main/java/ca/mjdsystems/jmatio/types/MLUInt64.java deleted file mode 100644 index 584c752..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLUInt64.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -/** - * Class represents Int64 (long) array (matrix) - * - * @author Wojciech Gradkowski - */ -public class MLUInt64 extends MLNumericArray { - - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type: here mxDOUBLE_CLASS - * @param attributes - array flags - */ - public MLUInt64(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - /** - * Create a {@link MLUInt64} array with given name, - * and dimensions. - * - * @param name - array name - * @param dims - array dimensions - */ - public MLUInt64(String name, int[] dims) { - super(name, dims, MLArray.mxUINT64_CLASS, 0); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLUInt64(String name, Long[] vals, int m) { - super(name, MLArray.mxUINT64_CLASS, vals, m); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from byte[][] - * - * Note: array is converted to Byte[] - * - * @param name - array name - * @param vals - two-dimensional array of values - */ - public MLUInt64(String name, long[][] vals) { - this(name, long2DToLong(vals), vals.length); - } - - /** - * Jama [math.nist.gov] style: - * construct a matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLUInt64(String name, long[] vals, int m) { - this(name, castToLong(vals), m); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) - */ - public Long[] createArray(int m, int n) { - return new Long[m * n]; - } - - /** - * Gets two-dimensional real array. - * - * @return - 2D real array - */ - public long[][] getArray() { - long[][] result = new long[getM()][]; - - for (int m = 0; m < getM(); m++) { - result[m] = new long[getN()]; - - for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); - } - } - return result; - } - - /** - * Casts Double[] to byte[] - * - * @param - source Long[] - * @return - result long[] - */ - private static Long[] castToLong(long[] d) { - Long[] dest = new Long[d.length]; - for (int i = 0; i < d.length; i++) { - dest[i] = (long) d[i]; - } - return dest; - } - - /** - * Converts byte[][] to Long[] - * - * @param dd - * @return - */ - private static Long[] long2DToLong(long[][] dd) { - Long[] d = new Long[dd.length * dd[0].length]; - for (int n = 0; n < dd[0].length; n++) { - for (int m = 0; m < dd.length; m++) { - d[m + n * dd.length] = dd[m][n]; - } - } - return d; - } - - public Long buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return ByteBuffer.wrap(bytes).getLong(); - } - - public int getBytesAllocated() { - return Long.SIZE >> 3; - } - - public Class getStorageClazz() { - return Long.class; - } - - public byte[] getByteArray(Long value) { - int byteAllocated = getBytesAllocated(); - ByteBuffer buff = ByteBuffer.allocate(byteAllocated); - buff.putLong(value); - return buff.array(); - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLUInt8.java b/src/main/java/ca/mjdsystems/jmatio/types/MLUInt8.java deleted file mode 100644 index cb64ab6..0000000 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLUInt8.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.types; - -import java.nio.ByteBuffer; - -/** - * Class represents UInt8 (byte) array (matrix) - * - * @author Wojciech Gradkowski - */ -public class MLUInt8 extends MLNumericArray { - - /** - * Normally this constructor is used only by MatFileReader and MatFileWriter - * - * @param name - array name - * @param dims - array dimensions - * @param type - array type: here mxDOUBLE_CLASS - * @param attributes - array flags - */ - public MLUInt8(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - } - - /** - * Create a {@link MLUInt8} array with given name, - * and dimensions. - * - * @param name - array name - * @param dims - array dimensions - */ - public MLUInt8(String name, int[] dims) { - super(name, dims, MLArray.mxUINT8_CLASS, 0); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLUInt8(String name, Byte[] vals, int m) { - super(name, MLArray.mxUINT8_CLASS, vals, m); - } - - /** - * Jama [math.nist.gov] style: - * construct a 2D real matrix from byte[][] - * - * Note: array is converted to Byte[] - * - * @param name - array name - * @param vals - two-dimensional array of values - */ - public MLUInt8(String name, byte[][] vals) { - this(name, byte2DToByte(vals), vals.length); - } - - /** - * Jama [math.nist.gov] style: - * construct a matrix from a one-dimensional packed array - * - * @param name - array name - * @param vals - One-dimensional array of doubles, packed by columns (ala Fortran). - * @param m - Number of rows - */ - public MLUInt8(String name, byte[] vals, int m) { - this(name, castToByte(vals), m); - } - - /* (non-Javadoc) - * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) - */ - public Byte[] createArray(int m, int n) { - return new Byte[m * n]; - } - - /** - * Gets two-dimensional real array. - * - * @return - 2D real array - */ - public byte[][] getArray() { - byte[][] result = new byte[getM()][]; - - for (int m = 0; m < getM(); m++) { - result[m] = new byte[getN()]; - - for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); - } - } - return result; - } - - /** - * Casts Double[] to byte[] - * - * @param - source Byte[] - * @return - result byte[] - */ - private static Byte[] castToByte(byte[] d) { - Byte[] dest = new Byte[d.length]; - for (int i = 0; i < d.length; i++) { - dest[i] = (byte) d[i]; - } - return dest; - } - - /** - * Converts byte[][] to Byte[] - * - * @param dd - * @return - */ - private static Byte[] byte2DToByte(byte[][] dd) { - Byte[] d = new Byte[dd.length * dd[0].length]; - for (int n = 0; n < dd[0].length; n++) { - for (int m = 0; m < dd.length; m++) { - d[m + n * dd.length] = dd[m][n]; - } - } - return d; - } - - public Byte buldFromBytes(byte[] bytes) { - if (bytes.length != getBytesAllocated()) { - throw new IllegalArgumentException( - "To build from byte array I need array of size: " - + getBytesAllocated()); - } - return bytes[0]; - } - - public byte[] getByteArray(Byte value) { - return new byte[]{value}; - } - - public int getBytesAllocated() { - return Byte.SIZE >> 3; - } - - public Class getStorageClazz() { - return Byte.class; - } - - /** - * Override to accelerate the performance - * - * @see ca.mjdsystems.jmatio.types.MLNumericArray#_get(java.nio.ByteBuffer, int) - */ - @Override - protected Byte _get(ByteBuffer buffer, int index) { - return buffer.get(index); - } - -} diff --git a/src/main/java/com/jmatio/common/AbstractIterator.java b/src/main/java/com/jmatio/common/AbstractIterator.java new file mode 100644 index 0000000..adf1533 --- /dev/null +++ b/src/main/java/com/jmatio/common/AbstractIterator.java @@ -0,0 +1,129 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.common; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** Copied from Guava. */ +abstract class AbstractIterator implements Iterator { + private State state = State.NOT_READY; + + /** Constructor for use by subclasses. */ + protected AbstractIterator() {} + + private enum State { + /** We have computed the next element and haven't returned it yet. */ + READY, + + /** We haven't yet computed or have already returned the element. */ + NOT_READY, + + /** We have reached the end of the data and are finished. */ + DONE, + + /** We've suffered an exception and are kaput. */ + FAILED, + } + + private T next; + + /** + * Returns the next element. Note: the implementation must call {@link + * #endOfData()} when there are no elements left in the iteration. Failure to + * do so could result in an infinite loop. + * + *

The initial invocation of {@link #hasNext()} or {@link #next()} calls + * this method, as does the first invocation of {@code hasNext} or {@code + * next} following each successful call to {@code next}. Once the + * implementation either invokes {@code endOfData} or throws an exception, + * {@code computeNext} is guaranteed to never be called again. + * + *

If this method throws an exception, it will propagate outward to the + * {@code hasNext} or {@code next} invocation that invoked this method. Any + * further attempts to use the iterator will result in an {@link + * IllegalStateException}. + * + *

The implementation of this method may not invoke the {@code hasNext}, + * {@code next}, or {@link #peek()} methods on this instance; if it does, an + * {@code IllegalStateException} will result. + * + * @return the next element if there was one. If {@code endOfData} was called + * during execution, the return value will be ignored. + * @throws RuntimeException if any unrecoverable error happens. This exception + * will propagate outward to the {@code hasNext()}, {@code next()}, or + * {@code peek()} invocation that invoked this method. Any further + * attempts to use the iterator will result in an + * {@link IllegalStateException}. + */ + protected abstract T computeNext(); + + /** + * Implementations of {@link #computeNext} must invoke this method when + * there are no elements left in the iteration. + * + * @return {@code null}; a convenience so your {@code computeNext} + * implementation can use the simple statement {@code return endOfData();} + */ + protected final T endOfData() { + state = State.DONE; + return null; + } + + @Override + public final boolean hasNext() { + if (state == State.FAILED) { + throw new IllegalStateException(); + } + switch (state) { + case DONE: + return false; + case READY: + return true; + default: + } + return tryToComputeNext(); + } + + private boolean tryToComputeNext() { + state = State.FAILED; // temporary pessimism + next = computeNext(); + if (state != State.DONE) { + state = State.READY; + return true; + } + return false; + } + + @Override + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + state = State.NOT_READY; + T result = next; + next = null; + return result; + } + + /** + * Returns the next element in the iteration without advancing the iteration, + * according to the contract of {@link PeekingIterator#peek()}. + * + *

Implementations of {@code AbstractIterator} that wish to expose this + * functionality should implement {@code PeekingIterator}. + */ + public final T peek() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return next; + } + + public final void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/jmatio/common/DeterministicKeyMap.java b/src/main/java/com/jmatio/common/DeterministicKeyMap.java new file mode 100644 index 0000000..e71638d --- /dev/null +++ b/src/main/java/com/jmatio/common/DeterministicKeyMap.java @@ -0,0 +1,147 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.common; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A map implementation which guarantees that all of its iterators + * (keySet(), values(), and entrySet()) will be in the same order + * as the keyOrder set which is passed in the constructor. + * + * The keySet must contain all of the keys in the delegate, but it's + * okay if the keySet contains more. + * + * Useful in MLObject and MLStruct for ensuring that arrays have + * their fields in the same order. + */ +public class DeterministicKeyMap extends ForwardingMap { + private final Set keyOrder; + + /** + * + * @param keyOrder A set which must always contain all of the keys in delegate, and may contain more. + * @param delegate An underlying map. + */ + public DeterministicKeyMap(Set keyOrder, Map delegate) { + super(delegate); + this.keyOrder = keyOrder; + } + + /** Returns the keyset of this map in the same order as keyOrder. */ + @SuppressWarnings("unchecked") + @Override + public Set keySet() { + return new DeterministicSet(delegate.keySet(), (Function) identity); + } + + /** Returns the values of this map in the same order as keyOrder. */ + @Override + public Collection values() { + return new DeterministicCollection(delegate.values(), new Function() { + @Override + public V apply(K input) { + return delegate.get(input); + } + }); + } + + /** Returns the entries of this map in the same order as keyOrder. */ + @Override + public Set> entrySet() { + return new DeterministicSet>(delegate.entrySet(), new Function>() { + @Override + public Map.Entry apply(final K key) { + return new Map.Entry() { + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return delegate.get(key); + } + + @Override + public V setValue(V value) { + return delegate.put(key, value); + } + }; + } + }); + } + + @Override + public boolean equals(Object other) { + return super.equals(other); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + /** Java didn't find functional programming until Java 8. */ + static interface Function { + R apply(T input); + } + + static final Function identity = new Function() { + @Override + public Object apply(Object input) { + return input; + } + }; + + /** DeterministicCollection which guarantees a Set delegate. */ + class DeterministicSet extends DeterministicCollection implements Set { + DeterministicSet(Set delegate, Function keyToValue) { + super(delegate, keyToValue); + } + } + + /** A collection which iterates over the key set, transformed into values using the given function. */ + class DeterministicCollection extends ForwardingCollection { + Function keyToValue; + + DeterministicCollection(Collection delegate, Function keyToValue) { + super(delegate); + this.keyToValue = keyToValue; + } + + @Override + public Iterator iterator() { + final Iterator iterByKey = keyOrder.iterator(); + return new AbstractIterator() { + @Override + protected T computeNext() { + while (iterByKey.hasNext()) { + K nextKey = iterByKey.next(); + T value = keyToValue.apply(nextKey); + if (delegate.contains(value)) { + return value; + } + } + return endOfData(); + } + }; + } + + @Override + public boolean equals(Object other) { + return super.equals(other); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + } +} diff --git a/src/main/java/com/jmatio/common/ForwardingCollection.java b/src/main/java/com/jmatio/common/ForwardingCollection.java new file mode 100644 index 0000000..1f05c78 --- /dev/null +++ b/src/main/java/com/jmatio/common/ForwardingCollection.java @@ -0,0 +1,106 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.common; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * A Collection which forwards all calls to a delegate collection, + * except for toArray(), which are overridden to reflect any changes + * in the iterator() method. + * + * Used to implement {@link DeterministicKeyMap}. + */ +class ForwardingCollection implements Collection { + protected final Collection delegate; + + protected ForwardingCollection(Collection delegate) { + this.delegate = delegate; + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + @Override + public Object[] toArray() { + Object[] result = new Object[this.size()]; + return toArray(result); + } + + @Override + public R[] toArray(R[] a) { + ArrayList result = new ArrayList(size()); + Iterator iter = iterator(); + while (iter.hasNext()) { + result.add(iter.next()); + } + return result.toArray(a); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public boolean add(T e) { + return delegate.add(e); + } + + @Override + public boolean remove(Object o) { + return delegate.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return delegate.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + return delegate.addAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return delegate.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return delegate.retainAll(c); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object other) { + return delegate.equals(other); + } +} diff --git a/src/main/java/com/jmatio/common/ForwardingMap.java b/src/main/java/com/jmatio/common/ForwardingMap.java new file mode 100644 index 0000000..28ef4dc --- /dev/null +++ b/src/main/java/com/jmatio/common/ForwardingMap.java @@ -0,0 +1,89 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.common; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** A Map which forwards all of its method calls to a delegate map. Used to implement {@link DeterministicKeyMap}. */ +class ForwardingMap implements Map { + protected final Map delegate; + + protected ForwardingMap(Map delegate) { + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public V get(Object key) { + return delegate.get(key); + } + + @Override + public V put(K key, V value) { + return delegate.put(key, value); + } + + @Override + public V remove(Object key) { + return delegate.remove(key); + } + + @Override + public void putAll(Map m) { + delegate.putAll(m); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public Set keySet() { + return delegate.keySet(); + } + + @Override + public Collection values() { + return delegate.values(); + } + + @Override + public Set> entrySet() { + return delegate.entrySet(); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object other) { + return delegate.equals(other); + } +} diff --git a/src/main/java/com/jmatio/common/util/MLArrayQuery.java b/src/main/java/com/jmatio/common/util/MLArrayQuery.java index 0722bf4..d725efb 100644 --- a/src/main/java/com/jmatio/common/util/MLArrayQuery.java +++ b/src/main/java/com/jmatio/common/util/MLArrayQuery.java @@ -75,7 +75,7 @@ public Object query(MLArray array) { if (current == null) { current = array; - if (!current.getName().equals(name) && !current.getName().equals("@")) { + if (!current.getName().equals(name) && !current.getName().equals(MLArray.DEFAULT_NAME)) { throw new RuntimeException("No such array or field <" + name + "> in <" + current.getName() + ">"); } @@ -90,7 +90,7 @@ public Object query(MLArray array) { switch (type) { case MLArray.mxOBJECT_CLASS: { MLObject object = cast(current, MLObject.class); - MLArray field = object.getObject().getField(name, prevM, prevN); + MLArray field = object.getField(name); if (field == null) { throw new RuntimeException("no such field: " + name); } diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MLObjectPlaceholder.java b/src/main/java/com/jmatio/io/MLObjectPlaceholder.java similarity index 63% rename from src/main/java/ca/mjdsystems/jmatio/io/MLObjectPlaceholder.java rename to src/main/java/com/jmatio/io/MLObjectPlaceholder.java index 2b1318b..e400649 100644 --- a/src/main/java/ca/mjdsystems/jmatio/io/MLObjectPlaceholder.java +++ b/src/main/java/com/jmatio/io/MLObjectPlaceholder.java @@ -3,21 +3,17 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.io; +package com.jmatio.io; -import java.util.Arrays; - -import ca.mjdsystems.jmatio.types.MLArray; +import com.jmatio.types.MLObject; /** * * @author Matthew Dawson */ -class MLObjectPlaceholder extends MLArray { +class MLObjectPlaceholder extends MLObject { MLObjectPlaceholder(String name, String className, int[][] information) { - super(name, new int[]{information[2][0], information[3][0]}, -1, 0); - this.className = className; - this.information = information; + super(name, className, new int[]{information[2][0], information[3][0]}, -1); this.objectIds = new int[information.length - 5]; for (int i = 0; i < objectIds.length; ++i) { @@ -26,8 +22,10 @@ class MLObjectPlaceholder extends MLArray { this.classId = information[information.length - 1][0]; } - final String className; - final int[][] information; final int[] objectIds; final int classId; + + void setTarget(MLObject obj) { + copyFrom(obj); + } } diff --git a/src/main/java/com/jmatio/io/MatFile.java b/src/main/java/com/jmatio/io/MatFile.java index a7ac6ad..9a31af5 100644 --- a/src/main/java/com/jmatio/io/MatFile.java +++ b/src/main/java/com/jmatio/io/MatFile.java @@ -8,10 +8,8 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.Map; -import com.jmatio.io.MatFileHeader; import com.jmatio.types.MLArray; /** @@ -44,24 +42,19 @@ public Map getContent() { public static Map readBare(MatFileHeader header, ByteBuffer buffer) throws IOException { MatFileReader reader = new MatFileReader(); reader.matFileHeader = header; - - // set the byteOrder based on this - if (header.getEndianIndicator()[0] == 'I' && header.getEndianIndicator()[1] == 'M') { - reader.byteOrder = ByteOrder.LITTLE_ENDIAN; - } else if (header.getEndianIndicator()[0] == 'M' && header.getEndianIndicator()[1] == 'I') { - reader.byteOrder = ByteOrder.BIG_ENDIAN; - } else { - throw new IllegalArgumentException("Unsupported endianness!"); - } - buffer.order(reader.byteOrder); - + buffer.order(header.getByteOrder()); reader.readData(buffer); return reader.data; } /** Reads a full set of bytes (including the header). */ public static MatFile readFull(ByteBuffer buf) throws IOException { - MatFileReader reader = new MatFileReader(); + return readFull(buf, MatFileType.Regular); + } + + /** Reads a full set of bytes (including the header). */ + public static MatFile readFull(ByteBuffer buf, MatFileType type) throws IOException { + MatFileReader reader = new MatFileReader(type); reader.readHeader(buf); while (buf.remaining() > 0) { reader.readData(buf); diff --git a/src/main/java/com/jmatio/io/MatFileHeader.java b/src/main/java/com/jmatio/io/MatFileHeader.java index 1cdff4b..a5938f9 100644 --- a/src/main/java/com/jmatio/io/MatFileHeader.java +++ b/src/main/java/com/jmatio/io/MatFileHeader.java @@ -5,10 +5,11 @@ */ package com.jmatio.io; -import java.io.UnsupportedEncodingException; +import java.nio.ByteOrder; +import java.util.Arrays; import java.util.Date; -import com.jmatio.common.MatDataTypes; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * MAT-file header @@ -18,15 +19,61 @@ * * @author Wojciech Gradkowski (wgradkowski@gmail.com) */ +@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}, justification = "This code is unlikely to be used in a security-sensitive environment.") public class MatFileHeader { - private static final String DEFAULT_DESCRIPTIVE_TEXT = "MATLAB 5.0 MAT-file, Platform: " + private static String DEFAULT_DESCRIPTIVE_TEXT = "MATLAB 5.0 MAT-file, Platform: " + System.getProperty("os.name") + ", CREATED on: "; public static final int DEFAULT_VERSION = 0x0100; + public static final ByteOrder DEFAULT_ENDIAN = ByteOrder.BIG_ENDIAN; + + private static final byte[] ENDIAN_INDICATOR_BIG = new byte[]{(byte) 'M', (byte) 'I'}; + private static final byte[] ENDIAN_INDICATOR_LITTLE = new byte[]{(byte) 'I', (byte) 'M'}; private int version; private String description; - private byte[] endianIndicator; + private final ByteOrder byteOrder; + + /** + * Parses a MatFileHeader from its desciption and the raw bytes of the version and endian indicator. + * + * @param description - descriptive test + * @param bversion - 2-byte array containing the version (raw from a MAT-File) + * @param endianIndicator - 2-byte array containing the endian indicator (raw from a MAT-File( + * @return + */ + public static MatFileHeader parseFrom(String description, byte[] bversion, byte[] endianIndicator) { + int version; + ByteOrder byteOrder = parseByteOrder(endianIndicator); + if (byteOrder == ByteOrder.BIG_ENDIAN) { + version = bversion[0] & 0xff | bversion[1] << 8; + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + version = bversion[1] & 0xff | bversion[0] << 8; + } else { + throw new IllegalArgumentException("Unknown byteOrder " + byteOrder); + } + return new MatFileHeader(description, version, byteOrder); + } + + /** + * Parses out the byte order based on a byte array containing + * either 'MI' (big-endian) or 'IM' (little-endian). + * + * @param endianIndicator 2-byte long array holding the endian indicator + */ + public static ByteOrder parseByteOrder(byte[] endianIndicator) { + if (Arrays.equals(ENDIAN_INDICATOR_BIG, endianIndicator)) { + return ByteOrder.BIG_ENDIAN; + } else if (Arrays.equals(ENDIAN_INDICATOR_LITTLE, endianIndicator)) { + return ByteOrder.LITTLE_ENDIAN; + } else { + StringBuilder arrayBuilder = new StringBuilder(); + for (int i = 0; i < endianIndicator.length; ++i) { + arrayBuilder.append(endianIndicator[i]); + } + throw new IllegalArgumentException("Unknown endian indicator " + arrayBuilder.toString()); + } + } /** * New MAT-file header @@ -35,11 +82,10 @@ public class MatFileHeader { * @param version - by default is set to 0x0100 * @param endianIndicator - byte array size of 2 indicating byte-swapping requirement */ - public MatFileHeader(String description, int version, byte[] endianIndicator) { + public MatFileHeader(String description, int version, ByteOrder byteOrder) { this.description = description; this.version = version; - this.endianIndicator = new byte[endianIndicator.length]; - System.arraycopy(endianIndicator, 0, this.endianIndicator, 0, endianIndicator.length); + this.byteOrder = byteOrder; } /** @@ -57,8 +103,14 @@ public String getDescription() { * * @return - a byte array size of 2 */ - byte[] getEndianIndicator() { - return endianIndicator; + public byte[] getEndianIndicator() { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + return Arrays.copyOf(ENDIAN_INDICATOR_BIG, 2); + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + return Arrays.copyOf(ENDIAN_INDICATOR_LITTLE, 2); + } else { + throw new IllegalArgumentException("Unknown byteOrder '" + byteOrder + "'"); + } } /** @@ -84,28 +136,23 @@ public int getVersion() { public static MatFileHeader createHeader() { return new MatFileHeader(DEFAULT_DESCRIPTIVE_TEXT + (new Date()).toString(), DEFAULT_VERSION, - DEFAULT_ENDIAN_INDICATOR()); - } - - /** Returns the default endianness indicator. */ - private static final byte[] DEFAULT_ENDIAN_INDICATOR() { - return new byte[]{(byte) 'M', (byte) 'I'}; + DEFAULT_ENDIAN); } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { - try { - StringBuffer sb = new StringBuffer(); - sb.append("["); - sb.append("desriptive text: " + description); - sb.append(", version: " + version); - sb.append(", endianIndicator: " + new String(endianIndicator, MatDataTypes.CHARSET)); - sb.append("]"); - return sb.toString(); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + StringBuffer sb = new StringBuffer(); + sb.append("["); + sb.append("desriptive text: " + description); + sb.append(", version: " + version); + sb.append(", byteOrder: " + byteOrder); + sb.append("]"); + return sb.toString(); + } + + public ByteOrder getByteOrder() { + return byteOrder; } } diff --git a/src/main/java/com/jmatio/io/MatFileIncrementalWriter.java b/src/main/java/com/jmatio/io/MatFileIncrementalWriter.java index d7d3516..45a7035 100644 --- a/src/main/java/com/jmatio/io/MatFileIncrementalWriter.java +++ b/src/main/java/com/jmatio/io/MatFileIncrementalWriter.java @@ -13,7 +13,6 @@ import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.Collection; -import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.zip.DataFormatException; @@ -246,9 +245,9 @@ private void writeMatrix(DataOutputStream output, MLArray array) throws IOExcept //write char data buffer = new ByteArrayOutputStream(); bufferDOS = new DataOutputStream(buffer); - List ac = ((MLChar) array).exportChar(); - for (int i = 0; i < ac.size(); i++) { - bufferDOS.writeByte((byte) ac.get(i).charValue()); + Character[] ac = ((MLChar) array).exportChar(); + for (int i = 0; i < ac.length; i++) { + bufferDOS.writeByte((byte) ac[i].charValue()); } tag = new OSArrayTag(MatDataTypes.miUTF8, buffer.toByteArray()); tag.writeTo(dos); diff --git a/src/main/java/com/jmatio/io/MatFileReader.java b/src/main/java/com/jmatio/io/MatFileReader.java index 9b23849..a6ee0cc 100644 --- a/src/main/java/com/jmatio/io/MatFileReader.java +++ b/src/main/java/com/jmatio/io/MatFileReader.java @@ -5,27 +5,29 @@ */ package com.jmatio.io; +import java.io.EOFException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; -import java.lang.ref.WeakReference; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.zip.InflaterInputStream; import com.jmatio.common.MatDataTypes; -import com.jmatio.io.stream.BufferedOutputStream; +import com.jmatio.io.MatFileWriter.ByteArrayOutputStream2; import com.jmatio.io.stream.ByteBufferInputStream; -import com.jmatio.io.stream.ByteBufferedOutputStream; +import com.jmatio.io.stream.HeapBufferDataOutputStream; import com.jmatio.io.stream.MatFileInputStream; import com.jmatio.types.ByteStorageSupport; import com.jmatio.types.MLArray; @@ -33,6 +35,7 @@ import com.jmatio.types.MLChar; import com.jmatio.types.MLDouble; import com.jmatio.types.MLEmptyArray; +import com.jmatio.types.MLHandle; import com.jmatio.types.MLInt16; import com.jmatio.types.MLInt32; import com.jmatio.types.MLInt64; @@ -63,14 +66,23 @@ * Map content = mfr.getContent(); * * - * @see com.jmatio.io.MatFileFilter + * @see ca.mjdsystems.jmatio.io.MatFileFilter * @author Wojciech Gradkowski (wgradkowski@gmail.com) */ +/** + * @author Wojciech Gradkowski (wgradkowski@gmail.com) + * + */ public class MatFileReader { public static final int MEMORY_MAPPED_FILE = 1; public static final int DIRECT_BYTE_BUFFER = 2; public static final int HEAP_BYTE_BUFFER = 4; + /** + * Type of matlab mat file. + */ + private final MatFileType matType; /** * MAT-file header */ @@ -79,14 +91,18 @@ public class MatFileReader { * Container for red MLArrays */ Map data; - /** - * Tells how bytes are organized in the buffer. - */ - ByteOrder byteOrder; /** * Array name filter */ private MatFileFilter filter; + /** + * Whether or not we have found an MCOS type variable. Needed to know if further processing is needed. + */ + private Set mcosToFind = new HashSet(); + /** + * Holds the likely candidate for the MCOS extra data at the end of a MAT file. + */ + private MLUInt8 mcosData; /** * Creates instance of MatFileReader and reads MAT-file @@ -98,7 +114,7 @@ public class MatFileReader { * @throws IOException when error occurred while processing the file. */ public MatFileReader(String fileName) throws FileNotFoundException, IOException { - this(new File(fileName), new MatFileFilter()); + this(new File(fileName), new MatFileFilter(), MatFileType.Regular); } /** @@ -113,11 +129,11 @@ public MatFileReader(String fileName) throws FileNotFoundException, IOException * @throws IOException when error occurred while processing the file. */ public MatFileReader(String fileName, MatFileFilter filter) throws IOException { - this(new File(fileName), filter); + this(new File(fileName), filter, MatFileType.Regular); } /** - * Creates instance of MatFileReader and reads MAT-file + * Creates instance of MatFileReader and reads MAT-file * from file. * * This method reads MAT-file without filtering. @@ -126,7 +142,7 @@ public MatFileReader(String fileName, MatFileFilter filter) throws IOException { * @throws IOException when error occurred while processing the file. */ public MatFileReader(File file) throws IOException { - this(file, new MatFileFilter()); + this(file, new MatFileFilter(), MatFileType.Regular); } @@ -147,17 +163,66 @@ public MatFileReader(File file) throws IOException { * @throws IOException * when error occurred while processing the file. */ - public MatFileReader(File file, MatFileFilter filter) throws IOException { - this(); + public MatFileReader(File file, MatFileFilter filter, MatFileType matType) throws IOException { + this(matType); read(file, filter, MEMORY_MAPPED_FILE); } - public MatFileReader() { + public MatFileReader(File file, MatFileFilter filter) throws IOException { + this(file, filter, MatFileType.Regular); + } + + public MatFileReader(MatFileType matType) { + this.matType = matType; filter = new MatFileFilter(); data = new LinkedHashMap(); } + public MatFileReader() { + this(MatFileType.Regular); + } + + /** + * Creates instance of MatFileReader and reads MAT-file from + * file. + * + * This method reads MAT-file without filtering. + * + * @param stream + * the MAT-file stream + * @throws IOException + * when error occurred while processing the file. + */ + public MatFileReader(InputStream stream, MatFileType type) throws IOException { + this(stream, new MatFileFilter(), type); + } + + /** + * Creates instance of MatFileReader and reads MAT-file from + * file. + *

+ * Results are filtered by MatFileFilter. Arrays that do not + * meet filter match condition will not be available in results. + *

+ * Note: this method reads file using the memory mapped file policy, see + * notes to + * {@link #read(File, MatFileFilter, com.jmatio.io.MatFileReader.MallocPolicy)} + * + * + * @param stream + * the MAT-file stream + * @param MatFileFilter + * array name filter. + * @throws IOException + * when error occurred while processing the file. + */ + public MatFileReader(InputStream stream, MatFileFilter filter, MatFileType type) throws IOException { + this(type); + + read(stream, filter); + } + /** * Reads the content of a MAT-file and returns the mapped content. *

@@ -174,6 +239,21 @@ public synchronized Map read(File file) throws IOException { return read(file, new MatFileFilter(), MEMORY_MAPPED_FILE); } + /** + * Reads the content of a MAT-file and returns the mapped content. + *

+ * This method calls read(stream, new MatFileFilter()). + * + * @param stream + * a valid MAT-file stream to be read + * @return the same as {@link #getContent()} + * @throws IOException + * if error occurs during file processing + */ + public synchronized Map read(InputStream stream) throws IOException { + return read(stream, new MatFileFilter()); + } + /** * Reads the content of a MAT-file and returns the mapped content. *

@@ -222,6 +302,7 @@ public synchronized Map read(File file, int policy) throws IOEx * @throws IOException * if error occurs during file processing */ + private static final int DIRECT_BUFFER_LIMIT = 1 << 25; public synchronized Map read(File file, MatFileFilter filter, int policy) throws IOException { return read(new RandomAccessFile(file, "r"), filter, policy); @@ -230,14 +311,10 @@ public synchronized Map read(File file, MatFileFilter filter, i public synchronized Map read(RandomAccessFile raFile, MatFileFilter filter, int policy) throws IOException { this.filter = filter; - //clear the results - for (String key : data.keySet()) { - data.remove(key); - } + data.clear(); FileChannel roChannel = null; ByteBuffer buf = null; - WeakReference bufferWeakRef = null; try { //Create a read-only memory-mapped file roChannel = raFile.getChannel(); @@ -253,7 +330,6 @@ public synchronized Map read(RandomAccessFile raFile, MatFileFi break; case HEAP_BYTE_BUFFER: int filesize = (int) roChannel.size(); - System.gc(); buf = ByteBuffer.allocate(filesize); // The following two methods couldn't be used (at least under MS Windows) @@ -280,86 +356,287 @@ public synchronized Map read(RandomAccessFile raFile, MatFileFi break; case MEMORY_MAPPED_FILE: buf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int) roChannel.size()); - bufferWeakRef = new WeakReference((MappedByteBuffer) buf); break; default: throw new IllegalArgumentException("Unknown file allocation policy"); } - //read in file header - readHeader(buf); - - while (buf.remaining() > 0) { - readData(buf); - } + // Do the actual work. + parseData(buf); return getContent(); } catch (IOException e) { throw e; } finally { + if (buf != null && buf.isDirect()) { + // Forcefully unmap memory mapped buffer or direct buffer. This is a + // workaround for #4724038. + // Note that subsequent accesses to the buffer will crash the runtime, so it may + // only be applied to internal buffers. + Unsafe9R.invokeCleaner(buf); + } if (roChannel != null) { roChannel.close(); } if (raFile != null) { raFile.close(); } - if (buf != null && bufferWeakRef != null && policy == MEMORY_MAPPED_FILE) { - try { - clean(buf); - } catch (Exception e) { - int GC_TIMEOUT_MS = 1000; - buf = null; - long start = System.currentTimeMillis(); - while (bufferWeakRef.get() != null) { - if (System.currentTimeMillis() - start > GC_TIMEOUT_MS) { - break; //a hell cannot be unmapped - hopefully GC will - //do it's job later - } - System.gc(); - Thread.yield(); + } + } + + private void parseData(ByteBuffer buf) throws IOException { + //read in file header + readHeader(buf); + + while (buf.remaining() > 0) { + readData(buf); + } + if (!mcosToFind.isEmpty()) { + parseMCOS(mcosData, mcosToFind); + if (data.get("@") == mcosData) { + data.remove("@"); + } + for (Map.Entry it : data.entrySet()) { + if (it.getValue() == mcosData) { + data.remove(it.getKey()); + break; + } + } + } + mcosData = null; + mcosToFind.clear(); + } + + private static void parseMCOS(MLUInt8 mcosData, Set mcosPlaceholders) throws IOException { + // First, parse back out the mcosData. + ByteBuffer buffer = mcosData.getRealByteBuffer(); + ByteBufferInputStream dataStream = new ByteBufferInputStream(buffer, buffer.limit()); + + MatFileReader matFile = new MatFileReader(dataStream, MatFileType.ReducedHeader); + Map mcosContent = matFile.getContent(); + MLCell mcosInfo = (MLCell) ((MLStructure) mcosContent.get("@0")).getField("MCOS"); + ByteBuffer mcosDataBuf = ((MLUInt8) mcosInfo.get(0)).getRealByteBuffer(); + // This bytebuffer needs to be read in the byte order of the MAT file order. Thus fix. + mcosDataBuf.order(matFile.getMatFileHeader().getByteOrder()); + + // Parse out the data buffer. First get version information. Should always equal 2. + int version = mcosDataBuf.getInt(); + if (version != 2) { + throw new IllegalStateException("MAT file's MCOS data has a different version(?). Got: " + version + ", wanted 2."); + } + + // Get the string count + define the string array. + int strCount = mcosDataBuf.getInt(); + String[] strs = new String[strCount]; + + // Get the segment indexes. + int segmentIndexes[] = new int[6]; + for (int i = 0; i < segmentIndexes.length; ++i) { + segmentIndexes[i] = mcosDataBuf.getInt(); + } + + // There should now be 8 0 bytes. Make sure this is true to avoid object format changes. + if (mcosDataBuf.getLong() != 0) { + throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); + } + + // Finally, read in each string. Java doesn't provide an easy way to do this in bulk, so just use a stupid formula for now. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < strCount; ++i) { + sb.setLength(0); + char next = (char) mcosDataBuf.get(); + while (next != '\0') { + sb.append(next); + next = (char) mcosDataBuf.get(); + } + strs[i] = sb.toString(); + } + + // Sanity check, next 8 byte aligned position in the buffer should equal the start of the first segment! + if (((mcosDataBuf.position() + 0x07) & ~0x07) != segmentIndexes[0]) { + throw new IllegalStateException("Data from the strings section was not all read!"); + } + + // First segment, class information. Really just need the class names. + List classNamesList = new ArrayList(); + mcosDataBuf.position(segmentIndexes[0]); + // There are 16 unknown bytes. Ensure they are 0. + if (mcosDataBuf.getLong() != 0 || mcosDataBuf.getLong() != 0) { + throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); + } + while (mcosDataBuf.position() < segmentIndexes[1]) { + mcosDataBuf.getInt(); // packageNameIndex - Unused for now. + int classNameIndex = mcosDataBuf.getInt(); + String className = strs[classNameIndex - 1]; + classNamesList.add(className); + if (mcosDataBuf.getLong() != 0) { + throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); + } + } + + // Sanity check, position in the buffer should equal the start of the second segment! + if (mcosDataBuf.position() != segmentIndexes[1]) { + throw new IllegalStateException("Data from the class section was not all read!"); + } + + // @todo: Second segment, Object properties containing other properties. Not used yet, thus ignored. + mcosDataBuf.position(segmentIndexes[2]); + + // Third segment. Contains all the useful per-object information. + Map objectInfoList = new HashMap(); + // There are 24 unknown bytes. Ensure they are 0. + if (mcosDataBuf.getLong() != 0 || mcosDataBuf.getLong() != 0 || mcosDataBuf.getLong() != 0) { + throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); + } + int objectCount = 1; + while (mcosDataBuf.position() < segmentIndexes[3]) { + // First fetch the data. + int classIndex = mcosDataBuf.getInt(); + if (mcosDataBuf.getLong() != 0) { + throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); + } + int segment2Index = mcosDataBuf.getInt(); + int segment4Index = mcosDataBuf.getInt(); + mcosDataBuf.getInt(); // This value is random. But we need to move the buffer forward, so read it without a check. + int objectId = objectCount++; // It would appear that the "objectId" is in fact some other MATLAB value. Thus ignore, + // and use the index into this segment as the id instead. + + // Then parse it into the form needed for the object. + objectInfoList.put(objectId - 1, new MatMCOSObjectInformation(classNamesList.get(classIndex - 1), classIndex, objectId, segment2Index, segment4Index)); + } + + // Sanity check, position in the buffer should equal the start of the fourth segment! + if (mcosDataBuf.position() != segmentIndexes[3]) { + throw new IllegalStateException("Data from the object section was not all read! At: " + mcosDataBuf.position() + ", wanted: " + segmentIndexes[3]); + } + + // Fourth segment. Contains the regular properties for objects. + // There are 8 unknown bytes. Ensure they are 0. + if (mcosDataBuf.getLong() != 0) { + throw new IllegalStateException("MAT file's MCOS data has different byte values for unknown fields! Aborting!"); + } + List> segment4Properties = new ArrayList>(); + while (mcosDataBuf.position() < segmentIndexes[4]) { + Map properties = new HashMap(); + int propertiesCount = mcosDataBuf.getInt(); + for (int i = 0; i < propertiesCount; ++i) { + int nameIndex = mcosDataBuf.getInt(); + int flag = mcosDataBuf.getInt(); + int heapIndex = mcosDataBuf.getInt(); + + String propertyName = strs[nameIndex - 1]; + MLArray property; + switch (flag) { + case 0: + property = new MLChar(propertyName, strs[heapIndex - 1]); + break; + case 1: + property = mcosInfo.get(heapIndex + 2); + break; + case 2: + // @todo: Handle a boolean. + throw new UnsupportedOperationException("Mat file parsing does not yet support booleans!"); + default: + throw new UnsupportedOperationException("Don't yet support parameter type: " + flag + "!"); + } + if (property instanceof MLUInt32) { + int[][] data = ((MLUInt32) property).getArray(); + if (data[0][0] == 0xdd000000 && data[1][0] == 0x02) { + MLObjectPlaceholder objHolder = new MLObjectPlaceholder(propertyName, "", data); + mcosPlaceholders.add(objHolder); + property = objHolder; } } + properties.put(propertyName, property); } + segment4Properties.add(properties); + mcosDataBuf.position((mcosDataBuf.position() + 0x07) & ~0x07); + } + + // Sanity check, position in the buffer should equal the start of the fifth segment! + if (mcosDataBuf.position() != segmentIndexes[4]) { + throw new IllegalStateException("Data from the properties section (2) was not all read! At: " + mcosDataBuf.position() + ", wanted: " + segmentIndexes[4]); + } + + // Now merge in the properties from segment 4 into object. + for (MatMCOSObjectInformation it : objectInfoList.values()) { + Map objAttributes = it.structure; + if (it.segment4PropertiesIndex > 0) { + for (Map.Entry attribute : segment4Properties.get(it.segment4PropertiesIndex - 1).entrySet()) { + objAttributes.put(attribute.getKey(), attribute.getValue()); + } + } else { + throw new IllegalStateException("Properties are not found! Not sure where to look ..."); + } + } + + // Finally, merge in attributes from the global grab bag. + MLCell attribBag = (MLCell) mcosInfo.get(mcosInfo.getSize() - 1); // Get the grab bag. + for (MatMCOSObjectInformation it : objectInfoList.values()) { + MLStructure attributes = (MLStructure) attribBag.get(it.classId); + Collection attributeNames = attributes.getFieldNames(); + Map objAttributes = it.structure; + for (String attributeName : attributeNames) { + if (objAttributes.get(attributeName) == null) { + objAttributes.put(attributeName, attributes.getField(attributeName)); + } + } + } + + for (MLObjectPlaceholder placeholder : mcosPlaceholders) { + processMCOS(placeholder, classNamesList, objectInfoList); } } - private static final int DIRECT_BUFFER_LIMIT = 1 << 25; + private static void processMCOS(MLObjectPlaceholder objHolder, List classNamesList, Map objectInfoList) { + int classId = objHolder.classId; + MLObject obj = new MLObject(objHolder.name, classNamesList.get(classId - 1), objHolder.getDimensions(), 0); + for (int i = 0; i < obj.getSize(); ++i) { + MatMCOSObjectInformation objectInformation = objectInfoList.get(objHolder.objectIds[i] - 1); + if (classId != objectInformation.classId) { + throw new IllegalStateException("Found an object in array with a different class id! Actual: " + objectInformation.classId + ", expected: " + classId + "!"); + } + obj.setFields(i, objectInformation.structure); + } + objHolder.setTarget(obj); + } /** - * Workaround taken from bug #4724038 - * to release the memory mapped byte buffer. - *

- * Little quote from SUN: This is highly inadvisable, to put it mildly. - * It is exceedingly dangerous to forcibly unmap a mapped byte buffer that's - * visible to Java code. Doing so risks both the security and stability of - * the system - *

- * Since the memory byte buffer used to map the file is not exposed to the - * outside world, maybe it's save to use it without being cursed by the SUN. - * Since there is no other solution this will do (don't trust voodoo GC - * invocation) + * Read a mat file from a stream. Internally this will read the stream fully + * into memory before parsing it. * - * @param buffer - * the buffer to be unmapped - * @throws Exception - * all kind of evil stuff + * @param stream + * a valid MAT-file stream to be read + * @param filter + * the array filter applied during reading + * + * @return the same as {@link #getContent()} + * @see MatFileFilter + * @throws IOException + * if error occurs during file processing */ - private void clean(final Object buffer) throws Exception { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - try { - Method getCleanerMethod = buffer.getClass().getMethod( - "cleaner", new Class[0]); - getCleanerMethod.setAccessible(true); - sun.misc.Cleaner cleaner = (sun.misc.Cleaner) getCleanerMethod - .invoke(buffer, new Object[0]); - cleaner.clean(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - }); + public synchronized Map read(InputStream stream, MatFileFilter filter) throws IOException { + this.filter = filter; + + data.clear(); + + ByteBuffer buf = null; + + final ByteArrayOutputStream2 baos = new ByteArrayOutputStream2(); + copy(stream, baos); + buf = ByteBuffer.wrap(baos.getBuf(), 0, baos.getCount()); + + // Do the actual work + parseData(buf); + + return getContent(); + } + + private void copy(InputStream stream, ByteArrayOutputStream2 output) throws IOException { + final byte[] buffer = new byte[1024 * 4]; + int n = 0; + while (-1 != (n = stream.read(buffer))) { + output.write(buffer, 0, n); + } } /** @@ -424,7 +701,7 @@ void readData(ByteBuffer buf) throws IOException { ISMatTag tag = new ISMatTag(buf); switch (tag.type) { case MatDataTypes.miCOMPRESSED: - long numOfBytes = tag.size; + int numOfBytes = tag.size; //inflate and recur if (buf.remaining() < numOfBytes) { throw new MatlabIOException("Compressed buffer length miscalculated!"); @@ -436,28 +713,29 @@ void readData(ByteBuffer buf) throws IOException { //process data decompression byte[] result = new byte[1024]; - // use direct buffer allocation - // BufferedOutputStream dos = new FileBufferedOutputStream(); - BufferedOutputStream dos = new ByteBufferedOutputStream(tag.size, false); + HeapBufferDataOutputStream dos = new HeapBufferDataOutputStream(); + int i; + try { + do { + i = iis.read(result, 0, result.length); + int len = Math.max(0, i); + dos.write(result, 0, len); + } while (i > 0); + } catch (EOFException eofe) { + System.out.println("EOFException detected!"); + } catch (IOException e) { + throw new MatlabIOException("Could not decompress data: " + e); + } finally { + iis.close(); + dos.flush(); + } + //create a ByteBuffer from the deflated data + ByteBuffer out = dos.getByteBuffer(); + + //with proper byte ordering + out.order(matFileHeader.getByteOrder()); + try { - int i; - try { - do { - i = iis.read(result, 0, result.length); - int len = Math.max(0, i); - dos.write(result, 0, len); - } while (i > 0); - } catch (IOException e) { - throw new MatlabIOException("Could not decompress data: " + e); - } finally { - iis.close(); - dos.flush(); - } - //create a ByteBuffer from the deflated data - ByteBuffer out = dos.buffer(); - out.rewind(); - //with proper byte ordering - out.order(byteOrder); readData(out); } catch (IOException e) { throw e; @@ -466,30 +744,36 @@ void readData(ByteBuffer buf) throws IOException { } break; case MatDataTypes.miMATRIX: - //read in the matrix int pos = buf.position(); MLArray element = readMatrix(buf, true); - - if (element != null && !data.containsKey(element.getName())) { - data.put(element.getName(), element); + if (element != null) { + // Sometimes a MAT file will contain more than one unnamed + // element. This ensures that all of them will be accessible + // in the end result. + if (!data.containsKey(element.getName())) { + data.put(element.getName(), element); + } + if (element.getName() == MLArray.DEFAULT_NAME) { + // identity comparison is okay because we assigned it in the first place + int nextIndex = 0; + for (; data.containsKey("@" + nextIndex); nextIndex++) {} + data.put(MLArray.DEFAULT_NAME + nextIndex, element); + } } else { - int red = buf.position() - pos; - int toread = tag.size - red; - buf.position(buf.position() + toread); + int read = buf.position() - pos; + int toRead = tag.size - read; + buf.position(buf.position() + toRead); } - int red = buf.position() - pos; - - int toread = tag.size - red; - - if (toread != 0) { - throw new MatlabIOException("Matrix was not red fully! " + toread + " remaining in the buffer."); + int read = buf.position() - pos; + int toRead = tag.size - read; + if (toRead != 0) { + throw new MatlabIOException("Matrix was not read fully! " + toRead + " remaining in the buffer."); } break; default: throw new MatlabIOException("Incorrect data tag: " + tag); - } } @@ -528,7 +812,7 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { int[] dims = readDimension(buf); //read array Name - String name = readName(buf); + String name = readName(buf, matFileHeader); //if this array is filtered out return immediately if (isRoot && !filter.matches(name)) { @@ -539,7 +823,7 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { switch (type) { case MLArray.mxSTRUCT_CLASS: - MLStructure struct = new MLStructure(name, dims, type, attributes); + MLStructure struct = new MLStructure(name, dims, attributes); //field name lenght - this subelement always uses the compressed data element format new ISMatTag(buf); @@ -624,6 +908,12 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } + + // This might be the MCOS extra data. If there is no name, set it as the current set of data. + if (name.equals("")) { + mcosData = (MLUInt8) mlArray; + } + break; case MLArray.mxINT8_CLASS: mlArray = new MLInt8(name, dims, type, attributes); @@ -660,7 +950,6 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } - //System.out.println( new String( buf.array() ) ); break; case MLArray.mxINT32_CLASS: mlArray = new MLInt32(name, dims, type, attributes); @@ -716,7 +1005,7 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { //read real tag = new ISMatTag(buf); // char[] ac = tag.readToCharArray(); - String str = tag.readToString(); + String str = tag.readToString(matFileHeader.getByteOrder()); for (int i = 0; i < str.length(); i++) { mlchar.setChar(str.charAt(i), i); @@ -732,19 +1021,18 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { tag = new ISMatTag(buf); int[] jc = tag.readToIntArray(); - //read pr (real part) - tag = new ISMatTag(buf); - double[] ad1 = tag.readToDoubleArray(); - int count = 0; - for (int column = 0; column < sparse.getN(); column++) { - while (count < jc[column + 1]) { - sparse.setReal(ad1[count], ir[count], column); - count++; + if (sparse.isComplex()) { + //read pr (real part) + tag = new ISMatTag(buf); + double[] ad1 = tag.readToDoubleArray(); + int count = 0; + for (int column = 0; column < sparse.getN(); column++) { + while (count < jc[column + 1]) { + sparse.setReal(ad1[count], ir[count], column); + count++; + } } - } - //read pi (imaginary part) - if (sparse.isComplex()) { tag = new ISMatTag(buf); double[] ad2 = tag.readToDoubleArray(); @@ -755,6 +1043,17 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { count++; } } + } else { + //read pi (real part) + tag = new ISMatTag(buf); + double[] ad1 = tag.readToDoubleArray(); + int count = 0; + for (int column = 0; column < sparse.getN(); column++) { + while (count < jc[column + 1]) { + sparse.set(ad1[count], ir[count], column); + count++; + } + } } mlArray = sparse; break; @@ -763,10 +1062,7 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { //read class name tag = new ISMatTag(buf); // class name - String className = tag.readToString(); - // System.out.println( "Class name: " + className ); - // should be "java" - // System.out.println( "Array name: " + name ); + String className = tag.readToString(matFileHeader.getByteOrder()); // the stored array name // read array name stored in dims (!) @@ -775,40 +1071,64 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { nn[i] = (byte) dims[i]; } String arrName = new String(nn, MatDataTypes.CHARSET); - //System.out.println( "Array name: " + arrName ); // next tag should be miMatrix ISMatTag contentTag = new ISMatTag(buf); if (contentTag.type == MatDataTypes.miMATRIX) { - //should return UInt8 or UInt32, but MLNumericArray is the LCD - MLArray wrappedContent = readMatrix(buf, false); - - //our first job is to find the binary content - MLNumericArray binaryContent = null; - if (wrappedContent instanceof MLCell) { - //sometimes we'll get a cell array - //in that case, we'll take the first NumericArray we can find - MLCell cellContent = (MLCell) wrappedContent; - for (MLArray candidate : cellContent.cells()) { - if (candidate instanceof MLNumericArray) { - binaryContent = (MLNumericArray) candidate; - break; + if (name.equals("java")) { + //should return UInt8 or UInt32, but MLNumericArray is the LCD + MLArray wrappedContent = readMatrix(buf, false); + + //our first job is to find the binary content + MLNumericArray binaryContent = null; + if (wrappedContent instanceof MLCell) { + //sometimes we'll get a cell array + //in that case, we'll take the first NumericArray we can find + MLCell cellContent = (MLCell) wrappedContent; + for (MLArray candidate : cellContent.cells()) { + if (candidate instanceof MLNumericArray) { + binaryContent = (MLNumericArray) candidate; + break; + } } + } else if (wrappedContent instanceof MLNumericArray) { + binaryContent = (MLNumericArray) wrappedContent; + } else if (wrappedContent instanceof MLStructure) { + MLStructure structureContent = (MLStructure) wrappedContent; + MLCell cellContent = (MLCell) structureContent.getField("Values", 0); + binaryContent = (MLNumericArray) cellContent.get(0); + } else { + throw new IOException("Unexpected array type: " + wrappedContent.name); } - } else if (wrappedContent instanceof MLNumericArray) { - binaryContent = (MLNumericArray) wrappedContent; - } else if (wrappedContent instanceof MLStructure) { - MLStructure structureContent = (MLStructure) wrappedContent; - MLCell cellContent = (MLCell) structureContent.getField("Values", 0); - binaryContent = (MLNumericArray) cellContent.get(0); + + mlArray = new MLJavaObject(arrName, className, binaryContent); + } else if (name.equals("MCOS")) { + // FileWrapper__ is a special MATLAB internal name. Should never appear from users. + if (!className.equals("FileWrapper__")) { + MLUInt32 content = (MLUInt32) readMatrix(buf, false); + int[][] t = content.getArray(); + + // Check that the first four numbers are the same, as expected. + if (t[0][0] != 0xdd000000 || t[1][0] != 2) { + throw new IOException("MCOS per-object header was different then expected! Got: " + content.contentToString()); + } + + MLObjectPlaceholder placeholder = new MLObjectPlaceholder(arrName, className, t); + mcosToFind.add(placeholder); + mlArray = placeholder; + } else { + // This is where we get the useful MCOS data. Only used on FileWrapper__ classes. + mlArray = readMatrix(buf, false); + } + } else if (name.equals("handle")) { + MLCell wrappedContent = (MLCell) readMatrix(buf, true); + mlArray = new MLHandle(arrName, className, wrappedContent); } else { - throw new IOException("Unexpected array type: " + wrappedContent.name); + throw new IOException("Unknown object type (" + name + ") found."); } - - mlArray = new MLJavaObject(arrName, className, binaryContent); } else { - throw new IOException("Unexpected java object content"); + throw new IOException("Unexpected object content"); } break; case MLArray.mxOBJECT_CLASS: @@ -816,14 +1136,14 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { tag = new ISMatTag(buf); // class name - className = tag.readToString(); + className = tag.readToString(matFileHeader.getByteOrder()); // TODO: currently copy pasted from structure - struct = new MLStructure(name, dims, type, attributes); + mlArray = new MLObject(name, className, dims, attributes); //field name lenght - this subelement always uses the compressed data element format - new ISMatTag(buf); + tag = new ISMatTag(buf); maxlen = buf.getInt(); //maximum field length ////// read fields data as Int8 @@ -837,28 +1157,26 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { buf.get(names); fieldNames[i] = zeroEndByteArrayToString(names); } - buf.position(buf.position() + tag.padding); //read fields - for (int index = 0; index < 1; index++) { + for (int index = 0; index < mlArray.getM() * mlArray.getN(); index++) { for (int i = 0; i < numOfFields; i++) { //read matrix recursively tag = new ISMatTag(buf); + MLArray array; if (tag.size > 0) { - MLArray fieldValue = readMatrix(buf, false); - struct.setField(fieldNames[i], fieldValue, index); + array = readMatrix(buf, false); } else { - struct.setField(fieldNames[i], new MLEmptyArray(), index); + array = new MLEmptyArray(); } + array.name = fieldNames[i]; + ((MLObject) mlArray).setField(fieldNames[i], array, index); } } - - mlArray = new MLObject(name, className, struct); break; default: throw new MatlabIOException("Incorrect matlab array class: " + MLArray.typeToString(type)); - } return mlArray; } @@ -872,14 +1190,12 @@ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { * @return String retrieved from byte array. * @throws IOException if reading error occurred. */ - private String zeroEndByteArrayToString(byte[] bytes) throws IOException { + private static String zeroEndByteArrayToString(byte[] bytes) throws IOException { int i = 0; - - for (i = 0; i < bytes.length && bytes[i] != 0; i++) - ; - + while (i < bytes.length && bytes[i] != 0) { + ++i; + } return new String(bytes, 0, i, MatDataTypes.CHARSET); - } /** @@ -891,11 +1207,9 @@ private String zeroEndByteArrayToString(byte[] bytes) throws IOException { * @return flags int array * @throws IOException if reading from buffer fails */ - private int[] readFlags(ByteBuffer buf) throws IOException { + private static int[] readFlags(ByteBuffer buf) throws IOException { ISMatTag tag = new ISMatTag(buf); - int[] flags = tag.readToIntArray(); - return flags; } @@ -908,12 +1222,10 @@ private int[] readFlags(ByteBuffer buf) throws IOException { * @return dimensions int array * @throws IOException if reading from buffer fails */ - private int[] readDimension(ByteBuffer buf) throws IOException { - + private static int[] readDimension(ByteBuffer buf) throws IOException { ISMatTag tag = new ISMatTag(buf); int[] dims = tag.readToIntArray(); return dims; - } /** @@ -925,10 +1237,9 @@ private int[] readDimension(ByteBuffer buf) throws IOException { * @return name String * @throws IOException if reading from buffer fails */ - private String readName(ByteBuffer buf) throws IOException { + private static String readName(ByteBuffer buf, MatFileHeader header) throws IOException { ISMatTag tag = new ISMatTag(buf); - - return tag.readToString(); + return tag.readToString(header.getByteOrder()); } /** @@ -945,21 +1256,25 @@ private String readName(ByteBuffer buf) throws IOException { void readHeader(ByteBuffer buf) throws IOException { //header values String description; - int version; byte[] endianIndicator = new byte[2]; - //descriptive text 116 bytes - byte[] descriptionBuffer = new byte[116]; - buf.get(descriptionBuffer); + // This part of the header is missing if the file isn't a regular mat file. So ignore. + if (matType == MatFileType.Regular) { + //descriptive text 116 bytes + byte[] descriptionBuffer = new byte[116]; + buf.get(descriptionBuffer); - description = zeroEndByteArrayToString(descriptionBuffer); + description = zeroEndByteArrayToString(descriptionBuffer); - if (!description.matches("MATLAB 5.0 MAT-file.*")) { - throw new MatlabIOException("This is not a valid MATLAB 5.0 MAT-file."); - } + if (!description.matches("MATLAB 5.0 MAT-file.*")) { + throw new MatlabIOException("This is not a valid MATLAB 5.0 MAT-file."); + } - //subsyst data offset 8 bytes - buf.position(buf.position() + 8); + //subsyst data offset 8 bytes + buf.position(buf.position() + 8); + } else { + description = "Simulink generated MATLAB 5.0 MAT-file"; // Default simulink description. + } byte[] bversion = new byte[2]; //version 2 bytes @@ -968,19 +1283,12 @@ void readHeader(ByteBuffer buf) throws IOException { //endian indicator 2 bytes buf.get(endianIndicator); - //program reading the MAT-file must perform byte swapping to interpret the data - //in the MAT-file correctly - if ((char) endianIndicator[0] == 'I' && (char) endianIndicator[1] == 'M') { - byteOrder = ByteOrder.LITTLE_ENDIAN; - version = bversion[1] & 0xff | bversion[0] << 8; - } else { - byteOrder = ByteOrder.BIG_ENDIAN; - version = bversion[0] & 0xff | bversion[1] << 8; - } + matFileHeader = MatFileHeader.parseFrom(description, bversion, endianIndicator); + buf.order(matFileHeader.getByteOrder()); - buf.order(byteOrder); - - matFileHeader = new MatFileHeader(description, version, endianIndicator); + // After the header, the next read must be aligned. Thus force the alignment. Only matters with reduced header data, + // but apply it regardless for safety. + buf.position((buf.position() + 7) & 0xfffffff8); } /** @@ -1005,8 +1313,7 @@ public ISMatTag(ByteBuffer buf) throws IOException { type = tmp; size = buf.getInt(); compressed = false; - } else //data _packed_ in the tag (compressed) - { + } else { //data _packed_ in the tag (compressed) size = tmp >> 16; // 2 more significant bytes type = tmp & 0xffff; // 2 less significant bytes; compressed = true; @@ -1032,6 +1339,7 @@ public byte[] readToByteArray() throws IOException { //skip padding mfis.skip(padding); + return ab; } @@ -1045,6 +1353,7 @@ public double[] readToDoubleArray() throws IOException { } //skip padding + mfis.skip(padding); return ad; } @@ -1063,12 +1372,22 @@ public int[] readToIntArray() throws IOException { return ai; } - public String readToString() throws IOException { - // - byte[] bytes = readToByteArray(); - - return new String(bytes, "UTF-8"); + private String charset(ByteOrder byteOrder) { + switch (type) { + case MatDataTypes.miUTF8: + return "UTF-8"; + case MatDataTypes.miUTF16: + return byteOrder == ByteOrder.BIG_ENDIAN ? "UTF-16BE" : "UTF-16LE"; + case MatDataTypes.miUTF32: + return byteOrder == ByteOrder.BIG_ENDIAN ? "UTF-32BE" : "UTF-32LE"; + default: + return "US-ASCII"; + } + } + public String readToString(ByteOrder byteOrder) throws IOException { + byte[] bytes = readToByteArray(); + return new String(bytes, charset(byteOrder)); } } } diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatFileType.java b/src/main/java/com/jmatio/io/MatFileType.java similarity index 91% rename from src/main/java/ca/mjdsystems/jmatio/io/MatFileType.java rename to src/main/java/com/jmatio/io/MatFileType.java index 9923d4d..bdd6aa1 100644 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatFileType.java +++ b/src/main/java/com/jmatio/io/MatFileType.java @@ -3,7 +3,7 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.io; +package com.jmatio.io; /** Describes the type of Mat file. * @author Matthew Dawson diff --git a/src/main/java/com/jmatio/io/MatFileWriter.java b/src/main/java/com/jmatio/io/MatFileWriter.java index ad690ed..1a22106 100644 --- a/src/main/java/com/jmatio/io/MatFileWriter.java +++ b/src/main/java/com/jmatio/io/MatFileWriter.java @@ -13,7 +13,6 @@ import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.Collection; -import java.util.List; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; @@ -28,33 +27,62 @@ /** * MAT-file writer. - * + * * Usage: - *

+ *
+ * 
+ * 
  * //1. First create example arrays
  * double[] src = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
  * MLDouble mlDouble = new MLDouble( "double_arr", src, 3 );
  * MLChar mlChar = new MLChar( "char_arr", "I am dummy" );
- *         
+ *
  * //2. write arrays to file
  * ArrayList list = new ArrayList();
  * list.add( mlDouble );
  * list.add( mlChar );
- * 
+ *
  * new MatFileWriter( "mat_file.mat", list );
- * 
- * + *
+ *
+ * * this is "equal" to Matlab commands: - *

+ *
+ * 
+ * 
  * >> double_arr = [ 1 2; 3 4; 5 6];
  * >> char_arr = 'I am dummy';
  * >>
  * >> save('mat_file.mat', 'double_arr', 'char_arr');
- * 
- * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) + *
+ * + * + * + * @author Wojciech Gradkowski (wgradkowski@gmail.com) */ public class MatFileWriter { + + /** + * A hack stolen from Greg Wilkins of Mortbay. + * A {@link ByteArrayOutputStream} with revealed internals so there is no more wasteful + * copying when calling {@link ByteArrayOutputStream#toByteArray()} + * @author ss + * + */ + static class ByteArrayOutputStream2 extends ByteArrayOutputStream { + public ByteArrayOutputStream2() { + super(); + } + + public byte[] getBuf() { + return buf; + } + + public int getCount() { + return count; + } + } // private static final Logger logger = Logger.getLogger(MatFileWriter.class); /** @@ -66,7 +94,7 @@ public MatFileWriter() { /** * Writes MLArrays into file given by fileName. - * + * * @param fileName - name of ouput file * @param data - Collection of MLArray elements * @throws IOException @@ -78,22 +106,22 @@ public MatFileWriter(String fileName, Collection data) throws IOExcepti /** * Writes MLArrays into File. - * + * * @param file - an output File * @param data - Collection of MLArray elements * @throws IOException * @throws DataFormatException */ - @SuppressWarnings("resource") // the channel is closed after writing + @SuppressWarnings("resource") public MatFileWriter(File file, Collection data) throws IOException { this((new FileOutputStream(file)).getChannel(), data); } /** * Writes MLArrays into OuputSteram. - * + * * Writes MAT-file header and compressed data (miCOMPRESSED). - * + * * @param output - OutputStream * @param data - Collection of MLArray elements * @throws IOException @@ -105,7 +133,7 @@ public MatFileWriter(WritableByteChannel channel, Collection data) thro /** * Writes MLArrays into file created from * filepath. - * + * * @param filepath * the absolute file path of a MAT-file to which data is written * @param data @@ -120,7 +148,7 @@ public synchronized void write(String filepath, Collection data) /** * Writes MLArrays into File - * + * * @param file * the MAT-file to which data is written * @param data @@ -143,7 +171,7 @@ public synchronized void write(File file, Collection data) /** * Writes MLArrays into WritableByteChannel. - * + * * @param channel * the channel to write to * @param data @@ -159,31 +187,24 @@ private synchronized void write(WritableByteChannel channel, //write data for (MLArray matrix : data) { - //prepare buffer for MATRIX data - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - //write MATRIX bytes into buffer - writeMatrix(dos, matrix); - //compress data to save storage Deflater compresser = new Deflater(); - byte[] input = baos.toByteArray(); - - ByteArrayOutputStream compressed = new ByteArrayOutputStream(); + ByteArrayOutputStream2 compressed = new ByteArrayOutputStream2(); DataOutputStream dout = new DataOutputStream(new DeflaterOutputStream(compressed, compresser)); - dout.write(input); - + writeMatrix(dout, matrix); + dout.flush(); dout.close(); - compressed.close(); //write COMPRESSED tag and compressed data into output channel - byte[] compressedBytes = compressed.toByteArray(); - ByteBuffer buf = ByteBuffer.allocateDirect(2 * 4 /* Int size */ + compressedBytes.length); + + int compressedSize = compressed.getCount(); + ByteBuffer buf = ByteBuffer.allocateDirect(2 * 4 /* Int size */ + compressedSize); buf.putInt(MatDataTypes.miCOMPRESSED); - buf.putInt(compressedBytes.length); - buf.put(compressedBytes); + + buf.putInt(compressedSize); + buf.put(compressed.getBuf(), 0, compressedSize); buf.flip(); channel.write(buf); @@ -230,7 +251,7 @@ private void writeHeader(WritableByteChannel channel) throws IOException { /** * Writes MATRIX into OutputStream. - * + * * @param os - OutputStream * @param array - a MLArray * @throws IOException @@ -256,9 +277,9 @@ private void writeMatrix(DataOutputStream output, MLArray array) throws IOExcept //write char data buffer = new ByteArrayOutputStream(); bufferDOS = new DataOutputStream(buffer); - List ac = ((MLChar) array).exportChar(); - for (int i = 0; i < ac.size(); i++) { - String temp = new StringBuffer().append(ac.get(i).charValue()).toString(); + Character[] ac = ((MLChar) array).exportChar(); + for (int i = 0; i < ac.length; i++) { + String temp = new StringBuffer().append(ac[i].charValue()).toString(); bufferDOS.write(temp.getBytes("UTF-8")); } tag = new OSArrayTag(MatDataTypes.miUTF8, buffer.toByteArray()); @@ -330,6 +351,17 @@ private void writeMatrix(DataOutputStream output, MLArray array) throws IOExcept tag.writeTo(dos); } break; + case MLArray.mxINT32_CLASS: + tag = new OSArrayTag(MatDataTypes.miINT32, + ((MLNumericArray) array).getRealByteBuffer()); + tag.writeTo(dos); + + if (array.isComplex()) { + tag = new OSArrayTag(MatDataTypes.miINT32, + ((MLNumericArray) array).getImaginaryByteBuffer()); + tag.writeTo(dos); + } + break; case MLArray.mxINT64_CLASS: tag = new OSArrayTag(MatDataTypes.miINT64, @@ -378,6 +410,7 @@ private void writeMatrix(DataOutputStream output, MLArray array) throws IOExcept case MLArray.mxSPARSE_CLASS: int[] ai; //write ir + buffer = new ByteArrayOutputStream(); bufferDOS = new DataOutputStream(buffer); ai = ((MLSparse) array).getIR(); @@ -432,7 +465,7 @@ private void writeMatrix(DataOutputStream output, MLArray array) throws IOExcept /** * Writes MATRIX flags into OutputStream. - * + * * @param os - OutputStream * @param array - a MLArray * @throws IOException @@ -455,7 +488,7 @@ private void writeFlags(DataOutputStream os, MLArray array) throws IOException { /** * Writes MATRIX dimensions into OutputStream. - * + * * @param os - OutputStream * @param array - a MLArray * @throws IOException @@ -475,7 +508,7 @@ private void writeDimensions(DataOutputStream os, MLArray array) throws IOExcept /** * Writes MATRIX name into OutputStream. - * + * * @param os - OutputStream * @param array - a MLArray * @throws IOException @@ -483,9 +516,11 @@ private void writeDimensions(DataOutputStream os, MLArray array) throws IOExcept private void writeName(DataOutputStream os, MLArray array) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); DataOutputStream bufferDOS = new DataOutputStream(buffer); + byte[] nameByteArray = array.getNameToByteArray(); bufferDOS.write(nameByteArray); OSArrayTag tag = new OSArrayTag(MatDataTypes.miINT8, buffer.toByteArray()); tag.writeTo(os); } + } diff --git a/src/main/java/ca/mjdsystems/jmatio/io/MatMCOSObjectInformation.java b/src/main/java/com/jmatio/io/MatMCOSObjectInformation.java similarity index 87% rename from src/main/java/ca/mjdsystems/jmatio/io/MatMCOSObjectInformation.java rename to src/main/java/com/jmatio/io/MatMCOSObjectInformation.java index 6dba3b1..8e4d221 100644 --- a/src/main/java/ca/mjdsystems/jmatio/io/MatMCOSObjectInformation.java +++ b/src/main/java/com/jmatio/io/MatMCOSObjectInformation.java @@ -3,13 +3,12 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.io; +package com.jmatio.io; import java.util.HashMap; import java.util.Map; -import ca.mjdsystems.jmatio.types.MLArray; -import ca.mjdsystems.jmatio.types.MLStructure; +import com.jmatio.types.MLArray; /** * diff --git a/src/main/java/ca/mjdsystems/jmatio/io/SimulinkDecoder.java b/src/main/java/com/jmatio/io/SimulinkDecoder.java similarity index 82% rename from src/main/java/ca/mjdsystems/jmatio/io/SimulinkDecoder.java rename to src/main/java/com/jmatio/io/SimulinkDecoder.java index ba062a7..48cc3ca 100644 --- a/src/main/java/ca/mjdsystems/jmatio/io/SimulinkDecoder.java +++ b/src/main/java/com/jmatio/io/SimulinkDecoder.java @@ -3,23 +3,29 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.io; +package com.jmatio.io; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; +import com.jmatio.common.MatDataTypes; + /** This class decodes the Simulink base64 representation of a MAT file. * * @author Matthew Dawson */ public class SimulinkDecoder extends InputStream { public SimulinkDecoder(String input) { - this.input = input.getBytes(); - for (byte i = 0; i < 64; ++i) { - decoderRing.put((byte) (i + ' '), i); + try { + this.input = input.getBytes(MatDataTypes.CHARSET); + for (byte i = 0; i < 64; ++i) { + decoderRing.put((byte) (i + ' '), i); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); } } diff --git a/src/main/java/com/jmatio/io/Unsafe9R.java b/src/main/java/com/jmatio/io/Unsafe9R.java new file mode 100644 index 0000000..432e3d4 --- /dev/null +++ b/src/main/java/com/jmatio/io/Unsafe9R.java @@ -0,0 +1,129 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.io; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + +import sun.misc.Unsafe; + +/** + * Provides reflective access to new methods that were added to sun.misc.Unsafe + * in Java 9. Earlier platforms will fall back to a a behavior-equivalent + * implementation that uses available operations. + *

+ * ========== JDK Warning: sun.misc.Unsafe ========== + *

+ * A collection of methods for performing low-level, unsafe operations. + * Although the class and all methods are public, use of this class is + * limited because only trusted code can obtain instances of it. + * + * Note: It is the resposibility of the caller to make sure + * arguments are checked before methods of this class are + * called. While some rudimentary checks are performed on the input, + * the checks are best effort and when performance is an overriding + * priority, as when methods of this class are optimized by the + * runtime compiler, some or all checks (if any) may be elided. Hence, + * the caller must not rely on the checks and corresponding + * exceptions! + * + * @author Florian Enner + */ +class Unsafe9R { + + /** + * Invokes the given direct byte buffer's cleaner, if any. + * + * @param directBuffer a direct byte buffer + * @throws NullPointerException if {@code directBuffer} is null + * @throws IllegalArgumentException if {@code directBuffer} is non-direct + * @throws IllegalArgumentException if {@code directBuffer} is slice or duplicate (Java 9+ only) + */ + public static void invokeCleaner(ByteBuffer directBuffer) { + if (!directBuffer.isDirect()) + throw new IllegalArgumentException("buffer is non-direct"); + if (useJava9) + Java9.invokeCleaner(directBuffer); + else { + Java6.invokeCleaner(directBuffer); + } + } + + static { + + // Get Java version + String version = System.getProperty("java.specification.version", "6"); + String majorVersion = version.startsWith("1.") ? version.substring(2) : version; + useJava9 = Integer.parseInt(majorVersion) >= 9; + + } + + private static final boolean useJava9; + + private static class Java9 { + + static void invokeCleaner(ByteBuffer buffer) { + try { + INVOKE_CLEANER.invoke(UNSAFE, buffer); + } catch (Exception e) { + throw new IllegalStateException("Java 9 Cleaner failed to free DirectBuffer", e); + } + } + + static final Unsafe UNSAFE; + static final Method INVOKE_CLEANER; + + static { + try { + final PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + @Override + public Unsafe run() throws Exception { + final Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } + }; + UNSAFE = AccessController.doPrivileged(action); + INVOKE_CLEANER = UNSAFE.getClass().getMethod("invokeCleaner", ByteBuffer.class); + + } catch (final Exception ex) { + throw new IllegalStateException("Java 9 Cleaner not available", ex); + } + + } + + } + + private static class Java6 { + + static void invokeCleaner(ByteBuffer buffer) { + try { + Object cleaner = GET_CLEANER.invoke(buffer); + if (cleaner != null) + INVOKE_CLEANER.invoke(cleaner); + } catch (Exception e) { + throw new IllegalStateException("Java 6 Cleaner failed to free DirectBuffer", e); + } + } + + static { + try { + GET_CLEANER = Class.forName("sun.nio.ch.DirectBuffer").getMethod("cleaner"); + INVOKE_CLEANER = Class.forName("sun.misc.Cleaner").getMethod("clean"); + } catch (Exception e) { + throw new IllegalStateException("Java 6 Cleaner not available", e); + } + } + + static final Method GET_CLEANER; + static final Method INVOKE_CLEANER; + + } + +} diff --git a/src/main/java/com/jmatio/io/stream/ByteBufferInputStream.java b/src/main/java/com/jmatio/io/stream/ByteBufferInputStream.java index 92d0902..4b5f6b2 100644 --- a/src/main/java/com/jmatio/io/stream/ByteBufferInputStream.java +++ b/src/main/java/com/jmatio/io/stream/ByteBufferInputStream.java @@ -12,9 +12,9 @@ public class ByteBufferInputStream extends InputStream { private ByteBuffer buf; - private long limit; + private int limit; - public ByteBufferInputStream(final ByteBuffer buf, final long limit) { + public ByteBufferInputStream(final ByteBuffer buf, final int limit) { this.buf = buf; this.limit = limit; } @@ -34,7 +34,7 @@ public synchronized int read(byte[] bytes, int off, int len) if (!(limit > 0)) { return -1; } - len = (int) Math.min(len, limit); + len = Math.min(len, limit); // Read only what's left buf.get(bytes, off, len); limit -= len; diff --git a/src/main/java/com/jmatio/io/stream/ByteBufferedOutputStream.java b/src/main/java/com/jmatio/io/stream/ByteBufferedOutputStream.java deleted file mode 100644 index ae19415..0000000 --- a/src/main/java/com/jmatio/io/stream/ByteBufferedOutputStream.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package com.jmatio.io.stream; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * From https://gist.github.com/manzke/985007 - * - */ -public class ByteBufferedOutputStream extends BufferedOutputStream { - private ByteBuffer buffer; - - private boolean onHeap; - - private float increasing = DEFAULT_INCREASING_FACTOR; - - public static final float DEFAULT_INCREASING_FACTOR = 1.5f; - - public ByteBufferedOutputStream(int size) { - this(size, DEFAULT_INCREASING_FACTOR, false); - } - - public ByteBufferedOutputStream(int size, boolean onHeap) { - this(size, DEFAULT_INCREASING_FACTOR, onHeap); - } - - public ByteBufferedOutputStream(int size, float increasingBy) { - this(size, increasingBy, false); - } - - public ByteBufferedOutputStream(int size, float increasingBy, boolean onHeap) { - if (increasingBy <= 1) { - throw new IllegalArgumentException("Increasing Factor must be greater than 1.0"); - } - if (onHeap) { - buffer = ByteBuffer.allocate(size); - } else { - buffer = ByteBuffer.allocateDirect(size); - } - this.onHeap = onHeap; - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - int position = buffer.position(); - int limit = buffer.limit(); - - long newTotal = position + len; - if (newTotal > limit) { - int capacity = (int) (buffer.capacity() * increasing); - while (capacity <= newTotal) { - capacity = (int) (capacity * increasing); - } - - increase(capacity); - } - - buffer.put(b, 0, len); - } - - @Override - public void write(int b) throws IOException { - if (!buffer.hasRemaining()) { - increase((int) (buffer.capacity() * increasing)); - } - buffer.put((byte) b); - } - - protected void increase(int newCapacity) { - buffer.limit(buffer.position()); - buffer.rewind(); - - ByteBuffer newBuffer; - if (onHeap) { - newBuffer = ByteBuffer.allocate(newCapacity); - } else { - newBuffer = ByteBuffer.allocateDirect(newCapacity); - } - - newBuffer.put(buffer); - buffer.clear(); - buffer = newBuffer; - } - - @Override - public long size() { - return buffer.position(); - } - - public long capacity() { - return buffer.capacity(); - } - - public ByteBuffer buffer() { - return buffer; - } -} diff --git a/src/main/java/com/jmatio/io/stream/DataOutputStream.java b/src/main/java/com/jmatio/io/stream/DataOutputStream.java index a3fd5e0..34dd886 100644 --- a/src/main/java/com/jmatio/io/stream/DataOutputStream.java +++ b/src/main/java/com/jmatio/io/stream/DataOutputStream.java @@ -25,7 +25,7 @@ interface DataOutputStream { * @return the {@link ByteBuffer} * @throws IOException */ - public abstract ByteBuffer buffer() throws IOException; + public abstract ByteBuffer getByteBuffer() throws IOException; /** * Writes a sequence of bytes to this stream from the given buffer. diff --git a/src/main/java/com/jmatio/io/stream/FileBufferedOutputStream.java b/src/main/java/com/jmatio/io/stream/FileBufferedOutputStream.java deleted file mode 100644 index af83f19..0000000 --- a/src/main/java/com/jmatio/io/stream/FileBufferedOutputStream.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package com.jmatio.io.stream; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; - -import com.jmatio.types.MLArray; - -/** - * This is an {@link OutputStream} that is backed by a {@link RandomAccessFile} - * and accessed with buffered access. - * - * @author Wojciech Gradkowski (wgradkowski@gmail.com) - * - */ -public class FileBufferedOutputStream extends BufferedOutputStream { - private static final int BUFFER_SIZE = 1024; - private ByteBuffer buf; - private FileChannel rwChannel; - private RandomAccessFile raFile; - private final File file; - - public FileBufferedOutputStream() throws IOException { - file = File.createTempFile("jmatio-", null); - file.deleteOnExit(); - raFile = new RandomAccessFile(file, "rw"); - rwChannel = raFile.getChannel(); - buf = ByteBuffer.allocate(BUFFER_SIZE); - } - - public FileBufferedOutputStream(MLArray array) throws IOException { - file = File.createTempFile("jmatio-" + array.getName() + "-", null); - file.deleteOnExit(); - raFile = new RandomAccessFile(file, "rw"); - rwChannel = raFile.getChannel(); - buf = ByteBuffer.allocate(BUFFER_SIZE); - } - - @Override - public void write(int b) throws IOException { - if (buf.position() >= buf.capacity()) { - flush(); - } - - buf.put((byte) (b & 0xff)); - } - - /* (non-Javadoc) - * @see java.io.OutputStream#write(byte[]) - */ - @Override - public void write(byte[] b) throws IOException { - write(b, 0, b.length); - } - - /* (non-Javadoc) - * @see java.io.OutputStream#write(byte[], int, int) - */ - @Override - public void write(byte[] b, int off, int len) throws IOException { - int wbytes = len; - int offset = off; - - while (wbytes > 0) { - if (buf.position() >= buf.capacity()) { - flush(); - } - - int length = Math.min(wbytes, buf.limit() - buf.position()); - - buf.put(b, offset, length); - - offset += length; - wbytes -= length; - } - } - - /* (non-Javadoc) - * @see java.io.OutputStream#close() - */ - @Override - public void close() throws IOException { - flush(); - - buf = null; - - if (rwChannel.isOpen()) { - - rwChannel.close(); - } - - raFile.close(); - rwChannel = null; - raFile = null; - } - - /* (non-Javadoc) - * @see java.io.OutputStream#flush() - */ - @Override - public void flush() throws IOException { - if (buf != null && buf.position() > 0) { - buf.flip(); - rwChannel.write(buf); - buf.clear(); - } - } - - /* (non-Javadoc) - * @see com.jmatio.io.DataOutputStream#size() - */ - public long size() throws IOException { - flush(); - - return (int) file.length(); - } - - /* (non-Javadoc) - * @see com.jmatio.io.DataOutputStream#getByteBuffer() - */ - @Override - public ByteBuffer buffer() throws IOException { - return rwChannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); - } - - /* (non-Javadoc) - * @see com.jmatio.io.DataOutputStream#write(java.nio.ByteBuffer) - */ - public void write(ByteBuffer byteBuffer) throws IOException { - byte[] tmp = new byte[BUFFER_SIZE]; - - while (byteBuffer.hasRemaining()) { - int length = Math.min(byteBuffer.remaining(), tmp.length); - byteBuffer.get(tmp, 0, length); - write(tmp, 0, length); - } - } - -} diff --git a/src/main/java/ca/mjdsystems/jmatio/io/HeapBufferDataOutputStream.java b/src/main/java/com/jmatio/io/stream/HeapBufferDataOutputStream.java similarity index 90% rename from src/main/java/ca/mjdsystems/jmatio/io/HeapBufferDataOutputStream.java rename to src/main/java/com/jmatio/io/stream/HeapBufferDataOutputStream.java index 84a8d96..e8cd78e 100644 --- a/src/main/java/ca/mjdsystems/jmatio/io/HeapBufferDataOutputStream.java +++ b/src/main/java/com/jmatio/io/stream/HeapBufferDataOutputStream.java @@ -3,14 +3,14 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.io; +package com.jmatio.io.stream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; public class HeapBufferDataOutputStream extends ByteArrayOutputStream implements DataOutputStream { - private final int BUFFER_SIZE = 1024; + private static final int BUFFER_SIZE = 1024; public ByteBuffer getByteBuffer() throws IOException { return ByteBuffer.wrap(super.buf); diff --git a/src/main/java/com/jmatio/io/stream/MatFileInputStream.java b/src/main/java/com/jmatio/io/stream/MatFileInputStream.java index 9aaf64e..83704cc 100644 --- a/src/main/java/com/jmatio/io/stream/MatFileInputStream.java +++ b/src/main/java/com/jmatio/io/stream/MatFileInputStream.java @@ -139,6 +139,8 @@ public byte readByte() { case MatDataTypes.miDOUBLE: return (byte) buf.getDouble(); case MatDataTypes.miUTF8: + case MatDataTypes.miUTF16: + case MatDataTypes.miUTF32: return (byte) buf.get(); default: throw new IllegalArgumentException("Unknown data type: " + type); diff --git a/src/main/java/com/jmatio/types/MLArray.java b/src/main/java/com/jmatio/types/MLArray.java index f7658d5..a7d2e6e 100644 --- a/src/main/java/com/jmatio/types/MLArray.java +++ b/src/main/java/com/jmatio/types/MLArray.java @@ -6,6 +6,9 @@ package com.jmatio.types; import java.io.UnsupportedEncodingException; +import java.util.Arrays; + +import javax.annotation.Nonnull; import com.jmatio.common.MatDataTypes; @@ -36,23 +39,62 @@ public class MLArray { public static final int mtFLAG_LOGICAL = 0x0200; public static final int mtFLAG_TYPE = 0xff; - protected int dims[]; + @Nonnull + protected final int dims[]; + @Nonnull public String name; - protected int attributes; - protected int type; + protected final int attributes; + protected final int type; + private final int dimStrides[]; // Used to convert multidimensional index to linear index + + public static final String DEFAULT_NAME = "@"; public MLArray(String name, int[] dims, int type, int attributes) { - this.dims = new int[dims.length]; - System.arraycopy(dims, 0, this.dims, 0, dims.length); + this.dims = Arrays.copyOf(dims, dims.length); if (name != null && !name.equals("")) { this.name = name; } else { - this.name = "@"; //default name + this.name = DEFAULT_NAME; //default name } this.type = type; this.attributes = attributes; + + // the indices are processed in order from 0 to max in order to match Matlab's + // column major ordering of storage in the .mat file + dimStrides = new int[dims.length]; + for (int dimIx = 0, f = 1; dimIx < dims.length; dimIx++) { + dimStrides[dimIx] = f; + f *= dims[dimIx]; + } + } + + /** + * Returns the one-dim index for the multi-dimensional indexes. Compatible with matlab multi-dimensional indexing. + * + * Note: this performs the same logical function as getIndex, but the indices are computed in column major order + * for compatibility with .mat files generated by Matlab. + * + * @param indexes Length must be same as number of dimensions. Element value must be >= 0 and < dimension size for the corresponding dimension. + * @return The linear index + */ + public int getIndex(int... indexes) { + if (indexes.length != dims.length) { + throw new IllegalArgumentException("Cannot use " + indexes.length + " indexes for " + dims.length + " dimensions."); + } + int ix = 0; + for (int dimIx = 0; dimIx < indexes.length; dimIx++) { + ix += dimStrides[dimIx] * validateDimSize(dimIx, indexes[dimIx]); + } + return ix; + } + + private int validateDimSize(int dimIx, int ixInDim) { + if (dims[dimIx] > ixInDim) { + return ixInDim; + } + throw new IllegalArgumentException("Index " + ixInDim + " does not exist for dimension " + dimIx); } /** @@ -65,9 +107,7 @@ public String getName() { } public int getFlags() { - int flags = type & mtFLAG_TYPE | attributes & 0xffffff00; - - return flags; + return type & mtFLAG_TYPE | attributes & 0xffffff00; } public byte[] getNameToByteArray() { @@ -79,43 +119,23 @@ public byte[] getNameToByteArray() { } public int[] getDimensions() { - int ai[] = null; - if (dims != null) { - ai = new int[dims.length]; - System.arraycopy(dims, 0, ai, 0, dims.length); - } - return ai; + return Arrays.copyOf(dims, dims.length); } public int getM() { - int i = 0; - if (dims != null) { - i = dims[0]; - } - return i; + return dims[0]; } public int getN() { - int i = 0; - if (dims != null) { - if (dims.length > 2) { - i = 1; - for (int j = 1; j < dims.length; j++) { - i *= dims[j]; - } - } else { - i = dims[1]; - } + int i = dims[1]; + for (int j = 2; j < dims.length; j++) { + i *= dims[j]; } return i; } public int getNDimensions() { - int i = 0; - if (dims != null) { - i = dims.length; - } - return i; + return dims.length; } public int getSize() { @@ -132,65 +152,29 @@ public boolean isEmpty() { public static final String typeToString(int type) { String s; + // @formatter:off switch (type) { - case mxUNKNOWN_CLASS: - s = "unknown"; - break; - case mxCELL_CLASS: - s = "cell"; - break; - case mxSTRUCT_CLASS: - s = "struct"; - break; - case mxCHAR_CLASS: - s = "char"; - break; - case mxSPARSE_CLASS: - s = "sparse"; - break; - case mxDOUBLE_CLASS: - s = "double"; - break; - case mxSINGLE_CLASS: - s = "single"; - break; - case mxINT8_CLASS: - s = "int8"; - break; - case mxUINT8_CLASS: - s = "uint8"; - break; - case mxINT16_CLASS: - s = "int16"; - break; - case mxUINT16_CLASS: - s = "uint16"; - break; - case mxINT32_CLASS: - s = "int32"; - break; - case mxUINT32_CLASS: - s = "uint32"; - break; - case mxINT64_CLASS: - s = "int64"; - break; - case mxUINT64_CLASS: - s = "uint64"; - break; - case mxFUNCTION_CLASS: - s = "function_handle"; - break; - case mxOPAQUE_CLASS: - s = "opaque"; - break; - case mxOBJECT_CLASS: - s = "object"; - break; - default: - s = "unknown"; - break; + case mxUNKNOWN_CLASS: s = "unknown"; break; + case mxCELL_CLASS: s = "cell"; break; + case mxSTRUCT_CLASS: s = "struct"; break; + case mxCHAR_CLASS: s = "char"; break; + case mxSPARSE_CLASS: s = "sparse"; break; + case mxDOUBLE_CLASS: s = "double"; break; + case mxSINGLE_CLASS: s = "single"; break; + case mxINT8_CLASS: s = "int8"; break; + case mxUINT8_CLASS: s = "uint8"; break; + case mxINT16_CLASS: s = "int16"; break; + case mxUINT16_CLASS: s = "uint16"; break; + case mxINT32_CLASS: s = "int32"; break; + case mxUINT32_CLASS: s = "uint32"; break; + case mxINT64_CLASS: s = "int64"; break; + case mxUINT64_CLASS: s = "uint64"; break; + case mxFUNCTION_CLASS: s = "function_handle"; break; + case mxOPAQUE_CLASS: s = "opaque"; break; + case mxOBJECT_CLASS: s = "object"; break; + default: s = "unknown"; break; } + // @formatter:on return s; } @@ -278,41 +262,38 @@ protected int getIndex(int m, int n) { return m + n * getM(); } + @Override public String toString() { - StringBuffer sb = new StringBuffer(); - if (dims != null) { - sb.append('['); - if (dims.length > 3) { - sb.append(dims.length); - sb.append('D'); - } else { - sb.append(dims[0]); + StringBuilder sb = new StringBuilder(); + sb.append('['); + if (dims.length > 3) { + sb.append(dims.length); + sb.append('D'); + } else { + sb.append(dims[0]); + sb.append('x'); + sb.append(dims[1]); + if (dims.length == 3) { sb.append('x'); - sb.append(dims[1]); - if (dims.length == 3) { - sb.append('x'); - sb.append(dims[2]); - } + sb.append(dims[2]); } - sb.append(" "); - sb.append(typeToString(type)); - if (isLogical()) { - sb.append(" (logical)"); - } - sb.append(" array"); - if (isSparse()) { - sb.append(" (sparse"); - if (isComplex()) { - sb.append(" complex"); - } - sb.append(")"); - } else if (isComplex()) { - sb.append(" (complex)"); + } + sb.append(" "); + sb.append(typeToString(type)); + if (isLogical()) { + sb.append(" (logical)"); + } + sb.append(" array"); + if (isSparse()) { + sb.append(" (sparse"); + if (isComplex()) { + sb.append(" complex"); } - sb.append(']'); - } else { - sb.append("[invalid]"); + sb.append(")"); + } else if (isComplex()) { + sb.append(" (complex)"); } + sb.append(']'); return sb.toString(); } @@ -320,8 +301,6 @@ public String contentToString() { return "content cannot be displayed"; } - public void dispose() { - - } - + /** Clears any memory used by this MLArray. The array cannot be used after this has been called. */ + public void dispose() {} } diff --git a/src/main/java/com/jmatio/types/MLChar.java b/src/main/java/com/jmatio/types/MLChar.java index f791c59..bc85d2f 100644 --- a/src/main/java/com/jmatio/types/MLChar.java +++ b/src/main/java/com/jmatio/types/MLChar.java @@ -6,7 +6,8 @@ package com.jmatio.types; import java.util.Arrays; -import java.util.List; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class MLChar extends MLArray { Character[] chars; @@ -72,11 +73,7 @@ public MLChar(String name, String[] values, int maxlen) { public MLChar(String name, int[] dims, int type, int attributes) { super(name, dims, type, attributes); - chars = createArray(getM(), getN()); - } - - protected Character[] createArray(int m, int n) { - return new Character[m * n]; + chars = new Character[getM() * getN()]; } public void setChar(char ch, int index) { @@ -102,7 +99,6 @@ public void set(String value) { */ public void set(String value, int idx) { int rowOffset = getM(); - for (int i = 0; i < getN(); i++) { if (i < value.length()) { setChar(value.charAt(i), idx + (rowOffset * i)); @@ -116,8 +112,9 @@ public Character getChar(int m, int n) { return chars[getIndex(m, n)]; } - public List exportChar() { - return Arrays.asList(chars); + @SuppressFBWarnings(value = {"EI_EXPOSE_REP"}, justification = "This code is unlikely to be used in a security-sensitive environment.") + public Character[] exportChar() { + return chars; } @Override diff --git a/src/main/java/com/jmatio/types/MLDouble.java b/src/main/java/com/jmatio/types/MLDouble.java index afb0d1c..944c370 100644 --- a/src/main/java/com/jmatio/types/MLDouble.java +++ b/src/main/java/com/jmatio/types/MLDouble.java @@ -74,6 +74,13 @@ public MLDouble(String name, double[] vals, int m) { this(name, castToDouble(vals), m); } + /* (non-Javadoc) + * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) + */ + public Double[] createArray(int m, int n) { + return new Double[m * n]; + } + /** * Gets two-dimensional real array. * @@ -86,7 +93,7 @@ public double[][] getArray() { result[m] = new double[getN()]; for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); + result[m][n] = get(m, n); } } return result; @@ -146,4 +153,9 @@ public byte[] getByteArray(Double value) { public Class getStorageClazz() { return Double.class; } + + @Override + protected Double zero() { + return Zeros.DOUBLE; + } } diff --git a/src/main/java/com/jmatio/types/MLHandle.java b/src/main/java/com/jmatio/types/MLHandle.java new file mode 100644 index 0000000..80364c9 --- /dev/null +++ b/src/main/java/com/jmatio/types/MLHandle.java @@ -0,0 +1,35 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.types; + +/** + * We have observed a need for this class in a proprietary model. + * It is [not documented in the MAT-File spec](http://stackoverflow.com/questions/36025747/matlab-documentation-on-handle-variables-and-mat-files), + * and we can't figure out how to make it happen ourselves. In + * the testsuite (`TestHandleClass.m` and `handles.mat`) we have + * made our best effort at creating a handle class which triggers + * this code, but these test cases do not actually trigger this code. + * + * Highly experimental until MATLAB releases docs on this behavior. + */ +public class MLHandle extends MLArray { + private final String className; + private final MLCell content; + + public MLHandle(String name, String className, MLCell content) { + super(name, new int[]{1, 1}, MLArray.mxOBJECT_CLASS, 0); + this.className = className; + this.content = content; + } + + public String getClassName() { + return className; + } + + public MLCell getContent() { + return content; + } +} diff --git a/src/main/java/com/jmatio/types/MLInt16.java b/src/main/java/com/jmatio/types/MLInt16.java index 963e5b1..8c02ee4 100644 --- a/src/main/java/com/jmatio/types/MLInt16.java +++ b/src/main/java/com/jmatio/types/MLInt16.java @@ -73,6 +73,13 @@ public MLInt16(String name, short[] vals, int m) { this(name, castToShort(vals), m); } + /* (non-Javadoc) + * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) + */ + public Short[] createArray(int m, int n) { + return new Short[m * n]; + } + /** * Gets two-dimensional real array. * @@ -145,4 +152,9 @@ public byte[] getByteArray(Short value) { public Class getStorageClazz() { return Short.class; } + + @Override + protected Short zero() { + return Zeros.SHORT; + } } diff --git a/src/main/java/com/jmatio/types/MLInt32.java b/src/main/java/com/jmatio/types/MLInt32.java index 73e18c0..4db83b3 100644 --- a/src/main/java/com/jmatio/types/MLInt32.java +++ b/src/main/java/com/jmatio/types/MLInt32.java @@ -69,6 +69,13 @@ public MLInt32(String name, int[] vals, int m) { this(name, castToInteger(vals), m); } + /* (non-Javadoc) + * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) + */ + public Integer[] createArray(int m, int n) { + return new Integer[m * n]; + } + /** * Gets two-dimensional real array. * @@ -81,7 +88,7 @@ public int[][] getArray() { result[m] = new int[getN()]; for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); + result[m][n] = get(m, n); } } return result; @@ -141,4 +148,8 @@ public byte[] getByteArray(Integer value) { return buff.array(); } + @Override + protected Integer zero() { + return Zeros.INTEGER; + } } diff --git a/src/main/java/com/jmatio/types/MLInt64.java b/src/main/java/com/jmatio/types/MLInt64.java index 1dc6f18..0660692 100644 --- a/src/main/java/com/jmatio/types/MLInt64.java +++ b/src/main/java/com/jmatio/types/MLInt64.java @@ -74,6 +74,13 @@ public MLInt64(String name, long[] vals, int m) { this(name, castToLong(vals), m); } + /* (non-Javadoc) + * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) + */ + public Long[] createArray(int m, int n) { + return new Long[m * n]; + } + /** * Gets two-dimensional real array. * @@ -146,4 +153,8 @@ public byte[] getByteArray(Long value) { return buff.array(); } + @Override + protected Long zero() { + return Zeros.LONG; + } } diff --git a/src/main/java/com/jmatio/types/MLInt8.java b/src/main/java/com/jmatio/types/MLInt8.java index c96a04c..7fa3cee 100644 --- a/src/main/java/com/jmatio/types/MLInt8.java +++ b/src/main/java/com/jmatio/types/MLInt8.java @@ -68,6 +68,13 @@ public MLInt8(String name, byte[] vals, int m) { this(name, castToByte(vals), m); } + /* (non-Javadoc) + * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) + */ + public Byte[] createArray(int m, int n) { + return new Byte[m * n]; + } + /** * Gets two-dimensional real array. * @@ -140,11 +147,15 @@ public Class getStorageClazz() { /** * Override to accelerate the performance * - * @see com.jmatio.types.MLNumericArray#get(java.nio.ByteBuffer, int) + * @see com.jmatio.types.MLNumericArray#_get(java.nio.ByteBuffer, int) */ @Override - protected Byte get(ByteBuffer buffer, int index) { + protected Byte _get(ByteBuffer buffer, int index) { return buffer.get(index); } + @Override + protected Byte zero() { + return Zeros.BYTE; + } } diff --git a/src/main/java/com/jmatio/types/MLNumericArray.java b/src/main/java/com/jmatio/types/MLNumericArray.java index de095c0..b35b33b 100644 --- a/src/main/java/com/jmatio/types/MLNumericArray.java +++ b/src/main/java/com/jmatio/types/MLNumericArray.java @@ -20,8 +20,8 @@ * * @param */ -public abstract class MLNumericArray extends MLArray - implements ByteStorageSupport { +public abstract class MLNumericArray extends MLArray implements + ByteStorageSupport { private ByteBuffer real; private ByteBuffer imaginary; /** The buffer for creating Number from bytes */ @@ -38,7 +38,6 @@ public abstract class MLNumericArray extends MLArray public MLNumericArray(String name, int[] dims, int type, int attributes) { super(name, dims, type, attributes); allocate(); - } protected void allocate() { @@ -49,6 +48,9 @@ protected void allocate() { bytes = new byte[getBytesAllocated()]; } + /** Returns the value of "zero" for this type of array. */ + protected abstract T zero(); + /** * Jama [math.nist.gov] style: * construct a 2D real matrix from a one-dimensional packed array @@ -66,51 +68,37 @@ public MLNumericArray(String name, int type, T[] vals, int m) { } } - /** - * Gets single real array element of A(m,n). - * - * @param m - row index - * @param n - column index - * @return - array element - */ + /** Gets a single real array element. */ + public T getReal(int index) { + return _get(real, index); + } + + /** Gets a single real array element. */ public T getReal(int m, int n) { return getReal(getIndex(m, n)); } - /** - * @param index - * @return - */ - public T getReal(int index) { - return get(real, index); + /** Gets a single real array element. */ + public T getReal(int... indices) { + return getReal(getIndex(indices)); } - /** - * Sets single real array element. - * - * @param value - element value - * @param m - row index - * @param n - column index - */ + /** Sets a single real array element. */ + public void setReal(T value, int index) { + _set(real, value, index); + } + + /** Sets a single real array element. */ public void setReal(T value, int m, int n) { setReal(value, getIndex(m, n)); } - /** - * Sets single real array element. - * - * @param value - element value - * @param index - column-packed vector index - */ - public void setReal(T value, int index) { - set(real, value, index); + /** Sets a single real array element. */ + public void setReal(T value, int... indices) { + setReal(value, getIndex(indices)); } - /** - * Sets real part of matrix - * - * @param vector - column-packed vector of elements - */ + /** Sets real part of a matrix. */ public void setReal(T[] vector) { if (vector.length != getSize()) { throw new IllegalArgumentException("Matrix dimensions do not match. " + getSize() + " not " + vector.length); @@ -118,140 +106,96 @@ public void setReal(T[] vector) { System.arraycopy(vector, 0, real, 0, vector.length); } - /** - * Sets single imaginary array element. - * - * @param value - element value - * @param m - row index - * @param n - column index - */ + /** Sets a single imaginary array element. */ + public void setImaginary(T value, int index) { + assertComplex(); + _set(imaginary, value, index); + } + + /** Sets a single imaginary array element. */ public void setImaginary(T value, int m, int n) { setImaginary(value, getIndex(m, n)); } - /** - * Sets single real array element. - * - * @param value - element value - * @param index - column-packed vector index - */ - public void setImaginary(T value, int index) { + /** Sets a single imaginary array element. */ + public void setImaginary(T value, int... indices) { + setImaginary(value, getIndex(indices)); + } + + /** Returns the imaginary value at the given index, always 0 for non-complex arrays. */ + public T getImaginary(int index) { if (isComplex()) { - set(imaginary, value, index); + return _get(imaginary, index); + } else { + return zero(); } } - /** - * Gets single imaginary array element of A(m,n). - * - * @param m - row index - * @param n - column index - * @return - array element - */ + /** Returns the imaginary value at the given index. */ public T getImaginary(int m, int n) { return getImaginary(getIndex(m, n)); } - /** - * @param index - * @return - */ - public T getImaginary(int index) { - return get(imaginary, index); + /** Returns the imaginary value at the given index. */ + public T getImaginary(int... indices) { + return getImaginary(getIndex(indices)); } - /** - * Exports column-packed vector of real elements - * - * @return - column-packed vector of real elements - */ - // public T[] exportReal() - // { - // return real.clone(); - // } - /** - * Exports column-packed vector of imaginary elements - * - * @return - column-packed vector of imaginary elements - */ - - // public T[] exportImaginary() - // { - // return imaginary.clone(); - // } - /** - * Does the same as setReal. - * - * @param value - element value - * @param m - row index - * @param n - column index - */ - public void set(T value, int m, int n) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); + protected void assertComplex() { + if (!isComplex()) { + throw new UnsupportedOperationException("Cannot use this method for non-Complex matrices"); } - setReal(value, m, n); } - /** - * Does the same as setReal. - * - * @param value - element value - * @param index - column-packed vector index - */ + /** Sets the value at the given index for non-complex arrays. */ public void set(T value, int index) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } setReal(value, index); } - /** - * Does the same as getReal. - * - * @param m - row index - * @param n - column index - * @return - array element - */ - public T get(int m, int n) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } - return getReal(m, n); + /** Sets the value at the given index for non-complex arrays. */ + public void set(T value, int m, int n) { + set(value, getIndex(m, n)); } - /** - * @param index - * @return - */ + /** Sets the value at the given index for non-complex arrays. */ + public void set(T value, int... indices) { + set(value, getIndex(indices)); + } + + /** Returns the value at the given index for non-complex arrays. */ public T get(int index) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); - } - return get(real, index); + return getReal(index); } - /** - * @param vector - */ + /** Returns the value at the given index for non-complex arrays. */ + public T get(int m, int n) { + return get(getIndex(m, n)); + } + + /** Returns the value at the given index for non-complex arrays. */ + public T get(int... indices) { + return get(getIndex(indices)); + } + + /** Sets the content of this entire array for non-complex arrays. */ public void set(T[] vector) { - if (isComplex()) { - throw new IllegalStateException("Cannot use this method for Complex matrices"); + if (vector.length != getSize()) { + throw new IllegalArgumentException("Matrix dimensions do not match. " + getSize() + " not " + vector.length); } - setReal(vector); + System.arraycopy(vector, 0, real, 0, vector.length); } private int getByteOffset(int index) { return index * getBytesAllocated(); } - protected T get(ByteBuffer buffer, int index) { + protected T _get(ByteBuffer buffer, int index) { buffer.position(getByteOffset(index)); buffer.get(bytes, 0, bytes.length); return buldFromBytes(bytes); } - protected void set(ByteBuffer buffer, T value, int index) { + protected void _set(ByteBuffer buffer, T value, int index) { buffer.position(getByteOffset(index)); buffer.put(getByteArray(value)); } @@ -277,9 +221,6 @@ public ByteBuffer getRealByteBuffer() { return real; } - /* (non-Javadoc) - * @see com.jmatio.types.MLArray#contentToString() - */ @Override public String contentToString() { if (getSize() > 1000) { @@ -303,9 +244,6 @@ public String contentToString() { return sb.toString(); } - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object o) { if (o instanceof MLNumericArray) { @@ -369,7 +307,5 @@ public void dispose() { if (imaginary != null) { real.clear(); } - } - } diff --git a/src/main/java/com/jmatio/types/MLObject.java b/src/main/java/com/jmatio/types/MLObject.java index 6389c2d..7a9516c 100644 --- a/src/main/java/com/jmatio/types/MLObject.java +++ b/src/main/java/com/jmatio/types/MLObject.java @@ -5,13 +5,23 @@ */ package com.jmatio.types; -public class MLObject extends MLArray { - private final MLStructure o; - private final String className; +import java.util.Map; - public MLObject(String name, String className, MLStructure o) { - super(name, new int[]{1, 1}, MLArray.mxOBJECT_CLASS, 0); - this.o = o; +import com.jmatio.common.DeterministicKeyMap; + +/** + * This class represents Matlab's Object object (object array). + * + * Note: array of structures can contain only structures of the same type + * , that means structures that have the same field names. + * + * @author Wojciech Gradkowski + */ +public class MLObject extends MLStructureObjectBase { + private String className; + + public MLObject(String name, String className, int[] dimensions, int attributes) { + super(name, dimensions, MLArray.mxOBJECT_CLASS, 0); this.className = className; } @@ -19,7 +29,16 @@ public String getClassName() { return className; } - public MLStructure getObject() { - return o; + public void setFields(int i, Map structure) { + keys.addAll(structure.keySet()); + mlStructArray.put(i, new DeterministicKeyMap(keys, structure)); + } + + /** Only used by {@link com.jmatio.io.MLObjectPlaceholder}. */ + protected void copyFrom(MLObject obj) { + this.className = obj.className; + this.keys = obj.keys; + this.mlStructArray = obj.mlStructArray; + this.currentIndex = obj.currentIndex; } } diff --git a/src/main/java/com/jmatio/types/MLSingle.java b/src/main/java/com/jmatio/types/MLSingle.java index 0d6f068..29f3fb4 100644 --- a/src/main/java/com/jmatio/types/MLSingle.java +++ b/src/main/java/com/jmatio/types/MLSingle.java @@ -17,6 +17,10 @@ public MLSingle(String name, int[] dims, int type, int attributes) { super(name, dims, type, attributes); } + public Float[] createArray(int m, int n) { + return new Float[m * n]; + } + public Float buldFromBytes(byte[] bytes) { if (bytes.length != getBytesAllocated()) { throw new IllegalArgumentException( @@ -41,4 +45,8 @@ public Class getStorageClazz() { return Float.class; } + @Override + protected Float zero() { + return Zeros.FLOAT; + } } diff --git a/src/main/java/com/jmatio/types/MLSparse.java b/src/main/java/com/jmatio/types/MLSparse.java index a655179..4beeea3 100644 --- a/src/main/java/com/jmatio/types/MLSparse.java +++ b/src/main/java/com/jmatio/types/MLSparse.java @@ -29,12 +29,15 @@ public MLSparse(String name, int[] dims, int attributes, int nzmax) { this.nzmax = nzmax; } + @Override protected void allocate() { + indexSet = new TreeSet(); real = new TreeMap(); if (isComplex()) { imaginary = new TreeMap(); + } else { + imaginary = null; } - indexSet = new TreeSet(); } /** @@ -61,12 +64,27 @@ public int[] getIR() { return ir; } + /** + * Gets column indices + * + * ic points to an integer array of length nzmax containing the column indices of + * the corresponding elements in pr and pi. + */ + public int[] getIC() { + int[] ic = new int[nzmax]; + int i = 0; + for (IndexMN index : indexSet) { + ic[i++] = index.n; + } + return ic; + } + /** * Gets column indices. * * jc points to an integer array of length N+1 that contains column index information. - * For j, in the range 0<=j<=N�1, jc[j] is the index in ir and pr (and pi - * if it exists) of the first nonzero entry in the jth column and jc[j+1]�1 index + * For j, in the range 0<=j<=N, jc[j] is the index in ir and pr (and pi + * if it exists) of the first nonzero entry in the jth column and jc[j+1]?????????1 index * of the last nonzero entry. As a result, jc[N] is also equal to nnz, the number * of nonzero entries in the matrix. If nnz is less than nzmax, then more nonzero * entries can be inserted in the array without allocating additional storage @@ -84,84 +102,85 @@ public int[] getJC() { return jc; } - /* (non-Javadoc) - * @see com.paradigmdesigner.matlab.types.MLNumericArray#getReal(int, int) - */ + @Override public Double getReal(int m, int n) { IndexMN i = new IndexMN(m, n); - if (real.containsKey(i)) { - return real.get(i); - } - return 0.0; + Double result = real.get(i); + return result == null ? Zeros.DOUBLE : result; } - /* (non-Javadoc) - * @see com.jmatio.types.MLNumericArray#getReal(int) - */ + @Override public Double getReal(int index) { - throw new IllegalArgumentException("Can't get Sparse array elements by index. " + - "Please use getReal(int index) instead."); + throw new UnsupportedOperationException("Can't get Sparse array elements by index. " + + "Please use getReal(int m, int n) instead."); } - /** - * @param value - * @param m - * @param n - */ + @Override public void setReal(Double value, int m, int n) { IndexMN i = new IndexMN(m, n); indexSet.add(i); real.put(i, value); } - /** - * @param value - * @param index - */ + @Override public void setReal(Double value, int index) { - throw new IllegalArgumentException("Can't set Sparse array elements by index. " + + throw new UnsupportedOperationException("Can't set Sparse array elements by index. " + "Please use setReal(Double value, int m, int n) instead."); } - /** - * @param value - * @param m - * @param n - */ + @Override public void setImaginary(Double value, int m, int n) { + assertComplex(); IndexMN i = new IndexMN(m, n); indexSet.add(i); imaginary.put(i, value); } - /** - * @param value - * @param index - */ + @Override public void setImaginary(Double value, int index) { throw new IllegalArgumentException("Can't set Sparse array elements by index. " + "Please use setImaginary(Double value, int m, int n) instead."); } - /* (non-Javadoc) - * @see com.paradigmdesigner.matlab.types.MLNumericArray#getImaginary(int, int) - */ + @Override public Double getImaginary(int m, int n) { IndexMN i = new IndexMN(m, n); - if (imaginary.containsKey(i)) { - return imaginary.get(i); - } - return 0.0; + Double result = imaginary.get(i); + return result == null ? Zeros.DOUBLE : result; } - /* (non-Javadoc) - * @see com.jmatio.types.MLNumericArray#getImaginary(int) - */ + @Override public Double getImaginary(int index) { throw new IllegalArgumentException("Can't get Sparse array elements by index. " + "Please use getImaginary(int index) instead."); } + @Override + public void set(Double value, int m, int n) { + IndexMN i = new IndexMN(m, n); + indexSet.add(i); + real.put(i, value); + } + + @Override + public void set(Double value, int index) { + throw new IllegalArgumentException("Can't set Sparse array elements by index. " + + "Please use setImaginary(Double value, int m, int n) instead."); + } + + @Override + public Double get(int m, int n) { + IndexMN i = new IndexMN(m, n); + Double result = real.get(i); + return result == null ? Zeros.DOUBLE : result; + } + + @Override + public Double get(int index) { + throw new IllegalArgumentException("Can't get Sparse array elements by index. " + + "Please use getImaginary(int index) instead."); + } + /** * Returns the real part (PR) array. PR has length number-of-nonzero-values. * @@ -174,7 +193,7 @@ public Double[] exportReal() { if (real.containsKey(index)) { ad[i] = real.get(index); } else { - ad[i] = 0.0; + ad[i] = Zeros.DOUBLE; } i++; } @@ -193,7 +212,7 @@ public Double[] exportImaginary() { if (imaginary.containsKey(index)) { ad[i] = imaginary.get(index); } else { - ad[i] = 0.0; + ad[i] = Zeros.DOUBLE; } i++; } @@ -203,6 +222,7 @@ public Double[] exportImaginary() { /* (non-Javadoc) * @see com.paradigmdesigner.matlab.types.MLArray#contentToString() */ + @Override public String contentToString() { StringBuffer sb = new StringBuffer(); sb.append(name + " = \n"); @@ -275,10 +295,12 @@ public String toString() { } } + @Override public int getBytesAllocated() { return Double.SIZE << 3; } + @Override public Double buldFromBytes(byte[] bytes) { if (bytes.length != getBytesAllocated()) { throw new IllegalArgumentException( @@ -289,6 +311,7 @@ public Double buldFromBytes(byte[] bytes) { } + @Override public byte[] getByteArray(Double value) { int byteAllocated = getBytesAllocated(); ByteBuffer buff = ByteBuffer.allocate(byteAllocated); @@ -296,8 +319,13 @@ public byte[] getByteArray(Double value) { return buff.array(); } + @Override public Class getStorageClazz() { return Double.class; } + @Override + protected Double zero() { + return Zeros.DOUBLE; + } } diff --git a/src/main/java/com/jmatio/types/MLStructure.java b/src/main/java/com/jmatio/types/MLStructure.java index 5dbe560..3208c96 100644 --- a/src/main/java/com/jmatio/types/MLStructure.java +++ b/src/main/java/com/jmatio/types/MLStructure.java @@ -5,18 +5,6 @@ */ package com.jmatio.types; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - /** * This class represents Matlab's Structure object (structure array). * @@ -25,197 +13,12 @@ * * @author Wojciech Gradkowski */ -public class MLStructure extends MLArray { - /** - * A Set that keeps structure field names - */ - private Set keys; - /** - * Array of structures - */ - private List> mlStructArray; - /** - * Current structure pointer for bulk insert - */ - private int currentIndex = 0; - +public class MLStructure extends MLStructureObjectBase { public MLStructure(String name, int[] dims) { - this(name, dims, MLArray.mxSTRUCT_CLASS, 0); - } - - public MLStructure(String name, int[] dims, int type, int attributes) { - super(name, dims, type, attributes); - - mlStructArray = new ArrayList>(dims[0] * dims[1]); - keys = new LinkedHashSet(); - } - - /** - * Sets field for current structure - * - * @param name - name of the field - * @param value - MLArray field value - */ - public void setField(String name, MLArray value) { - //fields.put(name, value); - setField(name, value, currentIndex); - } - - /** - * Sets field for (m,n)'th structure in struct array - * - * @param name - name of the field - * @param value - MLArray field value - * @param m - * @param n - */ - public void setField(String name, MLArray value, int m, int n) { - setField(name, value, getIndex(m, n)); - } - - /** - * Sets filed for structure described by index in struct array - * - * @param name - name of the field - * @param value - MLArray field value - * @param index - */ - public void setField(String name, MLArray value, int index) { - keys.add(name); - currentIndex = index; - - if (mlStructArray.isEmpty() || mlStructArray.size() <= index) { - mlStructArray.add(index, new LinkedHashMap()); - } - mlStructArray.get(index).put(name, value); - } - - /** - * Gets the maximum length of field descriptor - * - * @return - */ - public int getMaxFieldLenth() { - //get max field name - int maxLen = 0; - for (String s : keys) { - maxLen = s.length() > maxLen ? s.length() : maxLen; - } - return maxLen + 1; - - } - - /** - * Dumps field names to byte array. Field names are written as Zero End Strings - * - * @return - */ - public byte[] getKeySetToByteArray() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - - char[] buffer = new char[getMaxFieldLenth()]; - - try { - for (String s : keys) { - Arrays.fill(buffer, (char) 0); - System.arraycopy(s.toCharArray(), 0, buffer, 0, s.length()); - dos.writeBytes(new String(buffer)); - } - } catch (IOException e) { - System.err.println("Could not write Structure key set to byte array: " + e); - return new byte[0]; - } - return baos.toByteArray(); - - } - - /** - * Gets all field from sruct array as flat list of fields. - * - * @return - */ - public Collection getAllFields() { - ArrayList fields = new ArrayList(); - - for (Map struct : mlStructArray) { - fields.addAll(struct.values()); - } - return fields; - } - - /** - * Returns the {@link Collection} of keys for this structure. - * @return the {@link Collection} of keys for this structure - */ - public Collection getFieldNames() { - Set fieldNames = new LinkedHashSet(); - - fieldNames.addAll(keys); - - return fieldNames; - - } - - /** - * Gets a value of the field described by name from current struct - * in struct array or null if the field doesn't exist. - * - * @param name - * @return - */ - public MLArray getField(String name) { - return getField(name, currentIndex); - } - - /** - * Gets a value of the field described by name from (m,n)'th struct - * in struct array or null if the field doesn't exist. - * - * @param name - * @param m - * @param n - * @return - */ - public MLArray getField(String name, int m, int n) { - return getField(name, getIndex(m, n)); + this(name, dims, 0); } - /** - * Gets a value of the field described by name from index'th struct - * in struct array or null if the field doesn't exist. - * - * @param name - * @param index - * @return value of the field or null if the field doesn't exist - */ - public MLArray getField(String name, int index) { - if (mlStructArray.isEmpty()) { - return null; - } - return mlStructArray.get(index).get(name); + public MLStructure(String name, int[] dims, int attributes) { + super(name, dims, MLArray.mxSTRUCT_CLASS, attributes); } - - /* (non-Javadoc) - * @see com.paradigmdesigner.matlab.types.MLArray#contentToString() - */ - public String contentToString() { - StringBuffer sb = new StringBuffer(); - sb.append(name + " = \n"); - - if (getM() * getN() == 1) { - for (String key : keys) { - sb.append("\t" + key + " : " + getField(key) + "\n"); - } - } else { - sb.append("\n"); - sb.append(getM() + "x" + getN()); - sb.append(" struct array with fields: \n"); - for (String key : keys) { - sb.append("\t" + key + "\n"); - } - } - return sb.toString(); - } - } diff --git a/src/main/java/ca/mjdsystems/jmatio/types/MLStructure.java b/src/main/java/com/jmatio/types/MLStructureObjectBase.java old mode 100755 new mode 100644 similarity index 76% rename from src/main/java/ca/mjdsystems/jmatio/types/MLStructure.java rename to src/main/java/com/jmatio/types/MLStructureObjectBase.java index 2b4e7ff..b8cb458 --- a/src/main/java/ca/mjdsystems/jmatio/types/MLStructure.java +++ b/src/main/java/com/jmatio/types/MLStructureObjectBase.java @@ -3,7 +3,7 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.types; +package com.jmatio.types; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -11,43 +11,31 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.LinkedHashMap; +import java.util.HashMap; import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.jmatio.common.DeterministicKeyMap; /** - * This class represents Matlab's Structure object (structure array). + * Base class for MLStructure and MLObject. * * Note: array of structures can contain only structures of the same type - * , that means structures that have the same field names. - * - * @author Wojciech Gradkowski + * , that means structures must have the same field names. */ -public class MLStructure extends MLArray { - /** - * A Set that keeps structure field names - */ - private Set keys; - /** - * Array of structures - */ - private List> mlStructArray; - /** - * Current structure pointer for bulk insert - */ - private int currentIndex = 0; - - public MLStructure(String name, int[] dims) { - this(name, dims, MLArray.mxSTRUCT_CLASS, 0); - } - - public MLStructure(String name, int[] dims, int type, int attributes) { +public abstract class MLStructureObjectBase extends MLArray { + /** A Set that keeps structure field names */ + protected Set keys = new LinkedHashSet(); + /** Array of structures */ + protected SortedMap> mlStructArray = new TreeMap>(); + /** Current structure pointer for bulk insert */ + protected int currentIndex = 0; + + protected MLStructureObjectBase(String name, int[] dims, int type, int attributes) { super(name, dims, type, attributes); - - mlStructArray = new ArrayList>(dims[0] * dims[1]); - keys = new LinkedHashSet(); } /** @@ -57,7 +45,6 @@ public MLStructure(String name, int[] dims, int type, int attributes) { * @param value - MLArray field value */ public void setField(String name, MLArray value) { - //fields.put(name, value); setField(name, value, currentIndex); } @@ -84,10 +71,12 @@ public void setField(String name, MLArray value, int index) { keys.add(name); currentIndex = index; - if (mlStructArray.isEmpty() || mlStructArray.size() <= index) { - mlStructArray.add(index, new LinkedHashMap()); + DeterministicKeyMap map = mlStructArray.get(index); + if (map == null) { + map = new DeterministicKeyMap(keys, new HashMap(keys.size())); + mlStructArray.put(index, map); } - mlStructArray.get(index).put(name, value); + map.put(name, value); } /** @@ -99,10 +88,9 @@ public int getMaxFieldLenth() { //get max field name int maxLen = 0; for (String s : keys) { - maxLen = s.length() > maxLen ? s.length() : maxLen; + maxLen = Math.max(maxLen, s.length()); } return maxLen + 1; - } /** @@ -138,7 +126,7 @@ public byte[] getKeySetToByteArray() { public Collection getAllFields() { ArrayList fields = new ArrayList(); - for (Map struct : mlStructArray) { + for (Map struct : mlStructArray.values()) { fields.addAll(struct.values()); } return fields; @@ -149,12 +137,7 @@ public Collection getAllFields() { * @return the {@link Collection} of keys for this structure */ public Collection getFieldNames() { - Set fieldNames = new LinkedHashSet(); - - fieldNames.addAll(keys); - - return fieldNames; - + return new LinkedHashSet(keys); } /** @@ -168,6 +151,11 @@ public MLArray getField(String name) { return getField(name, currentIndex); } + /** Returns all the fields for the given index. */ + public Map getFields(int i) { + return mlStructArray.get(i); + } + /** * Gets a value of the field described by name from (m,n)'th struct * in struct array or null if the field doesn't exist. diff --git a/src/main/java/com/jmatio/types/MLUInt64.java b/src/main/java/com/jmatio/types/MLUInt64.java index 709ac72..d1c2951 100644 --- a/src/main/java/com/jmatio/types/MLUInt64.java +++ b/src/main/java/com/jmatio/types/MLUInt64.java @@ -74,6 +74,13 @@ public MLUInt64(String name, long[] vals, int m) { this(name, castToLong(vals), m); } + /* (non-Javadoc) + * @see ca.mjdsystems.jmatio.types.GenericArrayCreator#createArray(int, int) + */ + public Long[] createArray(int m, int n) { + return new Long[m * n]; + } + /** * Gets two-dimensional real array. * @@ -146,4 +153,8 @@ public byte[] getByteArray(Long value) { return buff.array(); } + @Override + protected Long zero() { + return Zeros.LONG; + } } diff --git a/src/main/java/com/jmatio/types/MLUInt8.java b/src/main/java/com/jmatio/types/MLUInt8.java index 13985ba..55b0a58 100644 --- a/src/main/java/com/jmatio/types/MLUInt8.java +++ b/src/main/java/com/jmatio/types/MLUInt8.java @@ -93,7 +93,7 @@ public byte[][] getArray() { result[m] = new byte[getN()]; for (int n = 0; n < getN(); n++) { - result[m][n] = getReal(m, n); + result[m][n] = get(m, n); } } return result; @@ -156,8 +156,12 @@ public Class getStorageClazz() { * @see com.jmatio.types.MLNumericArray#get(java.nio.ByteBuffer, int) */ @Override - protected Byte get(ByteBuffer buffer, int index) { + protected Byte _get(ByteBuffer buffer, int index) { return buffer.get(index); } + @Override + protected Byte zero() { + return Zeros.BYTE; + } } diff --git a/src/main/java/com/jmatio/types/Zeros.java b/src/main/java/com/jmatio/types/Zeros.java new file mode 100644 index 0000000..23865f8 --- /dev/null +++ b/src/main/java/com/jmatio/types/Zeros.java @@ -0,0 +1,17 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.types; + +/** Singleton implementations of zero for each number type. */ +public class Zeros { + static final Double DOUBLE = Double.valueOf(0.0); + static final Float FLOAT = Float.valueOf(0.0f); + + static final Long LONG = Long.valueOf(0l); + static final Integer INTEGER = Integer.valueOf(0); + static final Short SHORT = Short.valueOf((short) 0); + static final Byte BYTE = Byte.valueOf((byte) 0); +} diff --git a/src/test/java/ca/mjdsystems/jmatio/test/MatIOTest.java b/src/test/java/ca/mjdsystems/jmatio/test/MatIOTest.java deleted file mode 100755 index 405b374..0000000 --- a/src/test/java/ca/mjdsystems/jmatio/test/MatIOTest.java +++ /dev/null @@ -1,1049 +0,0 @@ -/* - * Code licensed under new-style BSD (see LICENSE). - * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski - * All code after tags/original: Copyright (c) 2015, DiffPlug - */ -package ca.mjdsystems.jmatio.test; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import ca.mjdsystems.jmatio.io.*; -import ca.mjdsystems.jmatio.types.MLArray; -import ca.mjdsystems.jmatio.types.MLCell; -import ca.mjdsystems.jmatio.types.MLChar; -import ca.mjdsystems.jmatio.types.MLDouble; -import ca.mjdsystems.jmatio.types.MLInt64; -import ca.mjdsystems.jmatio.types.MLInt8; -import ca.mjdsystems.jmatio.types.MLNumericArray; -import ca.mjdsystems.jmatio.types.MLSingle; -import ca.mjdsystems.jmatio.types.MLSparse; -import ca.mjdsystems.jmatio.types.MLStructure; -import ca.mjdsystems.jmatio.types.MLUInt64; -import ca.mjdsystems.jmatio.types.MLUInt8; - -/** - * The test suite for JMatIO - * - * @author Wojciech Gradkowski - */ -public class MatIOTest { - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - // - // //@Test - // public void testBenchmarkDouble() throws Exception - // { - // final String fileName = "bb.mat"; - // final String name = "bigdouble"; - //// final int SIZE = 1000; - // //System.out.println(14e6); - //// ByteBuffer.allocateDirect(1000000000); - // - // - //// MLDouble mlDouble = new MLDouble( name, new int[] {SIZE, SIZE}); - //// - //// for ( int i = 0; i < SIZE*SIZE; i++ ) - //// { - //// mlDouble.set((double)i, i); - //// } - //// - //// - //// //write array to file - //// ArrayList list = new ArrayList(); - //// list.add( mlDouble ); - //// - //// //write arrays to file - //// new MatFileWriter( fileName, list ); - //// - // //read array form file - // MatFileReader mfr = new MatFileReader( fileName ); - // MLArray mlArrayRetrived = mfr.getMLArray( name ); - //// - //// System.out.println( mlArrayRetrived ); - //// System.out.println( mlArrayRetrived.contentToString() ); - // - // //test if MLArray objects are equal - //// assertEquals("Test if value red from file equals value stored", mlDouble, mlArrayRetrived); - // } - - /** - * @throws Exception - */ - @Test - public void testBenchmarkUInt8() throws Exception { - final String fileName = newTempFileLocation("bigbyte.mat"); - final String name = "bigbyte"; - final int SIZE = 1024; - - MLUInt8 mluint8 = new MLUInt8(name, new int[]{SIZE, SIZE}); - - //write array to file - ArrayList list = new ArrayList(); - list.add(mluint8); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLArray mlArrayRetrived = mfr.getMLArray(name); - - final long start = System.nanoTime(); - for (int i = 0; i < mlArrayRetrived.getSize(); i++) { - ((MLNumericArray) mlArrayRetrived).get(i); - } - final long stop = System.nanoTime(); - System.out.println("--> " + (stop - start) / 1e6 + "[ns]"); - - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", mluint8, mlArrayRetrived); - } - - private String newTempFileLocation(String string) throws IOException { - File f = folder.newFile(string); - f.delete(); - return f.getAbsolutePath(); - } - - private File fileFromStream(String location) throws IOException { - String outname = location.replace("/", "_"); - File f = folder.newFile(outname); - InputStream stream = MatIOTest.class.getResourceAsStream(location); - OutputStream fos = new BufferedOutputStream(new FileOutputStream(f)); - byte[] buffer = new byte[1024]; - int read = 0; - while ((read = stream.read(buffer)) != -1) { - fos.write(buffer, 0, read); - } - fos.flush(); - fos.close(); - return f; - } - - @Test - public void testCellFromMatlabCreatedFile() throws IOException { - //array name - File file = fileFromStream("/cell.mat"); - MatFileReader reader = new MatFileReader(file); - MLArray mlArray = reader.getMLArray("cel"); - - List towrite = Arrays.asList(mlArray); - String filename = newTempFileLocation("cellcopy.mat"); - MatFileWriter writer = new MatFileWriter(filename, towrite); - - reader = new MatFileReader(filename); - MLArray mlArrayRetrieved = reader.getMLArray("cel"); - - //assertEquals( ((MLCell)mlArray).get(0), ((MLCell)mlArrayRetrieved).get(0)); - } - - /** - * Tests filtered reading - * - * @throws IOException - */ - @Test - public void testFilteredReading() throws IOException { - //1. First create arrays - //array name - String name = "doublearr"; - String name2 = "dummy"; - //file name in which array will be storred - String fileName = newTempFileLocation("filter.mat"); - - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - MLDouble mlDouble = new MLDouble(name, src, 3); - MLChar mlChar = new MLChar(name2, "I am dummy"); - - //2. write arrays to file - ArrayList list = new ArrayList(); - list.add(mlDouble); - list.add(mlChar); - new MatFileWriter(fileName, list); - - //3. create new filter instance - MatFileFilter filter = new MatFileFilter(); - filter.addArrayName(name); - - //4. read array form file - MatFileReader mfr = new MatFileReader(fileName, filter); - - //check size of - Map content = mfr.getContent(); - assertEquals("Test if only one array was red", 1, content.size()); - - } - - /** - * Test MatFileFilter options - */ - @Test - public void testMatFileFilter() { - //create new filter instance - MatFileFilter filter = new MatFileFilter(); - - //empty filter should match all patterns - assertEquals("Test if empty filter matches all patterns", true, filter.matches("any")); - - //now add something to the filter - filter.addArrayName("my_array"); - - //test if filter matches my_array - assertEquals("Test if filter matches given array name", true, filter.matches("my_array")); - - //test if filter returns false if does not match given name - assertEquals("Test if filter does not match non existent name", false, filter.matches("dummy")); - - } - - /** - * Test MatFileFilter options - * @throws IOException - */ - @Test - public void testMLCell() throws IOException { - //array name - String name = "doublearr"; - String name2 = "name"; - //file name in which array will be storred - String fileName = newTempFileLocation("mlcell.mat"); - - //test column-packed vector - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLDouble mlDouble = new MLDouble(name, src, 3); - MLChar mlChar = new MLChar(name2, "none"); - - MLCell mlCell = new MLCell("cl", new int[]{2, 1}); - mlCell.set(mlChar, 0); - mlCell.set(mlDouble, 1); - - //write array to file - ArrayList list = new ArrayList(); - list.add(mlCell); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLCell mlArrayRetrived = (MLCell) mfr.getMLArray("cl"); - - assertEquals(mlDouble, mlArrayRetrived.get(1)); - assertEquals(mlChar, mlArrayRetrived.get(0)); - - } - - /** - * Tests MLChar reading and writing. - * - * @throws IOException - */ - @Test - public void testMLCharArray() throws IOException { - //array name - String name = "chararr"; - //file name in which array will be storred - String fileName = newTempFileLocation("mlchar.mat"); - //temp - String valueS; - - //create MLChar array of a name "chararr" containig one - //string value "dummy" - MLChar mlChar = new MLChar(name, "dummy"); - - //get array name - valueS = mlChar.getName(); - assertEquals("MLChar name getter", name, valueS); - - //get value of the first element - valueS = mlChar.getString(0); - assertEquals("MLChar value getter", "dummy", valueS); - - //write array to file - ArrayList list = new ArrayList(); - list.add(mlChar); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLArray mlCharRetrived = mfr.getMLArray(name); - - assertEquals("Test if value red from file equals value stored", mlChar, mlCharRetrived); - - //try to read non existent array - mlCharRetrived = mfr.getMLArray("nonexistent"); - assertEquals("Test if non existent value is null", null, mlCharRetrived); - } - - /** - * Tests MLDouble reading and writing. - * - * @throws IOException - */ - @Test - public void testMLDoubleArray() throws IOException { - //array name - String name = "doublearr"; - //file name in which array will be storred - String fileName = newTempFileLocation("mldouble.mat"); - - //test column-packed vector - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - //test 2D array coresponding to test vector - double[][] src2D = new double[][]{{1.3, 4.0}, - {2.0, 5.0}, - {3.0, 6.0} - }; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLDouble mlDouble = new MLDouble(name, src, 3); - - //write array to file - ArrayList list = new ArrayList(); - list.add(mlDouble); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLArray mlArrayRetrived = mfr.getMLArray(name); - - //System.out.println( mlDouble.contentToString() ); - //System.out.println( mlArrayRetrived.contentToString() ); - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", mlDouble, mlArrayRetrived); - - //test if 2D array match - for (int i = 0; i < src2D.length; i++) { - boolean result = Arrays.equals(src2D[i], ((MLDouble) mlArrayRetrived).getArray()[i]); - assertEquals("2D array match", true, result); - } - - //test new constructor - MLArray mlDouble2D = new MLDouble(name, src2D); - //compare it with original - assertEquals("Test if double[][] constructor produces the same matrix as normal one", mlDouble2D, mlDouble); - } - - /** - * Test MatFileFilter options - * @throws IOException - */ - @Test - public void testMLStructure() throws IOException { - //array name - //file name in which array will be storred - String fileName = newTempFileLocation("mlstruct.mat"); - - //test column-packed vector - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLDouble mlDouble = new MLDouble(null, src, 3); - MLChar mlChar = new MLChar(null, "I am dummy"); - - MLStructure mlStruct = new MLStructure("str", new int[]{1, 1}); - mlStruct.setField("f1", mlDouble); - mlStruct.setField("f2", mlChar); - - //write array to file - ArrayList list = new ArrayList(); - list.add(mlStruct); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLStructure mlArrayRetrived = (MLStructure) mfr.getMLArray("str"); - - assertEquals(mlDouble, mlArrayRetrived.getField("f1")); - assertEquals(mlChar, mlArrayRetrived.getField("f2")); - - } - - @Test - public void testMLStructureFieldNames() throws IOException { - //test column-packed vector - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLDouble mlDouble = new MLDouble(null, src, 3); - MLChar mlChar = new MLChar(null, "I am dummy"); - - MLStructure mlStruct = new MLStructure("str", new int[]{1, 1}); - mlStruct.setField("f1", mlDouble); - mlStruct.setField("f2", mlChar); - - Collection fieldNames = mlStruct.getFieldNames(); - - assertEquals(2, fieldNames.size()); - assertTrue(fieldNames.contains("f1")); - assertTrue(fieldNames.contains("f2")); - } - - /** - * Tests MLUint8 reading and writing. - * - * @throws IOException - */ - @Test - public void testMLUInt8Array() throws IOException { - //array name - String name = "arr"; - //file name in which array will be storred - String fileName = newTempFileLocation("mluint8tst.mat"); - - //test column-packed vector - byte[] src = new byte[]{1, 2, 3, 4, 5, 6}; - //test 2D array coresponding to test vector - byte[][] src2D = new byte[][]{{1, 4}, - {2, 5}, - {3, 6} - }; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLUInt8 mluint8 = new MLUInt8(name, src, 3); - - //write array to file - ArrayList list = new ArrayList(); - list.add(mluint8); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLArray mlArrayRetrived = mfr.getMLArray(name); - - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", mluint8, mlArrayRetrived); - - //test if 2D array match - for (int i = 0; i < src2D.length; i++) { - boolean result = Arrays.equals(src2D[i], ((MLUInt8) mlArrayRetrived).getArray()[i]); - assertEquals("2D array match", true, result); - } - - //test new constructor - MLArray mlMLUInt82D = new MLUInt8(name, src2D); - //compare it with original - assertEquals("Test if double[][] constructor produces the same matrix as normal one", mlMLUInt82D, mluint8); - } - - /** - * Tests MLSparse reading and writing. - * - * @throws IOException - */ - @Test - public void testMLSparse() throws IOException { - //array name - String name = "sparsearr"; - //file name in which array will be storred - String fileName = newTempFileLocation("mlsparse.mat"); - - //test 2D array coresponding to test vector - double[][] referenceReal = new double[][]{{1.3, 4.0}, - {2.0, 0.0}, - {0.0, 0.0} - }; - double[][] referenceImaginary = new double[][]{{0.0, 0.0}, - {2.0, 0.0}, - {0.0, 6.0} - }; - - MLSparse mlSparse = new MLSparse(name, new int[]{3, 2}, MLArray.mtFLAG_COMPLEX, 5); - mlSparse.setReal(1.3, 0, 0); - mlSparse.setReal(4.0, 0, 1); - mlSparse.setReal(2.0, 1, 0); - mlSparse.setImaginary(2.0, 1, 0); - mlSparse.setImaginary(6.0, 2, 1); - - //write array to file - ArrayList list = new ArrayList(); - list.add(mlSparse); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLArray mlArrayRetrived = mfr.getMLArray(name); - - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", mlSparse, mlArrayRetrived); - - //test if 2D array match - for (int i = 0; i < referenceReal.length; i++) { - for (int j = 0; j < referenceReal[i].length; j++) { - assertEquals("2D array mismatch (real)", referenceReal[i][j], (double) ((MLSparse) mlArrayRetrived).getReal(i, j), 0.0001f); - } - } - for (int i = 0; i < referenceImaginary.length; i++) { - for (int j = 0; j < referenceImaginary[i].length; j++) { - assertEquals("2D array mismatch (imaginary)", referenceImaginary[i][j], ((MLSparse) mlArrayRetrived).getImaginary(i, j), 0.0001f); - } - } - } - - /** - * Regression bug - * - * @throws Exception - */ - @Test - public void testDoubleFromMatlabCreatedFile() throws Exception { - //array name - String name = "arr"; - //file name in which array will be stored - File file = fileFromStream("/matnativedouble.mat"); - String fileName = file.getAbsolutePath(); - - //test column-packed vector - double[] src = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - MLDouble mlDouble = new MLDouble(name, src, 3); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLArray mlArrayRetrived = mfr.getMLArray(name); - - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", mlDouble, mlArrayRetrived); - } - - /** - * Regression bug. - - *


-	 * Matlab code:
-	 * >> arr = [1.1, 4.4; 2.2, 5.5; 3.3, 6.6];
-	 * >> save('matnativedouble2', arr);
-	 * 
- * - * @throws IOException - */ - @Test - public void testDoubleFromMatlabCreatedFile2() throws IOException { - //array name - String name = "arr"; - //file name in which array will be stored - File file = fileFromStream("/matnativedouble2.mat"); - String fileName = file.getAbsolutePath(); - - //test column-packed vector - double[] src = new double[]{1.1, 2.2, 3.3, 4.4, 5.5, 6.6}; - MLDouble mlDouble = new MLDouble(name, src, 3); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - MLArray mlArrayRetrived = mfr.getMLArray(name); - - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", mlDouble, mlArrayRetrived); - } - - @Test - public void testSparseFromMatlabCreatedFile() throws IOException { - //array name - File file = fileFromStream("/sparse.mat"); - MatFileReader reader = new MatFileReader(file); - MLArray mlArray = reader.getMLArray("spa"); - - List towrite = Arrays.asList(mlArray); - String filename = newTempFileLocation("sparsecopy.mat"); - new MatFileWriter(filename, towrite); - - reader = new MatFileReader(filename); - MLArray mlArrayRetrieved = reader.getMLArray("spa"); - - assertEquals(mlArray, mlArrayRetrieved); - - } - - @Test - public void testStructureFromMatlabCreatedFile() throws IOException { - //array name - File file = fileFromStream("/simplestruct.mat"); - MatFileReader reader = new MatFileReader(file); - MLArray mlArray = reader.getMLArray("structure"); - - List towrite = Arrays.asList(mlArray); - - String filename = newTempFileLocation("simplestructcopy.mat"); - new MatFileWriter(filename, towrite); - - reader = new MatFileReader(filename); - MLArray mlArrayRetrieved = reader.getMLArray("structure"); - } - - /** - * Regression bug: Test writing several arrays into a single file. - * - * @throws IOException - */ - @Test - public void testWritingManyArraysInFile() throws IOException { - final String fileName = newTempFileLocation("multi.mat"); - - //test column-packed vector - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - double[] src2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - double[] src3 = new double[]{3.1415}; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLDouble m1 = new MLDouble("m1", src, 3); - MLDouble m2 = new MLDouble("m2", src2, 3); - MLDouble m3 = new MLDouble("m3", src3, 1); - //write array to file - ArrayList list = new ArrayList(); - list.add(m1); - list.add(m2); - list.add(m3); - - //write arrays to file - new MatFileWriter(fileName, list); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", m1, mfr.getMLArray("m1")); - assertEquals("Test if value red from file equals value stored", m2, mfr.getMLArray("m2")); - assertEquals("Test if value red from file equals value stored", m3, mfr.getMLArray("m3")); - } - - /** - * Regression bug: Test writing several arrays into a single file. - * - * @throws IOException - */ - @Test - public void testIncrementalWrite() throws IOException { - final String fileName = newTempFileLocation("multi.mat"); - - //test column-packed vector - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - double[] src2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - double[] src3 = new double[]{3.1415}; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLDouble m1 = new MLDouble("m1", src, 3); - MLDouble m2 = new MLDouble("m2", src2, 3); - MLDouble m3 = new MLDouble("m3", src3, 1); - //write array to file - ArrayList list = new ArrayList(); - list.add(m1); - list.add(m2); - list.add(m3); - - //write arrays to file - MatFileIncrementalWriter writer = new MatFileIncrementalWriter(fileName); - writer.write(m1); - writer.write(m2); - writer.write(m3); - writer.close(); - - //read array from file - MatFileReader mfr = new MatFileReader(fileName); - - //test if MLArray objects are equal - assertEquals("Test if value red from file equals value stored", m1, mfr.getMLArray("m1")); - assertEquals("Test if value red from file equals value stored", m2, mfr.getMLArray("m2")); - assertEquals("Test if value red from file equals value stored", m3, mfr.getMLArray("m3")); - } - - /** - * - *

-	 * >> x = NaN;
-	 * >> save('nan', 'x');
-	 * 
- * @throws IOException - */ - @Test - public void testReadingNaN() throws IOException { - File file = fileFromStream("/nan.mat"); - String fileName = file.getAbsolutePath(); - - //read array form file - MatFileReader mfr = new MatFileReader(fileName); - - assertEquals("Test if value red from file equals NaN", Double.NaN, - ((MLDouble) mfr.getMLArray("x")).get(0, 0), 0.001f); - - } - - @Test - public void testUInt8() throws Exception { - File file = fileFromStream("/uint8.mat"); - String fileName = file.getAbsolutePath(); - String arrName = "arr"; - MatFileReader mfr; - MLArray src; - - //read array form file - mfr = new MatFileReader(fileName); - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - (byte) 0, - ((MLUInt8) mfr.getMLArray(arrName)).get(0, 0), 0.001f); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - (byte) 255, - ((MLUInt8) mfr.getMLArray(arrName)).get(0, 1), 0.001f); - - src = mfr.getMLArray(arrName); - - //write - fileName = newTempFileLocation("uint8out.mat"); - ArrayList towrite = new ArrayList(); - towrite.add(mfr.getMLArray(arrName)); - new MatFileWriter(fileName, towrite); - - //read again - mfr = new MatFileReader(fileName); - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - (byte) 0, - ((MLUInt8) mfr.getMLArray(arrName)).get(0, 0), 0.001f); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - (byte) 255, - ((MLUInt8) mfr.getMLArray(arrName)).get(0, 1), 0.001f); - - assertEquals("Test if array retrieved from " + fileName + " equals source array", - src, - mfr.getMLArray(arrName)); - } - - @Test - public void testInt8() throws Exception { - File file = fileFromStream("/int8.mat"); - String fileName = file.getAbsolutePath(); - String arrName = "arr"; - MatFileReader mfr; - MLArray src; - - //read array form file - mfr = new MatFileReader(fileName); - - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - (byte) -128, - ((MLInt8) mfr.getMLArray("arr")).get(0, 0), 0.001f); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - (byte) 127, - ((MLInt8) mfr.getMLArray("arr")).get(0, 1), 0.001f); - - src = mfr.getMLArray("arr"); - - //write - fileName = newTempFileLocation("int8out.mat"); - ArrayList towrite = new ArrayList(); - towrite.add(mfr.getMLArray(arrName)); - new MatFileWriter(fileName, towrite); - - //read again - mfr = new MatFileReader(fileName); - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - (byte) -128, - ((MLInt8) mfr.getMLArray(arrName)).get(0, 0), 0.001f); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - (byte) 127, - ((MLInt8) mfr.getMLArray(arrName)).get(0, 1), 0.001f); - - assertEquals("Test if array retrieved from " + fileName + " equals source array", - src, - mfr.getMLArray(arrName)); - - } - - @Test - public void testInt64() throws Exception { - File file = fileFromStream("/int64.mat"); - String fileName = file.getAbsolutePath(); - String arrName = "arr"; - MatFileReader mfr; - MLArray src; - - Long max = Long.parseLong("9223372036854775807"); - Long min = Long.parseLong("-9223372036854775808"); - - //read array form file - mfr = new MatFileReader(fileName); - - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - min, - ((MLInt64) mfr.getMLArray("arr")).get(0, 0)); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - max, - ((MLInt64) mfr.getMLArray("arr")).get(0, 1)); - - src = mfr.getMLArray("arr"); - - //write - fileName = newTempFileLocation("int64out.mat"); - ArrayList towrite = new ArrayList(); - towrite.add(mfr.getMLArray(arrName)); - new MatFileWriter(fileName, towrite); - - //read again - mfr = new MatFileReader(fileName); - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - min, - ((MLInt64) mfr.getMLArray(arrName)).get(0, 0)); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - max, - ((MLInt64) mfr.getMLArray(arrName)).get(0, 1)); - - assertEquals("Test if array retrieved from " + fileName + " equals source array", - src, - mfr.getMLArray(arrName)); - - } - - @Test - public void testUInt64() throws Exception { - File file = fileFromStream("/uint64.mat"); - String fileName = file.getAbsolutePath(); - String arrName = "arr"; - MatFileReader mfr; - MLArray src; - - Long max = Long.MAX_VALUE; - Long min = Long.parseLong("0"); - - //read array form file - mfr = new MatFileReader(fileName); - - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - min, - ((MLUInt64) mfr.getMLArray("arr")).get(0, 0)); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - max, - ((MLUInt64) mfr.getMLArray("arr")).get(0, 1)); - - src = mfr.getMLArray("arr"); - - //write - fileName = newTempFileLocation("uint64out.mat"); - ArrayList towrite = new ArrayList(); - towrite.add(mfr.getMLArray(arrName)); - new MatFileWriter(fileName, towrite); - - //read again - mfr = new MatFileReader(fileName); - assertEquals("Test min. value from file:" + fileName + " array: " + arrName, - min, - ((MLUInt64) mfr.getMLArray(arrName)).get(0, 0)); - - assertEquals("Test max. value from file:" + fileName + " array: " + arrName, - max, - ((MLUInt64) mfr.getMLArray(arrName)).get(0, 1)); - - assertEquals("Test if array retrieved from " + fileName + " equals source array", - src, - mfr.getMLArray(arrName)); - - } - - @Test - public void testWritingMethods() throws IOException { - final String fileName = newTempFileLocation("nwrite.mat"); - final File f = new File(fileName); - //test column-packed vector - double[] src = new double[]{1.3, 2.0, 3.0, 4.0, 5.0, 6.0}; - - //create 3x2 double matrix - //[ 1.0 4.0 ; - // 2.0 5.0 ; - // 3.0 6.0 ] - MLDouble m1 = new MLDouble("m1", src, 3); - //write array to file - ArrayList list = new ArrayList(); - list.add(m1); - - MatFileWriter writer = new MatFileWriter(); - - writer.write(f, list); - - assertTrue("Test if file was created", f.exists()); - - MLArray array = null; - - //try to read it - MatFileReader reader = new MatFileReader(MatFileType.Regular); - reader.read(f, MatFileReader.MEMORY_MAPPED_FILE); - array = reader.getMLArray("m1"); - assertEquals("Test if is correct file", array, m1); - - //try to delete the file - assertTrue("Test if file can be deleted", f.delete()); - - writer.write(fileName, list); - - assertTrue("Test if file was created", f.exists()); - reader.read(f, MatFileReader.MEMORY_MAPPED_FILE); - assertEquals("Test if is correct file", reader.getMLArray("m1"), m1); - - //try the same with direct buffer allocation - reader.read(f, MatFileReader.DIRECT_BYTE_BUFFER); - array = reader.getMLArray("m1"); - assertEquals("Test if is correct file", array, m1); - - //try to delete the file - assertTrue("Test if file can be deleted", f.delete()); - - writer.write(fileName, list); - - assertTrue("Test if file was created", f.exists()); - reader.read(f, MatFileReader.DIRECT_BYTE_BUFFER); - assertEquals("Test if is correct file", reader.getMLArray("m1"), m1); - - //try the same with direct buffer allocation - reader.read(f, MatFileReader.HEAP_BYTE_BUFFER); - array = reader.getMLArray("m1"); - assertEquals("Test if is correct file", array, m1); - - //try to delete the file - assertTrue("Test if file can be deleted", f.delete()); - - writer.write(fileName, list); - - assertTrue("Test if file was created", f.exists()); - reader.read(f, MatFileReader.HEAP_BYTE_BUFFER); - assertEquals("Test if is correct file", reader.getMLArray("m1"), m1); - - } - - /** - * Test case that exposes the bug found by Julien C. from polymtl.ca - *

- * The test file contains a sparse array on crashes the reader. The bug - * appeared when the {@link MLSparse} tried to allocate resources (very very - * big {@link ByteBuffer}) and {@link IllegalArgumentException} was thrown. - * - * @throws IOException - */ - @Test - public void testBigSparseFile() throws IOException { - //read array form file - MatFileReader mfr = new MatFileReader(MatFileType.Regular); - //reader crashes on reading this file - //bug caused by sparse array allocation - mfr.read(fileFromStream("/bigsparse.mat"), MatFileReader.DIRECT_BYTE_BUFFER); - - } - - /** - * Tests the mxSINGLE - * @throws Exception - */ - @Test - public void testSingle() throws Exception { - - Float[] expected = new Float[]{1.1f, 2.2f, 3.3f}; - String name = "arr"; - - //create MLSingle type - MLSingle single = new MLSingle(name, expected, 1); - assertEquals(expected[0], single.get(0)); - assertEquals(expected[1], single.get(1)); - assertEquals(expected[2], single.get(2)); - - //Test writing the MLSingle - MatFileWriter writer = new MatFileWriter(); - String filename = newTempFileLocation("singletmp.mat"); - writer.write(filename, Arrays.asList((MLArray) single)); - - //Test reading the MLSingle - MatFileReader reader = new MatFileReader(MatFileType.Regular); - MLSingle readSingle = (MLSingle) reader.read(new File(filename)).get("arr"); - - assertEquals(single, readSingle); - - //Test reading the MLSingle generated natively by Regular - MLSingle readSingleMatlabGenerated = (MLSingle) reader.read(fileFromStream("/single.mat")).get("arr"); - - assertEquals(single, readSingleMatlabGenerated); - - } - - @Test - public void testMLCharStringArray() { - String[] expected = new String[]{"a", "quick", "brown", "fox"}; - - MLChar mlchar = new MLChar("array", expected); - - assertEquals(expected[0], mlchar.getString(0)); - assertEquals(expected[1], mlchar.getString(1)); - assertEquals(expected[2], mlchar.getString(2)); - assertEquals(expected[3], mlchar.getString(3)); - } - - @Test - public void testMultipleEmptyNames() throws IOException { - File f = fileFromStream("/emptyname.mat"); - MatFileReader r = new MatFileReader(f); - Map content = r.getContent(); - - // There are 3 elements in that file, should have 4 in the array (the first value under @, and the three values under @*) - assertThat(content.size(), is(4)); - - // Check the three values came through alright. Order is fixed as they are read in in file order. Also check @. - assertThat(((MLDouble) content.get("@")).get(0), is(1.0)); - assertThat(((MLDouble) content.get("@0")).get(0), is(1.0)); - assertThat(((MLDouble) content.get("@1")).get(0), is(2.0)); - assertThat(((MLDouble) content.get("@2")).get(0), is(3.0)); - } -} diff --git a/src/test/java/com/jmatio/common/util/DeterministicKeyMapTest.java b/src/test/java/com/jmatio/common/util/DeterministicKeyMapTest.java new file mode 100644 index 0000000..74138d4 --- /dev/null +++ b/src/test/java/com/jmatio/common/util/DeterministicKeyMapTest.java @@ -0,0 +1,88 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.common.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +import com.jmatio.common.DeterministicKeyMap; + +public class DeterministicKeyMapTest { + @Test + public void testOrdering() { + Set ordering = new LinkedHashSet(); + DeterministicKeyMap map = new DeterministicKeyMap(ordering, new LinkedHashMap()); + Assert.assertEquals(true, map.keySet().isEmpty()); + Assert.assertEquals(true, map.values().isEmpty()); + Assert.assertEquals(true, map.entrySet().isEmpty()); + + // add them to the map in one order + map.put(1, "1"); + map.put(2, "2"); + map.put(3, "3"); + + { + // and pull them out in another + ordering.add(3); + ordering.add(2); + ordering.add(1); + + List keys = new ArrayList(map.keySet()); + List values = new ArrayList(map.values()); + Assert.assertEquals(Arrays.asList(3, 2, 1), keys); + Assert.assertEquals(Arrays.asList("3", "2", "1"), values); + List> entries = new ArrayList>(map.entrySet()); + Assert.assertEquals(keys.size(), entries.size()); + for (int i = 0; i < keys.size(); ++i) { + Assert.assertEquals(keys.get(i), entries.get(i).getKey()); + Assert.assertEquals(values.get(i), entries.get(i).getValue()); + } + } + + { + // now pull them out in a different order + ordering.clear(); + ordering.add(2); + ordering.add(3); + ordering.add(1); + + List keys = new ArrayList(map.keySet()); + List values = new ArrayList(map.values()); + Assert.assertEquals(Arrays.asList(2, 3, 1), keys); + Assert.assertEquals(Arrays.asList("2", "3", "1"), values); + List> entries = new ArrayList>(map.entrySet()); + Assert.assertEquals(keys.size(), entries.size()); + for (int i = 0; i < keys.size(); ++i) { + Assert.assertEquals(keys.get(i), entries.get(i).getKey()); + Assert.assertEquals(values.get(i), entries.get(i).getValue()); + } + } + + { + // now remove an object from the map + map.remove(3); + + List keys = new ArrayList(map.keySet()); + List values = new ArrayList(map.values()); + Assert.assertEquals(Arrays.asList(2, 1), keys); + Assert.assertEquals(Arrays.asList("2", "1"), values); + List> entries = new ArrayList>(map.entrySet()); + Assert.assertEquals(keys.size(), entries.size()); + for (int i = 0; i < keys.size(); ++i) { + Assert.assertEquals(keys.get(i), entries.get(i).getKey()); + Assert.assertEquals(values.get(i), entries.get(i).getValue()); + } + } + } +} diff --git a/src/test/java/com/jmatio/test/MatIOTest.java b/src/test/java/com/jmatio/io/MatIOTest.java similarity index 81% rename from src/test/java/com/jmatio/test/MatIOTest.java rename to src/test/java/com/jmatio/io/MatIOTest.java index e25b31d..f0178e8 100644 --- a/src/test/java/com/jmatio/test/MatIOTest.java +++ b/src/test/java/com/jmatio/io/MatIOTest.java @@ -3,7 +3,7 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package com.jmatio.test; +package com.jmatio.io; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -19,14 +19,11 @@ import java.util.List; import java.util.Map; +import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import com.jmatio.io.MatFileFilter; -import com.jmatio.io.MatFileIncrementalWriter; -import com.jmatio.io.MatFileReader; -import com.jmatio.io.MatFileWriter; import com.jmatio.types.MLArray; import com.jmatio.types.MLCell; import com.jmatio.types.MLChar; @@ -130,6 +127,173 @@ public void testBenchmarkUInt8() throws Exception { assertEquals("Test if value red from file equals value stored", mluint8, mlArrayRetrived); } + @Test + public void testMultipleDimArrayRealFromMatlabCreatedFile() throws IOException { + int ndims = 5; + int[] dims = new int[]{2, 3, 4, 5, 6}; + File file = getTestFile("multiDimMatrix.mat"); + MatFileReader reader = new MatFileReader(file); + MLDouble mlArray = (MLDouble) reader.getMLArray("in"); + + int testNDims = mlArray.getNDimensions(); + Assert.assertEquals(ndims, testNDims); + + int[] testDims = mlArray.getDimensions(); + for (int i = 0; i < ndims; i++) { + Assert.assertEquals(dims[i], testDims[i]); + } + + Double expectedVal = 0.0; + for (int i = 0; i < dims[4]; i++) { + for (int j = 0; j < dims[3]; j++) { + for (int k = 0; k < dims[2]; k++) { + for (int l = 0; l < dims[1]; l++) { + for (int m = 0; m < dims[0]; m++, expectedVal += 1.0) { + Double actual = mlArray.get(mlArray.getIndex(m, l, k, j, i)); + Assert.assertEquals(expectedVal, actual); + } + } + } + } + } + } + + @Test + public void testMultipleDimArrayRealWIndicesFromMatlabCreatedFile() throws IOException { + int ndims = 5; + int[] dims = new int[]{2, 3, 4, 5, 6}; + File file = getTestFile("multiDimMatrix.mat"); + MatFileReader reader = new MatFileReader(file); + MLDouble mlArray = (MLDouble) reader.getMLArray("in"); + + int testNDims = mlArray.getNDimensions(); + Assert.assertEquals(ndims, testNDims); + + int[] testDims = mlArray.getDimensions(); + for (int i = 0; i < ndims; i++) { + Assert.assertEquals(dims[i], testDims[i]); + } + + Double expectedVal = 0.0; + for (int i = 0; i < dims[4]; i++) { + for (int j = 0; j < dims[3]; j++) { + for (int k = 0; k < dims[2]; k++) { + for (int l = 0; l < dims[1]; l++) { + for (int m = 0; m < dims[0]; m++, expectedVal += 1.0) { + Double actual = mlArray.get(m, l, k, j, i); + Assert.assertEquals(expectedVal, actual); + } + } + } + } + } + } + + @Test + public void testMultipleDimArrayGetFromMatlabCreatedFile() throws IOException { + int ndims = 5; + int[] dims = new int[]{2, 3, 4, 5, 6}; + File file = getTestFile("multiDimMatrix.mat"); + MatFileReader reader = new MatFileReader(file); + MLDouble mlArray = (MLDouble) reader.getMLArray("in"); + + int testNDims = mlArray.getNDimensions(); + Assert.assertEquals(ndims, testNDims); + + int[] testDims = mlArray.getDimensions(); + for (int i = 0; i < ndims; i++) { + Assert.assertEquals(dims[i], testDims[i]); + } + + Double expectedVal = 0.0; + for (int i = 0; i < dims[4]; i++) { + for (int j = 0; j < dims[3]; j++) { + for (int k = 0; k < dims[2]; k++) { + for (int l = 0; l < dims[1]; l++) { + for (int m = 0; m < dims[0]; m++, expectedVal += 1.0) { + Double actual = mlArray.get(m, l, k, j, i); + Assert.assertEquals(expectedVal, actual); + } + } + } + } + } + } + + @Test + public void testMultipleDimArrayComplexFromMatlabCreatedFile() throws IOException { + int ndims = 5; + int[] dims = new int[]{2, 3, 4, 5, 6}; + File file = getTestFile("multiDimComplexMatrix.mat"); + MatFileReader reader = new MatFileReader(file); + MLDouble mlArray = (MLDouble) reader.getMLArray("in"); + + int testNDims = mlArray.getNDimensions(); + Assert.assertEquals(ndims, testNDims); + + int[] testDims = mlArray.getDimensions(); + for (int i = 0; i < ndims; i++) { + Assert.assertEquals(dims[i], testDims[i]); + } + + Double expectedValRe = 0.0; + for (int i = 0; i < dims[4]; i++) { + for (int j = 0; j < dims[3]; j++) { + for (int k = 0; k < dims[2]; k++) { + for (int l = 0; l < dims[1]; l++) { + for (int m = 0; m < dims[0]; m++, expectedValRe += 1.0) { + Double actualRe = mlArray.getReal(mlArray.getIndex(m, l, k, j, i)); + Assert.assertEquals(expectedValRe, actualRe); + Double actualIm = mlArray.getImaginary(mlArray.getIndex(m, l, k, j, i)); + Double expectedValIm = 0.0; + if (expectedValRe != 0.0) { + expectedValIm = expectedValRe * -1.0; + } + Assert.assertEquals(expectedValIm, actualIm); + } + } + } + } + } + } + + @Test + public void testMultipleDimArrayComplexWIndicesFromMatlabCreatedFile() throws IOException { + int ndims = 5; + int[] dims = new int[]{2, 3, 4, 5, 6}; + File file = getTestFile("multiDimComplexMatrix.mat"); + MatFileReader reader = new MatFileReader(file); + MLDouble mlArray = (MLDouble) reader.getMLArray("in"); + + int testNDims = mlArray.getNDimensions(); + Assert.assertEquals(ndims, testNDims); + + int[] testDims = mlArray.getDimensions(); + for (int i = 0; i < ndims; i++) { + Assert.assertEquals(dims[i], testDims[i]); + } + + Double expectedValRe = 0.0; + for (int i = 0; i < dims[4]; i++) { + for (int j = 0; j < dims[3]; j++) { + for (int k = 0; k < dims[2]; k++) { + for (int l = 0; l < dims[1]; l++) { + for (int m = 0; m < dims[0]; m++, expectedValRe += 1.0) { + Double actualRe = mlArray.getReal(m, l, k, j, i); + Assert.assertEquals(expectedValRe, actualRe); + Double actualIm = mlArray.getImaginary(m, l, k, j, i); + Double expectedValIm = 0.0; + if (expectedValRe != 0.0) { + expectedValIm = expectedValRe * -1.0; + } + Assert.assertEquals(expectedValIm, actualIm); + } + } + } + } + } + } + @Test public void testCellFromMatlabCreatedFile() throws IOException { // array name @@ -1012,8 +1176,7 @@ public void testObject() throws Exception { MLObject mlObject = (MLObject) content.get("X"); assertEquals("inline", mlObject.getClassName()); - assertTrue(mlObject.getObject() instanceof MLStructure); - assertTrue(mlObject.getObject().getFieldNames().contains("expr")); + assertTrue(mlObject.getFields(0).keySet().contains("expr")); } @Test @@ -1032,4 +1195,54 @@ public void testInt32() throws IOException { // test if MLArray objects are equal assertEquals("Test if value red from file equals value stored", mlDouble, mlArrayRetrived); } + + @Test + public void testMultipleEmptyNames() throws IOException { + File f = getTestFile("emptyname.mat"); + MatFileReader r = new MatFileReader(f); + Map content = r.getContent(); + + // There are 3 elements in that file, should have 4 in the array (the first value under @, and the three values under @*) + assertEquals(4, content.size()); + + // Check the three values came through alright. Order is fixed as they are read in in file order. Also check @. + assertEquals(1.0, ((MLDouble) content.get("@")).get(0), .0001); + assertEquals(1.0, ((MLDouble) content.get("@0")).get(0), .0001); + assertEquals(2.0, ((MLDouble) content.get("@1")).get(0), .0001); + assertEquals(3.0, ((MLDouble) content.get("@2")).get(0), .0001); + } + + @Test + public void testUTF() throws IOException { + // read array form file + MatFileReader mfr = new MatFileReader(getTestFile("utf.mat")); + Map map = mfr.getContent(); + MLStructure val = (MLStructure) map.get("val"); + // extract each utf + MLChar utf8 = (MLChar) val.getField("utf8"); + MLChar utf16 = (MLChar) val.getField("utf16"); + MLChar utf32 = (MLChar) val.getField("utf32"); + // assert the content + String expected = "\uD841\uDF0E"; + Assert.assertEquals(expected, utf8.getString(0)); + Assert.assertEquals(expected, utf16.getString(0)); + Assert.assertEquals(expected, utf32.getString(0)); + } + + @Test + public void testHandleClass() throws IOException { + MatFileReader mfr = new MatFileReader(getTestFile("handles.mat")); + Map map = mfr.getContent(); + MLObject objA = (MLObject) map.get("objA"); + MLDouble myPropA = (MLDouble) objA.getField("myPropA"); + Assert.assertEquals(5.0, myPropA.get(0, 0), 0.01); + + MLObject objB = (MLObject) map.get("objB"); + MLObject objBField = (MLObject) objB.getField("myObjA"); + Assert.assertEquals(objA.getField("myPropA"), objBField.getField("myPropA")); + + MLObject objC = (MLObject) map.get("objC"); + MLObject objCField = (MLObject) objC.getField("myPropA"); + Assert.assertEquals(objA.getField("myPropA"), objCField.getField("myPropA")); + } } diff --git a/src/test/java/ca/mjdsystems/jmatio/test/MatlabMCOSTest.java b/src/test/java/com/jmatio/io/MatlabMCOSTest.java similarity index 52% rename from src/test/java/ca/mjdsystems/jmatio/test/MatlabMCOSTest.java rename to src/test/java/com/jmatio/io/MatlabMCOSTest.java index 529fd16..5b1e76e 100644 --- a/src/test/java/ca/mjdsystems/jmatio/test/MatlabMCOSTest.java +++ b/src/test/java/com/jmatio/io/MatlabMCOSTest.java @@ -3,15 +3,18 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.test; +package com.jmatio.io; -import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.sameInstance; import static org.junit.Assert.assertThat; -import java.io.*; -import java.util.Collection; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Map; import org.junit.Rule; @@ -20,8 +23,11 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import ca.mjdsystems.jmatio.io.MatFileReader; -import ca.mjdsystems.jmatio.types.*; +import com.jmatio.types.MLArray; +import com.jmatio.types.MLChar; +import com.jmatio.types.MLDouble; +import com.jmatio.types.MLInt8; +import com.jmatio.types.MLObject; /** * This test verifies that ReducedHeader generated mat files work correctly. @@ -39,14 +45,14 @@ public void testParsingSimpleEmptyMCOS() throws IOException { MatFileReader reader = new MatFileReader(file); Map content = reader.getContent(); - assertThat(content.size(), is(1)); + assertThat(content.size(), equalTo(1)); MLObject obj = (MLObject) content.get("obj"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj")); - assertThat(obj.getClassName(), is("SimpleEmpty")); - assertThat(obj.getFields(0).size(), is(0)); + assertThat(obj.getName(), equalTo("obj")); + assertThat(obj.getClassName(), equalTo("SimpleEmpty")); + assertThat(obj.getFieldNames().size(), equalTo(0)); } @Test @@ -55,21 +61,21 @@ public void testParsingMultipleSimpleEmptyMCOS() throws IOException { MatFileReader reader = new MatFileReader(file); Map content = reader.getContent(); - assertThat(content.size(), is(2)); + assertThat(content.size(), equalTo(2)); MLObject obj = (MLObject) content.get("obj1"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj1")); - assertThat(obj.getClassName(), is("SimpleEmpty")); - assertThat(obj.getFields(0).size(), is(0)); + assertThat(obj.getName(), equalTo("obj1")); + assertThat(obj.getClassName(), equalTo("SimpleEmpty")); + assertThat(obj.getFieldNames().size(), equalTo(0)); obj = (MLObject) content.get("obj2"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj2")); - assertThat(obj.getClassName(), is("SimpleEmpty")); - assertThat(obj.getFields(0).size(), is(0)); + assertThat(obj.getName(), equalTo("obj2")); + assertThat(obj.getClassName(), equalTo("SimpleEmpty")); + assertThat(obj.getFieldNames().size(), equalTo(0)); } @Test @@ -78,18 +84,18 @@ public void testParsingSimpleSingleTextUnmodifiedMCOS() throws IOException { MatFileReader reader = new MatFileReader(file); Map content = reader.getContent(); - assertThat(content.size(), is(1)); + assertThat(content.size(), equalTo(1)); MLObject obj = (MLObject) content.get("obj"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj")); - assertThat(obj.getClassName(), is("SimpleSingleText")); + assertThat(obj.getName(), equalTo("obj")); + assertThat(obj.getClassName(), equalTo("SimpleSingleText")); Map fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); MLChar field = (MLChar) fields.get("test_text"); - assertThat(field.getString(0), is("Default text")); + assertThat(field.getString(0), equalTo("Default text")); } @Test @@ -98,40 +104,40 @@ public void testParsingSimpleSingleTextMultipleMCOS() throws IOException { MatFileReader reader = new MatFileReader(file); Map content = reader.getContent(); - assertThat(content.size(), is(3)); + assertThat(content.size(), equalTo(3)); MLObject obj = (MLObject) content.get("obj1"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj1")); - assertThat(obj.getClassName(), is("SimpleSingleText")); + assertThat(obj.getName(), equalTo("obj1")); + assertThat(obj.getClassName(), equalTo("SimpleSingleText")); Map fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); MLChar field = (MLChar) fields.get("test_text"); - assertThat(field.getString(0), is("other text 1")); + assertThat(field.getString(0), equalTo("other text 1")); obj = (MLObject) content.get("obj2"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj2")); - assertThat(obj.getClassName(), is("SimpleSingleText")); + assertThat(obj.getName(), equalTo("obj2")); + assertThat(obj.getClassName(), equalTo("SimpleSingleText")); fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); field = (MLChar) fields.get("test_text"); - assertThat(field.getString(0), is("Default text")); + assertThat(field.getString(0), equalTo("Default text")); obj = (MLObject) content.get("obj3"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj3")); - assertThat(obj.getClassName(), is("SimpleSingleText")); + assertThat(obj.getName(), equalTo("obj3")); + assertThat(obj.getClassName(), equalTo("SimpleSingleText")); fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); field = (MLChar) fields.get("test_text"); - assertThat(field.getString(0), is("other text 3")); + assertThat(field.getString(0), equalTo("other text 3")); } @Test @@ -140,57 +146,57 @@ public void testParsingHandleSinglePropertyMultipleMCOS() throws IOException { MatFileReader reader = new MatFileReader(file); Map content = reader.getContent(); - assertThat(content.size(), is(4)); + assertThat(content.size(), equalTo(4)); MLObject obj = (MLObject) content.get("obj1"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj1")); - assertThat(obj.getClassName(), is("HandleSingle")); - assertThat(((MLObject) content.get("obj3")).getFields(0), is(sameInstance(obj.getFields(0)))); + assertThat(obj.getName(), equalTo("obj1")); + assertThat(obj.getClassName(), equalTo("HandleSingle")); + assertThat(((MLObject) content.get("obj3")).getFields(0), equalTo(obj.getFields(0))); Map fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); MLInt8 intField = (MLInt8) fields.get("myelement"); - assertThat(intField.getSize(), is(1)); - assertThat(intField.get(0).byteValue(), is((byte) 25)); + assertThat(intField.getSize(), equalTo(1)); + assertThat(intField.get(0).byteValue(), equalTo((byte) 25)); obj = (MLObject) content.get("obj3"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj3")); - assertThat(obj.getClassName(), is("HandleSingle")); - assertThat(((MLObject) content.get("obj1")).getFields(0), is(sameInstance(obj.getFields(0)))); + assertThat(obj.getName(), equalTo("obj3")); + assertThat(obj.getClassName(), equalTo("HandleSingle")); + assertThat(((MLObject) content.get("obj1")).getFields(0), equalTo(obj.getFields(0))); fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); intField = (MLInt8) fields.get("myelement"); - assertThat(intField.getSize(), is(1)); - assertThat(intField.get(0).byteValue(), is((byte) 25)); + assertThat(intField.getSize(), equalTo(1)); + assertThat(intField.get(0).byteValue(), equalTo((byte) 25)); obj = (MLObject) content.get("obj2"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj2")); - assertThat(obj.getClassName(), is("HandleSingle")); - assertThat(((MLObject) content.get("obj4")).getFields(0), is(sameInstance(obj.getFields(0)))); + assertThat(obj.getName(), equalTo("obj2")); + assertThat(obj.getClassName(), equalTo("HandleSingle")); + assertThat(((MLObject) content.get("obj4")).getFields(0), equalTo(obj.getFields(0))); fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); MLChar charField = (MLChar) fields.get("myelement"); - assertThat(charField.getString(0), is("testing")); + assertThat(charField.getString(0), equalTo("testing")); obj = (MLObject) content.get("obj4"); - assertThat(obj, is(notNullValue())); + assertThat(obj, notNullValue()); - assertThat(obj.getName(), is("obj4")); - assertThat(obj.getClassName(), is("HandleSingle")); - assertThat(((MLObject) content.get("obj2")).getFields(0), is(sameInstance(obj.getFields(0)))); + assertThat(obj.getName(), equalTo("obj4")); + assertThat(obj.getClassName(), equalTo("HandleSingle")); + assertThat(((MLObject) content.get("obj2")).getFields(0), equalTo(obj.getFields(0))); fields = obj.getFields(0); - assertThat(fields.size(), is(1)); + assertThat(fields.size(), equalTo(1)); charField = (MLChar) fields.get("myelement"); - assertThat(charField.getString(0), is("testing")); + assertThat(charField.getString(0), equalTo("testing")); } @Test @@ -199,17 +205,17 @@ public void testMultipleMCOSInArray() throws IOException { MatFileReader reader = new MatFileReader(file); Map content = reader.getContent(); - assertThat(content.size(), is(1)); + assertThat(content.size(), equalTo(1)); MLObject obj = (MLObject) content.get("a"); - assertThat(obj, is(notNullValue())); - - assertThat(obj.getName(), is("a")); - assertThat(obj.getClassName(), is("SimpleSingleText")); - assertThat(((MLDouble) obj.getFields(0).get("test_text")).get(0), is(1.0)); - assertThat(((MLDouble) obj.getFields(1).get("test_text")).get(0), is(2.0)); - assertThat(((MLDouble) obj.getFields(2).get("test_text")).get(0), is(3.0)); - assertThat(((MLDouble) obj.getFields(3).get("test_text")).get(0), is(4.0)); + assertThat(obj, notNullValue()); + + assertThat(obj.getName(), equalTo("a")); + assertThat(obj.getClassName(), equalTo("SimpleSingleText")); + assertThat(((MLDouble) obj.getFields(0).get("test_text")).get(0), equalTo(1.0)); + assertThat(((MLDouble) obj.getFields(1).get("test_text")).get(0), equalTo(2.0)); + assertThat(((MLDouble) obj.getFields(2).get("test_text")).get(0), equalTo(3.0)); + assertThat(((MLDouble) obj.getFields(3).get("test_text")).get(0), equalTo(4.0)); } private File fileFromStream(String location) throws IOException { diff --git a/src/test/java/ca/mjdsystems/jmatio/test/SimulinkMatTest.java b/src/test/java/com/jmatio/io/SimulinkMatTest.java similarity index 95% rename from src/test/java/ca/mjdsystems/jmatio/test/SimulinkMatTest.java rename to src/test/java/com/jmatio/io/SimulinkMatTest.java index fc7f120..508b49c 100644 --- a/src/test/java/ca/mjdsystems/jmatio/test/SimulinkMatTest.java +++ b/src/test/java/com/jmatio/io/SimulinkMatTest.java @@ -3,10 +3,10 @@ * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski * All code after tags/original: Copyright (c) 2015, DiffPlug */ -package ca.mjdsystems.jmatio.test; +package com.jmatio.io; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; @@ -19,11 +19,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import ca.mjdsystems.jmatio.io.MatFileFilter; -import ca.mjdsystems.jmatio.io.MatFileReader; -import ca.mjdsystems.jmatio.io.MatFileType; -import ca.mjdsystems.jmatio.io.SimulinkDecoder; -import ca.mjdsystems.jmatio.types.*; +import com.jmatio.types.*; /** This test verifies that ReducedHeader generated mat files work correctly. * @@ -41,49 +37,49 @@ public void testParsingQuadraticRootsTET() throws IOException { MLObject data = (MLObject) reader.getContent().get("@"); // First check that the root element is correct. - assertThat(data, is(notNullValue())); - assertThat(data.getClassName(), is("Data")); - assertThat(data.getSize(), is(1)); + assertThat(data, notNullValue()); + assertThat(data.getClassName(), equalTo("Data")); + assertThat(data.getSize(), equalTo(1)); Map dataO = data.getFields(0); - assertThat(dataO.size(), is(10)); - assertThat(((MLChar) dataO.get("function_name")).getString(0), is("quad_fcn_subtype")); - assertThat(((MLChar) dataO.get("function_inputs")).getString(0), is("c,a,b:{x:real|(a=0 => x /= 0) AND (a /= 0 => (x^2) - 4*a*c >= 0)}")); - assertThat(((MLDouble) dataO.get("open")).get(0), is(0.0)); - assertThat(((MLDouble) dataO.get("fig")).getSize(), is(0)); // This field should just exist, but be empty! - assertThat(((MLDouble) dataO.get("multi_mode")).get(0), is(1.0)); - assertThat(((MLDouble) dataO.get("checked")).get(0), is(0.0)); + assertThat(dataO.size(), equalTo(10)); + assertThat(((MLChar) dataO.get("function_name")).getString(0), equalTo("quad_fcn_subtype")); + assertThat(((MLChar) dataO.get("function_inputs")).getString(0), equalTo("c,a,b:{x:real|(a=0 => x /= 0) AND (a /= 0 => (x^2) - 4*a*c >= 0)}")); + assertThat(((MLDouble) dataO.get("open")).get(0), equalTo(0.0)); + assertThat(((MLDouble) dataO.get("fig")).getSize(), equalTo(0)); // This field should just exist, but be empty! + assertThat(((MLDouble) dataO.get("multi_mode")).get(0), equalTo(1.0)); + assertThat(((MLDouble) dataO.get("checked")).get(0), equalTo(0.0)); // Next, make sure the settings structure came out right. Not super important, but a good test. MLStructure settings = (MLStructure) dataO.get("settings"); - assertThat(settings.getAllFields().size(), is(5)); - assertThat(((MLDouble) settings.getField("set")).get(0), is(1.0)); - assertThat((settings.getField("inputs")), is(instanceOf(MLDouble.class))); - assertThat(((MLDouble) settings.getField("count")).get(0), is(1000.0)); - assertThat(((MLDouble) settings.getField("range")).get(0), is(100.0)); - assertThat(((MLDouble) settings.getField("except")).get(0), is(0.0)); + assertThat(settings.getAllFields().size(), equalTo(5)); + assertThat(((MLDouble) settings.getField("set")).get(0), equalTo(1.0)); + assertThat((settings.getField("inputs")), instanceOf(MLDouble.class)); + assertThat(((MLDouble) settings.getField("count")).get(0), equalTo(1000.0)); + assertThat(((MLDouble) settings.getField("range")).get(0), equalTo(100.0)); + assertThat(((MLDouble) settings.getField("except")).get(0), equalTo(0.0)); // Next, verify Grid2, as it is easiest. MLObject Grid2 = (MLObject) dataO.get("Grid2"); - assertThat(Grid2.getClassName(), is("Grid")); - assertThat(Grid2.getSize(), is(1)); + assertThat(Grid2.getClassName(), equalTo("Grid")); + assertThat(Grid2.getSize(), equalTo(1)); Map gridO = Grid2.getFields(0); - assertThat(((MLDouble) gridO.get("num_cells")).get(0), is(2.0)); - assertThat(gridO.get("split_pb").getSize(), is(0)); - assertThat(gridO.get("parent_grid").getSize(), is(0)); - assertThat(gridO.get("parent_cell").getSize(), is(0)); - assertThat(gridO.get("new_cell_pb").getSize(), is(0)); - assertThat(gridO.get("delete_cell_pb").getSize(), is(0)); - assertThat(((MLObject) gridO.get("rGrid")).getFields(0), is(((MLObject) dataO.get("Grid0")).getFields(0))); - assertThat(((MLDouble) gridO.get("grid_index")).get(0), is(2.0)); + assertThat(((MLDouble) gridO.get("num_cells")).get(0), equalTo(2.0)); + assertThat(gridO.get("split_pb").getSize(), equalTo(0)); + assertThat(gridO.get("parent_grid").getSize(), equalTo(0)); + assertThat(gridO.get("parent_cell").getSize(), equalTo(0)); + assertThat(gridO.get("new_cell_pb").getSize(), equalTo(0)); + assertThat(gridO.get("delete_cell_pb").getSize(), equalTo(0)); + assertThat(((MLObject) gridO.get("rGrid")).getFields(0), equalTo(((MLObject) dataO.get("Grid0")).getFields(0))); + assertThat(((MLDouble) gridO.get("grid_index")).get(0), equalTo(2.0)); MLObject cells = (MLObject) gridO.get("cells"); - assertThat(cells.getSize(), is(2)); + assertThat(cells.getSize(), equalTo(2)); Map cellO = cells.getFields(0); - assertThat(((MLChar) cellO.get("cond_text")).getString(0), is("a == 0")); + assertThat(((MLChar) cellO.get("cond_text")).getString(0), equalTo("a == 0")); - assertThat(((MLChar) cells.getFields(1).get("cond_text")).getString(0), is("a ~= 0")); + assertThat(((MLChar) cells.getFields(1).get("cond_text")).getString(0), equalTo("a ~= 0")); } // This just ensures the SimulinkDecoder actually decodes correctly. @@ -97,21 +93,22 @@ public void testParsingSimulinkEncoding() throws IOException { int read = decoder.read(buf); - assertThat(read, is(expectedTETSize)); + assertThat(read, equalTo(expectedTETSize)); // Verify we are at the end of the stream - assertThat(decoder.read(), is(-1)); + assertThat(decoder.read(), equalTo(-1)); // Double check that EOF will be re-thrown on each extra call to read. - assertThat(decoder.read(), is(-1)); + assertThat(decoder.read(), equalTo(-1)); byte[] expectedBuf = new byte[expectedTETSize]; InputStream finished = SimulinkMatTest.class.getResourceAsStream("/simulink_tet_out.mat"); // Sanity check, make sure there are no hidden bytes left on the stream, and all bytes have been read in. - assertThat(finished.read(expectedBuf), is(expectedTETSize)); - assertThat(finished.read(), is(-1)); + assertThat(finished.read(expectedBuf), equalTo(expectedTETSize)); + assertThat(finished.read(), equalTo(-1)); - assertThat(buf, is(expectedBuf)); + assertThat(buf, equalTo(expectedBuf)); + decoder.close(); } // This just ensures the SimulinkDecoder works correctly with MatFileReader. @@ -124,9 +121,9 @@ public void testParsingTETInSimulinkEncoding() throws IOException { MLObject data = (MLObject) reader.getContent().get("@"); // Just check that the root element is basically correct. - assertThat(data, is(notNullValue())); - assertThat(data.getClassName(), is("Data")); - assertThat(data.getSize(), is(1)); + assertThat(data, notNullValue()); + assertThat(data.getClassName(), equalTo("Data")); + assertThat(data.getSize(), equalTo(1)); } private File fileFromStream(String location) throws IOException { diff --git a/src/test/java/com/jmatio/io/StructureWithTimeseriesTest.java b/src/test/java/com/jmatio/io/StructureWithTimeseriesTest.java new file mode 100644 index 0000000..472a86c --- /dev/null +++ b/src/test/java/com/jmatio/io/StructureWithTimeseriesTest.java @@ -0,0 +1,122 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.io; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Map; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.CustomMatcher; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import com.jmatio.types.MLArray; +import com.jmatio.types.MLChar; +import com.jmatio.types.MLDouble; +import com.jmatio.types.MLInt8; +import com.jmatio.types.MLObject; +import com.jmatio.types.MLStructure; +import com.jmatio.types.MLStructureObjectBase; + +/** + * This test verifies {@link issue #13}. + *

+ * During tests the introspection of data did not show essential vectors. + * It comes out that the properties are propagated, but their corresponding + * names are not propagated to {@link MLStructureObjectBase#getFieldNames()}. + * + * @author Piotr Smolinski + */ +@RunWith(JUnit4.class) +public class StructureWithTimeseriesTest { + + /** + * Tests reading of a timeseries object contained in a struct. + */ + @Test + public void testReadingTimeSeries() throws Exception { + + MatFileReader reader = new MatFileReader( + getClass().getResourceAsStream("/timeseries.mat"), + MatFileType.Regular); + + Map content = reader.getContent(); + + assertThat(content.get("s"), is(instanceOf(MLStructure.class))); + MLStructure s = (MLStructure) content.get("s"); + + assertThat(s.getField("test"), is(instanceOf(MLObject.class))); + MLObject test = (MLObject) s.getField("test"); + assertThat(test.getClassName(), equalTo("timeseries")); + + assertThat(test.getField("Data_"), is(instanceOf(MLDouble.class))); + MLDouble data = (MLDouble) test.getField("Data_"); + assertThat(data.getSize(), equalTo(5)); + + assertThat(test.getField("Quality_"), is(instanceOf(MLInt8.class))); + MLInt8 quality = (MLInt8) test.getField("Quality_"); + assertThat(quality.getSize(), equalTo(5)); + + assertThat(test.getField("Time_"), isMatlabUndefined()); + + assertThat(test.getField("DataInfo"), not(isMatlabUndefined())); + assertThat(test.getField("DataInfo"), is(instanceOf(MLObject.class))); + MLObject dataInfo = (MLObject) test.getField("DataInfo"); + assertThat(dataInfo.getClassName(), equalTo("datametadata")); + + assertThat(dataInfo.getField("Interpolation"), is(instanceOf(MLObject.class))); + assertThat(dataInfo.getFieldNames(), hasItem("Interpolation")); + + assertThat(test.getField("QualityInfo"), not(isMatlabUndefined())); + assertThat(test.getField("QualityInfo"), is(instanceOf(MLObject.class))); + MLObject qualityInfo = (MLObject) test.getField("QualityInfo"); + assertThat(qualityInfo.getClassName(), equalTo("qualmetadata")); + + assertThat(qualityInfo.getField("Version"), is(instanceOf(MLDouble.class))); + assertThat(qualityInfo.getFieldNames(), hasItem("Version")); + + assertThat(test.getField("TimeInfo"), not(isMatlabUndefined())); + assertThat(test.getField("TimeInfo"), is(instanceOf(MLObject.class))); + MLObject timeInfo = (MLObject) test.getField("TimeInfo"); + assertThat(timeInfo.getClassName(), equalTo("timemetadata")); + + // apparently this works even when #13 fix is not applied + assertThat(timeInfo.getField("Time_"), is(instanceOf(MLDouble.class))); + // but this fails + assertThat(timeInfo.getFieldNames(), hasItem("Time_")); + MLDouble timeInfoTime = (MLDouble) timeInfo.getField("Time_"); + assertThat(timeInfoTime.getSize(), equalTo(5)); + + assertThat(timeInfo.getField("Units"), is(instanceOf(MLChar.class))); + assertThat(timeInfo.getFieldNames(), hasItem("Units")); + MLChar timeInfoUnits = (MLChar) timeInfo.getField("Units"); + assertThat(timeInfoUnits.getString(0), equalTo("seconds")); + + } + + /** + * Tests if given {@link MLArray} is undefined in sense of JMatIO. + * Actually this should just return null, but in the JMatIO empty + * properties are resolved to empty double vector + */ + private Matcher isMatlabUndefined() { + return CoreMatchers.allOf( + is(instanceOf(MLDouble.class)), + new CustomMatcher("empty double vector") { + @Override + public boolean matches(Object item) { + if (!(item instanceof MLDouble)) + return false; + return ((MLDouble) item).getSize() == 0; + } + }); + } + +} diff --git a/src/test/java/com/jmatio/types/MLNumericArrayTest.java b/src/test/java/com/jmatio/types/MLNumericArrayTest.java new file mode 100644 index 0000000..ab18fc5 --- /dev/null +++ b/src/test/java/com/jmatio/types/MLNumericArrayTest.java @@ -0,0 +1,26 @@ +/* + * Code licensed under new-style BSD (see LICENSE). + * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski + * All code after tags/original: Copyright (c) 2015, DiffPlug + */ +package com.jmatio.types; + +import org.junit.Assert; +import org.junit.Test; + +public class MLNumericArrayTest { + @Test + public void testMultipleDimArray() { + int[] dims = new int[]{3, 4, 5}; + MLInt32 multidim = new MLInt32("multiDimTest", dims); + for (int i = 0; i < dims[0]; ++i) { + for (int j = 0; j < dims[1]; ++j) { + for (int k = 0; k < dims[2]; ++k) { + int value = i * j * k; + multidim.set(value, i, j, k); + Assert.assertEquals(value, multidim.get(i, j, k).intValue()); + } + } + } + } +} diff --git a/src/test/matlab/com/jmatio/TestMultiDimArray.m b/src/test/matlab/com/jmatio/TestMultiDimArray.m new file mode 100644 index 0000000..e155f32 --- /dev/null +++ b/src/test/matlab/com/jmatio/TestMultiDimArray.m @@ -0,0 +1,45 @@ + + +% determine if script is being run from maven style project directory +% if so, set root dir approriately, otherwise just use current directory +% in maven directory structure this file should be located in directory +% project_root/src/test/matlab/com/jmatio/TestMultiDimArray.m +proj_root = '../../../../../' +test_rsrc_dir = 'src/test/resources/' + +rsrc_dir = strcat( proj_root, test_rsrc_dir ) + +fnMultiDimMatrix = 'multiDimMatrix.mat' + +% by default, use current directory as root +test_dir = './'; +if exist( rsrc_dir, 'dir' ) + test_dir = rsrc_dir; +end + +genMultiDimMatrix(strcat( test_dir, fnMultiDimMatrix )) + + + + +function op = genMultiDimMatrix( filePath ) + in = zeros(2, 3, 4, 5, 6); + e = 0; + for i =1:6 + for j = 1:5 + for k = 1:4 + for l = 1:3 + for m = 1:2 + in(m, l, k, j, i) = e; + e = e + 1; + end + end + end + end + end + + + + + save( filePath, 'in' ) +end diff --git a/src/test/matlab/com/jmatio/TestMultiDimComplexArray.m b/src/test/matlab/com/jmatio/TestMultiDimComplexArray.m new file mode 100644 index 0000000..1443f82 --- /dev/null +++ b/src/test/matlab/com/jmatio/TestMultiDimComplexArray.m @@ -0,0 +1,46 @@ + + +% determine if script is being run from maven style project directory +% if so, set root dir approriately, otherwise just use current directory +% in maven directory structure this file should be located in directory +% project_root/src/test/matlab/com/jmatio/TestMultiDimComplexArray.m +proj_root = '../../../../../' +test_rsrc_dir = 'src/test/resources/' + +rsrc_dir = strcat( proj_root, test_rsrc_dir ) + +fnMultiDimComplexMatrix = 'multiDimComplexMatrix.mat' + +% by default, use current directory as root +test_dir = './'; +if exist( rsrc_dir, 'dir' ) + test_dir = rsrc_dir; +end + +genMultiDimComplexMatrix(strcat( test_dir, fnMultiDimComplexMatrix )) + + + + +function op = genMultiDimComplexMatrix( filePath ) + a = zeros(2, 3, 4, 5, 6); + in = complex(a, 0); + e = 0; + for i =1:6 + for j = 1:5 + for k = 1:4 + for l = 1:3 + for m = 1:2 + in(m, l, k, j, i) = complex(e, -e); + e = e + 1; + end + end + end + end + end + + + + + save( filePath, 'in' ); +end diff --git a/src/test/resources/handles.mat b/src/test/resources/handles.mat new file mode 100644 index 0000000..364f134 Binary files /dev/null and b/src/test/resources/handles.mat differ diff --git a/src/test/resources/multiDimComplexMatrix.mat b/src/test/resources/multiDimComplexMatrix.mat new file mode 100644 index 0000000..ed96368 Binary files /dev/null and b/src/test/resources/multiDimComplexMatrix.mat differ diff --git a/src/test/resources/multiDimMatrix.mat b/src/test/resources/multiDimMatrix.mat new file mode 100644 index 0000000..cabf133 Binary files /dev/null and b/src/test/resources/multiDimMatrix.mat differ diff --git a/src/test/resources/timeseries.mat b/src/test/resources/timeseries.mat new file mode 100644 index 0000000..b017d91 Binary files /dev/null and b/src/test/resources/timeseries.mat differ diff --git a/src/test/resources/utf.m b/src/test/resources/utf.m new file mode 100644 index 0000000..d866c5a --- /dev/null +++ b/src/test/resources/utf.m @@ -0,0 +1,8 @@ + +%% generate a curly d (U+0221) in UTF 8, 16, and 32 +val.utf8 = native2unicode(hex2dec(['F0'; 'A0'; '9C'; '8E'])', 'UTF-8'); +val.utf16 = native2unicode(hex2dec(['D8'; '41'; 'DF'; '0E'])', 'UTF-16'); +val.utf32 = native2unicode(hex2dec(['00'; '02'; '07'; '0E'])', 'UTF-32'); + +%% save it out to disk +save('utf.mat', 'val') diff --git a/src/test/resources/utf.mat b/src/test/resources/utf.mat new file mode 100644 index 0000000..d8b5a7f Binary files /dev/null and b/src/test/resources/utf.mat differ