Compare commits

..

81 Commits

Author SHA1 Message Date
Harald Kuhr 873420286d [maven-release-plugin] prepare for next development iteration 2021-03-08 14:31:07 +01:00
Harald Kuhr 4993331a5d [maven-release-plugin] prepare release twelvemonkeys-3.6.4 2021-03-08 14:31:01 +01:00
Harald Kuhr af0a3889db #595 Avoid infinite loop on corrupted JPEG stream
(cherry picked from commit ba0bb7b903)
2021-03-08 14:24:12 +01:00
Harald Kuhr 6acdfd3be6 Readme updates, mentioning JPEG lossless and built-in support (closes #471).
(cherry picked from commit d03dc28764)
2021-03-08 14:24:11 +01:00
Harald Kuhr cfb664a76a Updated version numbers.
(cherry picked from commit 20a785ea5e)
2021-03-08 14:23:19 +01:00
Harald Kuhr 86d95e1f02 [maven-release-plugin] prepare for next development iteration 2021-02-26 18:51:06 +01:00
Harald Kuhr 54dd9b6d7b [maven-release-plugin] prepare release twelvemonkeys-3.6.3 2021-02-26 18:50:59 +01:00
Harald Kuhr 4e10fc019e #588 Clipping path from JPEG with multiple APP13 segments
(cherry picked from commit 970f4f3a7e)
2021-02-26 18:42:42 +01:00
Harald Kuhr 1295951ead Fix SGI source subsampling + test optimizations.
(cherry picked from commit 6d192968d1)
2021-02-26 18:42:38 +01:00
Harald Kuhr d5e664cdcc JPEG Exif/thumbnail fixes.
(cherry picked from commit fbc738f2d4)
2021-02-26 18:39:43 +01:00
Harald Kuhr efe5f3c34a No longer reads thumbnails, as part of the readWithOrientation method.
(cherry picked from commit 80c595cea8)
2021-02-26 18:33:47 +01:00
Harald Kuhr 2488f6f67c JPEG Exif/thumbnail fixes.
(cherry picked from commit fbc738f2d4)
2021-02-26 18:33:46 +01:00
Harald Kuhr 6a66d2e059 More standard key mapping, more correct fit size. Nicer color!
(cherry picked from commit 3e3acf3332)
2021-02-26 18:33:44 +01:00
Koen De Groote ebd5533879 Upgraded the Apache Batik library from 1.12 to 1.14 due to fixed CVEs.
(cherry picked from commit 72cd3aade3)
2021-02-26 18:32:21 +01:00
Harald Kuhr e6e4e96309 Update README.md
Removed JDK 7 from recommended build.

(cherry picked from commit 88bd9cd2ba)
2021-02-26 18:32:20 +01:00
Harald Kuhr aadc62dde9 Removed XWD plugin that will be in 3.7.
Fixed some incorrect code escaping.

(cherry picked from commit 5ee8678a29)
2021-02-26 18:32:20 +01:00
Harald Kuhr 24cbe57240 Updated README with latest version numbers.
(cherry picked from commit fb1937ae63)
2021-02-26 18:32:20 +01:00
Harald Kuhr f7d8ae0cd2 [maven-release-plugin] prepare for next development iteration 2021-01-23 17:09:08 +01:00
Harald Kuhr 5da934e11b [maven-release-plugin] prepare release twelvemonkeys-3.6.2 2021-01-23 17:08:59 +01:00
Harald Kuhr 51297ad496 #582: Fix for missing Exif thumbnail, now only issues warning.
(cherry picked from commit de02e3d7e0)
2021-01-23 17:01:14 +01:00
Harald Kuhr 80a534cd62 Fix some corner cases in BufferedImageInputStream.
(cherry picked from commit 8a1a90dafd)
2021-01-23 17:01:08 +01:00
Harald Kuhr 24130d466d #579 More reliable CCITT compression type detection
(cherry picked from commit 253f04066b)
2021-01-23 17:01:05 +01:00
Harald Kuhr 7559686782 StandardCharsets.US_ASCII instead of Charset.forName("ascii")
(cherry picked from commit 74902b3fb4)
2021-01-23 17:00:58 +01:00
Harald Kuhr b6988c37a7 #577 Fix TGA subsampling + bonus metadata fix and palette conversion.
(cherry picked from commit af1a6492d4)
2021-01-23 17:00:48 +01:00
Harald Kuhr bbffb1d416 BufferedImageInputStream performance optimizations.
(cherry picked from commit c7d2f422b8)
2021-01-23 17:00:31 +01:00
Harald Kuhr c68de3bc92 Updated links to latest version.
(cherry picked from commit 25150b421c)
2021-01-23 16:59:58 +01:00
Harald Kuhr a12b6044c6 Add XWD to BOM.
(cherry picked from commit 94031a2913)
2021-01-23 16:59:48 +01:00
Harald Kuhr 9a0e2d9659 [maven-release-plugin] prepare for next development iteration 2020-11-19 22:40:02 +01:00
Harald Kuhr b904f8952f [maven-release-plugin] prepare release twelvemonkeys-3.6.1 2020-11-19 22:39:53 +01:00
Harald Kuhr 187c952b8e Setting SNAPSHOT versions. 2020-11-19 22:07:30 +01:00
Harald Kuhr ca86605923 #574 Better test data.
(cherry picked from commit 1d4f681b8f)
2020-11-19 21:50:40 +01:00
Harald Kuhr 8bc863298f #574 Fix for possible OOME in Exif metadata.
(cherry picked from commit eda2cd76db)
2020-11-19 21:50:39 +01:00
Harald Kuhr 3447d1782c Some minor code clean-up.
(cherry picked from commit 4adc60a6c6)
2020-11-19 21:50:35 +01:00
Harald Kuhr bf245fde5f #330 ImageReaderBase.getDestination now throws IIOException for too large dimension/size.
(cherry picked from commit 0d5577a9a4)
2020-11-19 21:50:31 +01:00
Harald Kuhr c0748dcfd7 #330 Now correctly calculates scanline for 1 & 4 bits
(cherry picked from commit 918f92aba7)
2020-11-19 21:50:26 +01:00
Harald Kuhr c293516201 #330 Now correctly uses USHORT instead of SHORT for 16 bit DIB.
(cherry picked from commit 7a24d55be7)
2020-11-19 21:50:26 +01:00
Harald Kuhr f6dae36b7e #330 Now guards against buffer overruns in RLE decoder.
(cherry picked from commit a84cc1c060)
2020-11-19 21:50:26 +01:00
Harald Kuhr a14b481e9e #330 Minor improvements to avoid RuntimeExceptions.
(cherry picked from commit 31cb79d2b9)
2020-11-19 21:50:22 +01:00
Harald Kuhr d9c1a39c37 Fixed Maven Central link URL to more relevant URL.
(cherry picked from commit d995e7baa0)
2020-11-19 21:50:17 +01:00
Harald Kuhr e9d9f99bb0 Fixed Maven Central link URL
(cherry picked from commit e7fe6d5c22)
2020-11-19 21:50:13 +01:00
Harald Kuhr b9749b94b0 Release notes already on the Github page.
(cherry picked from commit 918b698e50)
2020-11-19 21:50:13 +01:00
Harald Kuhr 4996dff6e4 ...and again.
(cherry picked from commit 2427b2323f)
2020-11-19 21:50:13 +01:00
Harald Kuhr b1a2244c7f ...and again.
(cherry picked from commit 0a8222fea3)
2020-11-19 21:50:08 +01:00
Harald Kuhr c6fe747ca3 Fixed metadata support (not all formats have it yet).
(cherry picked from commit 60a00b89ae)
2020-11-19 21:50:04 +01:00
Harald Kuhr e1af4d7da9 Removed empty lines. Added missing BMP info.
(cherry picked from commit 4c88efa19d)
2020-11-19 21:50:03 +01:00
Harald Kuhr 33556cc0ec New & improved README with tables and link to Wiki!
(cherry picked from commit 17d65a1f6f)
2020-11-19 21:50:03 +01:00
Harald Kuhr eaf13b102f Added PayPal donation link. Go use it! :-)
(cherry picked from commit fcd03eb903)
2020-11-19 21:49:59 +01:00
Harald Kuhr 74718f1ffb Now correctly uses Image*Input*Stream instead of ImageOutputStream...
(cherry picked from commit 4e69efce28)
2020-11-19 21:49:55 +01:00
Harald Kuhr bd3700ea59 ...and fix the broken test.
(cherry picked from commit 16caec4a22)
2020-11-19 21:49:50 +01:00
Harald Kuhr 8fccd9445f Minor improvements and better test cases.
(cherry picked from commit 08282ea09d)
2020-11-19 21:49:49 +01:00
Harald Kuhr cf0ed8f95c Update README.md
(cherry picked from commit a16fce0749)
2020-11-19 21:49:49 +01:00
Harald Kuhr c12e4e5646 Fixed URL now works, ideally should point to correct branch...
(cherry picked from commit 26e2fa0168)
2020-11-19 21:49:03 +01:00
Harald Kuhr 287b73c732 More standard way for getting vendor name and version info.
(cherry picked from commit 120deb3ad4)
2020-11-19 21:49:01 +01:00
Harald Kuhr 24271b8cad NetBPM clean-up, fixes and better tests.
(cherry picked from commit 0a9e2df5de)
2020-11-19 21:48:59 +01:00
Harald Kuhr 3e2f54ee7c Verify that RGB data is correct.
(cherry picked from commit 6ffcb88872)
2020-11-19 21:48:59 +01:00
Harald Kuhr 2511b2d0cd Added test to verify how to write CMYK JPEG without ICC profile.
(cherry picked from commit 960e764c7b)
2020-11-19 21:48:58 +01:00
Harald Kuhr a15d54c92c Code clean-up.
(cherry picked from commit d88f27b251)
2020-11-19 21:48:56 +01:00
Harald Kuhr 47b7c4b16c Added missing tests.
(cherry picked from commit e5b3e9755e)
2020-11-19 21:48:55 +01:00
Harald Kuhr 9e1b01a7fd ImageWriterAbstractTest refactorings.
(cherry picked from commit 6c34fb211f)
2020-11-19 21:48:55 +01:00
Harald Kuhr 769acc8726 ImageReaderAbstractTest refactorings.
(cherry picked from commit 9fdbc3b1fc)
2020-11-19 21:48:15 +01:00
Harald Kuhr 1ace3a6d5f Getting rid of more JUnit deprecation.
(cherry picked from commit 622c6f40d4)
2020-11-19 21:44:36 +01:00
Harald Kuhr a06eb53cd2 Dependabot broke my build...
(cherry picked from commit 107da17ca9)
2020-11-19 21:44:36 +01:00
dependabot[bot] 1e1a640a6c Bump junit from 4.7 to 4.13.1 in /sandbox
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 19c62ac7da)
2020-11-19 21:44:35 +01:00
dependabot[bot] 2444bc5ad4 Bump junit from 4.7 to 4.13.1 in /common
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit a4d20a4af4)
2020-11-19 21:42:48 +01:00
dependabot[bot] 2e656a45f9 Bump junit from 4.7 to 4.13.1 in /imageio
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 0643d5910a)
2020-11-19 21:42:32 +01:00
dependabot[bot] 657928f4a5 Bump junit from 4.7 to 4.13.1 in /contrib
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit c78a456985)
2020-11-19 21:42:32 +01:00
dependabot[bot] 08f7e070dc Bump junit from 4.7 to 4.13.1 in /servlet
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 27017576d3)
2020-11-19 21:42:32 +01:00
Harald Kuhr b205226f0c Better output when debugging readers.
(cherry picked from commit 021aba1a98)
2020-11-19 21:42:32 +01:00
Harald Kuhr 821df11d90 Removed work in progress...
(cherry picked from commit a0b68adff3)
2020-11-19 21:42:31 +01:00
Harald Kuhr a62a838a0d Fixed a minor dependency issue. All test-jar dependencies now has correct test scope.
(cherry picked from commit fa4586663c)
2020-11-19 21:42:31 +01:00
Harald Kuhr 75ff0f265f Better PFM support.
(cherry picked from commit 623d13a517)
2020-11-19 21:42:31 +01:00
Harald Kuhr 15c7cfe9a6 Code clean-up.
(cherry picked from commit a7ebc1b52f)
2020-11-19 21:39:58 +01:00
Harald Kuhr 1286077b02 Added PNMImageWriterTest
(cherry picked from commit f54f4370c0)
2020-11-19 21:39:56 +01:00
Harald Kuhr 5757743db7 Add missing tests.
(cherry picked from commit 5040e9fe8a)
2020-11-19 21:39:55 +01:00
Harald Kuhr fbaa13d48d Minor language fix.
(cherry picked from commit fc72cd34a1)
2020-11-19 21:39:53 +01:00
Harald Kuhr f12df442e9 Added section about re-packaging and Shade plugin.
(cherry picked from commit 6d71a3d306)
2020-11-19 21:39:51 +01:00
Harald Kuhr 0c0712ab30 Comment fix
(cherry picked from commit 86f8cf52a5)
2020-11-19 21:39:50 +01:00
Harald Kuhr 9267842788 #556 PICTImageReaderSpi no longer claim to decode known formats
(cherry picked from commit bda6544a5f)
2020-11-19 21:39:48 +01:00
Harald Kuhr bcffeb04ec #466 TGA extension size fix for 3ds max files
(cherry picked from commit 49c7cd1979)
2020-11-19 21:39:47 +01:00
Harald Kuhr d1a1bab18c Code clean-up.
(cherry picked from commit 9dae58d5a6)
2020-11-19 21:39:47 +01:00
Harald Kuhr 6b5e75a22b Update readme to 3.6.
(cherry picked from commit ed14b97199)
2020-11-19 21:39:47 +01:00
888 changed files with 51467 additions and 38872 deletions
-1
View File
@@ -1 +0,0 @@
github: haraldk
-53
View File
@@ -1,53 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: Reported bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Version information**
1. The version of the TwelveMonkeys ImageIO library in use.
For example: 4.0.0
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
For example:
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
**To Reproduce**
Steps to reproduce the behavior:
1. Compile the below sample code
2. Download the sample image file
3. Run the code with the sample file
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Example code**
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
**Sample file(s)**
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
**Stak trace**
Always include the stack trace you experience.
**Screenshots**
If applicable, add screenshots to help explain your problem.
Do not add screenshots of code or stack traces. 😀
**Additional context**
Add any other context about the problem here.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: New feature
assignees: ''
---
**Is your feature request related to a use case or a problem you are working on? Please describe.**
A clear and concise description of what the problem or use case is. Understanding the rationale is key, to be able to implemeent the right solution.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've already considered, and why they won't work.
**Additional context**
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
@@ -1,13 +0,0 @@
---
name: Trouble shooting and programming help
about: "General programming issues will reach a wider audience at StackOverflow. Tag
questions with javax-imageio and/or twelvemonkeys \U0001F600 "
title: ''
labels: Trouble-shooting
assignees: ''
---
General programming issues and problems will reach a much wider audience at StackOverflow, we suggest you ask them there. This will offload our work with maintaining the library, and make sure you get better help sooner.
Tag the question with `javax-imageio` and/or `twelvemonkeys` and we'll find them there.
@@ -1,11 +0,0 @@
**What is fixed** Add link to the issue this PR fixes.
Example: Fixes #42.
**Why is this change proposed** If this change does *not* fix an open issue, briefly describe the rationale for this PR.
**What is changed** Briefly describe the changes proposed in this pull request:
* Fixed rare exception happening in `x >= 42` case
* Small optimization of `decompress()` method
* Corrected API doc for `compress()` method to reflect current implementation
-17
View File
@@ -1,17 +0,0 @@
version: 2
updates:
# Maven/Java library updates
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
open-pull-requests-limit: 10
# GitHub actions updates
- package-ecosystem: "github-actions"
directory: "/.github/workflows"
schedule:
interval: "daily"
cooldown:
default-days: 7
-143
View File
@@ -1,143 +0,0 @@
name: CI
on:
push:
branches:
- '**'
- '!dependabot/**'
pull_request:
branches: [ 'master' ]
permissions: read-all
jobs:
test:
name: Test OpenJDK ${{ matrix.java }} on ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
java: [ 8, 11, 17, 21, 25 ]
exclude:
- os: macos-latest
java: 8
runs-on: ${{ matrix.os }}
permissions:
checks: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
java-package: jdk
cache: 'maven'
- name: Run Tests
run: mvn --batch-mode --no-transfer-progress test
- name: Publish Test Report
uses: mikepenz/action-junit-report@49b2ca06f62aa7ef83ae6769a2179271e160d8e4 # v5
if: ${{ !cancelled() }}
with:
report_paths: "**/target/surefire-reports/TEST*.xml"
check_name: Unit Test Results for OpenJDK ${{ matrix.java }} on ${{ matrix.os }}
test-jdk8-macos:
name: Test OpenJDK 8 on macos-14
runs-on: macos-14
permissions:
checks: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'zulu'
java-version: '8'
java-package: jdk
cache: 'maven'
- name: Run Tests
run: mvn --batch-mode --no-transfer-progress test
- name: Publish Test Report
uses: mikepenz/action-junit-report@49b2ca06f62aa7ef83ae6769a2179271e160d8e4 # v5
if: ${{ !cancelled() }}
with:
report_paths: "**/target/surefire-reports/TEST*.xml"
check_name: Unit Test Results for OpenJDK ${{ matrix.java }} on ${{ matrix.os }}
test-oracle:
name: Test Oracle JDK 8 with KCMS=${{ matrix.kcms }}
runs-on: ubuntu-latest
permissions:
checks: write
strategy:
matrix:
kcms: [ true, false ]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- run: |
download_url="https://javadl.oracle.com/webapps/download/AutoDL?BundleId=245038_d3c52aa6bfa54d3ca74e617f18309292"
wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'jdkfile'
jdkFile: ${{ runner.temp }}/java_package.tar.gz
java-version: '8'
cache: 'maven'
- name: Set MAVEN_OPTS
if: ${{ matrix.kcms }}
run: |
echo "MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider" >> $GITHUB_ENV
- name: Display Java version
run: java -version
- name: Run Tests
run: mvn --batch-mode --no-transfer-progress test
- name: Publish Test Report
uses: mikepenz/action-junit-report@49b2ca06f62aa7ef83ae6769a2179271e160d8e4 # v5
if: ${{ !cancelled() }}
with:
report_paths: "**/target/surefire-reports/TEST*.xml"
check_name: Unit Test Results for Oracle JDK 8 with KCMS=${{ matrix.kcms }}
javadoc:
name: Build JavaDoc on OpenJDK ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [8, 11, 25 ] # We only need a few versions here
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
java-package: jdk
cache: 'maven'
- name: Create JavaDoc
run: mvn --batch-mode --no-transfer-progress -DskipTests package javadoc:javadoc
release:
name: Deploy
needs: [ test, test-jdk8-macos, test-oracle, javadoc ]
if: github.ref == 'refs/heads/master' # only perform on latest master
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Maven Central
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with: # running setup-java again overwrites the settings.xml
distribution: 'temurin'
java-version: '8'
java-package: jdk
server-id: central # Value of the distributionManagement/repository/id field of the pom.xml
server-username: MAVEN_CENTRAL_USERNAME # env variable for username in deploy (1)
server-password: MAVEN_CENTRAL_PASSWORD # env variable for token in deploy (2)
- name: Get Project Version
run: |
echo "PROJECT_VERSION=$(mvn --batch-mode help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
- name: Publish to Maven Central
if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
run: mvn --batch-mode --no-transfer-progress deploy -P release -DskipTests -Dgpg.signer=bc
env:
MAVEN_CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }} # must be the same env variable name as (1)
MAVEN_CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }} # must be the same env variable name as (2)
MAVEN_GPG_KEY: ${{ secrets.GPG_KEY }} # Value of the GPG private key to import
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
-69
View File
@@ -1,69 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '26 13 * * 6'
permissions: {}
jobs:
analyze:
name: Analyze
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners
# Consider using larger runners for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
category: "/language:${{matrix.language}}"
-62
View File
@@ -1,62 +0,0 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '38 8 * * 2'
push:
branches: [ "master" ]
permissions: read-all # Declare default permissions as read only.
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
security-events: write # to upload the results to code-scanning dashboard.
id-token: write # to publish results and get a badge
steps:
- name: "Checkout code"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# you want to enable the Branch-Protection check on the repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Publish Results:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
sarif_file: results.sarif
-1
View File
@@ -15,4 +15,3 @@ private
profiles.xml profiles.xml
Thumbs.db Thumbs.db
.DS_Store .DS_Store
/.metadata/
+14
View File
@@ -0,0 +1,14 @@
dist: trusty
language: java
jdk:
- oraclejdk8
# Oracle JDK 7 no longer supported, we use env matrix to test various CMM providers
# - oraclejdk7
# Some JPEGImageReader tests fail on OpenJDK, need to investigate/fix before enabling
# - openjdk7
env:
- MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
- MAVEN_OPTS=""
cache:
directories:
- $HOME/.m2
+118 -169
View File
@@ -1,63 +1,57 @@
[![CI](https://github.com/haraldk/TwelveMonkeys/actions/workflows/ci.yml/badge.svg)](https://github.com/haraldk/TwelveMonkeys/actions/workflows/ci.yml) [![Build Status](https://travis-ci.org/haraldk/TwelveMonkeys.svg?branch=master)](https://travis-ci.org/haraldk/TwelveMonkeys)
[![CodeQL](https://github.com/haraldk/TwelveMonkeys/actions/workflows/codeql.yml/badge.svg)](https://github.com/haraldk/TwelveMonkeys/actions/workflows/codeql.yml) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio/badge.svg?color=slateblue)](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/haraldk/TwelveMonkeys/badge)](https://securityscorecards.dev/viewer/?uri=github.com/haraldk/TwelveMonkeys)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7900/badge)](https://www.bestpractices.dev/projects/7900)
[![Maven Central](https://img.shields.io/maven-central/v/com.twelvemonkeys.imageio/imageio?color=slateblue)](https://search.maven.org/search?q=g:com.twelvemonkeys.imageio)
[![Maven Snapshot](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fcentral.sonatype.com%2Frepository%2Fmaven-snapshots%2Fcom%2Ftwelvemonkeys%2Fimageio%2Fimageio%2Fmaven-metadata.xml&label=development&server=https%3A%2F%2Foss.sonatype.org&color=slateblue)](https://central.sonatype.com/repository/maven-snapshots/com/twelvemonkeys/imageio/imageio/maven-metadata.xml)
[![StackOverflow](https://img.shields.io/badge/stack_overflow-twelvemonkeys-orange.svg)](https://stackoverflow.com/questions/tagged/twelvemonkeys) [![StackOverflow](https://img.shields.io/badge/stack_overflow-twelvemonkeys-orange.svg)](https://stackoverflow.com/questions/tagged/twelvemonkeys)
[![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/haraldk76/100) [![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/haraldk76/100)
![Logo](logo.png)
## About ## About
TwelveMonkeys ImageIO provides extended image file format support for the Java platform, through plugins for the `javax.imageio.*` package. TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
The main goal of this project is to provide support for file formats not covered by the JDK. These plugins extend the number of image file formats supported in Java, using the `javax.imageio.*` package.
Support for these formats is important, to be able to read data found The main purpose of this project is to provide support for formats not covered by the JRE itself.
Support for formats is important, both to be able to read data found
"in the wild", as well as to maintain access to data in legacy formats. "in the wild", as well as to maintain access to data in legacy formats.
As there is lots of legacy data out there, we see the need for open implementations of readers for popular formats. Because there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
The goal is to create a set of efficient and robust ImageIO plug-ins, that can be distributed independently.
---- ----
## File formats supported ## File formats supported
| Plugin | Format | Description | R | W | Metadata | Notes | | Plugin | Format | Description | Read | Write | Metadata | Notes |
| ------ | -------- |---------------------------------------------------------|:---:|:---:| -------- | ----- | | ------ | -------- | ----------- |:----:|:-----:| -------- | ----- |
| Batik | **SVG** | Scalable Vector Graphics | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/) | | Batik | **SVG** | Scalable Vector Graphics | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
| | WMF | MS Windows Metafile | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/) | | | WMF | MS Windows Metafile | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/bmp_metadata.html), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | ✔ | ✔ | Native & Standard |
| | CUR | MS Windows Cursor Format | ✔ | - | - | | | CUR | MS Windows Cursor Format | ✔ | - | - |
| | ICO | MS Windows Icon Format | ✔ | ✔ | - | | | ICO | MS Windows Icon Format | ✔ | ✔ | - |
| [DDS](https://github.com/haraldk/TwelveMonkeys/wiki/DDS-Plugin) | DDS | MS Direct Draw Surface Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | ✔ | - | Standard |
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | ✔ | | - |
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | ✔ | ✔ | - | | [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | ✔ | ✔ | Standard |
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | ✔ | ✔ | Native & Standard |
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | ✔ | | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | | **JPEG Lossless** | | ✔ | - | Native & Standard |
| | JPEG Lossless | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | Standard |
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | | DCX | Multi-page PCX fax document | ✔ | - | Standard |
| | DCX | Multi-page PCX fax document | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple Mac Paint Picture Format | ✔ | - | - |
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | ✔ | Standard |
| | PNTG | Apple MacPaint Picture Format | | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | | PBM | NetPBM Portable Bit Map | ✔ | - | Standard |
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | | PGM | NetPBM Portable Grey Map | ✔ | - | Standard |
| | PBM | NetPBM Portable Bit Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | | PPM | NetPBM Portable Pix Map | ✔ | | Standard |
| | PGM | NetPBM Portable Grey Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | | PFM | Portable Float Map | ✔ | - | Standard |
| | PPM | NetPBM Portable Pix Map | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | ✔ | - | Native & Standard |
| | PFM | Portable Float Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | | PSB | Adobe Photoshop Large Document | ✔ | - | Native & Standard |
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | ✔ | (✔) | Native, [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | ✔ | - | Standard |
| | PSB | Adobe Photoshop Large Document | ✔ | - | Native, [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | ✔ | | Standard |
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | |ThumbsDB| Thumbs.db| MS Windows Thumbs DB | ✔ | - | - | OLE2 Compound Document based format only
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | ✔ | ✔ | Native & Standard |
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | ✔ | - | - | OLE2 Compound Document based format only | | | BigTIFF | | ✔ | - | Native & Standard |
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | ✔ | - | Standard | In progress
| | BigTIFF | | ✔ | | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | | XWD | XWD | X11 Window Dump Format | ✔ | - | Standard |
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| XWD | XWD | X11 Window Dump Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html), **Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html),
and make sure you use an updated and secure version.* and make sure you use version 1.14 or later.*
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
[JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html). [JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html).
@@ -171,7 +165,7 @@ finally {
``` ```
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
[Java Image I/O API Guide](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html) [Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
from Oracle. from Oracle.
#### Adobe Clipping Path support #### Adobe Clipping Path support
@@ -223,53 +217,16 @@ BufferedImageOp ditherer = new DiffusionDither();
BufferedImage output = ditherer.filter(input, null); BufferedImage output = ditherer.filter(input, null);
``` ```
#### Working with damaged images
When using the normal patterns for loading images, trying to load a damaged image will result in an `IOException` being thrown.
```java
BufferedImage image = null;
try {
image = ImageIO.read(file);
} catch (IOException exception) {
// Handle, log a warning/error etc
}
```
In this scenario, if the image is damaged, and `ImageIO.read` throws an exception, `image` is still `null` - it's not possible for a function to both return a value and throw an exception.
However, in some cases it may be possible to get usable image data from a damaged image. The way to do this is use an `ImageReadParam` to set a `BufferedImage` as a destination.
```java
int width = reader.getWidth(0);
int height = reader.getHeight(0);
ImageTypeSpecifier imageType = reader.getRawImageType(0);
BufferedImage image = imageType.createBufferedImage(width, height);
ImageReadParam param = reader.getDefaultReadParam();
param.setDestination(image);
try {
reader.read(0, param);
}
catch (IOException e) {
// Handle, log a warning/error etc
}
```
In theory this should work for all plugins, but the result is very much plugin/implementation specific. With some formats and some forms of damaged file, you may get an image that is mostly useful.
However, you should be prepared for the possibility this just gives a blank or empty image.
## Building ## Building
Download the project (using [Git](https://git-scm.com/downloads)): Download the project (using [Git](http://git-scm.com/downloads)):
$ git clone git@github.com:haraldk/TwelveMonkeys.git $ git clone git@github.com:haraldk/TwelveMonkeys.git
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys` This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
folder, and issue the command below to build. folder, and issue the command below to build.
Build the project (using [Maven](https://maven.apache.org/download.cgi)): Build the project (using [Maven](http://maven.apache.org/download.cgi)):
$ mvn package $ mvn package
@@ -316,12 +273,12 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId> <artifactId>imageio-jpeg</artifactId>
<version>3.13.1</version> <version>3.6.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId> <artifactId>imageio-tiff</artifactId>
<version>3.13.1</version> <version>3.6.3</version>
</dependency> </dependency>
<!-- <!--
@@ -331,17 +288,7 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
<dependency> <dependency>
<groupId>com.twelvemonkeys.servlet</groupId> <groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId> <artifactId>servlet</artifactId>
<version>3.13.1</version> <version>3.6.3</version>
</dependency>
<!--
Or Jakarta version, for Servlet API 5.0
-->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.13.1</version>
<classifier>jakarta</classifier>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
@@ -350,18 +297,18 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path: To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
twelvemonkeys-common-lang-3.13.1.jar twelvemonkeys-common-lang-3.6.3.jar
twelvemonkeys-common-io-3.13.1.jar twelvemonkeys-common-io-3.6.3.jar
twelvemonkeys-common-image-3.13.1.jar twelvemonkeys-common-image-3.6.3.jar
twelvemonkeys-imageio-core-3.13.1.jar twelvemonkeys-imageio-core-3.6.3.jar
twelvemonkeys-imageio-metadata-3.13.1.jar twelvemonkeys-imageio-metadata-3.6.3.jar
twelvemonkeys-imageio-jpeg-3.13.1.jar twelvemonkeys-imageio-jpeg-3.6.3.jar
twelvemonkeys-imageio-tiff-3.13.1.jar twelvemonkeys-imageio-tiff-3.6.3.jar
#### Deploying the plugins in a web app #### Deploying the plugins in a web app
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it does not work well with Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
servlet contexts as-is. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder. servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all. Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context). In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
@@ -397,16 +344,6 @@ or other ImageIO plugins as well.
Another safe option, is to place the JAR files in the application server's shared or common lib folder. Another safe option, is to place the JAR files in the application server's shared or common lib folder.
##### Jakarta Servlet Support
For those transitioning from the old `javax.servlet` to the new `jakarta.servlet` package, there is a separate
dependency available. It contains exactly the same servlet classes as mentioned above, but built against the new Jakarta EE
packages. The dependency has the same group name and identifier as before, but a `jakarta` *classifier* appended, to
distinguish it from the non-Jakarta package.
See the [Maven dependency example](#maven-dependency-example) for how to enable it with Maven.
Gradle or other build tools will have similar options.
#### Including the plugins in a "fat" JAR #### Including the plugins in a "fat" JAR
The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar. The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar.
@@ -432,52 +369,79 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
### Links to prebuilt binaries ### Links to prebuilt binaries
##### Latest version (3.13.1) ##### Latest version (3.6.3)
The latest version that will run on Java 7 is 3.9.4. Later versions will require Java 8 or later. Requires Java 7 or later.
Common dependencies Common dependencies
* [common-lang-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.13.1/common-lang-3.13.1.jar) * [common-lang-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.6.3/common-lang-3.6.3.jar)
* [common-io-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.13.1/common-io-3.13.1.jar) * [common-io-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.6.3/common-io-3.6.3.jar)
* [common-image-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.13.1/common-image-3.13.1.jar) * [common-image-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.6.3/common-image-3.6.3.jar)
ImageIO dependencies ImageIO dependencies
* [imageio-core-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.13.1/imageio-core-3.13.1.jar) * [imageio-core-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.3/imageio-core-3.6.3.jar)
* [imageio-metadata-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.13.1/imageio-metadata-3.13.1.jar) * [imageio-metadata-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.6.3/imageio-metadata-3.6.3.jar)
ImageIO plugins ImageIO plugins
* [imageio-bmp-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.13.1/imageio-bmp-3.13.1.jar) * [imageio-bmp-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.3/imageio-bmp-3.6.3.jar)
* [imageio-dds-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-dds/3.13.1/imageio-dds-3.13.1.jar) * [imageio-hdr-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.6.3/imageio-hdr-3.6.3.jar)
* [imageio-hdr-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.13.1/imageio-hdr-3.13.1.jar) * [imageio-icns-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.6.3/imageio-icns-3.6.3.jar)
* [imageio-icns-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.13.1/imageio-icns-3.13.1.jar) * [imageio-iff-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.6.3/imageio-iff-3.6.3.jar)
* [imageio-iff-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.13.1/imageio-iff-3.13.1.jar) * [imageio-jpeg-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.6.3/imageio-jpeg-3.6.3.jar)
* [imageio-jpeg-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.13.1/imageio-jpeg-3.13.1.jar) * [imageio-pcx-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.6.3/imageio-pcx-3.6.3.jar)
* [imageio-pcx-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.13.1/imageio-pcx-3.13.1.jar) * [imageio-pict-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.6.3/imageio-pict-3.6.3.jar)
* [imageio-pict-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.13.1/imageio-pict-3.13.1.jar) * [imageio-pnm-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.6.3/imageio-pnm-3.6.3.jar)
* [imageio-pnm-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.13.1/imageio-pnm-3.13.1.jar) * [imageio-psd-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.6.3/imageio-psd-3.6.3.jar)
* [imageio-psd-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.13.1/imageio-psd-3.13.1.jar) * [imageio-sgi-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.6.3/imageio-sgi-3.6.3.jar)
* [imageio-sgi-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.13.1/imageio-sgi-3.13.1.jar) * [imageio-tga-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.6.3/imageio-tga-3.6.3.jar)
* [imageio-tga-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.13.1/imageio-tga-3.13.1.jar) * [imageio-thumbsdb-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.6.3/imageio-thumbsdb-3.6.3.jar)
* [imageio-thumbsdb-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.13.1/imageio-thumbsdb-3.13.1.jar) * [imageio-tiff-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.6.3/imageio-tiff-3.6.3.jar)
* [imageio-tiff-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.13.1/imageio-tiff-3.13.1.jar)
* [imageio-webp-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.13.1/imageio-webp-3.13.1.jar)
* [imageio-xwd-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.13.1/imageio-xwd-3.13.1.jar)
ImageIO plugins requiring 3rd party libs ImageIO plugins requiring 3rd party libs
* [imageio-batik-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.13.1/imageio-batik-3.13.1.jar) * [imageio-batik-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.6.3/imageio-batik-3.6.3.jar)
Photoshop Path support for ImageIO Photoshop Path support for ImageIO
* [imageio-clippath-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.13.1/imageio-clippath-3.13.1.jar) * [imageio-clippath-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.6.3/imageio-clippath-3.6.3.jar)
Servlet support Servlet support
* [servlet-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.13.1/servlet-3.13.1.jar) for legacy Java EE (javax.servlet) * [servlet-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.6.3/servlet-3.6.3.jar)
* [servlet-3.13.1-jakarta.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.13.1/servlet-3.13.1-jakrta.jar) for Jakarta EE (jakarta.servlet)
##### Old version (3.0.x)
Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 or later*.
Common dependencies
* [common-lang-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
* [common-io-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
* [common-image-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
ImageIO dependencies
* [imageio-core-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
* [imageio-metadata-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
ImageIO plugins
* [imageio-jpeg-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
* [imageio-tiff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
* [imageio-psd-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
* [imageio-pict-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
* [imageio-iff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
* [imageio-icns-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
* [imageio-ico-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
* [imageio-thumbsdb-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
ImageIO plugins requiring 3rd party libs
* [imageio-batik-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
* [imageio-jmagick-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
Servlet support
* [servlet-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
## License ## License
This project is provided under the OSI approved [BSD license](https://opensource.org/licenses/BSD-3-Clause): This project is provided under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
Copyright (c) 2008-2022, Harald Kuhr Copyright (c) 2008-2020, Harald Kuhr
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -509,9 +473,8 @@ This project is provided under the OSI approved [BSD license](https://opensource
q: How do I use it? q: How do I use it?
a: The easiest way is to build your own project using Maven, Gradle or other build tool with dependency management, a: The easiest way is to build your own project using Maven, and just add dependencies to the specific plug-ins you need.
and just add dependencies to the specific plug-ins you need. If you don't use Maven, make sure you have all the necessary JARs in classpath. See the Install section above.
If you don't use such a build tool, make sure you have all the necessary JARs in classpath. See the Install section above.
q: What changes do I have to make to my code in order to use the plug-ins? q: What changes do I have to make to my code in order to use the plug-ins?
@@ -529,41 +492,27 @@ q: How does it work?
a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses a service lookup mechanism, to discover plug-ins at runtime. a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses a service lookup mechanism, to discover plug-ins at runtime.
All you have to do, is to make sure you have the TwelveMonkeys ImageIO JARs in your classpath. All you have have to do, is to make sure you have the TwelveMonkeys JARs in your classpath.
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](https://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html). You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](http://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and
utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
the Sun/Oracle provided `JPEGImageReader`, `BMPImageReader` `TIFFImageReader`, and the Apple provided `TIFFImageReader` on OS X, the Sun/Oracle provided `JPEGImageReader` and `BMPImageReader`, and the Apple provided `TIFFImageReader` on OS X,
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
cases you'll end up using the TwelveMonkeys plug-ins instead. cases you'll end up using the TwelveMonkeys plug-ins instead.
q: Why is there no support for common formats like GIF or PNG? q: Why is there no support for common formats like GIF or PNG?
a: The short answer is simply that the built-in support in ImageIO for these formats are considered good enough as-is. a: The short answer is simply that the built-in support in ImageIO for these formats are good enough as-is.
If you are looking for better PNG write performance on Java 7 and 8, see [JDK9 PNG Writer Backport](https://github.com/gredler/jdk9-png-writer-backport). If you are looking for better PNG write performance on Java 7 and 8, see [JDK9 PNG Writer Backport](https://github.com/gredler/jdk9-png-writer-backport).
q: When is the next release? What is the current release schedule?
a: The goal is to make monthly releases, containing bug fixes and minor new features.
And quarterly releases with more "major" features.
q: I love this project! How can I help?
a: Have a look at the open issues, and see if there are any issues you can help fix, or provide sample file or create test cases for.
It is also possible for you or your organization to become a sponsor, through GitHub Sponsors.
Providing funding will allow us to spend more time on fixing bugs and implementing new features.
q: What about JAI? Several of the formats are already supported by JAI. q: What about JAI? Several of the formats are already supported by JAI.
a: While JAI (and jai-imageio in particular) have support for some of the same formats, JAI has some major issues. a: While JAI (and jai-imageio in particular) have support for some of the same formats, JAI has some major issues.
The most obvious being: The most obvious being:
- It's not actively developed. No issue has been fixed for years. - It's not actively developed. No issues has been fixed for years.
- To get full format support, you need native libs. - To get full format support, you need native libs.
Native libs does not exist for several popular platforms/architectures, and further the native libs are not open source. Native libs does not exist for several popular platforms/architectures, and further the native libs are not open source.
Some environments may also prevent deployment of native libs, which brings us back to square one. Some environments may also prevent deployment of native libs, which brings us back to square one.
-5
View File
@@ -1,5 +0,0 @@
# Security Policy
To report a security issue, please disclose it at [security advisory](https://github.com/haraldk/TwelveMonkeys/security/advisories/new).
Vulnerabilities will be disclosed in a best effort base.
+1 -11
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.13.2-SNAPSHOT</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.bom</groupId> <groupId>com.twelvemonkeys.bom</groupId>
@@ -63,11 +63,6 @@
<artifactId>imageio-hdr</artifactId> <artifactId>imageio-hdr</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-dds</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-icns</artifactId> <artifactId>imageio-icns</artifactId>
@@ -128,11 +123,6 @@
<artifactId>imageio-tiff</artifactId> <artifactId>imageio-tiff</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-xwd</artifactId> <artifactId>imageio-xwd</artifactId>
+9 -15
View File
@@ -4,19 +4,15 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.13.2-SNAPSHOT</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-image</artifactId> <artifactId>common-image</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>TwelveMonkeys :: Common :: Image</name> <name>TwelveMonkeys :: Common :: Image</name>
<description> <description>
TwelveMonkeys Common image support classes. The TwelveMonkeys Common Image support
</description> </description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.image</project.jpms.module.name>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
@@ -28,14 +24,12 @@
<artifactId>common-io</artifactId> <artifactId>common-io</artifactId>
</dependency> </dependency>
<dependency>
<groupId>jmagick</groupId>
<artifactId>jmagick</artifactId>
<version>6.2.4</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> </project>
@@ -34,13 +34,7 @@ import java.awt.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage; import java.awt.image.*;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorModel;
import java.awt.image.ImagingOpException;
import java.awt.image.Raster;
import java.awt.image.RasterOp;
import java.awt.image.WritableRaster;
/** /**
* This is a drop-in replacement for {@link java.awt.image.AffineTransformOp}. * This is a drop-in replacement for {@link java.awt.image.AffineTransformOp}.
@@ -76,7 +70,6 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
delegate = new java.awt.image.AffineTransformOp(xform, interpolationType); delegate = new java.awt.image.AffineTransformOp(xform, interpolationType);
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public BufferedImage filter(final BufferedImage src, BufferedImage dst) { public BufferedImage filter(final BufferedImage src, BufferedImage dst) {
try { try {
@@ -87,9 +80,10 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
dst = createCompatibleDestImage(src, src.getColorModel()); dst = createCompatibleDestImage(src, src.getColorModel());
} }
Graphics2D g2d = dst.createGraphics(); Graphics2D g2d = null;
try { try {
g2d = dst.createGraphics();
int interpolationType = delegate.getInterpolationType(); int interpolationType = delegate.getInterpolationType();
if (interpolationType > 0) { if (interpolationType > 0) {
@@ -115,7 +109,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
return dst; return dst;
} }
finally { finally {
g2d.dispose(); if (g2d != null) {
g2d.dispose();
}
} }
} }
} }
@@ -79,7 +79,7 @@ public final class BufferedImageFactory {
private int scanSize; private int scanSize;
private ColorModel sourceColorModel; private ColorModel sourceColorModel;
private Hashtable<?, ?> sourceProperties; // ImageConsumer API dictates Hashtable private Hashtable sourceProperties; // ImageConsumer API dictates Hashtable
private Object sourcePixels; private Object sourcePixels;
@@ -91,21 +91,21 @@ public final class BufferedImageFactory {
/** /**
* Creates a {@code BufferedImageFactory}. * Creates a {@code BufferedImageFactory}.
* @param source the source image * @param pSource the source image
* @throws IllegalArgumentException if {@code source == null} * @throws IllegalArgumentException if {@code pSource == null}
*/ */
public BufferedImageFactory(final Image source) { public BufferedImageFactory(final Image pSource) {
this(source != null ? source.getSource() : null); this(pSource != null ? pSource.getSource() : null);
} }
/** /**
* Creates a {@code BufferedImageFactory}. * Creates a {@code BufferedImageFactory}.
* @param source the source image producer * @param pSource the source image producer
* @throws IllegalArgumentException if {@code source == null} * @throws IllegalArgumentException if {@code pSource == null}
*/ */
public BufferedImageFactory(final ImageProducer source) { public BufferedImageFactory(final ImageProducer pSource) {
Validate.notNull(source, "source"); Validate.notNull(pSource, "source");
producer = source; producer = pSource;
} }
/** /**
@@ -155,44 +155,44 @@ public final class BufferedImageFactory {
/** /**
* Sets the source region (AOI) for the new image. * Sets the source region (AOI) for the new image.
* *
* @param region the source region * @param pRegion the source region
*/ */
public void setSourceRegion(final Rectangle region) { public void setSourceRegion(final Rectangle pRegion) {
// Re-fetch everything, if region changed // Re-fetch everything, if region changed
if (x != region.x || y != region.y || width != region.width || height != region.height) { if (x != pRegion.x || y != pRegion.y || width != pRegion.width || height != pRegion.height) {
dispose(); dispose();
} }
x = region.x; x = pRegion.x;
y = region.y; y = pRegion.y;
width = region.width; width = pRegion.width;
height = region.height; height = pRegion.height;
} }
/** /**
* Sets the source subsampling for the new image. * Sets the source subsampling for the new image.
* *
* @param xSubsampling horizontal subsampling factor * @param pXSub horizontal subsampling factor
* @param ySubsampling vertical subsampling factor * @param pYSub vertical subsampling factor
*/ */
public void setSourceSubsampling(int xSubsampling, int ySubsampling) { public void setSourceSubsampling(int pXSub, int pYSub) {
// Re-fetch everything, if subsampling changed // Re-fetch everything, if subsampling changed
if (xSub != xSubsampling || ySub != ySubsampling) { if (xSub != pXSub || ySub != pYSub) {
dispose(); dispose();
} }
if (xSubsampling > 1) { if (pXSub > 1) {
xSub = xSubsampling; xSub = pXSub;
} }
if (ySubsampling > 1) { if (pYSub > 1) {
ySub = ySubsampling; ySub = pYSub;
} }
} }
private synchronized void doFetch(final boolean colorModelOnly) throws ImageConversionException { private synchronized void doFetch(boolean pColorModelOnly) throws ImageConversionException {
if (!fetching && (!colorModelOnly && buffered == null || buffered == null && sourceColorModel == null)) { if (!fetching && (!pColorModelOnly && buffered == null || buffered == null && sourceColorModel == null)) {
// NOTE: Subsampling is only applied if extracting full image // NOTE: Subsampling is only applied if extracting full image
if (!colorModelOnly && (xSub > 1 || ySub > 1)) { if (!pColorModelOnly && (xSub > 1 || ySub > 1)) {
// If only sampling a region, the region must be scaled too // If only sampling a region, the region must be scaled too
if (width > 0 && height > 0) { if (width > 0 && height > 0) {
width = (width + xSub - 1) / xSub; width = (width + xSub - 1) / xSub;
@@ -207,41 +207,38 @@ public final class BufferedImageFactory {
// Start fetching // Start fetching
fetching = true; fetching = true;
readColorModelOnly = colorModelOnly; readColorModelOnly = pColorModelOnly;
producer.startProduction(consumer); // Note: If single-thread (synchronous), this call will block producer.startProduction(consumer); // Note: If single-thread (synchronous), this call will block
// Wait until the producer wakes us up, by calling imageComplete // Wait until the producer wakes us up, by calling imageComplete
while (fetching) { while (fetching) {
try { try {
wait(200L); wait(200l);
} }
catch (InterruptedException e) { catch (InterruptedException e) {
throw new ImageConversionException("Image conversion aborted: " + e.getMessage(), e); throw new ImageConversionException("Image conversion aborted: " + e.getMessage(), e);
} }
} }
try { if (consumerException != null) {
if (consumerException != null) { throw new ImageConversionException("Image conversion failed: " + consumerException.getMessage(), consumerException);
throw new ImageConversionException("Image conversion failed: " + consumerException.getMessage(), consumerException);
}
if (colorModelOnly) {
createColorModel();
}
else {
createBuffered();
}
} }
finally {
// Clean up, in case any objects are copied/cloned, so we can free resources if (pColorModelOnly) {
freeResources(); createColorModel();
}
else {
createBuffered();
} }
} }
} }
private void createColorModel() { private void createColorModel() {
colorModel = sourceColorModel; colorModel = sourceColorModel;
// Clean up, in case any objects are copied/cloned, so we can free resources
freeResources();
} }
private void createBuffered() { private void createBuffered() {
@@ -256,9 +253,8 @@ public final class BufferedImageFactory {
} }
} }
if (buffered == null) { // Clean up, in case any objects are copied/cloned, so we can free resources
throw new ImageConversionException("Could not create BufferedImage"); freeResources();
}
} }
private void freeResources() { private void freeResources() {
@@ -284,27 +280,27 @@ public final class BufferedImageFactory {
/** /**
* Adds a progress listener to this factory. * Adds a progress listener to this factory.
* *
* @param listener the progress listener * @param pListener the progress listener
*/ */
public void addProgressListener(ProgressListener listener) { public void addProgressListener(ProgressListener pListener) {
if (listener == null) { if (pListener == null) {
return; return;
} }
if (listeners == null) { if (listeners == null) {
listeners = new CopyOnWriteArrayList<>(); listeners = new CopyOnWriteArrayList<ProgressListener>();
} }
listeners.add(listener); listeners.add(pListener);
} }
/** /**
* Removes a progress listener from this factory. * Removes a progress listener from this factory.
* *
* @param listener the progress listener * @param pListener the progress listener
*/ */
public void removeProgressListener(ProgressListener listener) { public void removeProgressListener(ProgressListener pListener) {
if (listener == null) { if (pListener == null) {
return; return;
} }
@@ -312,7 +308,7 @@ public final class BufferedImageFactory {
return; return;
} }
listeners.remove(listener); listeners.remove(pListener);
} }
/** /**
@@ -328,22 +324,21 @@ public final class BufferedImageFactory {
* Converts an array of {@code int} pixels to an array of {@code short} * Converts an array of {@code int} pixels to an array of {@code short}
* pixels. The conversion is done, by masking out the * pixels. The conversion is done, by masking out the
* <em>higher 16 bits</em> of the {@code int}. * <em>higher 16 bits</em> of the {@code int}.
* <p> *
* For any given {@code int}, the {@code short} value is computed as * For any given {@code int}, the {@code short} value is computed as
* follows: * follows:
* <blockquote>{@code * <blockquote>{@code
* short value = (short) (intValue & 0x0000ffff); * short value = (short) (intValue & 0x0000ffff);
* }</blockquote> * }</blockquote>
* </p>
* *
* @param inputPixels the pixel data to convert * @param pPixels the pixel data to convert
* @return an array of {@code short}s, same length as {@code inputPixels} * @return an array of {@code short}s, same lenght as {@code pPixels}
*/ */
private static short[] toShortPixels(int[] inputPixels) { private static short[] toShortPixels(int[] pPixels) {
short[] pixels = new short[inputPixels.length]; short[] pixels = new short[pPixels.length];
for (int i = 0; i < pixels.length; i++) { for (int i = 0; i < pixels.length; i++) {
pixels[i] = (short) (inputPixels[i] & 0xffff); pixels[i] = (short) (pPixels[i] & 0xffff);
} }
return pixels; return pixels;
@@ -356,17 +351,17 @@ public final class BufferedImageFactory {
* @see BufferedImageFactory#addProgressListener * @see BufferedImageFactory#addProgressListener
* @see BufferedImageFactory#removeProgressListener * @see BufferedImageFactory#removeProgressListener
*/ */
public interface ProgressListener extends EventListener { public static interface ProgressListener extends EventListener {
/** /**
* Reports progress to this listener. * Reports progress to this listener.
* Invoked by the {@code BufferedImageFactory} to report progress in * Invoked by the {@code BufferedImageFactory} to report progress in
* the image decoding. * the image decoding.
* *
* @param factory the factory reporting the progress * @param pFactory the factory reporting the progress
* @param percentage the percentage of progress * @param pPercentage the percentage of progress
*/ */
void progress(BufferedImageFactory factory, float percentage); void progress(BufferedImageFactory pFactory, float pPercentage);
} }
private class Consumer implements ImageConsumer { private class Consumer implements ImageConsumer {
@@ -451,18 +446,18 @@ public final class BufferedImageFactory {
processProgress(pY + pHeight); processProgress(pY + pHeight);
} }
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, short[] pixels, int offset, int scanSize) { public void setPixels(int pX, int pY, int pWidth, int pHeight, ColorModel pModel, short[] pPixels, int pOffset, int pScanSize) {
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize); setPixelsImpl(pX, pY, pWidth, pHeight, pModel, pPixels, pOffset, pScanSize);
} }
private void setColorModelOnce(final ColorModel colorModel) { private void setColorModelOnce(final ColorModel pModel) {
// NOTE: There seems to be a "bug" in AreaAveragingScaleFilter, as it // NOTE: There seems to be a "bug" in AreaAveragingScaleFilter, as it
// first passes the original color model through in setColorModel, then // first passes the original color model through in setColorModel, then
// later replaces it with the default RGB in the first setPixels call // later replaces it with the default RGB in the first setPixels call
// (this is probably allowed according to the spec, but it's a waste of time and space). // (this is probably allowed according to the spec, but it's a waste of time and space).
if (sourceColorModel != colorModel) { if (sourceColorModel != pModel) {
if (sourcePixels == null) { if (/*sourceColorModel == null ||*/ sourcePixels == null) {
sourceColorModel = colorModel; sourceColorModel = pModel;
} }
else { else {
throw new IllegalStateException("Change of ColorModel after pixel delivery not supported"); throw new IllegalStateException("Change of ColorModel after pixel delivery not supported");
@@ -475,16 +470,17 @@ public final class BufferedImageFactory {
} }
} }
@Override public void imageComplete(int pStatus) {
public void imageComplete(int status) {
fetching = false; fetching = false;
if (producer != null) { if (producer != null) {
producer.removeConsumer(this); producer.removeConsumer(this);
} }
if (status == ImageConsumer.IMAGEERROR) { switch (pStatus) {
consumerException = new ImageConversionException("ImageConsumer.IMAGEERROR"); case ImageConsumer.IMAGEERROR:
consumerException = new ImageConversionException("ImageConsumer.IMAGEERROR");
break;
} }
synchronized (BufferedImageFactory.this) { synchronized (BufferedImageFactory.this) {
@@ -492,18 +488,16 @@ public final class BufferedImageFactory {
} }
} }
@Override public void setColorModel(ColorModel pModel) {
public void setColorModel(ColorModel colorModel) { setColorModelOnce(pModel);
setColorModelOnce(colorModel);
} }
@Override public void setDimensions(int pWidth, int pHeight) {
public void setDimensions(int w, int h) {
if (width < 0) { if (width < 0) {
width = w - x; width = pWidth - x;
} }
if (height < 0) { if (height < 0) {
height = h - y; height = pHeight - y;
} }
// Hmm.. Special case, but is it a good idea? // Hmm.. Special case, but is it a good idea?
@@ -512,31 +506,27 @@ public final class BufferedImageFactory {
} }
} }
@Override public void setHints(int pHintflags) {
public void setHints(int hintFlags) {
// ignore // ignore
} }
@Override public void setPixels(int pX, int pY, int pWidth, int pHeight, ColorModel pModel, byte[] pPixels, int pOffset, int pScanSize) {
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, byte[] pixels, int offset, int scanSize) { setPixelsImpl(pX, pY, pWidth, pHeight, pModel, pPixels, pOffset, pScanSize);
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize);
} }
@Override public void setPixels(int pX, int pY, int pWeigth, int pHeight, ColorModel pModel, int[] pPixels, int pOffset, int pScanSize) {
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, int[] pixels, int offset, int scanSize) { if (pModel.getTransferType() == DataBuffer.TYPE_USHORT) {
if (colorModel.getTransferType() == DataBuffer.TYPE_USHORT) {
// NOTE: Workaround for limitation in ImageConsumer API // NOTE: Workaround for limitation in ImageConsumer API
// Convert int[] to short[], to be compatible with the ColorModel // Convert int[] to short[], to be compatible with the ColorModel
setPixelsImpl(x, y, width, height, colorModel, toShortPixels(pixels), offset, scanSize); setPixelsImpl(pX, pY, pWeigth, pHeight, pModel, toShortPixels(pPixels), pOffset, pScanSize);
} }
else { else {
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize); setPixelsImpl(pX, pY, pWeigth, pHeight, pModel, pPixels, pOffset, pScanSize);
} }
} }
@Override public void setProperties(Hashtable pProperties) {
public void setProperties(Hashtable properties) { sourceProperties = pProperties;
sourceProperties = properties;
} }
} }
@@ -45,8 +45,8 @@ import java.awt.image.BufferedImage;
*/ */
public class BufferedImageIcon implements Icon { public class BufferedImageIcon implements Icon {
private final BufferedImage image; private final BufferedImage image;
private final int width; private int width;
private final int height; private int height;
private final boolean fast; private final boolean fast;
public BufferedImageIcon(BufferedImage pImage) { public BufferedImageIcon(BufferedImage pImage) {
@@ -81,10 +81,11 @@ public class BufferedImageIcon implements Icon {
else { else {
//System.out.println("Scaling using interpolation"); //System.out.println("Scaling using interpolation");
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
AffineTransform transform = AffineTransform.getTranslateInstance(x, y); AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
transform.scale(width / (double) image.getWidth(), height / (double) image.getHeight()); xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
g2.drawImage(image, transform, null); RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(image, xform, null);
} }
} }
} }
@@ -47,10 +47,10 @@ import java.util.Random;
* |3|5|1| * |3|5|1|
* - - --> * - - -->
* </p> * </p>
* <table border="1"> * <table border="1" cellpadding="4" cellspacing="0">
* <caption>Floyd-Steinberg error-diffusion weights</caption> * <caption>Floyd-Steinberg error-diffusion weights</caption>
* <tr><td style="background:#000000">&nbsp;</td><td class="TableHeadingColor" * <tr><td bgcolor="#000000">&nbsp;</td><td class="TableHeadingColor"
* style="text-align:center">x</td><td>7/16</td></tr> * align="center">x</td><td>7/16</td></tr>
* <tr><td>3/16</td><td>5/16</td><td>1/16</td></tr> * <tr><td>3/16</td><td>5/16</td><td>1/16</td></tr>
* </table> * </table>
* <p> * <p>
@@ -162,7 +162,7 @@ public final class ImageUtil {
/** /**
* The sharpen kernel. Uses the following 3 by 3 matrix: * The sharpen kernel. Uses the following 3 by 3 matrix:
* <table border="1"> * <table border="1" cellspacing="0">
* <caption>Sharpen Kernel Matrix</caption> * <caption>Sharpen Kernel Matrix</caption>
* <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr> * <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr>
* <tr><td>-0.3</td><td>2.2</td><td>-0.3</td></tr> * <tr><td>-0.3</td><td>2.2</td><td>-0.3</td></tr>
@@ -844,7 +844,7 @@ public final class ImageUtil {
return false; return false;
} }
for (int i = 0; i < mapSize1; i++) { for (int i = 0; i > mapSize1; i++) {
if (icm1.getRGB(i) != icm2.getRGB(i)) { if (icm1.getRGB(i) != icm2.getRGB(i)) {
return false; return false;
} }
@@ -1078,7 +1078,7 @@ public final class ImageUtil {
/** /**
* Sharpens an image using a convolution matrix. * Sharpens an image using a convolution matrix.
* The sharpen kernel used, is defined by the following 3 by 3 matrix: * The sharpen kernel used, is defined by the following 3 by 3 matrix:
* <table border="1"> * <table border="1" cellspacing="0">
* <caption>Sharpen Kernel Matrix</caption> * <caption>Sharpen Kernel Matrix</caption>
* <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr> * <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr>
* <tr><td>-0.3</td><td>2.2</td><td>-0.3</td></tr> * <tr><td>-0.3</td><td>2.2</td><td>-0.3</td></tr>
@@ -1100,7 +1100,7 @@ public final class ImageUtil {
/** /**
* Sharpens an image using a convolution matrix. * Sharpens an image using a convolution matrix.
* The sharpen kernel used, is defined by the following 3 by 3 matrix: * The sharpen kernel used, is defined by the following 3 by 3 matrix:
* <table border="1"> * <table border="1" cellspacing="0">
* <caption>Sharpen Kernel Matrix</caption> * <caption>Sharpen Kernel Matrix</caption>
* <tr><td>0.0</td><td>-{@code pAmount}</td><td>0.0</td></tr> * <tr><td>0.0</td><td>-{@code pAmount}</td><td>0.0</td></tr>
* <tr><td>-{@code pAmount}</td> * <tr><td>-{@code pAmount}</td>
@@ -587,7 +587,6 @@ class IndexImage {
* @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead! * @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead!
* This version will be removed in a later version of the API. * This version will be removed in a later version of the API.
*/ */
@Deprecated
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) { public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY); return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
} }
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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.
*/
package com.twelvemonkeys.image;
/**
* Magick
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/Magick.java#1 $
*/
final class Magick {
static final boolean DEBUG = useDebug();
private static boolean useDebug() {
try {
return "TRUE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.magick.debug"));
}
catch (Throwable t) {
// Most probably in case of a SecurityManager
return false;
}
}
private Magick() {}
}
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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.
*/
package com.twelvemonkeys.image;
import com.twelvemonkeys.lang.SystemUtil;
import magick.MagickImage;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
/**
* This class accelerates certain graphics operations, using
* JMagick and ImageMagick, if available.
* If those libraries are not installed, this class silently does nothing.
* <p>
* Set the system property {@code "com.twelvemonkeys.image.accel"} to
* {@code false}, to disable, even if JMagick is installed.
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickAccelerator.java#3 $
*/
final class MagickAccelerator {
private static final boolean DEBUG = Magick.DEBUG;
private static final boolean USE_MAGICK = useMagick();
private static final int RESAMPLE_OP = 0;
private static Class[] nativeOp = new Class[1];
static {
try {
nativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
}
catch (ClassNotFoundException e) {
System.err.println("Could not find class: " + e);
}
}
private static boolean useMagick() {
try {
boolean available = SystemUtil.isClassAvailable("magick.MagickImage");
if (DEBUG && !available) {
System.err.print("ImageMagick bindings not available.");
}
boolean useMagick =
available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel"));
if (DEBUG) {
System.err.println(
useMagick
? "Will use ImageMagick bindings to accelerate image resampling operations."
: "Will not use ImageMagick to accelerate image resampling operations."
);
}
return useMagick;
}
catch (Throwable t) {
// Most probably in case of a SecurityManager
System.err.println("Could not enable ImageMagick bindings: " + t);
return false;
}
}
private static int getNativeOpIndex(Class pOpClass) {
for (int i = 0; i < nativeOp.length; i++) {
if (pOpClass == nativeOp[i]) {
return i;
}
}
return -1;
}
public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) {
if (!USE_MAGICK) {
return null;
}
BufferedImage result = null;
switch (getNativeOpIndex(pOperation.getClass())) {
case RESAMPLE_OP:
ResampleOp resample = (ResampleOp) pOperation;
result = resampleMagick(pInput, resample.width, resample.height, resample.filterType);
// NOTE: If output parameter is non-null, we have to return that
// image, instead of result
if (pOutput != null) {
//pOutput.setData(result.getRaster()); // Fast, but less compatible
// NOTE: For some reason, this is sometimes super-slow...?
ImageUtil.drawOnto(pOutput, result);
result = pOutput;
}
break;
default:
// Simply fall through, allowing acceleration to be added later
break;
}
return result;
}
private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) {
// Convert to Magick, scale and convert back
MagickImage image = null;
MagickImage scaled = null;
try {
image = MagickUtil.toMagick(pSrc);
long start = 0;
if (DEBUG) {
start = System.currentTimeMillis();
}
// NOTE: setFilter affects zoomImage, NOT scaleImage
image.setFilter(pFilterType);
scaled = image.zoomImage(pWidth, pHeight);
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
if (DEBUG) {
long time = System.currentTimeMillis() - start;
System.out.println("Filtered: " + time + " ms");
}
return MagickUtil.toBuffered(scaled);
}
//catch (MagickException e) {
catch (Exception e) {
// NOTE: Stupid workaround: If MagickException is caught, a
// NoClassDefFoundError is thrown, when MagickException class is
// unavailable...
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new ImageConversionException(e.getMessage(), e);
}
finally {
// NOTE: ImageMagick might be unstable after a while, if image data
// is not deallocated. The GC/finalize method handles this, but in
// special circumstances, it's not triggered often enough.
if (image != null) {
image.destroyImages();
}
if (scaled != null) {
scaled.destroyImages();
}
}
}
}
@@ -0,0 +1,621 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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.
*/
package com.twelvemonkeys.image;
import magick.ImageType;
import magick.MagickException;
import magick.MagickImage;
import magick.PixelPacket;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.*;
/**
* Utility for converting JMagick {@code MagickImage}s to standard Java
* {@code BufferedImage}s and back.
* <p>
* <em>NOTE: This class is considered an implementation detail and not part of
* the public API. This class is subject to change without further notice.
* You have been warned. :-)</em>
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickUtil.java#4 $
*/
public final class MagickUtil {
// IMPORTANT NOTE: Disaster happens if any of these constants are used outside this class
// because you then have a dependency on MagickException (this is due to Java class loading
// and initialization magic).
// Do not use outside this class. If the constants need to be shared, move to Magick or ImageUtil.
/** Color Model usesd for bilevel (B/W) */
private static final IndexColorModel CM_MONOCHROME = MonochromeColorModel.getInstance();
/** Color Model usesd for raw ABGR */
private static final ColorModel CM_COLOR_ALPHA =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8, 8, 8, 8},
true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
/** Color Model usesd for raw BGR */
private static final ColorModel CM_COLOR_OPAQUE =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8, 8, 8},
false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
/** Color Model usesd for raw RGB */
//private static final ColorModel CM_COLOR_RGB = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x0);
/** Color Model usesd for raw GRAY + ALPHA */
private static final ColorModel CM_GRAY_ALPHA =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
/** Color Model usesd for raw GRAY */
private static final ColorModel CM_GRAY_OPAQUE =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
/** Band offsets for raw ABGR */
private static final int[] BAND_OFF_TRANS = new int[] {3, 2, 1, 0};
/** Band offsets for raw BGR */
private static final int[] BAND_OFF_OPAQUE = new int[] {2, 1, 0};
/** The point at {@code 0, 0} */
private static final Point LOCATION_UPPER_LEFT = new Point(0, 0);
private static final boolean DEBUG = Magick.DEBUG;
// Only static members and methods
private MagickUtil() {}
/**
* Converts a {@code MagickImage} to a {@code BufferedImage}.
* <p>
* The conversion depends on {@code pImage}'s {@code ImageType}:
* </p>
* <dl>
* <dt>{@code ImageType.BilevelType}</dt>
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_BINARY}</dd>
*
* <dt>{@code ImageType.GrayscaleType}</dt>
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_GRAY}</dd>
* <dt>{@code ImageType.GrayscaleMatteType}</dt>
* <dd>{@code BufferedImage} of type {@code TYPE_USHORT_GRAY}</dd>
*
* <dt>{@code ImageType.PaletteType}</dt>
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_BINARY} (for images
* with a palette of &lt;= 16 colors) or {@code TYPE_BYTE_INDEXED}</dd>
* <dt>{@code ImageType.PaletteMatteType}</dt>
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_BINARY} (for images
* with a palette of &lt;= 16 colors) or {@code TYPE_BYTE_INDEXED}</dd>
*
* <dt>{@code ImageType.TrueColorType}</dt>
* <dd>{@code BufferedImage} of type {@code TYPE_3BYTE_BGR}</dd>
* <dt>{@code ImageType.TrueColorPaletteType}</dt>
* <dd>{@code BufferedImage} of type {@code TYPE_4BYTE_ABGR}</dd>
* </dl>
*
* @param pImage the original {@code MagickImage}
* @return a new {@code BufferedImage}
*
* @throws IllegalArgumentException if {@code pImage} is {@code null}
* or if the {@code ImageType} is not one mentioned above.
* @throws MagickException if an exception occurs during conversion
*
* @see BufferedImage
*/
public static BufferedImage toBuffered(MagickImage pImage) throws MagickException {
if (pImage == null) {
throw new IllegalArgumentException("image == null");
}
long start = 0L;
if (DEBUG) {
start = System.currentTimeMillis();
}
BufferedImage image = null;
try {
switch (pImage.getImageType()) {
case ImageType.BilevelType:
image = bilevelToBuffered(pImage);
break;
case ImageType.GrayscaleType:
image = grayToBuffered(pImage, false);
break;
case ImageType.GrayscaleMatteType:
image = grayToBuffered(pImage, true);
break;
case ImageType.PaletteType:
image = paletteToBuffered(pImage, false);
break;
case ImageType.PaletteMatteType:
image = paletteToBuffered(pImage, true);
break;
case ImageType.TrueColorType:
image = rgbToBuffered(pImage, false);
break;
case ImageType.TrueColorMatteType:
image = rgbToBuffered(pImage, true);
break;
case ImageType.ColorSeparationType:
image = cmykToBuffered(pImage, false);
break;
case ImageType.ColorSeparationMatteType:
image = cmykToBuffered(pImage, true);
break;
case ImageType.OptimizeType:
default:
throw new IllegalArgumentException("Unknown JMagick image type: " + pImage.getImageType());
}
}
finally {
if (DEBUG) {
long time = System.currentTimeMillis() - start;
System.out.println("Converted JMagick image type: " + pImage.getImageType() + " to BufferedImage: " + image);
System.out.println("Conversion to BufferedImage: " + time + " ms");
}
}
return image;
}
/**
* Converts a {@code BufferedImage} to a {@code MagickImage}.
* <p>
* The conversion depends on {@code pImage}'s {@code ColorModel}:
* </p>
* <dl>
* <dt>{@code IndexColorModel} with 1 bit b/w</dt>
* <dd>{@code MagickImage} of type {@code ImageType.BilevelType}</dd>
* <dt>{@code IndexColorModel} &gt; 1 bit,</dt>
* <dd>{@code MagickImage} of type {@code ImageType.PaletteType}
* or {@code MagickImage} of type {@code ImageType.PaletteMatteType}
* depending on <tt>ColorModel.getAlpha()</tt></dd>
*
* <dt>{@code ColorModel.getColorSpace().getType() == ColorSpace.TYPE_GRAY}</dt>
* <dd>{@code MagickImage} of type {@code ImageType.GrayscaleType}
* or {@code MagickImage} of type {@code ImageType.GrayscaleMatteType}
* depending on <tt>ColorModel.getAlpha()</tt></dd>
*
* <dt>{@code ColorModel.getColorSpace().getType() == ColorSpace.TYPE_RGB}</dt>
* <dd>{@code MagickImage} of type {@code ImageType.TrueColorType}
* or {@code MagickImage} of type {@code ImageType.TrueColorPaletteType}</dd>
* </dl>
*
* @param pImage the original {@code BufferedImage}
* @return a new {@code MagickImage}
*
* @throws IllegalArgumentException if {@code pImage} is {@code null}
* or if the {@code ColorModel} is not one mentioned above.
* @throws MagickException if an exception occurs during conversion
*
* @see BufferedImage
*/
public static MagickImage toMagick(BufferedImage pImage) throws MagickException {
if (pImage == null) {
throw new IllegalArgumentException("image == null");
}
long start = 0L;
if (DEBUG) {
start = System.currentTimeMillis();
}
try {
ColorModel cm = pImage.getColorModel();
if (cm instanceof IndexColorModel) {
// Handles both BilevelType, PaletteType and PaletteMatteType
return indexedToMagick(pImage, (IndexColorModel) cm, cm.hasAlpha());
}
switch (cm.getColorSpace().getType()) {
case ColorSpace.TYPE_GRAY:
// Handles GrayType and GrayMatteType
return grayToMagick(pImage, cm.hasAlpha());
case ColorSpace.TYPE_RGB:
// Handles TrueColorType and TrueColorMatteType
return rgbToMagic(pImage, cm.hasAlpha());
case ColorSpace.TYPE_CMY:
case ColorSpace.TYPE_CMYK:
case ColorSpace.TYPE_HLS:
case ColorSpace.TYPE_HSV:
// Other types not supported yet
default:
throw new IllegalArgumentException("Unknown buffered image type: " + pImage);
}
}
finally {
if (DEBUG) {
long time = System.currentTimeMillis() - start;
System.out.println("Conversion to MagickImage: " + time + " ms");
}
}
}
private static MagickImage rgbToMagic(BufferedImage pImage, boolean pAlpha) throws MagickException {
MagickImage image = new MagickImage();
BufferedImage buffered = ImageUtil.toBuffered(pImage, pAlpha ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
// Need to get data of sub raster, not the full data array, this is
// just a convenient way
Raster raster;
if (buffered.getRaster().getParent() != null) {
raster = buffered.getData(new Rectangle(buffered.getWidth(), buffered.getHeight()));
}
else {
raster = buffered.getRaster();
}
image.constituteImage(buffered.getWidth(), buffered.getHeight(), pAlpha ? "ABGR" : "BGR",
((DataBufferByte) raster.getDataBuffer()).getData());
return image;
}
private static MagickImage grayToMagick(BufferedImage pImage, boolean pAlpha) throws MagickException {
MagickImage image = new MagickImage();
// TODO: Make a fix for TYPE_USHORT_GRAY
// The code below does not seem to work (JMagick issues?)...
/*
if (pImage.getType() == BufferedImage.TYPE_USHORT_GRAY) {
short[] data = ((DataBufferUShort) pImage.getRaster().getDataBuffer()).getData();
int[] intData = new int[data.length];
for (int i = 0; i < data.length; i++) {
intData[i] = (data[i] & 0xffff) * 0xffff;
}
image.constituteImage(pImage.getWidth(), pImage.getHeight(), "I", intData);
System.out.println("storageClass: " + image.getStorageClass());
System.out.println("depth: " + image.getDepth());
System.out.println("imageType: " + image.getImageType());
}
else {
*/
BufferedImage buffered = ImageUtil.toBuffered(pImage, pAlpha ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_BYTE_GRAY);
// Need to get data of sub raster, not the full data array, this is
// just a convenient way
Raster raster;
if (buffered.getRaster().getParent() != null) {
raster = buffered.getData(new Rectangle(buffered.getWidth(), buffered.getHeight()));
}
else {
raster = buffered.getRaster();
}
image.constituteImage(buffered.getWidth(), buffered.getHeight(), pAlpha ? "ABGR" : "I", ((DataBufferByte) raster.getDataBuffer()).getData());
//}
return image;
}
private static MagickImage indexedToMagick(BufferedImage pImage, IndexColorModel pColorModel, boolean pAlpha) throws MagickException {
MagickImage image = rgbToMagic(pImage, pAlpha);
int mapSize = pColorModel.getMapSize();
image.setNumberColors(mapSize);
return image;
}
/*
public static MagickImage toMagick(BufferedImage pImage) throws MagickException {
if (pImage == null) {
throw new IllegalArgumentException("image == null");
}
final int width = pImage.getWidth();
final int height = pImage.getHeight();
// int ARGB -> byte RGBA conversion
// NOTE: This is ImageMagick Q16 compatible raw RGBA format with 16 bits/sample...
// For a Q8 build, we could probably go with half the space...
// NOTE: This is close to insanity, as it wastes extreme ammounts of memory
final int[] argb = new int[width];
final byte[] raw16 = new byte[width * height * 8];
for (int y = 0; y < height; y++) {
// Fetch one line of ARGB data
pImage.getRGB(0, y, width, 1, argb, 0, width);
for (int x = 0; x < width; x++) {
int pixel = (x + (y * width)) * 8;
raw16[pixel ] = (byte) ((argb[x] >> 16) & 0xff); // R
raw16[pixel + 2] = (byte) ((argb[x] >> 8) & 0xff); // G
raw16[pixel + 4] = (byte) ((argb[x] ) & 0xff); // B
raw16[pixel + 6] = (byte) ((argb[x] >> 24) & 0xff); // A
}
}
// Create magick image
ImageInfo info = new ImageInfo();
info.setMagick("RGBA"); // Raw RGBA samples
info.setSize(width + "x" + height); // String?!?
MagickImage image = new MagickImage(info);
image.setImageAttribute("depth", "8");
// Set pixel data in 16 bit raw RGBA format
image.blobToImage(info, raw16);
return image;
}
*/
/**
* Converts a bi-level {@code MagickImage} to a {@code BufferedImage}, of
* type {@code TYPE_BYTE_BINARY}.
*
* @param pImage the original {@code MagickImage}
* @return a new {@code BufferedImage}
*
* @throws MagickException if an exception occurs during conversion
*
* @see BufferedImage
*/
private static BufferedImage bilevelToBuffered(MagickImage pImage) throws MagickException {
// As there is no way to get the binary representation of the image,
// convert to gray, and the create a binary image from it
BufferedImage temp = grayToBuffered(pImage, false);
BufferedImage image = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CM_MONOCHROME);
ImageUtil.drawOnto(image, temp);
return image;
}
/**
* Converts a gray {@code MagickImage} to a {@code BufferedImage}, of
* type {@code TYPE_USHORT_GRAY} or {@code TYPE_BYTE_GRAY}.
*
* @param pImage the original {@code MagickImage}
* @param pAlpha keep alpha channel
* @return a new {@code BufferedImage}
*
* @throws MagickException if an exception occurs during conversion
*
* @see BufferedImage
*/
private static BufferedImage grayToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
Dimension size = pImage.getDimension();
int length = size.width * size.height;
int bands = pAlpha ? 2 : 1;
byte[] pixels = new byte[length * bands];
// TODO: Make a fix for 16 bit TYPE_USHORT_GRAY?!
// Note: The ordering AI or I corresponds to BufferedImage
// TYPE_CUSTOM and TYPE_BYTE_GRAY respectively
pImage.dispatchImage(0, 0, size.width, size.height, pAlpha ? "AI" : "I", pixels);
// Init databuffer with array, to avoid allocation of empty array
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
int[] bandOffsets = pAlpha ? new int[] {1, 0} : new int[] {0};
WritableRaster raster =
Raster.createInterleavedRaster(buffer, size.width, size.height,
size.width * bands, bands, bandOffsets, LOCATION_UPPER_LEFT);
return new BufferedImage(pAlpha ? CM_GRAY_ALPHA : CM_GRAY_OPAQUE, raster, pAlpha, null);
}
/**
* Converts a palette-based {@code MagickImage} to a
* {@code BufferedImage}, of type {@code TYPE_BYTE_BINARY} (for images
* with a palette of &lt;= 16 colors) or {@code TYPE_BYTE_INDEXED}.
*
* @param pImage the original {@code MagickImage}
* @param pAlpha keep alpha channel
* @return a new {@code BufferedImage}
*
* @throws MagickException if an exception occurs during conversion
*
* @see BufferedImage
*/
private static BufferedImage paletteToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
// Create indexcolormodel for the image
IndexColorModel cm;
try {
cm = createIndexColorModel(pImage.getColormap(), pAlpha);
}
catch (MagickException e) {
// NOTE: Some MagickImages incorrecly (?) reports to be paletteType,
// but does not have a colormap, this is a workaround.
return rgbToBuffered(pImage, pAlpha);
}
// As there is no way to get the indexes of an indexed image, convert to
// RGB, and the create an indexed image from it
BufferedImage temp = rgbToBuffered(pImage, pAlpha);
BufferedImage image;
if (cm.getMapSize() <= 16) {
image = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_BYTE_BINARY, cm);
}
else {
image = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, cm);
}
// Create transparent background for images containing alpha
if (pAlpha) {
Graphics2D g = image.createGraphics();
try {
g.setComposite(AlphaComposite.Clear);
g.fillRect(0, 0, temp.getWidth(), temp.getHeight());
}
finally {
g.dispose();
}
}
// NOTE: This is (surprisingly) much faster than using g2d.drawImage()..
// (Tests shows 20-30ms, vs. 600-700ms on the same image)
BufferedImageOp op = new CopyDither(cm);
op.filter(temp, image);
return image;
}
/**
* Creates an {@code IndexColorModel} from an array of
* {@code PixelPacket}s.
*
* @param pColormap the original colormap as a {@code PixelPacket} array
* @param pAlpha keep alpha channel
*
* @return a new {@code IndexColorModel}
*/
public static IndexColorModel createIndexColorModel(PixelPacket[] pColormap, boolean pAlpha) {
int[] colors = new int[pColormap.length];
// TODO: Verify if this is correct for alpha...?
int trans = pAlpha ? colors.length - 1 : -1;
//for (int i = 0; i < pColormap.length; i++) {
for (int i = pColormap.length - 1; i != 0; i--) {
PixelPacket color = pColormap[i];
if (pAlpha) {
colors[i] = (0xff - (color.getOpacity() & 0xff)) << 24 |
(color.getRed() & 0xff) << 16 |
(color.getGreen() & 0xff) << 8 |
(color.getBlue() & 0xff);
}
else {
colors[i] = (color.getRed() & 0xff) << 16 |
(color.getGreen() & 0xff) << 8 |
(color.getBlue() & 0xff);
}
}
return new InverseColorMapIndexColorModel(8, colors.length, colors, 0, pAlpha, trans, DataBuffer.TYPE_BYTE);
}
/**
* Converts an (A)RGB {@code MagickImage} to a {@code BufferedImage}, of
* type {@code TYPE_4BYTE_ABGR} or {@code TYPE_3BYTE_BGR}.
*
* @param pImage the original {@code MagickImage}
* @param pAlpha keep alpha channel
* @return a new {@code BufferedImage}
*
* @throws MagickException if an exception occurs during conversion
*
* @see BufferedImage
*/
private static BufferedImage rgbToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
Dimension size = pImage.getDimension();
int length = size.width * size.height;
int bands = pAlpha ? 4 : 3;
byte[] pixels = new byte[length * bands];
// TODO: If we do multiple dispatches (one per line, typically), we could provide listener
// feedback. But it's currently a lot slower than fetching all the pixels in one go.
// Note: The ordering ABGR or BGR corresponds to BufferedImage
// TYPE_4BYTE_ABGR and TYPE_3BYTE_BGR respectively
pImage.dispatchImage(0, 0, size.width, size.height, pAlpha ? "ABGR" : "BGR", pixels);
// Init databuffer with array, to avoid allocation of empty array
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
int[] bandOffsets = pAlpha ? BAND_OFF_TRANS : BAND_OFF_OPAQUE;
WritableRaster raster =
Raster.createInterleavedRaster(buffer, size.width, size.height,
size.width * bands, bands, bandOffsets, LOCATION_UPPER_LEFT);
return new BufferedImage(pAlpha ? CM_COLOR_ALPHA : CM_COLOR_OPAQUE, raster, pAlpha, null);
}
/**
* Converts an {@code MagickImage} to a {@code BufferedImage} which holds an CMYK ICC profile
*
* @param pImage the original {@code MagickImage}
* @param pAlpha keep alpha channel
* @return a new {@code BufferedImage}
*
* @throws MagickException if an exception occurs during conversion
*
* @see BufferedImage
*/
private static BufferedImage cmykToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
Dimension size = pImage.getDimension();
int length = size.width * size.height;
// Retreive the ICC profile
ICC_Profile profile = ICC_Profile.getInstance(pImage.getColorProfile().getInfo());
ColorSpace cs = new ICC_ColorSpace(profile);
int bands = cs.getNumComponents() + (pAlpha ? 1 : 0);
int[] bits = new int[bands];
for (int i = 0; i < bands; i++) {
bits[i] = 8;
}
ColorModel cm = pAlpha ?
new ComponentColorModel(cs, bits, true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE) :
new ComponentColorModel(cs, bits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
byte[] pixels = new byte[length * bands];
// TODO: If we do multiple dispatches (one per line, typically), we could provide listener
// feedback. But it's currently a lot slower than fetching all the pixels in one go.
// TODO: handle more generic cases if profile is not CMYK
// TODO: Test "ACMYK"
pImage.dispatchImage(0, 0, size.width, size.height, pAlpha ? "ACMYK" : "CMYK", pixels);
// Init databuffer with array, to avoid allocation of empty array
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
// TODO: build array from bands variable, here it just works for CMYK
// The values has not been tested with an alpha picture actually...
int[] bandOffsets = pAlpha ? new int[] {0, 1, 2, 3, 4} : new int[] {0, 1, 2, 3};
WritableRaster raster =
Raster.createInterleavedRaster(buffer, size.width, size.height,
size.width * bands, bands, bandOffsets, LOCATION_UPPER_LEFT);
return new BufferedImage(cm, raster, pAlpha, null);
}
}
@@ -55,7 +55,9 @@
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import java.awt.*; import java.awt.*;
import java.awt.geom.*; import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.*; import java.awt.image.*;
/** /**
@@ -101,6 +103,15 @@ import java.awt.image.*;
* BufferedImage scaled = new ResampleOp(w, h).filter(temp, null); * BufferedImage scaled = new ResampleOp(w, h).filter(temp, null);
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* For maximum performance, this class will use native code, through
* <a href="http://www.yeo.id.au/jmagick/">JMagick</a>, when available.
* Otherwise, the class will silently fall back to pure Java mode.
* Native code may be disabled globally, by setting the system property
* {@code com.twelvemonkeys.image.accel} to {@code false}.
* To allow debug of the native code, set the system property
* {@code com.twelvemonkeys.image.magick.debug} to {@code true}.
* </p>
* <p>
* This {@code BufferedImageOp} is based on C example code found in * This {@code BufferedImageOp} is based on C example code found in
* <a href="http://www.acm.org/tog/GraphicsGems/">Graphics Gems III</a>, * <a href="http://www.acm.org/tog/GraphicsGems/">Graphics Gems III</a>,
* Filtered Image Rescaling, by Dale Schumacher (with additional improvments by * Filtered Image Rescaling, by Dale Schumacher (with additional improvments by
@@ -128,6 +139,9 @@ import java.awt.image.*;
// TODO: Consider using AffineTransformOp for more operations!? // TODO: Consider using AffineTransformOp for more operations!?
public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ { public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
// NOTE: These MUST correspond to ImageMagick filter types, for the
// MagickAccelerator to work consistently (see magick.FilterType).
/** /**
* Undefined interpolation, filter method will use default filter. * Undefined interpolation, filter method will use default filter.
*/ */
@@ -281,10 +295,11 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
new Value(KEY_RESAMPLE_INTERPOLATION, "Blackman-Sinc", FILTER_BLACKMAN_SINC); new Value(KEY_RESAMPLE_INTERPOLATION, "Blackman-Sinc", FILTER_BLACKMAN_SINC);
// Member variables // Member variables
private final int width; // Package access, to allow access from MagickAccelerator
private final int height; int width;
int height;
private final int filterType; int filterType;
/** /**
* RendereingHints.Key implementation, works only with Value values. * RendereingHints.Key implementation, works only with Value values.
@@ -532,6 +547,16 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
// Fall through // Fall through
} }
// Try to use native ImageMagick code
BufferedImage result = MagickAccelerator.filter(this, input, output);
if (result != null) {
return result;
}
// Otherwise, continue in pure Java mode
// TODO: What if output != null and wrong size? Create new? Render on only a part? Document?
// If filter type != POINT or BOX and input has IndexColorModel, convert // If filter type != POINT or BOX and input has IndexColorModel, convert
// to true color, with alpha reflecting that of the original color model. // to true color, with alpha reflecting that of the original color model.
BufferedImage temp; BufferedImage temp;
@@ -546,7 +571,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
// Create or convert output to a suitable image // Create or convert output to a suitable image
// TODO: OPTIMIZE: Don't really need to convert all types to same as input // TODO: OPTIMIZE: Don't really need to convert all types to same as input
BufferedImage result = output != null && temp.getType() != BufferedImage.TYPE_CUSTOM ? /*output*/ ImageUtil.toBuffered(output, temp.getType()) : createCompatibleDestImage(temp, null); result = output != null && temp.getType() != BufferedImage.TYPE_CUSTOM ? /*output*/ ImageUtil.toBuffered(output, temp.getType()) : createCompatibleDestImage(temp, null);
resample(temp, result, filter); resample(temp, result, filter);
@@ -1255,12 +1280,12 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
/* /*
* image rescaling routine * image rescaling routine
*/ */
static class Contributor { class Contributor {
int pixel; int pixel;
double weight; double weight;
} }
static class ContributorList { class ContributorList {
int n;/* number of contributors (may be < p.length) */ int n;/* number of contributors (may be < p.length) */
Contributor[] p;/* pointer to list of contributions */ Contributor[] p;/* pointer to list of contributions */
} }
@@ -30,24 +30,17 @@
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import static java.lang.Math.min; import org.junit.Test;
import static org.junit.jupiter.api.Assertions.*;
import javax.imageio.ImageTypeSpecifier;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.image.BufferedImage; import java.awt.image.*;
import java.awt.image.BufferedImageOp;
import java.awt.image.DataBuffer;
import java.awt.image.ImagingOpException;
import java.awt.image.Raster;
import java.awt.image.RasterOp;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.imageio.ImageTypeSpecifier; import static org.junit.Assert.*;
import org.junit.jupiter.api.Test;
/** /**
* AffineTransformOpTest. * AffineTransformOpTest.
@@ -108,7 +101,6 @@ public class AffineTransformOpTest {
private final int width = 30; private final int width = 30;
private final int height = 20; private final int height = 20;
private final double anchor = min(width, height) / 2.0;
@Test @Test
public void testGetPoint2D() { public void testGetPoint2D() {
@@ -136,33 +128,33 @@ public class AffineTransformOpTest {
@Test @Test
public void testFilterRotateBIStandard() { public void testFilterRotateBIStandard() {
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
for (Integer type : TYPES) { for (Integer type : TYPES) {
BufferedImage image = new BufferedImage(width, height, type); BufferedImage image = new BufferedImage(width, height, type);
BufferedImage jreResult = jreOp.filter(image, null); BufferedImage jreResult = jreOp.filter(image, null);
BufferedImage tmResult = tmOp.filter(image, null); BufferedImage tmResult = tmOp.filter(image, null);
assertNotNull(tmResult, "No result!"); assertNotNull("No result!", tmResult);
assertEquals(jreResult.getType(), tmResult.getType(), "Bad type"); assertEquals("Bad type", jreResult.getType(), tmResult.getType());
assertEquals(jreResult.getColorModel(), tmResult.getColorModel(), "Incorrect color model"); assertEquals("Incorrect color model", jreResult.getColorModel(), tmResult.getColorModel());
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width"); assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height"); assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
} }
} }
@Test @Test
public void testFilterRotateBICustom() { public void testFilterRotateBICustom() {
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
for (ImageTypeSpecifier spec : SPECS) { for (ImageTypeSpecifier spec : SPECS) {
BufferedImage image = spec.createBufferedImage(width, height); BufferedImage image = spec.createBufferedImage(width, height);
BufferedImage tmResult = tmOp.filter(image, null); BufferedImage tmResult = tmOp.filter(image, null);
assertNotNull(tmResult, "No result!"); assertNotNull("No result!", tmResult);
BufferedImage jreResult = null; BufferedImage jreResult = null;
@@ -174,18 +166,18 @@ public class AffineTransformOpTest {
} }
if (jreResult != null) { if (jreResult != null) {
assertEquals(jreResult.getType(), tmResult.getType(), "Bad type"); assertEquals("Bad type", jreResult.getType(), tmResult.getType());
assertEquals(jreResult.getColorModel(), tmResult.getColorModel(), "Incorrect color model"); assertEquals("Incorrect color model", jreResult.getColorModel(), tmResult.getColorModel());
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width"); assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height"); assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
} }
else { else {
assertEquals(spec.getBufferedImageType(), tmResult.getType(), "Bad type"); assertEquals("Bad type", spec.getBufferedImageType(), tmResult.getType());
assertEquals(spec.getColorModel(), tmResult.getColorModel(), "Incorrect color model"); assertEquals("Incorrect color model", spec.getColorModel(), tmResult.getColorModel());
assertEquals(height, tmResult.getWidth(), "Incorrect width"); assertEquals("Incorrect width", height, tmResult.getWidth());
assertEquals(width, tmResult.getHeight(), "Incorrect height"); assertEquals("Incorrect height", width, tmResult.getHeight());
} }
} }
} }
@@ -205,8 +197,8 @@ public class AffineTransformOpTest {
@Test @Test
public void testFilterRotateRasterStandard() { public void testFilterRotateRasterStandard() {
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
for (Integer type : TYPES) { for (Integer type : TYPES) {
Raster raster = new BufferedImage(width, height, type).getRaster(); Raster raster = new BufferedImage(width, height, type).getRaster();
@@ -229,25 +221,27 @@ public class AffineTransformOpTest {
fail("No result!"); fail("No result!");
} }
else { else {
System.err.println("AffineTransformOpTest.testFilterRotateRasterStandard");
System.err.println("type: " + type);
continue; continue;
} }
} }
if (jreResult != null) { if (jreResult != null) {
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width"); assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height"); assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
} }
else { else {
assertEquals(height, tmResult.getWidth(), "Incorrect width"); assertEquals("Incorrect width", height, tmResult.getWidth());
assertEquals(width, tmResult.getHeight(), "Incorrect height"); assertEquals("Incorrect height", width, tmResult.getHeight());
} }
} }
} }
@Test @Test
public void testFilterRotateRasterCustom() { public void testFilterRotateRasterCustom() {
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null); RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
for (ImageTypeSpecifier spec : SPECS) { for (ImageTypeSpecifier spec : SPECS) {
Raster raster = spec.createBufferedImage(width, height).getRaster(); Raster raster = spec.createBufferedImage(width, height).getRaster();
@@ -270,17 +264,19 @@ public class AffineTransformOpTest {
fail("No result!"); fail("No result!");
} }
else { else {
System.err.println("AffineTransformOpTest.testFilterRotateRasterCustom");
System.err.println("spec: " + spec);
continue; continue;
} }
} }
if (jreResult != null) { if (jreResult != null) {
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width"); assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height"); assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
} }
else { else {
assertEquals(height, tmResult.getWidth(), "Incorrect width"); assertEquals("Incorrect width", height, tmResult.getWidth());
assertEquals(width, tmResult.getHeight(), "Incorrect height"); assertEquals("Incorrect height", width, tmResult.getHeight());
} }
} }
} }
@@ -30,16 +30,18 @@
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import org.junit.jupiter.api.Disabled; import org.junit.Ignore;
import org.junit.jupiter.api.Test; import org.junit.Test;
import java.awt.*; import java.awt.*;
import java.awt.color.*; import java.awt.color.ColorSpace;
import java.awt.image.*; import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageProducer;
import java.awt.image.IndexColorModel;
import java.net.URL; import java.net.URL;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.*;
/** /**
* BufferedImageFactoryTestCase * BufferedImageFactoryTestCase
@@ -49,58 +51,50 @@ import static org.junit.jupiter.api.Assertions.*;
* @version $Id: BufferedImageFactoryTestCase.java,v 1.0 May 7, 2010 12:40:08 PM haraldk Exp$ * @version $Id: BufferedImageFactoryTestCase.java,v 1.0 May 7, 2010 12:40:08 PM haraldk Exp$
*/ */
public class BufferedImageFactoryTest { public class BufferedImageFactoryTest {
@Test @Test(expected = IllegalArgumentException.class)
public void testCreateNullImage() { public void testCreateNullImage() {
assertThrows(IllegalArgumentException.class, () -> { new BufferedImageFactory((Image) null);
new BufferedImageFactory((Image) null);
});
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testCreateNullProducer() { public void testCreateNullProducer() {
assertThrows(IllegalArgumentException.class, () -> { new BufferedImageFactory((ImageProducer) null);
new BufferedImageFactory((ImageProducer) null);
});
} }
// NPE in Toolkit, ok // NPE in Toolkit, ok
@Test @Test(expected = RuntimeException.class)
public void testGetBufferedImageErrorSourceByteArray() { public void testGetBufferedImageErrorSourceByteArray() {
assertThrows(RuntimeException.class, () -> { Image source = Toolkit.getDefaultToolkit().createImage((byte[]) null);
Image source = Toolkit.getDefaultToolkit().createImage((byte[]) null);
new BufferedImageFactory(source); new BufferedImageFactory(source);
});
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testGetBufferedImageErrorSourceImageProducer() { public void testGetBufferedImageErrorSourceImageProducer() {
Image source = Toolkit.getDefaultToolkit().createImage((ImageProducer) null); Image source = Toolkit.getDefaultToolkit().createImage((ImageProducer) null);
assertThrows(IllegalArgumentException.class, () -> {
new BufferedImageFactory(source); new BufferedImageFactory(source);
});
} }
// TODO: This is a quite serious bug, however, the bug is in the Toolkit, allowing such images in the first place... // TODO: This is a quite serious bug, however, the bug is in the Toolkit, allowing such images in the first place...
// In any case, there's not much we can do, except until someone is bored and kills the app/thread... :-P // In any case, there's not much we can do, except until someone is bored and kills the app/thread... :-P
@Disabled("Bug in Toolkit") @Ignore("Bug in Toolkit")
@Test @Test(timeout = 1000, expected = ImageConversionException.class)
public void testGetBufferedImageErrorSourceString() { public void testGetBufferedImageErrorSourceString() {
assertTimeoutPreemptively(Duration.ofMillis(1000), () -> { Image source = Toolkit.getDefaultToolkit().createImage((String) null);
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
BufferedImageFactory factory = new BufferedImageFactory(source); BufferedImageFactory factory = new BufferedImageFactory(source);
assertThrows(ImageConversionException.class, factory::getBufferedImage); factory.getBufferedImage();
});
} }
// This is a little random, and it would be nicer if we could throw an IllegalArgumentException on create. // This is a little random, and it would be nicer if we could throw an IllegalArgumentException on create.
// Unfortunately, the API doesn't allow this... // Unfortunately, the API doesn't allow this...
@Test @Test(timeout = 1000, expected = ImageConversionException.class)
public void testGetBufferedImageErrorSourceURL() { public void testGetBufferedImageErrorSourceURL() {
assertTimeoutPreemptively(Duration.ofMillis(1000), () -> { Image source = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/META-INF/MANIFEST.MF"));
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
BufferedImageFactory factory = new BufferedImageFactory(source); BufferedImageFactory factory = new BufferedImageFactory(source);
assertThrows(ImageConversionException.class, factory::getBufferedImage); factory.getBufferedImage();
});
} }
@Test @Test
@@ -172,7 +166,7 @@ public class BufferedImageFactoryTest {
assertEquals(3, colorModel.getNumColorComponents()); assertEquals(3, colorModel.getNumColorComponents());
assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colorModel.getColorSpace()); assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colorModel.getColorSpace());
assertInstanceOf(IndexColorModel.class, colorModel); assertTrue(colorModel instanceof IndexColorModel);
assertTrue(colorModel.hasAlpha()); assertTrue(colorModel.hasAlpha());
assertEquals(4, colorModel.getNumComponents()); assertEquals(4, colorModel.getNumComponents());
@@ -203,7 +197,7 @@ public class BufferedImageFactoryTest {
for (int y = 0; y < image.getHeight(); y++) { for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) { for (int x = 0; x < image.getWidth(); x++) {
assertEquals(original.getRGB(x * 2, y * 2), image.getRGB(x, y), "RGB[" + x + ", " + y + "]"); assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(x * 2, y * 2), image.getRGB(x, y));
} }
} }
} }
@@ -226,7 +220,7 @@ public class BufferedImageFactoryTest {
for (int y = 0; y < image.getHeight(); y++) { for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) { for (int x = 0; x < image.getWidth(); x++) {
assertEquals(original.getRGB(40 + x, 40 + y), image.getRGB(x, y), "RGB[" + x + ", " + y + "]"); assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(40 + x, 40 + y), image.getRGB(x, y));
} }
} }
} }
@@ -250,7 +244,7 @@ public class BufferedImageFactoryTest {
for (int y = 0; y < image.getHeight(); y++) { for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) { for (int x = 0; x < image.getWidth(); x++) {
assertEquals(original.getRGB(40 + x * 2, 40 + y * 2), image.getRGB(x, y), "RGB[" + x + ", " + y + "]"); assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(40 + x * 2, 40 + y * 2), image.getRGB(x, y));
} }
} }
} }
@@ -266,9 +260,9 @@ public class BufferedImageFactoryTest {
// Listener should abort ASAP // Listener should abort ASAP
factory.addProgressListener(new BufferedImageFactory.ProgressListener() { factory.addProgressListener(new BufferedImageFactory.ProgressListener() {
public void progress(BufferedImageFactory factory, float percentage) { public void progress(BufferedImageFactory pFactory, float pPercentage) {
if (percentage > 5) { if (pPercentage > 5) {
factory.abort(); pFactory.abort();
} }
} }
}); });
@@ -349,7 +343,7 @@ public class BufferedImageFactoryTest {
VerifyingListener listener = new VerifyingListener(factory); VerifyingListener listener = new VerifyingListener(factory);
factory.addProgressListener(listener); factory.addProgressListener(listener);
factory.removeProgressListener(new BufferedImageFactory.ProgressListener() { factory.removeProgressListener(new BufferedImageFactory.ProgressListener() {
public void progress(BufferedImageFactory factory, float percentage) { public void progress(BufferedImageFactory pFactory, float pPercentage) {
} }
}); });
factory.getBufferedImage(); factory.getBufferedImage();
@@ -386,11 +380,11 @@ public class BufferedImageFactoryTest {
this.factory = factory; this.factory = factory;
} }
public void progress(BufferedImageFactory factory, float percentage) { public void progress(BufferedImageFactory pFactory, float pPercentage) {
assertEquals(this.factory, factory); assertEquals(factory, pFactory);
assertTrue(percentage >= progress && percentage <= 100f); assertTrue(pPercentage >= progress && pPercentage <= 100f);
progress = percentage; progress = pPercentage;
} }
@@ -30,7 +30,7 @@
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import org.junit.jupiter.api.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.*; import java.awt.*;
@@ -39,7 +39,7 @@ import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.InputStream; import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.*;
public class ImageUtilTest { public class ImageUtilTest {
@@ -116,8 +116,8 @@ public class ImageUtilTest {
public void testImageIsNotBufferedImage() { public void testImageIsNotBufferedImage() {
// Should not be a buffered image // Should not be a buffered image
assertFalse( assertFalse(
scaled instanceof BufferedImage, "FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too.",
"FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too." scaled instanceof BufferedImage
); );
} }
@@ -247,7 +247,7 @@ public class ImageUtilTest {
if (original != notContrasted) { // Don't care to test if images are same if (original != notContrasted) { // Don't care to test if images are same
for (int y = 0; y < original.getHeight(); y++) { for (int y = 0; y < original.getHeight(); y++) {
for (int x = 0; x < original.getWidth(); x++) { for (int x = 0; x < original.getWidth(); x++) {
assertEquals(original.getRGB(x, y), notContrasted.getRGB(x, y), "0 constrast should not change image"); assertEquals("0 constrast should not change image", original.getRGB(x, y), notContrasted.getRGB(x, y));
} }
} }
} }
@@ -275,24 +275,24 @@ public class ImageUtilTest {
// RED // RED
if (oR < 127) { if (oR < 127) {
assertTrue(oR >= cR && cR >= dR, "Contrast should be decreased or same"); assertTrue("Contrast should be decreased or same", oR >= cR && cR >= dR);
} }
else { else {
assertTrue(oR <= cR && cR <= dR, "Contrast should be increased or same"); assertTrue("Contrast should be increased or same", oR <= cR && cR <= dR);
} }
// GREEN // GREEN
if (oG < 127) { if (oG < 127) {
assertTrue(oG >= cG && cG >= dG, "Contrast should be decreased or same"); assertTrue("Contrast should be decreased or same", oG >= cG && cG >= dG);
} }
else { else {
assertTrue(oG <= cG && cG <= dG, "Contrast should be increased or same"); assertTrue("Contrast should be increased or same", oG <= cG && cG <= dG);
} }
// BLUE // BLUE
if (oB < 127) { if (oB < 127) {
assertTrue(oB >= cB && cB >= dB, "Contrast should be decreased or same"); assertTrue("Contrast should be decreased or same", oB >= cB && cB >= dB);
} }
else { else {
assertTrue(oB <= cB && cB <= dB, "Contrast should be increased or same"); assertTrue("Contrast should be increased or same", oB <= cB && cB <= dB);
} }
} }
} }
@@ -304,9 +304,9 @@ public class ImageUtilTest {
int r = rgb >> 16 & 0xFF; int r = rgb >> 16 & 0xFF;
int g = rgb >> 8 & 0xFF; int g = rgb >> 8 & 0xFF;
int b = rgb & 0xFF; int b = rgb & 0xFF;
assertTrue(r == 0 || r == 255, "Max contrast should only produce primary colors"); assertTrue("Max contrast should only produce primary colors", r == 0 || r == 255);
assertTrue(g == 0 || g == 255, "Max contrast should only produce primary colors"); assertTrue("Max contrast should only produce primary colors", g == 0 || g == 255);
assertTrue(b == 0 || b == 255, "Max contrast should only produce primary colors"); assertTrue("Max contrast should only produce primary colors", b == 0 || b == 255);
} }
} }
@@ -327,24 +327,24 @@ public class ImageUtilTest {
// RED // RED
if (oR >= 127) { if (oR >= 127) {
assertTrue(oR >= cR, "Contrast should be decreased or same"); assertTrue("Contrast should be decreased or same", oR >= cR);
} }
else { else {
assertTrue(oR <= cR, "Contrast should be increased or same"); assertTrue("Contrast should be increased or same", oR <= cR);
} }
// GREEN // GREEN
if (oG >= 127) { if (oG >= 127) {
assertTrue(oG >= cG, "Contrast should be decreased or same"); assertTrue("Contrast should be decreased or same", oG >= cG);
} }
else { else {
assertTrue(oG <= cG, "Contrast should be increased or same"); assertTrue("Contrast should be increased or same", oG <= cG);
} }
// BLUE // BLUE
if (oB >= 127) { if (oB >= 127) {
assertTrue(oB >= cB, "Contrast should be decreased or same"); assertTrue("Contrast should be decreased or same", oB >= cB);
} }
else { else {
assertTrue(oB <= cB, "Contrast should be increased or same"); assertTrue("Contrast should be increased or same", oB <= cB);
} }
} }
} }
@@ -357,7 +357,7 @@ public class ImageUtilTest {
int r = rgb >> 16 & 0xFF; int r = rgb >> 16 & 0xFF;
int g = rgb >> 8 & 0xFF; int g = rgb >> 8 & 0xFF;
int b = rgb & 0xFF; int b = rgb & 0xFF;
assertTrue(r == 127 && g == 127 && b == 127, "Minimum contrast should be all gray"); assertTrue("Minimum contrast should be all gray", r == 127 && g == 127 && b == 127);
} }
} }
@@ -400,7 +400,7 @@ public class ImageUtilTest {
if (original != notSharpened) { // Don't care to test if images are same if (original != notSharpened) { // Don't care to test if images are same
for (int y = 0; y < original.getHeight(); y++) { for (int y = 0; y < original.getHeight(); y++) {
for (int x = 0; x < original.getWidth(); x++) { for (int x = 0; x < original.getWidth(); x++) {
assertEquals(original.getRGB(x, y), notSharpened.getRGB(x, y), "0 sharpen should not change image"); assertEquals("0 sharpen should not change image", original.getRGB(x, y), notSharpened.getRGB(x, y));
} }
} }
} }
@@ -446,13 +446,13 @@ public class ImageUtilTest {
} }
// assertEquals("Difference should not change", diffOriginal, diffSharpened); // assertEquals("Difference should not change", diffOriginal, diffSharpened);
assertTrue(absDiffOriginal < absDiffSharpened, "Abs difference should increase"); assertTrue("Abs difference should increase", absDiffOriginal < absDiffSharpened);
// assertEquals("Difference should not change", diffOriginal, diffDefault); // assertEquals("Difference should not change", diffOriginal, diffDefault);
assertTrue(absDiffOriginal < absDiffDefault, "Abs difference should increase"); assertTrue("Abs difference should increase", absDiffOriginal < absDiffDefault);
// assertEquals("Difference should not change", diffOriginal, diffMore); // assertEquals("Difference should not change", diffOriginal, diffMore);
assertTrue(absDiffOriginal < absDiffMore, "Abs difference should increase"); assertTrue("Abs difference should increase", absDiffOriginal < absDiffMore);
// assertEquals("Difference should not change", diffSharpened, diffMore); // assertEquals("Difference should not change", diffSharpened, diffMore);
assertTrue(absDiffSharpened < absDiffMore, "Abs difference should increase"); assertTrue("Abs difference should increase", absDiffSharpened < absDiffMore);
} }
@Test @Test
@@ -466,7 +466,7 @@ public class ImageUtilTest {
if (original != notBlurred) { // Don't care to test if images are same if (original != notBlurred) { // Don't care to test if images are same
for (int y = 0; y < original.getHeight(); y++) { for (int y = 0; y < original.getHeight(); y++) {
for (int x = 0; x < original.getWidth(); x++) { for (int x = 0; x < original.getWidth(); x++) {
assertEquals(original.getRGB(x, y), notBlurred.getRGB(x, y), "0 blur should not change image"); assertEquals("0 blur should not change image", original.getRGB(x, y), notBlurred.getRGB(x, y));
} }
} }
} }
@@ -512,13 +512,13 @@ public class ImageUtilTest {
} }
// assertEquals("Difference should not change", diffOriginal, diffBlurred); // assertEquals("Difference should not change", diffOriginal, diffBlurred);
assertTrue(absDiffOriginal > absDiffBlurred, String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred)); assertTrue(String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred), absDiffOriginal > absDiffBlurred);
// assertEquals("Difference should not change", diffOriginal, diffDefault); // assertEquals("Difference should not change", diffOriginal, diffDefault);
assertTrue(absDiffOriginal > absDiffDefault, "Abs difference should decrease"); assertTrue("Abs difference should decrease", absDiffOriginal > absDiffDefault);
// assertEquals("Difference should not change", diffOriginal, diffMore); // assertEquals("Difference should not change", diffOriginal, diffMore);
assertTrue(absDiffOriginal > absDiffMore, "Abs difference should decrease"); assertTrue("Abs difference should decrease", absDiffOriginal > absDiffMore);
// assertEquals("Difference should not change", diffBlurred, diffMore); // assertEquals("Difference should not change", diffBlurred, diffMore);
assertTrue(absDiffBlurred > absDiffMore, "Abs difference should decrease"); assertTrue("Abs difference should decrease", absDiffBlurred > absDiffMore);
} }
@Test @Test
@@ -528,7 +528,7 @@ public class ImageUtilTest {
assertNotNull(sunflower); assertNotNull(sunflower);
BufferedImage image = ImageUtil.createIndexed(sunflower); BufferedImage image = ImageUtil.createIndexed(sunflower);
assertNotNull(image, "Image was null"); assertNotNull("Image was null", image);
assertInstanceOf(IndexColorModel.class, image.getColorModel()); assertTrue(image.getColorModel() instanceof IndexColorModel);
} }
} }
@@ -30,8 +30,8 @@
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import org.junit.jupiter.api.Disabled; import org.junit.Ignore;
import org.junit.jupiter.api.Test; import org.junit.Test;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@@ -40,7 +40,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.*;
/** /**
* ResampleOpTestCase * ResampleOpTestCase
@@ -124,7 +124,7 @@ public class ResampleOpTest {
} }
} }
assertEquals(Collections.EMPTY_LIST, exceptions, "Filter threw exceptions: "); assertEquals("Filter threw exceptions: ", Collections.EMPTY_LIST, exceptions);
} }
// 1x1 // 1x1
@@ -358,7 +358,7 @@ public class ResampleOpTest {
} }
} }
@Disabled("Not for general unit testing") @Ignore("Not for general unit testing")
@Test @Test
public void testTime() { public void testTime() {
int iterations = 1000; int iterations = 1000;
+6
View File
@@ -0,0 +1,6 @@
TODO:
Remove compile-time dependency on JMagick:
- Extract interface for MagickAccelerator
- Move implementation to separate module
- Instantiate impl via reflection
DONE:
+2 -14
View File
@@ -4,19 +4,15 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.13.2-SNAPSHOT</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-io</artifactId> <artifactId>common-io</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>TwelveMonkeys :: Common :: IO</name> <name>TwelveMonkeys :: Common :: IO</name>
<description> <description>
TwelveMonkeys Common I/O support classes. The TwelveMonkeys Common IO support
</description> </description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.io</project.jpms.module.name>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
@@ -31,12 +27,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> </project>
@@ -56,8 +56,8 @@ public class CompoundReader extends Reader {
private int currentReader; private int currentReader;
private int markedReader; private int markedReader;
private long mark; private int mark;
private long next; private int mNext;
/** /**
* Create a new compound reader. * Create a new compound reader.
@@ -76,7 +76,7 @@ public class CompoundReader extends Reader {
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
// reference can't change, only it's elements // reference can't change, only it's elements
readers = new ArrayList<>(); readers = new ArrayList<Reader>();
boolean markSupported = true; boolean markSupported = true;
while (pReaders.hasNext()) { while (pReaders.hasNext()) {
@@ -101,7 +101,7 @@ public class CompoundReader extends Reader {
} }
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods! // NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
next = 0; mNext = 0;
return current; return current;
} }
@@ -135,7 +135,7 @@ public class CompoundReader extends Reader {
synchronized (finalLock) { synchronized (finalLock) {
ensureOpen(); ensureOpen();
mark = next; mark = mNext;
markedReader = currentReader; markedReader = currentReader;
current.mark(pReadLimit); current.mark(pReadLimit);
@@ -158,7 +158,7 @@ public class CompoundReader extends Reader {
} }
current.reset(); current.reset();
next = mark; mNext = mark;
} }
} }
@@ -177,13 +177,13 @@ public class CompoundReader extends Reader {
return read(); // In case of 0-length readers return read(); // In case of 0-length readers
} }
next++; mNext++;
return read; return read;
} }
} }
public int read(char[] pBuffer, int pOffset, int pLength) throws IOException { public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
synchronized (finalLock) { synchronized (finalLock) {
int read = current.read(pBuffer, pOffset, pLength); int read = current.read(pBuffer, pOffset, pLength);
@@ -192,7 +192,7 @@ public class CompoundReader extends Reader {
return read(pBuffer, pOffset, pLength); // In case of 0-length readers return read(pBuffer, pOffset, pLength); // In case of 0-length readers
} }
next += read; mNext += read;
return read; return read;
} }
@@ -213,7 +213,7 @@ public class CompoundReader extends Reader {
return skip(pChars); // In case of 0-length readers return skip(pChars); // In case of 0-length readers
} }
next += skipped; mNext += skipped;
return skipped; return skipped;
} }
@@ -34,7 +34,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays;
/** /**
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version * An unsynchronized {@code ByteArrayOutputStream} implementation. This version
@@ -43,8 +42,11 @@ import java.util.Arrays;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FastByteArrayOutputStream.java#2 $ * @version $Id: FastByteArrayOutputStream.java#2 $
*/ */
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block // TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
public final class FastByteArrayOutputStream extends ByteArrayOutputStream { public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
/** Max grow size (unless if writing more than this amount of bytes) */
protected int maxGrowSize = 1024 * 1024; // 1 MB
/** /**
* Creates a {@code ByteArrayOutputStream} with the given initial buffer * Creates a {@code ByteArrayOutputStream} with the given initial buffer
* size. * size.
@@ -95,8 +97,10 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
private void growIfNeeded(int pNewCount) { private void growIfNeeded(int pNewCount) {
if (pNewCount > buf.length) { if (pNewCount > buf.length) {
int newSize = Math.max(buf.length << 1, pNewCount); int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
buf = Arrays.copyOf(buf, newSize); byte[] newBuf = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
} }
} }
@@ -109,7 +113,10 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
// Non-synchronized version of toByteArray // Non-synchronized version of toByteArray
@Override @Override
public byte[] toByteArray() { public byte[] toByteArray() {
return Arrays.copyOf(buf, count); byte[] newBuf = new byte[count];
System.arraycopy(buf, 0, newBuf, 0, count);
return newBuf;
} }
/** /**
@@ -346,7 +346,7 @@ public final class FileUtil {
/** /**
* Gets the file (type) extension of the given file. * Gets the file (type) extension of the given file.
* A file extension is the part of the filename, after the last occurrence * A file extension is the part of the filename, after the last occurence
* of a period {@code '.'}. * of a period {@code '.'}.
* If the filename contains no period, {@code null} is returned. * If the filename contains no period, {@code null} is returned.
* *
@@ -65,7 +65,6 @@ import java.io.FilenameFilter;
* @see WildcardStringParser * @see WildcardStringParser
* @deprecated * @deprecated
*/ */
@Deprecated
public class FilenameMaskFilter implements FilenameFilter { public class FilenameMaskFilter implements FilenameFilter {
// TODO: Rewrite to use regexp, or create new class // TODO: Rewrite to use regexp, or create new class
@@ -442,7 +442,6 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
* @see java.io.BufferedReader#readLine() * @see java.io.BufferedReader#readLine()
* @see java.io.DataInputStream#readLine() * @see java.io.DataInputStream#readLine()
*/ */
@Deprecated
public String readLine() throws IOException { public String readLine() throws IOException {
DataInputStream ds = new DataInputStream(in); DataInputStream ds = new DataInputStream(in);
return ds.readLine(); return ds.readLine();
@@ -50,8 +50,8 @@ public class StringArrayReader extends StringReader {
protected final Object finalLock; protected final Object finalLock;
private int currentSting; private int currentSting;
private int markedString; private int markedString;
private long mark; private int mark;
private long next; private int next;
/** /**
* Create a new string array reader. * Create a new string array reader.
@@ -151,7 +151,7 @@ public class StringArrayReader extends StringReader {
} }
} }
public int read(char[] pBuffer, int pOffset, int pLength) throws IOException { public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
synchronized (finalLock) { synchronized (finalLock) {
int read = current.read(pBuffer, pOffset, pLength); int read = current.read(pBuffer, pOffset, pLength);
@@ -41,20 +41,21 @@ import java.io.InputStream;
* underlying stream. * underlying stream.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SubStream.java#2 $
*/ */
public final class SubStream extends FilterInputStream { public final class SubStream extends FilterInputStream {
private long bytesLeft; private long bytesLeft;
private int markLimit; private int markLimit;
/** /**
* Creates a {@code SubStream} of the given {@code stream}. * Creates a {@code SubStream} of the given {@code pStream}.
* *
* @param stream the underlying input stream * @param pStream the underlying input stream
* @param length maximum number of bytes to read from this stream * @param pLength maximum number of bytes to read drom this stream
*/ */
public SubStream(final InputStream stream, final long length) { public SubStream(final InputStream pStream, final long pLength) {
super(Validate.notNull(stream, "stream")); super(Validate.notNull(pStream, "stream"));
bytesLeft = Validate.isTrue(length >= 0, length, "length < 0: %s"); bytesLeft = pLength;
} }
/** /**
@@ -63,23 +64,22 @@ public final class SubStream extends FilterInputStream {
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
// NOTE: Do not close the underlying stream, but consume it // NOTE: Do not close the underlying stream
while (bytesLeft > 0) { while (bytesLeft > 0) {
if (skip(bytesLeft) <= 0 && read() < 0) { //noinspection ResultOfMethodCallIgnored
break; skip(bytesLeft);
}
} }
} }
@Override @Override
public int available() throws IOException { public int available() throws IOException {
return (int) findMaxLen(super.available()); return (int) Math.min(super.available(), bytesLeft);
} }
@Override @Override
public void mark(int readLimit) { public void mark(int pReadLimit) {
super.mark(readLimit);// This either succeeds or does nothing... super.mark(pReadLimit);// This either succeeds or does nothing...
markLimit = readLimit; markLimit = pReadLimit;
} }
@Override @Override
@@ -93,42 +93,44 @@ public final class SubStream extends FilterInputStream {
if (bytesLeft-- <= 0) { if (bytesLeft-- <= 0) {
return -1; return -1;
} }
return super.read(); return super.read();
} }
@Override @Override
public int read(byte[] bytes) throws IOException { public final int read(byte[] pBytes) throws IOException {
return read(bytes, 0, bytes.length); return read(pBytes, 0, pBytes.length);
} }
@Override @Override
public int read(final byte[] bytes, final int off, final int len) throws IOException { public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (bytesLeft <= 0) { if (bytesLeft <= 0) {
return -1; return -1;
} }
int read = super.read(bytes, off, (int) findMaxLen(len)); int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
bytesLeft = read < 0 ? 0 : bytesLeft - read; bytesLeft = read < 0 ? 0 : bytesLeft - read;
return read; return read;
} }
@Override
public long skip(long length) throws IOException {
long skipped = super.skip(findMaxLen(length)); // Skips 0 or more, never -1
bytesLeft -= skipped;
return skipped;
}
/** /**
* Finds the maximum number of bytes we can read or skip, from this stream. * Finds the maximum number of bytes we can read or skip, from this stream.
* *
* @param length the requested length * @param pLength the requested length
* @return the maximum number of bytes to read * @return the maximum number of bytes to read
*/ */
private long findMaxLen(long length) { private long findMaxLen(long pLength) {
return bytesLeft < length ? Math.max(bytesLeft, 0) : length; if (bytesLeft < pLength) {
return (int) Math.max(bytesLeft, 0);
}
else {
return pLength;
}
}
@Override
public long skip(long pLength) throws IOException {
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
bytesLeft -= skipped;
return skipped;
} }
} }
@@ -45,39 +45,39 @@ import java.nio.ByteBuffer;
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
*/ */
public final class DecoderStream extends FilterInputStream { public final class DecoderStream extends FilterInputStream {
private final ByteBuffer buffer; protected final ByteBuffer buffer;
private final Decoder decoder; protected final Decoder decoder;
/** /**
* Creates a new decoder stream and chains it to the * Creates a new decoder stream and chains it to the
* input stream specified by the {@code stream} argument. * input stream specified by the {@code pStream} argument.
* The stream will use a default decode buffer size. * The stream will use a default decode buffer size.
* *
* @param stream the underlying input stream. * @param pStream the underlying input stream.
* @param decoder the decoder that will be used to decode the underlying stream * @param pDecoder the decoder that will be used to decode the underlying stream
* *
* @see java.io.FilterInputStream#in * @see java.io.FilterInputStream#in
*/ */
public DecoderStream(final InputStream stream, final Decoder decoder) { public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
// TODO: Let the decoder decide preferred buffer size // TODO: Let the decoder decide preferred buffer size
this(stream, decoder, 1024); this(pStream, pDecoder, 1024);
} }
/** /**
* Creates a new decoder stream and chains it to the * Creates a new decoder stream and chains it to the
* input stream specified by the {@code stream} argument. * input stream specified by the {@code pStream} argument.
* *
* @param stream the underlying input stream. * @param pStream the underlying input stream.
* @param decoder the decoder that will be used to decode the underlying stream * @param pDecoder the decoder that will be used to decode the underlying stream
* @param bufferSize the size of the decode buffer * @param pBufferSize the size of the decode buffer
* *
* @see java.io.FilterInputStream#in * @see java.io.FilterInputStream#in
*/ */
public DecoderStream(final InputStream stream, final Decoder decoder, final int bufferSize) { public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
super(stream); super(pStream);
this.decoder = decoder; decoder = pDecoder;
buffer = ByteBuffer.allocate(bufferSize); // TODO: Allow decoder to specify minimum buffer size buffer = ByteBuffer.allocate(pBufferSize);
buffer.flip(); buffer.flip();
} }
@@ -95,15 +95,15 @@ public final class DecoderStream extends FilterInputStream {
return buffer.get() & 0xff; return buffer.get() & 0xff;
} }
public int read(final byte[] bytes, final int offset, final int length) throws IOException { public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
if (bytes == null) { if (pBytes == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
else if ((offset < 0) || (offset > bytes.length) || (length < 0) || else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((offset + length) > bytes.length) || ((offset + length) < 0)) { ((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException("bytes.length=" + bytes.length + " offset=" + offset + " length=" + length); throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
} }
else if (length == 0) { else if (pLength == 0) {
return 0; return 0;
} }
@@ -114,11 +114,11 @@ public final class DecoderStream extends FilterInputStream {
} }
} }
// Read until we have read length bytes, or have reached EOF // Read until we have read pLength bytes, or have reached EOF
int count = 0; int count = 0;
int off = offset; int off = pOffset;
while (length > count) { while (pLength > count) {
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
if (fill() < 0) { if (fill() < 0) {
break; break;
@@ -126,8 +126,8 @@ public final class DecoderStream extends FilterInputStream {
} }
// Copy as many bytes as possible // Copy as many bytes as possible
int dstLen = Math.min(length - count, buffer.remaining()); int dstLen = Math.min(pLength - count, buffer.remaining());
buffer.get(bytes, off, dstLen); buffer.get(pBytes, off, dstLen);
// Update offset (rest) // Update offset (rest)
off += dstLen; off += dstLen;
@@ -139,7 +139,7 @@ public final class DecoderStream extends FilterInputStream {
return count; return count;
} }
public long skip(final long length) throws IOException { public long skip(final long pLength) throws IOException {
// End of file? // End of file?
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
if (fill() < 0) { if (fill() < 0) {
@@ -147,10 +147,10 @@ public final class DecoderStream extends FilterInputStream {
} }
} }
// Skip until we have skipped length bytes, or have reached EOF // Skip until we have skipped pLength bytes, or have reached EOF
long total = 0; long total = 0;
while (total < length) { while (total < pLength) {
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
if (fill() < 0) { if (fill() < 0) {
break; break;
@@ -158,7 +158,7 @@ public final class DecoderStream extends FilterInputStream {
} }
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe // NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
int skipped = (int) Math.min(length - total, buffer.remaining()); int skipped = (int) Math.min(pLength - total, buffer.remaining());
buffer.position(buffer.position() + skipped); buffer.position(buffer.position() + skipped);
total += skipped; total += skipped;
} }
@@ -174,7 +174,7 @@ public final class DecoderStream extends FilterInputStream {
* *
* @throws IOException if an I/O error occurs * @throws IOException if an I/O error occurs
*/ */
private int fill() throws IOException { protected int fill() throws IOException {
buffer.clear(); buffer.clear();
int read = decoder.decode(in, buffer); int read = decoder.decode(in, buffer);
@@ -45,39 +45,41 @@ import java.nio.ByteBuffer;
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $
*/ */
public final class EncoderStream extends FilterOutputStream { public final class EncoderStream extends FilterOutputStream {
// TODO: This class need a test case ASAP!!!
private final Encoder encoder; protected final Encoder encoder;
private final boolean flushOnWrite; private final boolean flushOnWrite;
private final ByteBuffer buffer; protected final ByteBuffer buffer;
/** /**
* Creates an output stream filter built on top of the specified * Creates an output stream filter built on top of the specified
* underlying output stream. * underlying output stream.
* *
* @param stream the underlying output stream * @param pStream the underlying output stream
* @param encoder the encoder to use * @param pEncoder the encoder to use
*/ */
public EncoderStream(final OutputStream stream, final Encoder encoder) { public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
this(stream, encoder, false); this(pStream, pEncoder, false);
} }
/** /**
* Creates an output stream filter built on top of the specified * Creates an output stream filter built on top of the specified
* underlying output stream. * underlying output stream.
* *
* @param stream the underlying output stream * @param pStream the underlying output stream
* @param encoder the encoder to use * @param pEncoder the encoder to use
* @param flushOnWrite if {@code true}, calls to the byte-array * @param pFlushOnWrite if {@code true}, calls to the byte-array
* {@code write} methods will automatically flush the buffer. * {@code write} methods will automatically flush the buffer.
*/ */
public EncoderStream(final OutputStream stream, final Encoder encoder, final boolean flushOnWrite) { public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
super(stream); super(pStream);
this.encoder = encoder; encoder = pEncoder;
this.flushOnWrite = flushOnWrite; flushOnWrite = pFlushOnWrite;
buffer = ByteBuffer.allocate(1024); buffer = ByteBuffer.allocate(1024);
buffer.flip();
} }
public void close() throws IOException { public void close() throws IOException {
@@ -102,33 +104,33 @@ public final class EncoderStream extends FilterOutputStream {
} }
} }
public void write(final byte[] bytes) throws IOException { public final void write(final byte[] pBytes) throws IOException {
write(bytes, 0, bytes.length); write(pBytes, 0, pBytes.length);
} }
// TODO: Verify that this works for the general case (it probably won't)... // TODO: Verify that this works for the general case (it probably won't)...
// TODO: We might need a way to explicitly flush the encoder, or specify // TODO: We might need a way to explicitly flush the encoder, or specify
// that the encoder can't buffer. In that case, the encoder should probably // that the encoder can't buffer. In that case, the encoder should probably
// tell the EncoderStream how large buffer it prefers... // tell the EncoderStream how large buffer it prefers...
public void write(final byte[] values, final int offset, final int length) throws IOException { public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (!flushOnWrite && length < buffer.remaining()) { if (!flushOnWrite && pLength < buffer.remaining()) {
// Buffer data // Buffer data
buffer.put(values, offset, length); buffer.put(pBytes, pOffset, pLength);
} }
else { else {
// Encode data already in the buffer // Encode data already in the buffer
encodeBuffer(); encodeBuffer();
// Encode rest without buffering // Encode rest without buffering
encoder.encode(out, ByteBuffer.wrap(values, offset, length)); encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
} }
} }
public void write(final int value) throws IOException { public void write(final int pByte) throws IOException {
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
encodeBuffer(); // Resets bufferPos to 0 encodeBuffer(); // Resets bufferPos to 0
} }
buffer.put((byte) value); buffer.put((byte) pByte);
} }
} }
@@ -29,9 +29,6 @@
package com.twelvemonkeys.xml; package com.twelvemonkeys.xml;
import java.io.OutputStream;
import java.io.Writer;
import org.w3c.dom.DOMConfiguration; import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMImplementationList; import org.w3c.dom.DOMImplementationList;
import org.w3c.dom.Document; import org.w3c.dom.Document;
@@ -41,6 +38,9 @@ import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer; import org.w3c.dom.ls.LSSerializer;
import java.io.OutputStream;
import java.io.Writer;
/** /**
* {@code DOMImplementationLS} backed implementation. * {@code DOMImplementationLS} backed implementation.
* *
@@ -88,6 +88,17 @@ public final class DOMSerializer {
output.setCharacterStream(pStream); output.setCharacterStream(pStream);
} }
/*
// TODO: Is it useful?
public void setNewLine(final String pNewLine) {
serializer.setNewLine(pNewLine);
}
public String getNewLine() {
return serializer.getNewLine();
}
*/
/** /**
* Specifies wether the serializer should use indentation and optimize for * Specifies wether the serializer should use indentation and optimize for
* readability. * readability.
@@ -158,7 +169,13 @@ public final class DOMSerializer {
try { try {
return DOMImplementationRegistry.newInstance(); return DOMImplementationRegistry.newInstance();
} }
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
catch (InstantiationException e) {
throw new IllegalStateException(e);
}
catch (IllegalAccessException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
} }
@@ -30,23 +30,16 @@
package com.twelvemonkeys.xml; package com.twelvemonkeys.xml;
import java.io.ByteArrayInputStream; import com.twelvemonkeys.lang.StringUtil;
import java.io.ByteArrayOutputStream; import org.w3c.dom.*;
import java.io.IOException; import org.xml.sax.SAXException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.Date;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import org.w3c.dom.*; import java.nio.charset.Charset;
import org.xml.sax.SAXException; import java.util.Date;
import com.twelvemonkeys.lang.StringUtil;
/** /**
* XMLSerializer * XMLSerializer
@@ -297,7 +290,7 @@ public class XMLSerializer {
} }
private static int appendAndEscape(final String pString, int pStart, final int pEnd, final StringBuilder pBuilder, final String pEntity) { private static int appendAndEscape(final String pString, int pStart, final int pEnd, final StringBuilder pBuilder, final String pEntity) {
pBuilder.append(pString, pStart, pEnd); pBuilder.append(pString.substring(pStart, pEnd));
pBuilder.append(pEntity); pBuilder.append(pEntity);
return pEnd + 1; return pEnd + 1;
} }
@@ -534,7 +527,8 @@ public class XMLSerializer {
builder = factory.newDocumentBuilder(); builder = factory.newDocumentBuilder();
} }
catch (ParserConfigurationException e) { catch (ParserConfigurationException e) {
throw new IOException(e); //noinspection ThrowableInstanceNeverThrown BOGUS
throw (IOException) new IOException(e.getMessage()).initCause(e);
} }
DOMImplementation dom = builder.getDOMImplementation(); DOMImplementation dom = builder.getDOMImplementation();
@@ -32,6 +32,7 @@ package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.CollectionUtil; import com.twelvemonkeys.util.CollectionUtil;
import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
@@ -39,8 +40,7 @@ import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* CompoundReaderTestCase * CompoundReaderTestCase
@@ -30,11 +30,12 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.junit.jupiter.api.Test; import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* FastByteArrayOutputStreamTestCase * FastByteArrayOutputStreamTestCase
@@ -30,10 +30,11 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import org.junit.Test;
import java.io.*; import java.io.*;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* MemoryCacheSeekableStreamTestCase * MemoryCacheSeekableStreamTestCase
@@ -91,13 +92,13 @@ public class FileSeekableStreamTest extends SeekableInputStreamAbstractTest {
try { try {
FileUtil.read(stream); // Read until EOF FileUtil.read(stream); // Read until EOF
assertEquals(-1, stream.read(), "EOF not reached (test case broken)"); assertEquals("EOF not reached (test case broken)", -1, stream.read());
assertFalse(closed[0], "Underlying stream closed before close"); assertFalse("Underlying stream closed before close", closed[0]);
} }
finally { finally {
stream.close(); stream.close();
} }
assertTrue(closed[0], "Underlying stream not closed"); assertTrue("Underlying stream not closed", closed[0]);
} }
} }
@@ -46,14 +46,14 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.ObjectAbstractTest; import com.twelvemonkeys.lang.ObjectAbstractTest;
import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Random; import java.util.Random;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* InputStreamAbstractTestCase * InputStreamAbstractTestCase
@@ -104,15 +104,15 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
int size = 5; int size = 5;
InputStream input = makeInputStream(makeOrderedArray(size)); InputStream input = makeInputStream(makeOrderedArray(size));
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
assertTrue((size - i) >= input.available(), "Check Size [" + i + "]"); assertTrue("Check Size [" + i + "]", (size - i) >= input.available());
assertEquals(i, input.read(), "Check Value [" + i + "]"); assertEquals("Check Value [" + i + "]", i, input.read());
} }
assertEquals(0, input.available(), "Available after contents all read"); assertEquals("Available after contents all read", 0, input.available());
// Test reading after the end of file // Test reading after the end of file
try { try {
int result = input.read(); int result = input.read();
assertEquals( -1, result, "Wrong value read after end of file"); assertEquals("Wrong value read after end of file", -1, result);
} }
catch (IOException e) { catch (IOException e) {
fail("Should not have thrown an IOException: " + e.getMessage()); fail("Should not have thrown an IOException: " + e.getMessage());
@@ -122,12 +122,12 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testAvailable() throws Exception { public void testAvailable() throws Exception {
InputStream input = makeInputStream(1); InputStream input = makeInputStream(1);
assertFalse(input.read() < 0, "Unexpected EOF"); assertFalse("Unexpected EOF", input.read() < 0);
assertEquals(0, input.available(), "Available after contents all read"); assertEquals("Available after contents all read", 0, input.available());
// Check availbale is zero after End of file // Check availbale is zero after End of file
assertEquals(-1, input.read(), "End of File"); assertEquals("End of File", -1, input.read());
assertEquals( 0, input.available(), "Available after End of File"); assertEquals("Available after End of File", 0, input.available());
} }
@Test @Test
@@ -138,26 +138,26 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
// Read into array // Read into array
int count1 = input.read(bytes); int count1 = input.read(bytes);
assertEquals(bytes.length, count1, "Read 1"); assertEquals("Read 1", bytes.length, count1);
for (int i = 0; i < count1; i++) { for (int i = 0; i < count1; i++) {
assertEquals(i, bytes[i], "Check Bytes 1"); assertEquals("Check Bytes 1", i, bytes[i]);
} }
// Read into array // Read into array
int count2 = input.read(bytes); int count2 = input.read(bytes);
assertEquals(5, count2, "Read 2"); assertEquals("Read 2", 5, count2);
for (int i = 0; i < count2; i++) { for (int i = 0; i < count2; i++) {
assertEquals(count1 + i, bytes[i], "Check Bytes 2"); assertEquals("Check Bytes 2", count1 + i, bytes[i]);
} }
// End of File // End of File
int count3 = input.read(bytes); int count3 = input.read(bytes);
assertEquals(-1, count3, "Read 3 (EOF)"); assertEquals("Read 3 (EOF)", -1, count3);
// Test reading after the end of file // Test reading after the end of file
try { try {
int result = input.read(bytes); int result = input.read(bytes);
assertEquals(-1, result, "Wrong value read after end of file"); assertEquals("Wrong value read after end of file", -1, result);
} }
catch (IOException e) { catch (IOException e) {
fail("Should not have thrown an IOException: " + e.getMessage()); fail("Should not have thrown an IOException: " + e.getMessage());
@@ -170,20 +170,20 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
int offset = 2; int offset = 2;
int lth = 4; int lth = 4;
int count5 = input.read(bytes, offset, lth); int count5 = input.read(bytes, offset, lth);
assertEquals(lth, count5, "Read 5"); assertEquals("Read 5", lth, count5);
for (int i = offset; i < lth; i++) { for (int i = offset; i < lth; i++) {
assertEquals(i - offset, bytes[i], "Check Bytes 2"); assertEquals("Check Bytes 2", i - offset, bytes[i]);
} }
} }
@Test @Test
public void testEOF() throws Exception { public void testEOF() throws Exception {
InputStream input = makeInputStream(makeOrderedArray(2)); InputStream input = makeInputStream(makeOrderedArray(2));
assertEquals(0, input.read(), "Read 1"); assertEquals("Read 1", 0, input.read());
assertEquals(1, input.read(), "Read 2"); assertEquals("Read 2", 1, input.read());
assertEquals(-1, input.read(), "Read 3"); assertEquals("Read 3", -1, input.read());
assertEquals(-1, input.read(), "Read 4"); assertEquals("Read 4", -1, input.read());
assertEquals(-1, input.read(), "Read 5"); assertEquals("Read 5", -1, input.read());
} }
@Test @Test
@@ -205,7 +205,7 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
fail("Should throw IOException"); fail("Should throw IOException");
} }
catch (IOException e) { catch (IOException e) {
assertTrue(e.getMessage().contains("reset"), "Wrong messge: " + e.getMessage()); assertTrue("Wrong messge: " + e.getMessage(), e.getMessage().contains("reset"));
} }
} }
@@ -223,10 +223,10 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
// No mark may either throw exception, or reset to beginning of stream. // No mark may either throw exception, or reset to beginning of stream.
try { try {
input.reset(); input.reset();
assertEquals(0, input.read(), "Re-read of reset data should be same"); assertEquals("Re-read of reset data should be same", 0, input.read());
} }
catch (Exception e) { catch (Exception e) {
assertTrue(e.getMessage().contains("mark"), "Wrong no mark IOException message"); assertTrue("Wrong no mark IOException message", e.getMessage().contains("mark"));
} }
} }
@@ -249,7 +249,7 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
// Read further // Read further
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
assertEquals((position + i), input.read(), "Read After Mark [" + i + "]"); assertEquals("Read After Mark [" + i + "]", (position + i), input.read());
} }
// Reset // Reset
@@ -257,7 +257,7 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
// Read from marked position // Read from marked position
for (int i = 0; i < readlimit + 1; i++) { for (int i = 0; i < readlimit + 1; i++) {
assertEquals((position + i), input.read(), "Read After Reset [" + i + "]"); assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
} }
} }
@@ -280,16 +280,16 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
// Read past marked position // Read past marked position
for (int i = 0; i < readlimit + 1; i++) { for (int i = 0; i < readlimit + 1; i++) {
assertEquals((position + i), input.read(), "Read After Reset [" + i + "]"); assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
} }
// Reset after read limit passed, may either throw exception, or reset to last mark // Reset after read limit passed, may either throw exception, or reset to last mark
try { try {
input.reset(); input.reset();
assertEquals(1, input.read(), "Re-read of reset data should be same"); assertEquals("Re-read of reset data should be same", 1, input.read());
} }
catch (Exception e) { catch (Exception e) {
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message"); assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
} }
} }
@@ -302,29 +302,29 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
} }
int first = input.read(); int first = input.read();
assertTrue(first >= 0, "Expected to read positive value"); assertTrue("Expected to read positive value", first >= 0);
int readlimit = 5; int readlimit = 5;
// Mark // Mark
input.mark(readlimit); input.mark(readlimit);
int read = input.read(); int read = input.read();
assertTrue(read >= 0, "Expected to read positive value"); assertTrue("Expected to read positive value", read >= 0);
assertTrue(input.read() >= 0); assertTrue(input.read() >= 0);
assertTrue(input.read() >= 0); assertTrue(input.read() >= 0);
input.reset(); input.reset();
assertEquals(read, input.read(), "Expected value read differs from actual"); assertEquals("Expected value read differs from actual", read, input.read());
// Reset after read limit passed, may either throw exception, or reset to last good mark // Reset after read limit passed, may either throw exception, or reset to last good mark
try { try {
input.reset(); input.reset();
int reRead = input.read(); int reRead = input.read();
assertTrue(reRead == read || reRead == first, "Re-read of reset data should be same as initially marked or first"); assertTrue("Re-read of reset data should be same as initially marked or first", reRead == read || reRead == first);
} }
catch (Exception e) { catch (Exception e) {
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message"); assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
} }
} }
@@ -332,17 +332,17 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
public void testSkip() throws Exception { public void testSkip() throws Exception {
InputStream input = makeInputStream(makeOrderedArray(10)); InputStream input = makeInputStream(makeOrderedArray(10));
assertEquals(0, input.read(), "Unexpected value read"); assertEquals("Unexpected value read", 0, input.read());
assertEquals(1, input.read(), "Unexpected value read"); assertEquals("Unexpected value read", 1, input.read());
assertEquals(5, input.skip(5), "Unexpected number of bytes skipped"); assertEquals("Unexpected number of bytes skipped", 5, input.skip(5));
assertEquals(7, input.read(), "Unexpected value read"); assertEquals("Unexpected value read", 7, input.read());
assertEquals(2, input.skip(5), "Unexpected number of bytes skipped"); // only 2 left to skip assertEquals("Unexpected number of bytes skipped", 2, input.skip(5)); // only 2 left to skip
assertEquals(-1, input.read(), "Unexpected value read after EOF"); assertEquals("Unexpected value read after EOF", -1, input.read());
// Spec says skip might return 0 or negative after EOF... // Spec says skip might return 0 or negative after EOF...
assertTrue(input.skip(5) <= 0, "Positive value skipped after EOF"); // End of file assertTrue("Positive value skipped after EOF", input.skip(5) <= 0); // End of file
assertEquals(-1, input.read(), "Unexpected value read after EOF"); assertEquals("Unexpected value read after EOF", -1, input.read());
} }
@Test @Test
@@ -30,11 +30,12 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* LittleEndianDataInputStreamTest * LittleEndianDataInputStreamTest
@@ -31,12 +31,13 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.ObjectAbstractTest; import com.twelvemonkeys.lang.ObjectAbstractTest;
import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import org.junit.jupiter.api.Test; import static org.junit.Assert.assertNotNull;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.fail;
/** /**
* InputStreamAbstractTestCase * InputStreamAbstractTestCase
@@ -31,12 +31,12 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.ObjectAbstractTest; import com.twelvemonkeys.lang.ObjectAbstractTest;
import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* ReaderAbstractTestCase * ReaderAbstractTestCase
@@ -112,7 +112,7 @@ public abstract class ReaderAbstractTest extends ObjectAbstractTest {
int toSkip = mInput.length(); int toSkip = mInput.length();
while (toSkip > 0) { while (toSkip > 0) {
long skipped = reader.skip(toSkip); long skipped = reader.skip(toSkip);
assertFalse(skipped < 0, "Skipped < 0"); assertFalse("Skipped < 0", skipped < 0);
toSkip -= skipped; toSkip -= skipped;
} }
@@ -30,8 +30,10 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import org.junit.jupiter.api.Test; import org.junit.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/** /**
* SeekableAbstractTestCase * SeekableAbstractTestCase
@@ -30,13 +30,14 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* SeekableInputStreamAbstractTest * SeekableInputStreamAbstractTest
@@ -78,25 +79,25 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
return; // Not supported, skip test return; // Not supported, skip test
} }
assertTrue(input.read() >= 0, "Expected to read positive value"); assertTrue("Expected to read positive value", input.read() >= 0);
int readlimit = 5; int readlimit = 5;
// Mark // Mark
input.mark(readlimit); input.mark(readlimit);
int read = input.read(); int read = input.read();
assertTrue(read >= 0, "Expected to read positive value"); assertTrue("Expected to read positive value", read >= 0);
input.reset(); input.reset();
assertEquals(read, input.read(), "Expected value read differs from actual"); assertEquals("Expected value read differs from actual", read, input.read());
// Reset after read limit passed, may either throw exception, or reset to last good mark // Reset after read limit passed, may either throw exception, or reset to last good mark
try { try {
input.reset(); input.reset();
assertEquals(0, input.read(), "Re-read of reset data should be first"); assertEquals("Re-read of reset data should be first", 0, input.read());
} }
catch (Exception e) { catch (Exception e) {
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message"); assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
} }
} }
@@ -126,7 +127,7 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
seekable.seek(pos); seekable.seek(pos);
long streamPos = seekable.getStreamPosition(); long streamPos = seekable.getStreamPosition();
assertEquals(pos, streamPos, "Stream positon should match seeked position"); assertEquals("Stream positon should match seeked position", pos, streamPos);
} }
@Test @Test
@@ -136,7 +137,7 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
seekable.seek(pos); seekable.seek(pos);
seekable.flushBefore(pos); seekable.flushBefore(pos);
long flushedPos = seekable.getFlushedPosition(); long flushedPos = seekable.getFlushedPosition();
assertEquals(pos, flushedPos, "Flushed positon should match position"); assertEquals("Flushed positon should match position", pos, flushedPos);
try { try {
seekable.seek(pos - 1); seekable.seek(pos - 1);
@@ -381,13 +382,13 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
int val; int val;
val = stream.read(); val = stream.read();
assertFalse(val == -1, "Unexepected EOF"); assertFalse("Unexepected EOF", val == -1);
val = stream.read(); val = stream.read();
assertFalse(val == -1, "Unexepected EOF"); assertFalse("Unexepected EOF", val == -1);
val = stream.read(); val = stream.read();
assertFalse(val == -1, "Unexepected EOF"); assertFalse("Unexepected EOF", val == -1);
val = stream.read(); val = stream.read();
assertFalse(val == -1, "Unexepected EOF"); assertFalse("Unexepected EOF", val == -1);
stream.seek(0); stream.seek(0);
@@ -421,19 +422,19 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
stream.seek(0); stream.seek(0);
for (int i = 0; i < bytes.length; i += 2) { for (int i = 0; i < bytes.length; i += 2) {
assertEquals(i, stream.getStreamPosition(), "Wrong stream position"); assertEquals("Wrong stream position", i, stream.getStreamPosition());
int count = stream.read(buffer, 0, 2); int count = stream.read(buffer, 0, 2);
assertEquals(2, count); assertEquals(2, count);
assertEquals(bytes[i], buffer[0], String.format("Wrong value read at pos %d", stream.getStreamPosition())); assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], buffer[0]);
assertEquals(bytes[i + 1], buffer[1], String.format("Wrong value read at pos %d", stream.getStreamPosition())); assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i + 1], buffer[1]);
} }
stream.seek(0); stream.seek(0);
for (int i = 0; i < bytes.length; i++) { for (int i = 0; i < bytes.length; i++) {
assertEquals(i, stream.getStreamPosition(), "Wrong stream position"); assertEquals("Wrong stream position", i, stream.getStreamPosition());
int actual = stream.read(); int actual = stream.read();
assertEquals(bytes[i] & 0xff, actual, String.format("Wrong value read at pos %d", stream.getStreamPosition())); assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i] & 0xff, actual);
assertEquals(bytes[i], (byte) actual, String.format("Wrong value read at pos %d", stream.getStreamPosition())); assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], (byte) actual);
} }
} }
@@ -455,14 +456,14 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
try { try {
FileUtil.read(stream); // Read until EOF FileUtil.read(stream); // Read until EOF
assertEquals(-1, stream.read(), "EOF not reached (test case broken)"); assertEquals("EOF not reached (test case broken)", -1, stream.read());
assertFalse(closed[0], "Underlying stream closed before close"); assertFalse("Underlying stream closed before close", closed[0]);
} }
finally { finally {
stream.close(); stream.close();
} }
assertTrue(closed[0], "Underlying stream not closed"); assertTrue("Underlying stream not closed", closed[0]);
} }
@@ -31,12 +31,12 @@
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* StringArrayReaderTestCase * StringArrayReaderTestCase
@@ -1,119 +0,0 @@
package com.twelvemonkeys.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.util.Arrays;
import java.util.Random;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* SubStreamTest.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: SubStreamTest.java,v 1.0 07/11/2023 haraldk Exp$
*/
public class SubStreamTest {
private final Random rng = new Random(2918475687L);
@SuppressWarnings("resource")
@Test
public void testCreateNullStream() {
assertThrows(IllegalArgumentException.class, () -> {
new SubStream(null, 42);
});
}
@Test
public void testCreateNegativeLength() {
assertThrows(IllegalArgumentException.class, () -> {
new SubStream(new ByteArrayInputStream(new byte[1]), -1);
});
}
@Test
public void testReadAll() throws IOException {
byte[] buf = new byte[128];
rng.nextBytes(buf);
try (InputStream stream = new SubStream(new ByteArrayInputStream(buf), buf.length)) {
for (byte b : buf) {
assertEquals(b, (byte) stream.read());
}
assertEquals(-1, stream.read());
}
}
@Test
public void testReadAllArray() throws IOException {
byte[] buf = new byte[128];
rng.nextBytes(buf);
try (InputStream stream = new SubStream(new ByteArrayInputStream(buf), buf.length)) {
byte[] temp = new byte[buf.length / 4];
for (int i = 0; i < 4; i++) {
assertEquals(temp.length, stream.read(temp)); // Depends on ByteArrayInputStream specifics...
assertArrayEquals(Arrays.copyOfRange(buf, i * temp.length, (i + 1) * temp.length), temp);
}
assertEquals(-1, stream.read());
}
}
@Test
public void testSkipAll() throws IOException {
byte[] buf = new byte[128];
try (InputStream stream = new SubStream(new ByteArrayInputStream(buf), buf.length)) {
assertEquals(128, stream.skip(buf.length)); // Depends on ByteArrayInputStream specifics...
assertEquals(-1, stream.read());
}
}
@SuppressWarnings("EmptyTryBlock")
@Test
public void testCloseConsumesAll() throws IOException {
ByteArrayInputStream stream = new ByteArrayInputStream(new byte[128]);
try (InputStream ignore = new SubStream(stream, 128)) {
// Nothing here...
}
assertEquals(0, stream.available());
assertEquals(-1, stream.read());
}
@SuppressWarnings("EmptyTryBlock")
@Test
public void testCloseConsumesAllLongStream() throws IOException {
ByteArrayInputStream stream = new ByteArrayInputStream(new byte[256]);
try (InputStream ignore = new SubStream(stream, 128)) {
// Nothing here...
}
assertEquals(128, stream.available());
assertEquals(0, stream.read());
}
@SuppressWarnings("EmptyTryBlock")
@Test
public void testCloseConsumesAllShortStream() throws IOException {
assertTimeoutPreemptively(Duration.ofMillis(500), () -> {
ByteArrayInputStream stream = new ByteArrayInputStream(new byte[13]);
try (InputStream ignore = new SubStream(stream, 42)) {
// Nothing here...
}
assertEquals(0, stream.available());
assertEquals(-1, stream.read());
});
}
}
@@ -31,13 +31,15 @@
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.assertEquals;
/** /**
* Base64DecoderTest * Base64DecoderTest
* <p/> * <p/>
@@ -64,7 +66,7 @@ public class Base64DecoderTest extends DecoderAbstractTest {
FileUtil.copy(in, bytes); FileUtil.copy(in, bytes);
assertEquals("", new String(bytes.toByteArray()), "Strings does not match"); assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
} }
@Test @Test
@@ -76,7 +78,7 @@ public class Base64DecoderTest extends DecoderAbstractTest {
FileUtil.copy(in, bytes); FileUtil.copy(in, bytes);
assertEquals("test", new String(bytes.toByteArray()), "Strings does not match"); assertEquals("Strings does not match", "test", new String(bytes.toByteArray()));
} }
@Test @Test
@@ -91,12 +93,11 @@ public class Base64DecoderTest extends DecoderAbstractTest {
FileUtil.copy(in, bytes); FileUtil.copy(in, bytes);
assertEquals( assertEquals("Strings does not match",
"Lorem ipsum dolor sit amet, consectetuer adipiscing " + "Lorem ipsum dolor sit amet, consectetuer adipiscing " +
"elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " + "elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
"dapibus laoreet purus. Nunc viverra dictum nisl. Integer " + "dapibus laoreet purus. Nunc viverra dictum nisl. Integer " +
"ullamcorper, nisi in dictum amet.", "ullamcorper, nisi in dictum amet.",
new String(bytes.toByteArray()), new String(bytes.toByteArray()));
"Strings does not match");
} }
} }
@@ -30,12 +30,13 @@
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.assertEquals;
/** /**
* Base64EncoderTest * Base64EncoderTest
@@ -62,7 +63,7 @@ public class Base64EncoderTest extends EncoderAbstractTest {
OutputStream out = new EncoderStream(bytes, createEncoder(), true); OutputStream out = new EncoderStream(bytes, createEncoder(), true);
out.write(data.getBytes()); out.write(data.getBytes());
assertEquals("", new String(bytes.toByteArray()), "Strings does not match"); assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
} }
@Test @Test
@@ -73,7 +74,7 @@ public class Base64EncoderTest extends EncoderAbstractTest {
OutputStream out = new EncoderStream(bytes, createEncoder(), true); OutputStream out = new EncoderStream(bytes, createEncoder(), true);
out.write(data.getBytes()); out.write(data.getBytes());
assertEquals("dGVzdA==", new String(bytes.toByteArray()), "Strings does not match"); assertEquals("Strings does not match", "dGVzdA==", new String(bytes.toByteArray()));
} }
@Test @Test
@@ -87,12 +88,11 @@ public class Base64EncoderTest extends EncoderAbstractTest {
OutputStream out = new EncoderStream(bytes, createEncoder(), true); OutputStream out = new EncoderStream(bytes, createEncoder(), true);
out.write(data.getBytes()); out.write(data.getBytes());
assertEquals( assertEquals("Strings does not match",
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" + "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
"c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" + "c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
"b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" + "b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
"bmlzaSBpbiBkaWN0dW0gYW1ldC4=", "bmlzaSBpbiBkaWN0dW0gYW1ldC4=",
new String(bytes.toByteArray()), new String(bytes.toByteArray()));
"Strings does not match");
} }
} }
@@ -32,13 +32,12 @@ package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.ObjectAbstractTest; import com.twelvemonkeys.lang.ObjectAbstractTest;
import org.junit.Test;
import java.awt.image.ImageProducer;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* AbstractDecoderTest * AbstractDecoderTest
@@ -56,13 +55,13 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
return createDecoder(); return createDecoder();
} }
@Test @Test(expected = NullPointerException.class)
public final void testNullDecode() throws IOException { public final void testNullDecode() throws IOException {
Decoder decoder = createDecoder(); Decoder decoder = createDecoder();
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]); ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]);
assertThrows(NullPointerException.class, () -> {
decoder.decode(bytes, null); decoder.decode(bytes, null);
}); fail("null should throw NullPointerException");
} }
@Test @Test
@@ -72,7 +71,7 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
try { try {
int count = decoder.decode(bytes, ByteBuffer.allocate(128)); int count = decoder.decode(bytes, ByteBuffer.allocate(128));
assertEquals( 0, count, "Should not be able to read any bytes"); assertEquals("Should not be able to read any bytes", 0, count);
} }
catch (EOFException allowed) { catch (EOFException allowed) {
// Okay // Okay
@@ -95,7 +94,7 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
byte[] encoded = outBytes.toByteArray(); byte[] encoded = outBytes.toByteArray();
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder())); byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()));
assertArrayEquals(data, decoded, String.format("Data %d", pLength)); assertArrayEquals(String.format("Data %d", pLength), data, decoded);
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()); InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
outBytes = new ByteArrayOutputStream(); outBytes = new ByteArrayOutputStream();
@@ -104,7 +103,7 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
in.close(); in.close();
decoded = outBytes.toByteArray(); decoded = outBytes.toByteArray();
assertArrayEquals(data, decoded, String.format("Data %d", pLength)); assertArrayEquals(String.format("Data %d", pLength), data, decoded);
} }
@Test @Test
@@ -1,130 +0,0 @@
/*
* Copyright (c) 2022, Harald Kuhr
* 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 copyright holder 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 HOLDER 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.
*/
package com.twelvemonkeys.io.enc;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class DecoderStreamTest {
private final Random rng = new Random(5467809876546L);
private byte[] createData(final int length) {
byte[] data = new byte[length];
rng.nextBytes(data);
return data;
}
@Test
public void testDecodeSingleBytes() throws IOException {
byte[] data = createData(1327);
InputStream source = new ByteArrayInputStream(data);
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
for (byte datum : data) {
int read = stream.read();
assertNotEquals(-1, read);
assertEquals(datum, (byte) read);
}
assertEquals(-1, stream.read());
}
}
@Test
public void testDecodeArray() throws IOException {
int length = 793;
byte[] data = createData(length * 10);
InputStream source = new ByteArrayInputStream(data);
byte[] result = new byte[477];
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
int dataOffset = 0;
while (dataOffset < data.length) {
int count = stream.read(result);
assertFalse(count <= 0);
assertArrayEquals(Arrays.copyOfRange(data, dataOffset, dataOffset + count), Arrays.copyOfRange(result, 0, count));
dataOffset += count;
}
assertEquals(-1, stream.read());
}
}
@Test
public void testDecodeArrayOffset() throws IOException {
int length = 793;
byte[] data = createData(length * 10);
InputStream source = new ByteArrayInputStream(data);
byte[] result = new byte[477];
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
int dataOffset = 0;
while (dataOffset < data.length) {
int resultOffset = dataOffset % result.length;
int count = stream.read(result, resultOffset, result.length - resultOffset);
assertFalse(count <= 0);
assertArrayEquals(Arrays.copyOfRange(data, dataOffset + resultOffset, dataOffset + count), Arrays.copyOfRange(result, resultOffset, count));
dataOffset += count;
}
assertEquals(-1, stream.read());
}
}
private static final class NullDecoder implements Decoder {
@Override
public int decode(InputStream stream, ByteBuffer buffer) throws IOException {
int read = stream.read(buffer.array(), buffer.arrayOffset(), buffer.remaining());
if (read > 0) {
// Set position, should be equivalent to using buffer.put(stream.read()) until EOF or buffer full
buffer.position(read);
}
return read;
}
}
}
@@ -32,13 +32,14 @@ package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.ObjectAbstractTest; import com.twelvemonkeys.lang.ObjectAbstractTest;
import org.junit.Test;
import java.io.*; import java.io.*;
import java.util.Arrays;
import java.util.Random; import java.util.Random;
import org.junit.jupiter.api.Test; import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.fail;
/** /**
* AbstractEncoderTest * AbstractEncoderTest
@@ -72,7 +73,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
} }
} }
private byte[] createData(final int pLength) { private byte[] createData(final int pLength) throws Exception {
byte[] bytes = new byte[pLength]; byte[] bytes = new byte[pLength];
RANDOM.nextBytes(bytes); RANDOM.nextBytes(bytes);
return bytes; return bytes;
@@ -81,8 +82,9 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
private void runStreamTest(final int pLength) throws Exception { private void runStreamTest(final int pLength) throws Exception {
byte[] data = createData(pLength); byte[] data = createData(pLength);
ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) { try {
// Provoke failure for encoders that doesn't take array offset properly into account // Provoke failure for encoders that doesn't take array offset properly into account
int off = (data.length + 1) / 2; int off = (data.length + 1) / 2;
out.write(data, 0, off); out.write(data, 0, off);
@@ -90,6 +92,9 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
out.write(data, off, data.length - off); out.write(data, off, data.length - off);
} }
} }
finally {
out.close();
}
byte[] encoded = outBytes.toByteArray(); byte[] encoded = outBytes.toByteArray();
@@ -97,7 +102,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
// System.err.println("encoded: " + Arrays.toString(encoded)); // System.err.println("encoded: " + Arrays.toString(encoded));
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder())); byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
assertArrayEquals(data, decoded); assertTrue(Arrays.equals(data, decoded));
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()); InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
outBytes = new ByteArrayOutputStream(); outBytes = new ByteArrayOutputStream();
@@ -111,7 +116,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
} }
decoded = outBytes.toByteArray(); decoded = outBytes.toByteArray();
assertArrayEquals(data, decoded); assertTrue(Arrays.equals(data, decoded));
} }
@Test @Test
@@ -124,6 +129,10 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace(); e.printStackTrace();
fail(e.getMessage() + ": " + i); fail(e.getMessage() + ": " + i);
} }
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
for (int i = 100; i < 2000; i += 250) { for (int i = 100; i < 2000; i += 250) {
@@ -134,6 +143,10 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace(); e.printStackTrace();
fail(e.getMessage() + ": " + i); fail(e.getMessage() + ": " + i);
} }
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
for (int i = 2000; i < 80000; i += 1000) { for (int i = 2000; i < 80000; i += 1000) {
@@ -144,8 +157,14 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace(); e.printStackTrace();
fail(e.getMessage() + ": " + i); fail(e.getMessage() + ": " + i);
} }
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
} }
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset. // TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
} }
@@ -1,110 +0,0 @@
/*
* Copyright (c) 2022, Harald Kuhr
* 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 copyright holder 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 HOLDER 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.
*/
package com.twelvemonkeys.io.enc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class EncoderStreamTest {
private final Random rng = new Random(5467809876546L);
private byte[] createData(final int length) {
byte[] data = new byte[length];
rng.nextBytes(data);
return data;
}
@Test
public void testEncodeSingleBytes() throws IOException {
byte[] data = createData(1327);
ByteArrayOutputStream result = new ByteArrayOutputStream();
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
for (byte datum : data) {
stream.write(datum);
}
}
assertArrayEquals(data, result.toByteArray());
}
@Test
public void testEncodeArray() throws IOException {
byte[] data = createData(1793);
ByteArrayOutputStream result = new ByteArrayOutputStream();
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
for (int i = 0; i < 10; i++) {
stream.write(data);
}
}
byte[] encoded = result.toByteArray();
for (int i = 0; i < 10; i++) {
assertArrayEquals(data, Arrays.copyOfRange(encoded, i * data.length, (i + 1) * data.length));
}
}
@Test
public void testEncodeArrayOffset() throws IOException {
byte[] data = createData(87);
ByteArrayOutputStream result = new ByteArrayOutputStream();
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
for (int i = 0; i < 10; i++) {
stream.write(data, 13, 59);
}
}
byte[] original = Arrays.copyOfRange(data, 13, 13 + 59);
byte[] encoded = result.toByteArray();
for (int i = 0; i < 10; i++) {
assertArrayEquals(original, Arrays.copyOfRange(encoded, i * original.length, (i + 1) * original.length));
}
}
private static final class NullEncoder implements Encoder {
@Override
public void encode(OutputStream stream, ByteBuffer buffer) throws IOException {
stream.write(buffer.array(), buffer.arrayOffset(), buffer.remaining());
}
}
}
@@ -31,9 +31,9 @@
package com.twelvemonkeys.io.ole2; package com.twelvemonkeys.io.ole2;
import com.twelvemonkeys.io.MemoryCacheSeekableStream; import com.twelvemonkeys.io.MemoryCacheSeekableStream;
import org.junit.Test;
import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.image.ImageProducer;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -43,8 +43,8 @@ import java.nio.ByteOrder;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* CompoundDocumentTestCase * CompoundDocumentTestCase
* *
@@ -59,8 +59,8 @@ public class CompoundDocumentTest {
protected final CompoundDocument createTestDocument() throws IOException { protected final CompoundDocument createTestDocument() throws IOException {
URL input = getClass().getResource(SAMPLE_DATA); URL input = getClass().getResource(SAMPLE_DATA);
assertNotNull(input, "Missing test resource!"); assertNotNull("Missing test resource!", input);
assertEquals( "file", input.getProtocol(), "Test resource not a file:// resource"); assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
try { try {
return new CompoundDocument(new File(input.toURI())); return new CompoundDocument(new File(input.toURI()));
@@ -103,7 +103,7 @@ public class CompoundDocumentTest {
} }
} }
@Test @Test(expected = UnsupportedOperationException.class)
public void testChildEntriesUnmodifiable() throws IOException { public void testChildEntriesUnmodifiable() throws IOException {
try (CompoundDocument document = createTestDocument()) { try (CompoundDocument document = createTestDocument()) {
Entry root = document.getRootEntry(); Entry root = document.getRootEntry();
@@ -111,10 +111,9 @@ public class CompoundDocumentTest {
assertNotNull(root); assertNotNull(root);
SortedSet<Entry> children = root.getChildEntries(); SortedSet<Entry> children = root.getChildEntries();
assertThrows(UnsupportedOperationException.class, () -> {
// Should not be allowed, as it modifies the internal structure // Should not be allowed, as it modifies the internal structure
children.remove(children.first()); children.remove(children.first());
});
} }
} }
@@ -129,7 +128,7 @@ public class CompoundDocumentTest {
Entry catalog = root.getChildEntry("Catalog"); Entry catalog = root.getChildEntry("Catalog");
assertNotNull(catalog); assertNotNull(catalog);
assertNotNull(catalog.getInputStream(), "Input stream may not be null"); assertNotNull("Input stream may not be null", catalog.getInputStream());
} }
} }
@@ -137,7 +136,7 @@ public class CompoundDocumentTest {
public void testReadCatalogInputStream() throws IOException { public void testReadCatalogInputStream() throws IOException {
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA); InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
assertNotNull(input, "Missing test resource!"); assertNotNull("Missing test resource!", input);
CompoundDocument document = new CompoundDocument(input); CompoundDocument document = new CompoundDocument(input);
Entry root = document.getRootEntry(); Entry root = document.getRootEntry();
@@ -146,14 +145,14 @@ public class CompoundDocumentTest {
Entry catalog = root.getChildEntry("Catalog"); Entry catalog = root.getChildEntry("Catalog");
assertNotNull(catalog); assertNotNull(catalog);
assertNotNull(catalog.getInputStream(), "Input stream may not be null"); assertNotNull("Input stream may not be null", catalog.getInputStream());
} }
@Test @Test
public void testReadCatalogSeekableStream() throws IOException { public void testReadCatalogSeekableStream() throws IOException {
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA); InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
assertNotNull(input, "Missing test resource!"); assertNotNull("Missing test resource!", input);
CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input)); CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input));
Entry root = document.getRootEntry(); Entry root = document.getRootEntry();
@@ -162,14 +161,14 @@ public class CompoundDocumentTest {
Entry catalog = root.getChildEntry("Catalog"); Entry catalog = root.getChildEntry("Catalog");
assertNotNull(catalog); assertNotNull(catalog);
assertNotNull(catalog.getInputStream(), "Input stream may not be null"); assertNotNull("Input stream may not be null", catalog.getInputStream());
} }
@Test @Test
public void testReadCatalogImageInputStream() throws IOException { public void testReadCatalogImageInputStream() throws IOException {
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA); InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
assertNotNull(input, "Missing test resource!"); assertNotNull("Missing test resource!", input);
MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input); MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
@@ -184,6 +183,6 @@ public class CompoundDocumentTest {
Entry catalog = root.getChildEntry("Catalog"); Entry catalog = root.getChildEntry("Catalog");
assertNotNull(catalog); assertNotNull(catalog);
assertNotNull(catalog.getInputStream(), "Input stream may not be null"); assertNotNull("Input stream may not be null", catalog.getInputStream());
} }
} }
@@ -31,7 +31,7 @@
package com.twelvemonkeys.io.ole2; package com.twelvemonkeys.io.ole2;
import com.twelvemonkeys.io.*; import com.twelvemonkeys.io.*;
import org.junit.jupiter.api.Test; import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -33,6 +33,7 @@ package com.twelvemonkeys.io.ole2;
import com.twelvemonkeys.io.InputStreamAbstractTest; import com.twelvemonkeys.io.InputStreamAbstractTest;
import com.twelvemonkeys.io.LittleEndianDataOutputStream; import com.twelvemonkeys.io.LittleEndianDataOutputStream;
import com.twelvemonkeys.io.MemoryCacheSeekableStream; import com.twelvemonkeys.io.MemoryCacheSeekableStream;
import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -41,8 +42,7 @@ import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* CompoundDocument_StreamTestCase * CompoundDocument_StreamTestCase
@@ -165,8 +165,8 @@ public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
count++; count++;
} }
assertFalse(count < 32, "Short stream"); assertFalse("Short stream", count < 32);
assertFalse(count > 32, "Stream overrun"); assertFalse("Stream overrun", count > 32);
} }
@Test @Test
@@ -30,8 +30,9 @@
package com.twelvemonkeys.net; package com.twelvemonkeys.net;
import org.junit.jupiter.api.Test; import org.junit.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.Assert.assertEquals;
/** /**
* HTTPUtilTest * HTTPUtilTest
+2 -14
View File
@@ -4,25 +4,13 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.13.2-SNAPSHOT</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-lang</artifactId> <artifactId>common-lang</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>TwelveMonkeys :: Common :: Language support</name> <name>TwelveMonkeys :: Common :: Language support</name>
<description> <description>
TwelveMonkeys Common language support classes. The TwelveMonkeys Common Language support
</description> </description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.lang</project.jpms.module.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> </project>
@@ -770,7 +770,6 @@ public final class StringUtil {
*/ */
/*public*/ /*public*/
@Deprecated
static String formatNumber(long pNum, int pLen) throws IllegalArgumentException { static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
@@ -904,7 +903,7 @@ public final class StringUtil {
} }
catch (ParseException pe) { catch (ParseException pe) {
// Wrap in RuntimeException // Wrap in RuntimeException
throw new IllegalArgumentException(pe.getMessage() + " at pos " + pe.getErrorOffset()); throw new IllegalArgumentException(pe.getMessage());
} }
} }
@@ -1465,7 +1464,6 @@ public final class StringUtil {
*/ */
/*public*/ /*public*/
@Deprecated
static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) { static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) {
StringBuilder filteredString = new StringBuilder(); StringBuilder filteredString = new StringBuilder();
boolean insideDemarcatedArea = false; boolean insideDemarcatedArea = false;
@@ -1765,11 +1763,12 @@ public final class StringUtil {
* expression. * expression.
* <p> * <p>
* An invocation of this method of the form * An invocation of this method of the form
* {@code matches(str, regex)} yields exactly the * <tt>matches(<i>str</i>, <i>regex</i>)</tt> yields exactly the
* same result as the expression * same result as the expression
* </p> * </p>
* <blockquote>{@link Pattern}. * <blockquote><tt> {@link Pattern}.
* {@link Pattern#matches(String, CharSequence) matches(regex, str)}</blockquote> * {@link Pattern#matches(String, CharSequence) matches}
* (<i>regex</i>, <i>str</i>)</tt></blockquote>
* *
* @param pString the string * @param pString the string
* @param pRegex the regular expression to which this string is to be matched * @param pRegex the regular expression to which this string is to be matched
@@ -1788,14 +1787,16 @@ public final class StringUtil {
* regular expression with the given pReplacement. * regular expression with the given pReplacement.
* <p> * <p>
* An invocation of this method of the form * An invocation of this method of the form
* {@code replaceFirst(str, regex, repl)} * <tt>
* replaceFirst(<i>str</i>, <i>regex</i>, <i>repl</i>)
* </tt>
* yields exactly the same result as the expression: * yields exactly the same result as the expression:
* </p> * </p>
* <blockquote> * <blockquote><tt>
* {@link Pattern#compile(String) Pattern.compile(regex)} * {@link Pattern}.{@link Pattern#compile(String) compile}(<i>regex</i>).
* {@link Pattern#matcher .matcher(str)} * {@link Pattern#matcher matcher}(<i>str</i>).
* {@link java.util.regex.Matcher#replaceFirst .replaceFirst(repl)} * {@link java.util.regex.Matcher#replaceFirst replaceFirst}(<i>repl</i>)
* </blockquote> * </tt></blockquote>
* *
* @param pString the string * @param pString the string
* @param pRegex the regular expression to which this string is to be matched * @param pRegex the regular expression to which this string is to be matched
@@ -1814,14 +1815,14 @@ public final class StringUtil {
* regular expression with the given pReplacement. * regular expression with the given pReplacement.
* <p> * <p>
* An invocation of this method of the form * An invocation of this method of the form
* {@code replaceAll(str, pRegex, repl)} * <tt>replaceAll(<i>str</i>, <i>pRegex</i>, <i>repl</i>)</tt>
* yields exactly the same result as the expression * yields exactly the same result as the expression
* </p> * </p>
* <blockquote> * <blockquote><tt>
* {@link Pattern#compile(String) Pattern.compile(pRegex)} * {@link Pattern}.{@link Pattern#compile(String) compile}(<i>pRegex</i>).
* {@link Pattern#matcher .matcher(str)} * {@link Pattern#matcher matcher}(<i>str</i>{@code ).
* {@link java.util.regex.Matcher#replaceAll .replaceAll(repl)} * {@link java.util.regex.Matcher#replaceAll replaceAll}(}<i>repl</i>{@code )}
* </blockquote> * </tt></blockquote>
* *
* @param pString the string * @param pString the string
* @param pRegex the regular expression to which this string is to be matched * @param pRegex the regular expression to which this string is to be matched
@@ -1859,12 +1860,12 @@ public final class StringUtil {
* </p> * </p>
* <p> * <p>
* An invocation of this method of the form * An invocation of this method of the form
* {@code split(str, regex, n)} * <tt>split(<i>str</i>, <i>regex</i>, <i>n</i>)</tt>
* yields the same result as the expression: * yields the same result as the expression:
* </p> * </p>
* <blockquote>{@link Pattern}. * <blockquote>{@link Pattern}.
* {@link Pattern#compile(String) compile(regex)}. * {@link Pattern#compile(String) compile}<tt>(<i>regex</i>).
* {@link Pattern#split(CharSequence,int) split(str, n)} * {@link Pattern#split(CharSequence,int) split}(<i>str</i>, <i>n</i>)</tt>
* </blockquote> * </blockquote>
* *
* @param pString the string * @param pString the string
@@ -330,7 +330,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
} }
/** /**
* A simple Map.Entry implementation. * A simple Map.Entry implementaton.
*/ */
static class BasicEntry<K, V> implements Entry<K, V>, Serializable { static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
K mKey; K mKey;
@@ -157,7 +157,6 @@ public class Time {
* @see #parseTime(String) * @see #parseTime(String)
* @deprecated * @deprecated
*/ */
@Deprecated
public String toString(String pFormatStr) { public String toString(String pFormatStr) {
TimeFormat tf = new TimeFormat(pFormatStr); TimeFormat tf = new TimeFormat(pFormatStr);
@@ -175,7 +174,6 @@ public class Time {
* @see #toString(String) * @see #toString(String)
* @deprecated * @deprecated
*/ */
@Deprecated
public static Time parseTime(String pStr) { public static Time parseTime(String pStr) {
TimeFormat tf = TimeFormat.getInstance(); TimeFormat tf = TimeFormat.getInstance();
@@ -34,7 +34,7 @@ import java.io.Serializable;
import java.util.*; import java.util.*;
/** /**
* A {@code Map} implementation that removes (expires) its elements after * A {@code Map} implementation that removes (exipres) its elements after
* a given period. The map is by default backed by a {@link java.util.HashMap}, * a given period. The map is by default backed by a {@link java.util.HashMap},
* or can be instantiated with any given {@code Map} as backing. * or can be instantiated with any given {@code Map} as backing.
* <p> * <p>
@@ -67,7 +67,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
protected long expiryTime = 60000L; // 1 minute protected long expiryTime = 60000L; // 1 minute
////////////////////// //////////////////////
private volatile long nextExpiryTime = Long.MAX_VALUE; private volatile long nextExpiryTime;
////////////////////// //////////////////////
/** /**
@@ -178,7 +178,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* @return {@code true} if this map contains no key-value mappings. * @return {@code true} if this map contains no key-value mappings.
*/ */
public boolean isEmpty() { public boolean isEmpty() {
return size() <= 0; return (size() <= 0);
} }
/** /**
@@ -208,7 +208,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* @see #containsKey(java.lang.Object) * @see #containsKey(java.lang.Object)
*/ */
public V get(Object pKey) { public V get(Object pKey) {
TimedEntry entry = (TimedEntry) entries.get(pKey); TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
if (entry == null) { if (entry == null) {
return null; return null;
@@ -236,7 +236,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* {@code null} values. * {@code null} values.
*/ */
public V put(K pKey, V pValue) { public V put(K pKey, V pValue) {
TimedEntry entry = (TimedEntry) entries.get(pKey); TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
V oldValue; V oldValue;
if (entry == null) { if (entry == null) {
@@ -272,7 +272,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* {@code null} values. * {@code null} values.
*/ */
public V remove(Object pKey) { public V remove(Object pKey) {
TimedEntry entry = (TimedEntry) entries.remove(pKey); TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
return (entry != null) ? entry.getValue() : null; return (entry != null) ? entry.getValue() : null;
} }
@@ -284,12 +284,13 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
init(); init();
} }
/*protected*/ TimedEntry createEntry(K pKey, V pValue) { /*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
return new TimedEntry(pKey, pValue); return new TimedEntry<K, V>(pKey, pValue);
} }
/** /**
* Removes any expired mappings. * Removes any expired mappings.
*
*/ */
protected void removeExpiredEntries() { protected void removeExpiredEntries() {
// Remove any expired elements // Remove any expired elements
@@ -311,7 +312,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
long next = Long.MAX_VALUE; long next = Long.MAX_VALUE;
nextExpiryTime = next; // Avoid multiple runs... nextExpiryTime = next; // Avoid multiple runs...
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) { for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
TimedEntry entry = (TimedEntry) iterator.next(); TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
//// ////
long expires = entry.expires(); long expires = entry.expires();
if (expires < next) { if (expires < next) {
@@ -375,7 +376,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
while (mNext == null && mIterator.hasNext()) { while (mNext == null && mIterator.hasNext()) {
Entry<K, Entry<K, V>> entry = mIterator.next(); Entry<K, Entry<K, V>> entry = mIterator.next();
TimedEntry timed = (TimedEntry) entry.getValue(); TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
if (timed.isExpiredBy(mNow)) { if (timed.isExpiredBy(mNow)) {
// Remove from map, and continue // Remove from map, and continue
@@ -424,26 +425,17 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
/** /**
* Keeps track of timed objects * Keeps track of timed objects
*/ */
private class TimedEntry extends BasicEntry<K, V> { private class TimedEntry<K, V> extends BasicEntry<K, V> {
private long mTimestamp; private long mTimestamp;
TimedEntry(K pKey, V pValue) { TimedEntry(K pKey, V pValue) {
super(pKey, pValue); super(pKey, pValue);
updateTimestamp(); mTimestamp = System.currentTimeMillis();
} }
public V setValue(V pValue) { public V setValue(V pValue) {
updateTimestamp();
return super.setValue(pValue);
}
private void updateTimestamp() {
mTimestamp = System.currentTimeMillis(); mTimestamp = System.currentTimeMillis();
return super.setValue(pValue);
long expires = expires();
if (expires < nextExpiryTime) {
nextExpiryTime = expires;
}
} }
final boolean isExpired() { final boolean isExpired() {
@@ -111,7 +111,6 @@ import java.io.PrintStream;
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a> * @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
* @deprecated Will probably be removed in the near future * @deprecated Will probably be removed in the near future
*/ */
@Deprecated
public class WildcardStringParser { public class WildcardStringParser {
// TODO: Get rid of this class // TODO: Get rid of this class
@@ -30,13 +30,14 @@
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import org.junit.Test;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* BeanUtilTestCase * BeanUtilTestCase
@@ -160,8 +161,10 @@ public class BeanUtilTest {
} }
assertNotNull(bean.getAmbiguous()); assertNotNull(bean.getAmbiguous());
assertEquals("one", bean.getAmbiguous(), "String converted rather than invoking setAmbiguous(String), ordering not predictable"); assertEquals("String converted rather than invoking setAmbiguous(String), ordering not predictable",
assertSame(value, bean.getAmbiguous(), "String converted rather than invoking setAmbiguous(String), ordering not predictable"); "one", bean.getAmbiguous());
assertSame("String converted rather than invoking setAmbiguous(String), ordering not predictable",
value, bean.getAmbiguous());
} }
@Test @Test
@@ -181,10 +184,10 @@ public class BeanUtilTest {
} }
assertNotNull(bean.getAmbiguous()); assertNotNull(bean.getAmbiguous());
assertEquals(2, bean.getAmbiguous(), assertEquals("Integer converted rather than invoking setAmbiguous(Integer), ordering not predictable",
"Integer converted rather than invoking setAmbiguous(Integer), ordering not predictable"); 2, bean.getAmbiguous());
assertSame(value, bean.getAmbiguous(), assertSame("Integer converted rather than invoking setAmbiguous(Integer), ordering not predictable",
"Integer converted rather than invoking setAmbiguous(Integer), ordering not predictable"); value, bean.getAmbiguous());
} }
@Test @Test
@@ -204,8 +207,10 @@ public class BeanUtilTest {
} }
assertNotNull(bean.getAmbiguous()); assertNotNull(bean.getAmbiguous());
assertEquals(value.getClass(), bean.getAmbiguous().getClass(), "Object converted rather than invoking setAmbiguous(Object), ordering not predictable"); assertEquals("Object converted rather than invoking setAmbiguous(Object), ordering not predictable",
assertSame(value, bean.getAmbiguous(), "Object converted rather than invoking setAmbiguous(Object), ordering not predictable"); value.getClass(), bean.getAmbiguous().getClass());
assertSame("Object converted rather than invoking setAmbiguous(Object), ordering not predictable",
value, bean.getAmbiguous());
} }
static class TestBean { static class TestBean {
@@ -30,16 +30,16 @@
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import org.junit.jupiter.api.Test; import static org.junit.Assert.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* DateUtilTest * DateUtilTest
@@ -48,9 +48,12 @@ import static org.junit.jupiter.api.Assertions.*;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: DateUtilTest.java,v 1.0 11.04.12 16:21 haraldk Exp$ * @version $Id: DateUtilTest.java,v 1.0 11.04.12 16:21 haraldk Exp$
*/ */
@RunWith(Parameterized.class)
public class DateUtilTest { public class DateUtilTest {
private final TimeZone timeZone;
@Parameterized.Parameters
public static List<Object[]> timeZones() { public static List<Object[]> timeZones() {
return Arrays.asList(new Object[][] { return Arrays.asList(new Object[][] {
{TimeZone.getTimeZone("UTC")}, {TimeZone.getTimeZone("UTC")},
@@ -59,6 +62,10 @@ public class DateUtilTest {
}); });
} }
public DateUtilTest(final TimeZone timeZone) {
this.timeZone = timeZone;
}
private Calendar getCalendar(long time) { private Calendar getCalendar(long time) {
return getCalendar(time, TimeZone.getDefault()); return getCalendar(time, TimeZone.getDefault());
} }
@@ -94,9 +101,8 @@ public class DateUtilTest {
assertEquals(0, calendar.get(Calendar.MINUTE)); assertEquals(0, calendar.get(Calendar.MINUTE));
} }
@ParameterizedTest @Test
@MethodSource("timeZones") public void testRoundToHourTZ() {
public void testRoundToHourTZ(TimeZone timeZone) {
Calendar calendar = getCalendar(DateUtil.roundToHour(System.currentTimeMillis(), timeZone), timeZone); Calendar calendar = getCalendar(DateUtil.roundToHour(System.currentTimeMillis(), timeZone), timeZone);
assertEquals(0, calendar.get(Calendar.MILLISECOND)); assertEquals(0, calendar.get(Calendar.MILLISECOND));
@@ -114,9 +120,8 @@ public class DateUtilTest {
assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY)); assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY));
} }
@ParameterizedTest @Test
@MethodSource("timeZones") public void testRoundToDayTZ() {
public void testRoundToDayTZ(TimeZone timeZone) {
Calendar calendar = getCalendar(DateUtil.roundToDay(System.currentTimeMillis(), timeZone), timeZone); Calendar calendar = getCalendar(DateUtil.roundToDay(System.currentTimeMillis(), timeZone), timeZone);
assertEquals(0, calendar.get(Calendar.MILLISECOND)); assertEquals(0, calendar.get(Calendar.MILLISECOND));
@@ -30,11 +30,12 @@
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import org.junit.Test;
import java.io.*; import java.io.*;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* AbstractObjectTestCase * AbstractObjectTestCase
@@ -78,10 +79,10 @@ public abstract class ObjectAbstractTest {
Class cl = obj.getClass(); Class cl = obj.getClass();
if (isEqualsOverriden(cl)) { if (isEqualsOverriden(cl)) {
assertTrue(isHashCodeOverriden(cl), "Class " + cl.getName() + " implements equals but not hashCode"); assertTrue("Class " + cl.getName() + " implements equals but not hashCode", isHashCodeOverriden(cl));
} }
else if (isHashCodeOverriden(cl)) { else if (isHashCodeOverriden(cl)) {
assertTrue(isEqualsOverriden(cl), "Class " + cl.getName() + " implements hashCode but not equals"); assertTrue("Class " + cl.getName() + " implements hashCode but not equals", isEqualsOverriden(cl));
} }
} }
@@ -106,7 +107,7 @@ public abstract class ObjectAbstractTest {
@Test @Test
public void testObjectEqualsSelf() { public void testObjectEqualsSelf() {
Object obj = makeObject(); Object obj = makeObject();
assertEquals(obj, obj, "An Object should equal itself"); assertEquals("An Object should equal itself", obj, obj);
} }
@Test @Test
@@ -114,26 +115,32 @@ public abstract class ObjectAbstractTest {
Object obj = makeObject(); Object obj = makeObject();
// NOTE: Makes sure this doesn't throw NPE either // NOTE: Makes sure this doesn't throw NPE either
//noinspection ObjectEqualsNull //noinspection ObjectEqualsNull
assertFalse(obj.equals(null), "An object should never equal null"); assertFalse("An object should never equal null", obj.equals(null));
} }
@Test @Test
public void testObjectHashCodeEqualsSelfHashCode() { public void testObjectHashCodeEqualsSelfHashCode() {
Object obj = makeObject(); Object obj = makeObject();
assertEquals(obj.hashCode(), obj.hashCode(), "hashCode should be repeatable"); assertEquals("hashCode should be repeatable", obj.hashCode(), obj.hashCode());
} }
@Test @Test
public void testObjectHashCodeEqualsContract() { public void testObjectHashCodeEqualsContract() {
Object obj1 = makeObject(); Object obj1 = makeObject();
if (obj1.equals(obj1)) { if (obj1.equals(obj1)) {
assertEquals(obj1.hashCode(), obj1.hashCode(), "[1] When two objects are equal, their hashCodes should be also."); assertEquals(
"[1] When two objects are equal, their hashCodes should be also.",
obj1.hashCode(), obj1.hashCode());
} }
// TODO: Make sure we create at least one equal object, and one different object // TODO: Make sure we create at least one equal object, and one different object
Object obj2 = makeObject(); Object obj2 = makeObject();
if (obj1.equals(obj2)) { if (obj1.equals(obj2)) {
assertEquals(obj1.hashCode(), obj2.hashCode(), "[2] When two objects are equal, their hashCodes should be also."); assertEquals(
assertTrue(obj2.equals(obj1), "When obj1.equals(obj2) is true, then obj2.equals(obj1) should also be true"); "[2] When two objects are equal, their hashCodes should be also.",
obj1.hashCode(), obj2.hashCode());
assertTrue(
"When obj1.equals(obj2) is true, then obj2.equals(obj1) should also be true",
obj2.equals(obj1));
} }
} }
@@ -162,14 +169,14 @@ public abstract class ObjectAbstractTest {
Object cloned = clone.invoke(obj); Object cloned = clone.invoke(obj);
assertNotNull(cloned, "Cloned object should never be null"); assertNotNull("Cloned object should never be null", cloned);
// TODO: This can only be asserted if equals() test is based on // TODO: This can only be asserted if equals() test is based on
// value equality, not reference (identity) equality // value equality, not reference (identity) equality
// Maybe it's possible to do a reflective introspection of // Maybe it's possible to do a reflective introspection of
// the objects fields? // the objects fields?
if (isHashCodeOverriden(cl)) { if (isHashCodeOverriden(cl)) {
assertEquals(obj, cloned, "Cloned object not equal"); assertEquals("Cloned object not equal", obj, cloned);
} }
} }
} }
@@ -228,7 +235,7 @@ public abstract class ObjectAbstractTest {
// Maybe it's possible to do a reflective introspection of // Maybe it's possible to do a reflective introspection of
// the objects fields? // the objects fields?
if (isEqualsOverriden(obj.getClass())) { if (isEqualsOverriden(obj.getClass())) {
assertEquals(obj, dest, "obj != deserialize(serialize(obj))"); assertEquals("obj != deserialize(serialize(obj))", obj, dest);
} }
} }
} }
@@ -30,11 +30,13 @@
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import org.junit.Ignore;
import org.junit.Test;
import java.util.Properties; import java.util.Properties;
import org.junit.jupiter.api.Disabled; import static org.junit.Assert.assertEquals;
import org.junit.jupiter.api.Test; import static org.junit.Assert.assertNotNull;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* PlatformTest * PlatformTest
@@ -119,7 +121,7 @@ public class PlatformTest {
assertEquals(Platform.Architecture.X86, platform.getArchitecture()); assertEquals(Platform.Architecture.X86, platform.getArchitecture());
} }
@Disabled("Known issue, needs resolve") @Ignore("Known issue, needs resolve")
@Test @Test
public void testCreateWindows686() { public void testCreateWindows686() {
Platform platform = new Platform(createProperties("Windows", "5.1", "686")); Platform platform = new Platform(createProperties("Windows", "5.1", "686"));
@@ -127,7 +129,7 @@ public class PlatformTest {
assertEquals(Platform.Architecture.X86, platform.getArchitecture()); assertEquals(Platform.Architecture.X86, platform.getArchitecture());
} }
@Disabled("Known issue, needs resolve") @Ignore("Known issue, needs resolve")
@Test @Test
public void testCreateLinuxX86() { public void testCreateLinuxX86() {
Platform platform = new Platform(createProperties("Linux", "3.0.18", "x86")); Platform platform = new Platform(createProperties("Linux", "3.0.18", "x86"));
@@ -30,6 +30,8 @@
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import org.junit.Test;
import java.awt.*; import java.awt.*;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.DateFormat; import java.text.DateFormat;
@@ -39,8 +41,8 @@ import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* StringUtilTestCase * StringUtilTestCase
* *
@@ -74,24 +76,24 @@ public class StringUtilTest {
assertNull(StringUtil.valueOf(null)); assertNull(StringUtil.valueOf(null));
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void testToUpperCase() { public void testToUpperCase() {
String str = StringUtil.toUpperCase(TEST_STRING); String str = StringUtil.toUpperCase(TEST_STRING);
assertNotNull(str); assertNotNull(str);
assertEquals(TEST_STRING.toUpperCase(), str); assertEquals(TEST_STRING.toUpperCase(), str);
assertNull(StringUtil.toUpperCase(null)); str = StringUtil.toUpperCase(null);
assertNull(str);
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void testToLowerCase() { public void testToLowerCase() {
String str = StringUtil.toLowerCase(TEST_STRING); String str = StringUtil.toLowerCase(TEST_STRING);
assertNotNull(str); assertNotNull(str);
assertEquals(TEST_STRING.toLowerCase(), str); assertEquals(TEST_STRING.toLowerCase(), str);
assertNull(StringUtil.toLowerCase(null)); str = StringUtil.toLowerCase(null);
assertNull(str);
} }
@Test @Test
@@ -111,7 +113,6 @@ public class StringUtilTest {
assertFalse(StringUtil.isEmpty(new String[]{WHITESPACE_STRING, TEST_STRING})); assertFalse(StringUtil.isEmpty(new String[]{WHITESPACE_STRING, TEST_STRING}));
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void testContains() { public void testContains() {
assertTrue(StringUtil.contains(TEST_STRING, TEST_STRING)); assertTrue(StringUtil.contains(TEST_STRING, TEST_STRING));
@@ -144,7 +145,6 @@ public class StringUtilTest {
assertFalse(StringUtil.containsIgnoreCase(null, null)); assertFalse(StringUtil.containsIgnoreCase(null, null));
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void testContainsChar() { public void testContainsChar() {
for (int i = 0; i < TEST_STRING.length(); i++) { for (int i = 0; i < TEST_STRING.length(); i++) {
@@ -163,10 +163,10 @@ public class StringUtilTest {
// Test all alpha-chars // Test all alpha-chars
for (int i = 'a'; i < 'z'; i++) { for (int i = 'a'; i < 'z'; i++) {
if (TEST_STRING.indexOf(i) < 0) { if (TEST_STRING.indexOf(i) < 0) {
assertFalse(StringUtil.contains(TEST_STRING, i), TEST_STRING + " seems to contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i)); assertFalse(TEST_STRING + " seems to contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i), StringUtil.contains(TEST_STRING, i));
} }
else { else {
assertTrue(StringUtil.contains(TEST_STRING, i), TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i)); assertTrue(TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i), StringUtil.contains(TEST_STRING, i));
} }
} }
} }
@@ -197,10 +197,10 @@ public class StringUtilTest {
// Test all alpha-chars // Test all alpha-chars
for (int i = 'a'; i < 'z'; i++) { for (int i = 'a'; i < 'z'; i++) {
if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) { if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) {
assertFalse(StringUtil.containsIgnoreCase(TEST_STRING, i), TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i)))); assertFalse(TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i))), StringUtil.containsIgnoreCase(TEST_STRING, i));
} }
else { else {
assertTrue(StringUtil.containsIgnoreCase(TEST_STRING, i), TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i)); assertTrue(TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i), StringUtil.containsIgnoreCase(TEST_STRING, i));
} }
} }
} }
@@ -348,10 +348,10 @@ public class StringUtilTest {
// Test all alpha-chars // Test all alpha-chars
for (int i = 'a'; i < 'z'; i++) { for (int i = 'a'; i < 'z'; i++) {
if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) { if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) {
assertEquals(-1, StringUtil.indexOfIgnoreCase(TEST_STRING, i), TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i)))); assertEquals(TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i))), -1, StringUtil.indexOfIgnoreCase(TEST_STRING, i));
} }
else { else {
assertTrue(0 <= StringUtil.indexOfIgnoreCase(TEST_STRING, i), TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i)); assertTrue(TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i), 0 <= StringUtil.indexOfIgnoreCase(TEST_STRING, i));
} }
} }
} }
@@ -383,10 +383,10 @@ public class StringUtilTest {
// Test all alpha-chars // Test all alpha-chars
for (int i = 'a'; i < 'z'; i++) { for (int i = 'a'; i < 'z'; i++) {
if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) { if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) {
assertEquals(-1, StringUtil.indexOfIgnoreCase(TEST_STRING, i, 0), TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i)))); assertEquals(TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i))), -1, StringUtil.indexOfIgnoreCase(TEST_STRING, i, 0));
} }
else { else {
assertTrue(0 <= StringUtil.indexOfIgnoreCase(TEST_STRING, i, 0), TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i)); assertTrue(TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i), 0 <= StringUtil.indexOfIgnoreCase(TEST_STRING, i, 0));
} }
} }
} }
@@ -418,10 +418,10 @@ public class StringUtilTest {
// Test all alpha-chars // Test all alpha-chars
for (int i = 'a'; i < 'z'; i++) { for (int i = 'a'; i < 'z'; i++) {
if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) { if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) {
assertEquals(-1, StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i), TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i)))); assertEquals(TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i))), -1, StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i));
} }
else { else {
assertTrue(0 <= StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i), TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i)); assertTrue(TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i), 0 <= StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i));
} }
} }
} }
@@ -453,10 +453,10 @@ public class StringUtilTest {
// Test all alpha-chars // Test all alpha-chars
for (int i = 'a'; i < 'z'; i++) { for (int i = 'a'; i < 'z'; i++) {
if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) { if ((TEST_STRING.indexOf(i) < 0) && (TEST_STRING.indexOf(Character.toUpperCase((char) i)) < 0)) {
assertEquals(-1, StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i, TEST_STRING.length()), TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i)))); assertEquals(TEST_STRING + " seems to contain '" + (char) i + "', at index " + Math.max(TEST_STRING.indexOf(i), TEST_STRING.indexOf(Character.toUpperCase((char) i))), -1, StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i, TEST_STRING.length()));
} }
else { else {
assertTrue(0 <= StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i, TEST_STRING.length()), TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i)); assertTrue(TEST_STRING + " seems to not contain '" + (char) i + "', at index " + TEST_STRING.indexOf(i), 0 <= StringUtil.lastIndexOfIgnoreCase(TEST_STRING, i, TEST_STRING.length()));
} }
} }
} }
@@ -466,7 +466,7 @@ public class StringUtilTest {
assertEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING)); assertEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING));
assertEquals(TEST_STRING, StringUtil.ltrim(" " + TEST_STRING)); assertEquals(TEST_STRING, StringUtil.ltrim(" " + TEST_STRING));
assertEquals(TEST_STRING, StringUtil.ltrim(WHITESPACE_STRING + TEST_STRING)); assertEquals(TEST_STRING, StringUtil.ltrim(WHITESPACE_STRING + TEST_STRING));
assertNotEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING)); assertFalse(TEST_STRING.equals(StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING)));
// TODO: Test is not complete // TODO: Test is not complete
} }
@@ -475,7 +475,7 @@ public class StringUtilTest {
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING)); assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING));
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + " ")); assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + " "));
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + WHITESPACE_STRING)); assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + WHITESPACE_STRING));
assertNotEquals(TEST_STRING, StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING)); assertFalse(TEST_STRING.equals(StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING)));
// TODO: Test is not complete // TODO: Test is not complete
} }
@@ -516,7 +516,7 @@ public class StringUtilTest {
public void testCaptialize() { public void testCaptialize() {
assertNull(StringUtil.capitalize(null)); assertNull(StringUtil.capitalize(null));
assertEquals(TEST_STRING.toUpperCase(), StringUtil.capitalize(TEST_STRING.toUpperCase())); assertEquals(TEST_STRING.toUpperCase(), StringUtil.capitalize(TEST_STRING.toUpperCase()));
assertEquals('A', StringUtil.capitalize("abc").charAt(0)); assertTrue(StringUtil.capitalize("abc").charAt(0) == 'A');
} }
@Test @Test
@@ -552,13 +552,13 @@ public class StringUtilTest {
public void testToDateWithFormatString() { public void testToDateWithFormatString() {
Calendar cal = new GregorianCalendar(); Calendar cal = new GregorianCalendar();
cal.clear(); cal.clear();
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based cal.set(1976, 2, 16); // Month is 0-based
Date date = StringUtil.toDate("16.03.1976", "dd.MM.yyyy"); Date date = StringUtil.toDate("16.03.1976", "dd.MM.yyyy");
assertNotNull(date); assertNotNull(date);
assertEquals(cal.getTime(), date); assertEquals(cal.getTime(), date);
cal.clear(); cal.clear();
cal.set(2004, Calendar.MAY, 13, 23, 51, 3); cal.set(2004, 4, 13, 23, 51, 3);
date = StringUtil.toDate("2004-5-13 23:51 (03)", "yyyy-MM-dd hh:mm (ss)"); date = StringUtil.toDate("2004-5-13 23:51 (03)", "yyyy-MM-dd hh:mm (ss)");
assertNotNull(date); assertNotNull(date);
assertEquals(cal.getTime(), date); assertEquals(cal.getTime(), date);
@@ -576,23 +576,23 @@ public class StringUtilTest {
public void testToDateWithFormat() { public void testToDateWithFormat() {
Calendar cal = new GregorianCalendar(); Calendar cal = new GregorianCalendar();
cal.clear(); cal.clear();
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based cal.set(1976, 2, 16); // Month is 0-based
Date date = StringUtil.toDate("16.03.1976", new SimpleDateFormat("dd.MM.yyyy")); Date date = StringUtil.toDate("16.03.1976", new SimpleDateFormat("dd.MM.yyyy"));
assertNotNull(date); assertNotNull(date);
assertEquals(cal.getTime(), date); assertEquals(cal.getTime(), date);
cal.clear(); cal.clear();
cal.set(2004, Calendar.MAY, 13, 23, 51); cal.set(2004, 4, 13, 23, 51);
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO")); date = StringUtil.toDate("13.5.04 23:51",
date = StringUtil.toDate(format.format(cal.getTime()), format); DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO")));
assertNotNull(date); assertNotNull(date);
assertEquals(cal.getTime(), date); assertEquals(cal.getTime(), date);
cal.clear(); cal.clear();
cal.set(Calendar.HOUR, 1); cal.set(Calendar.HOUR, 1);
cal.set(Calendar.MINUTE, 2); cal.set(Calendar.MINUTE, 2);
format = new SimpleDateFormat("HH:mm"); date = StringUtil.toDate("1:02 am",
date = StringUtil.toDate("1:02", format); DateFormat.getTimeInstance(DateFormat.SHORT, Locale.US));
assertNotNull(date); assertNotNull(date);
assertEquals(cal.getTime(), date); assertEquals(cal.getTime(), date);
} }
@@ -601,9 +601,10 @@ public class StringUtilTest {
public void testToTimestamp() { public void testToTimestamp() {
Calendar cal = new GregorianCalendar(); Calendar cal = new GregorianCalendar();
cal.clear(); cal.clear();
cal.set(1976, Calendar.MARCH, 16, 21, 28, 4); // Month is 0-based cal.set(1976, 2, 16, 21, 28, 4); // Month is 0-based
Timestamp date = StringUtil.toTimestamp("1976-03-16 21:28:04"); Date date = StringUtil.toTimestamp("1976-03-16 21:28:04");
assertNotNull(date); assertNotNull(date);
assertTrue(date instanceof Timestamp);
assertEquals(cal.getTime(), date); assertEquals(cal.getTime(), date);
} }
@@ -820,7 +821,7 @@ public class StringUtilTest {
assertTrue(StringUtil.isNumber("12345")); assertTrue(StringUtil.isNumber("12345"));
assertTrue(StringUtil.isNumber(TEST_INTEGER.toString())); assertTrue(StringUtil.isNumber(TEST_INTEGER.toString()));
assertTrue(StringUtil.isNumber("1234567890123456789012345678901234567890")); assertTrue(StringUtil.isNumber("1234567890123456789012345678901234567890"));
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE)); assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
assertFalse(StringUtil.isNumber("abc")); assertFalse(StringUtil.isNumber("abc"));
assertFalse(StringUtil.isNumber(TEST_STRING)); assertFalse(StringUtil.isNumber(TEST_STRING));
} }
@@ -830,7 +831,7 @@ public class StringUtilTest {
assertTrue(StringUtil.isNumber("-12345")); assertTrue(StringUtil.isNumber("-12345"));
assertTrue(StringUtil.isNumber('-' + TEST_INTEGER.toString())); assertTrue(StringUtil.isNumber('-' + TEST_INTEGER.toString()));
assertTrue(StringUtil.isNumber("-1234567890123456789012345678901234567890")); assertTrue(StringUtil.isNumber("-1234567890123456789012345678901234567890"));
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE)); assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
assertFalse(StringUtil.isNumber("-abc")); assertFalse(StringUtil.isNumber("-abc"));
assertFalse(StringUtil.isNumber('-' + TEST_STRING)); assertFalse(StringUtil.isNumber('-' + TEST_STRING));
} }
@@ -30,7 +30,7 @@
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import org.junit.jupiter.api.Disabled; import org.junit.Ignore;
/** /**
* SystemUtilTest * SystemUtilTest
@@ -39,6 +39,6 @@ import org.junit.jupiter.api.Disabled;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: SystemUtilTest.java,v 1.0 11.04.12 16:21 haraldk Exp$ * @version $Id: SystemUtilTest.java,v 1.0 11.04.12 16:21 haraldk Exp$
*/ */
@Disabled @Ignore
public class SystemUtilTest { public class SystemUtilTest {
} }
File diff suppressed because it is too large Load Diff
@@ -45,11 +45,12 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.*; import java.util.*;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* Abstract test class for {@link java.util.Collection} methods and contracts. * Abstract test class for {@link java.util.Collection} methods and contracts.
@@ -250,8 +251,11 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
*/ */
public void verifyAll() { public void verifyAll() {
int confirmedSize = confirmed.size(); int confirmedSize = confirmed.size();
assertEquals(confirmedSize, collection.size(), "Collection size should match confirmed collection's"); assertEquals("Collection size should match confirmed collection's",
assertEquals(confirmed.isEmpty(), collection.isEmpty(), "Collection isEmpty() result should match confirmed collection's"); confirmedSize, collection.size());
assertEquals("Collection isEmpty() result should match confirmed " +
" collection's",
confirmed.isEmpty(), collection.isEmpty());
// verify the collections are the same by attempting to match each // verify the collections are the same by attempting to match each
// object in the collection and confirmed collection. To account for // object in the collection and confirmed collection. To account for
@@ -517,8 +521,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
boolean r = collection.add(elements[i]); boolean r = collection.add(elements[i]);
confirmed.add(elements[i]); confirmed.add(elements[i]);
verifyAll(); verifyAll();
assertTrue(r, "Empty collection changed after add"); assertTrue("Empty collection changed after add", r);
assertEquals(1, collection.size(), "Collection size is 1 after first add"); assertEquals("Collection size is 1 after first add", 1, collection.size());
} }
resetEmpty(); resetEmpty();
@@ -528,8 +532,10 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
confirmed.add(elements[i]); confirmed.add(elements[i]);
verifyAll(); verifyAll();
if (r) size++; if (r) size++;
assertEquals(size, collection.size(), "Collection size should grow after add"); assertEquals("Collection size should grow after add",
assertTrue(collection.contains(elements[i]), "Collection should contain added element"); size, collection.size());
assertTrue("Collection should contain added element",
collection.contains(elements[i]));
} }
} }
@@ -546,9 +552,10 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
boolean r = collection.addAll(Arrays.asList(elements)); boolean r = collection.addAll(Arrays.asList(elements));
confirmed.addAll(Arrays.asList(elements)); confirmed.addAll(Arrays.asList(elements));
verifyAll(); verifyAll();
assertTrue(r, "Empty collection should change after addAll"); assertTrue("Empty collection should change after addAll", r);
for (int i = 0; i < elements.length; i++) { for (int i = 0; i < elements.length; i++) {
assertTrue(collection.contains(elements[i]), "Collection should contain added element"); assertTrue("Collection should contain added element",
collection.contains(elements[i]));
} }
resetFull(); resetFull();
@@ -557,11 +564,13 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
r = collection.addAll(Arrays.asList(elements)); r = collection.addAll(Arrays.asList(elements));
confirmed.addAll(Arrays.asList(elements)); confirmed.addAll(Arrays.asList(elements));
verifyAll(); verifyAll();
assertTrue(r, "Full collection should change after addAll"); assertTrue("Full collection should change after addAll", r);
for (int i = 0; i < elements.length; i++) { for (int i = 0; i < elements.length; i++) {
assertTrue(collection.contains(elements[i]), "Full collection should contain added element"); assertTrue("Full collection should contain added element",
collection.contains(elements[i]));
} }
assertEquals(size + elements.length, collection.size(), "Size should increase after addAll"); assertEquals("Size should increase after addAll",
size + elements.length, collection.size());
resetFull(); resetFull();
size = collection.size(); size = collection.size();
@@ -569,9 +578,11 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
confirmed.addAll(Arrays.asList(getFullElements())); confirmed.addAll(Arrays.asList(getFullElements()));
verifyAll(); verifyAll();
if (r) { if (r) {
assertTrue(size < collection.size(), "Size should increase if addAll returns true"); assertTrue("Size should increase if addAll returns true",
size < collection.size());
} else { } else {
assertEquals(size, collection.size(), "Size should not change if addAll returns false"); assertEquals("Size should not change if addAll returns false",
size, collection.size());
} }
} }
@@ -655,14 +666,16 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
resetEmpty(); resetEmpty();
elements = getFullElements(); elements = getFullElements();
for(int i = 0; i < elements.length; i++) { for(int i = 0; i < elements.length; i++) {
assertTrue(!collection.contains(elements[i]), "Empty collection shouldn't contain element[" + i + "]"); assertTrue("Empty collection shouldn't contain element[" + i + "]",
!collection.contains(elements[i]));
} }
// make sure calls to "contains" don't change anything // make sure calls to "contains" don't change anything
verifyAll(); verifyAll();
elements = getOtherElements(); elements = getOtherElements();
for(int i = 0; i < elements.length; i++) { for(int i = 0; i < elements.length; i++) {
assertTrue(!collection.contains(elements[i]), "Empty collection shouldn't contain element[" + i + "]"); assertTrue("Empty collection shouldn't contain element[" + i + "]",
!collection.contains(elements[i]));
} }
// make sure calls to "contains" don't change anything // make sure calls to "contains" don't change anything
verifyAll(); verifyAll();
@@ -670,7 +683,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
resetFull(); resetFull();
elements = getFullElements(); elements = getFullElements();
for(int i = 0; i < elements.length; i++) { for(int i = 0; i < elements.length; i++) {
assertTrue(collection.contains(elements[i]), "Full collection should contain element[" + i + "]"); assertTrue("Full collection should contain element[" + i + "]",
collection.contains(elements[i]));
} }
// make sure calls to "contains" don't change anything // make sure calls to "contains" don't change anything
verifyAll(); verifyAll();
@@ -678,7 +692,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
resetFull(); resetFull();
elements = getOtherElements(); elements = getOtherElements();
for(int i = 0; i < elements.length; i++) { for(int i = 0; i < elements.length; i++) {
assertTrue(!collection.contains(elements[i]), "Full collection shouldn't contain element"); assertTrue("Full collection shouldn't contain element",
!collection.contains(elements[i]));
} }
} }
@@ -690,22 +705,22 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
public void testCollectionContainsAll() { public void testCollectionContainsAll() {
resetEmpty(); resetEmpty();
Collection col = new HashSet(); Collection col = new HashSet();
assertTrue(collection.containsAll(col), assertTrue("Every Collection should contain all elements of an " +
"Every Collection should contain all elements of an " + "empty Collection.", collection.containsAll(col));
"empty Collection.");
col.addAll(Arrays.asList(getOtherElements())); col.addAll(Arrays.asList(getOtherElements()));
assertTrue(!collection.containsAll(col), assertTrue("Empty Collection shouldn't contain all elements of " +
"Empty Collection shouldn't contain all elements of " + "a non-empty Collection.", !collection.containsAll(col));
"a non-empty Collection.");
// make sure calls to "containsAll" don't change anything // make sure calls to "containsAll" don't change anything
verifyAll(); verifyAll();
resetFull(); resetFull();
assertTrue(!collection.containsAll(col), "Full collection shouldn't contain other elements"); assertTrue("Full collection shouldn't contain other elements",
!collection.containsAll(col));
col.clear(); col.clear();
col.addAll(Arrays.asList(getFullElements())); col.addAll(Arrays.asList(getFullElements()));
assertTrue(collection.containsAll(col), "Full collection should containAll full elements"); assertTrue("Full collection should containAll full elements",
collection.containsAll(col));
// make sure calls to "containsAll" don't change anything // make sure calls to "containsAll" don't change anything
verifyAll(); verifyAll();
@@ -713,17 +728,18 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
int max = (getFullElements().length == 1 ? 1 : int max = (getFullElements().length == 1 ? 1 :
(getFullElements().length <= 5 ? getFullElements().length - 1 : 5)); (getFullElements().length <= 5 ? getFullElements().length - 1 : 5));
col = Arrays.asList(getFullElements()).subList(min, max); col = Arrays.asList(getFullElements()).subList(min, max);
assertTrue(collection.containsAll(col), "Full collection should containAll partial full " + assertTrue("Full collection should containAll partial full " +
"elements"); "elements", collection.containsAll(col));
assertTrue(collection.containsAll(collection), "Full collection should containAll itself"); assertTrue("Full collection should containAll itself",
collection.containsAll(collection));
// make sure calls to "containsAll" don't change anything // make sure calls to "containsAll" don't change anything
verifyAll(); verifyAll();
col = new ArrayList(); col = new ArrayList();
col.addAll(Arrays.asList(getFullElements())); col.addAll(Arrays.asList(getFullElements()));
col.addAll(Arrays.asList(getFullElements())); col.addAll(Arrays.asList(getFullElements()));
assertTrue(collection.containsAll(col), "Full collection should containAll duplicate full " + assertTrue("Full collection should containAll duplicate full " +
"elements"); "elements", collection.containsAll(col));
// make sure calls to "containsAll" don't change anything // make sure calls to "containsAll" don't change anything
verifyAll(); verifyAll();
@@ -735,12 +751,14 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testCollectionIsEmpty() { public void testCollectionIsEmpty() {
resetEmpty(); resetEmpty();
assertEquals(true, collection.isEmpty(), "New Collection should be empty."); assertEquals("New Collection should be empty.",
true, collection.isEmpty());
// make sure calls to "isEmpty() don't change anything // make sure calls to "isEmpty() don't change anything
verifyAll(); verifyAll();
resetFull(); resetFull();
assertEquals(false, collection.isEmpty(), "Full collection shouldn't be empty"); assertEquals("Full collection shouldn't be empty",
false, collection.isEmpty());
// make sure calls to "isEmpty() don't change anything // make sure calls to "isEmpty() don't change anything
verifyAll(); verifyAll();
} }
@@ -753,7 +771,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
public void testCollectionIterator() { public void testCollectionIterator() {
resetEmpty(); resetEmpty();
Iterator it1 = collection.iterator(); Iterator it1 = collection.iterator();
assertEquals(false, it1.hasNext(), "Iterator for empty Collection shouldn't have next."); assertEquals("Iterator for empty Collection shouldn't have next.",
false, it1.hasNext());
try { try {
it1.next(); it1.next();
fail("Iterator at end of Collection should throw " + fail("Iterator at end of Collection should throw " +
@@ -767,17 +786,18 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
resetFull(); resetFull();
it1 = collection.iterator(); it1 = collection.iterator();
for (int i = 0; i < collection.size(); i++) { for (int i = 0; i < collection.size(); i++) {
assertTrue(it1.hasNext(), "Iterator for full collection should haveNext"); assertTrue("Iterator for full collection should haveNext",
it1.hasNext());
it1.next(); it1.next();
} }
assertTrue(!it1.hasNext(), "Iterator should be finished"); assertTrue("Iterator should be finished", !it1.hasNext());
ArrayList list = new ArrayList(); ArrayList list = new ArrayList();
it1 = collection.iterator(); it1 = collection.iterator();
for (int i = 0; i < collection.size(); i++) { for (int i = 0; i < collection.size(); i++) {
Object next = it1.next(); Object next = it1.next();
assertTrue(collection.contains(next), "Collection should contain element returned by " + assertTrue("Collection should contain element returned by " +
"its iterator"); "its iterator", collection.contains(next));
list.add(next); list.add(next);
} }
try { try {
@@ -845,10 +865,11 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
} }
size--; size--;
assertEquals(size, collection.size(), "Collection should shrink by one after " + assertEquals("Collection should shrink by one after " +
"iterator.remove"); "iterator.remove", size, collection.size());
} }
assertTrue(collection.isEmpty(), "Collection should be empty after iterator purge"); assertTrue("Collection should be empty after iterator purge",
collection.isEmpty());
resetFull(); resetFull();
iter = collection.iterator(); iter = collection.iterator();
@@ -873,7 +894,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
resetEmpty(); resetEmpty();
Object[] elements = getFullElements(); Object[] elements = getFullElements();
for (int i = 0; i < elements.length; i++) { for (int i = 0; i < elements.length; i++) {
assertTrue(!collection.remove(elements[i]), "Shouldn't remove nonexistent element"); assertTrue("Shouldn't remove nonexistent element",
!collection.remove(elements[i]));
verifyAll(); verifyAll();
} }
@@ -881,14 +903,16 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
resetFull(); resetFull();
for (int i = 0; i < other.length; i++) { for (int i = 0; i < other.length; i++) {
assertTrue(!collection.remove(other[i]), "Shouldn't remove nonexistent other element"); assertTrue("Shouldn't remove nonexistent other element",
!collection.remove(other[i]));
verifyAll(); verifyAll();
} }
int size = collection.size(); int size = collection.size();
for (int i = 0; i < elements.length; i++) { for (int i = 0; i < elements.length; i++) {
resetFull(); resetFull();
assertTrue(collection.remove(elements[i]), "Collection should remove extant element: " + elements[i]); assertTrue("Collection should remove extant element: " + elements[i],
collection.remove(elements[i]));
// if the elements aren't distinguishable, we can just remove a // if the elements aren't distinguishable, we can just remove a
// matching element from the confirmed collection and verify // matching element from the confirmed collection and verify
@@ -903,7 +927,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
verifyAll(); verifyAll();
} }
assertEquals(size - 1, collection.size(), "Collection should shrink after remove"); assertEquals("Collection should shrink after remove",
size - 1, collection.size());
} }
} }
@@ -916,28 +941,28 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
if (!isRemoveSupported()) return; if (!isRemoveSupported()) return;
resetEmpty(); resetEmpty();
assertTrue(!collection.removeAll(Collections.EMPTY_SET), assertTrue("Emtpy collection removeAll should return false for " +
"Emtpy collection removeAll should return false for " + "empty input",
"empty input"); !collection.removeAll(Collections.EMPTY_SET));
verifyAll(); verifyAll();
assertTrue(!collection.removeAll(new ArrayList(collection)), assertTrue("Emtpy collection removeAll should return false for " +
"Emtpy collection removeAll should return false for " + "nonempty input",
"nonempty input"); !collection.removeAll(new ArrayList(collection)));
verifyAll(); verifyAll();
resetFull(); resetFull();
assertTrue(!collection.removeAll(Collections.EMPTY_SET), assertTrue("Full collection removeAll should return false for " +
"Full collection removeAll should return false for " + "empty input",
"empty input"); !collection.removeAll(Collections.EMPTY_SET));
verifyAll(); verifyAll();
assertTrue(!collection.removeAll(Arrays.asList(getOtherElements())), assertTrue("Full collection removeAll should return false for other elements",
"Full collection removeAll should return false for other elements"); !collection.removeAll(Arrays.asList(getOtherElements())));
verifyAll(); verifyAll();
assertTrue(collection.removeAll(new HashSet(collection)), assertTrue("Full collection removeAll should return true for full elements",
"Full collection removeAll should return true for full elements"); collection.removeAll(new HashSet(collection)));
confirmed.removeAll(new HashSet(confirmed)); confirmed.removeAll(new HashSet(confirmed));
verifyAll(); verifyAll();
@@ -947,14 +972,17 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
int max = (getFullElements().length == 1 ? 1 : int max = (getFullElements().length == 1 ? 1 :
(getFullElements().length <= 5 ? getFullElements().length - 1 : 5)); (getFullElements().length <= 5 ? getFullElements().length - 1 : 5));
Collection all = Arrays.asList(getFullElements()).subList(min, max); Collection all = Arrays.asList(getFullElements()).subList(min, max);
assertTrue(collection.removeAll(all), "Full collection removeAll should work"); assertTrue("Full collection removeAll should work",
collection.removeAll(all));
confirmed.removeAll(all); confirmed.removeAll(all);
verifyAll(); verifyAll();
assertTrue(collection.size() < size, "Collection should shrink after removeAll"); assertTrue("Collection should shrink after removeAll",
collection.size() < size);
Iterator iter = all.iterator(); Iterator iter = all.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
assertTrue(!collection.contains(iter.next()), "Collection shouldn't contain removed element"); assertTrue("Collection shouldn't contain removed element",
!collection.contains(iter.next()));
} }
} }
@@ -970,51 +998,59 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
List elements = Arrays.asList(getFullElements()); List elements = Arrays.asList(getFullElements());
List other = Arrays.asList(getOtherElements()); List other = Arrays.asList(getOtherElements());
assertTrue(!collection.retainAll(Collections.EMPTY_SET), "Empty retainAll() should return false"); assertTrue("Empty retainAll() should return false",
!collection.retainAll(Collections.EMPTY_SET));
verifyAll(); verifyAll();
assertTrue(!collection.retainAll(elements), "Empty retainAll() should return false"); assertTrue("Empty retainAll() should return false",
!collection.retainAll(elements));
verifyAll(); verifyAll();
resetFull(); resetFull();
assertTrue(collection.retainAll(Collections.EMPTY_SET), "Collection should change from retainAll empty"); assertTrue("Collection should change from retainAll empty",
collection.retainAll(Collections.EMPTY_SET));
confirmed.retainAll(Collections.EMPTY_SET); confirmed.retainAll(Collections.EMPTY_SET);
verifyAll(); verifyAll();
resetFull(); resetFull();
assertTrue(collection.retainAll(other), "Collection changed from retainAll other"); assertTrue("Collection changed from retainAll other",
collection.retainAll(other));
confirmed.retainAll(other); confirmed.retainAll(other);
verifyAll(); verifyAll();
resetFull(); resetFull();
int size = collection.size(); int size = collection.size();
assertTrue(!collection.retainAll(elements), "Collection shouldn't change from retainAll elements"); assertTrue("Collection shouldn't change from retainAll elements",
!collection.retainAll(elements));
verifyAll(); verifyAll();
assertEquals(size, collection.size(), "Collection size shouldn't change"); assertEquals("Collection size shouldn't change", size,
collection.size());
if (getFullElements().length > 1) { if (getFullElements().length > 1) {
resetFull(); resetFull();
size = collection.size(); size = collection.size();
int min = (getFullElements().length < 2 ? 0 : 2); int min = (getFullElements().length < 2 ? 0 : 2);
int max = (getFullElements().length <= 5 ? getFullElements().length - 1 : 5); int max = (getFullElements().length <= 5 ? getFullElements().length - 1 : 5);
assertTrue(collection.retainAll(elements.subList(min, max)), "Collection should changed by partial retainAll"); assertTrue("Collection should changed by partial retainAll",
collection.retainAll(elements.subList(min, max)));
confirmed.retainAll(elements.subList(min, max)); confirmed.retainAll(elements.subList(min, max));
verifyAll(); verifyAll();
Iterator iter = collection.iterator(); Iterator iter = collection.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
assertTrue(elements.subList(min, max).contains(iter.next()), "Collection only contains retained element"); assertTrue("Collection only contains retained element",
elements.subList(min, max).contains(iter.next()));
} }
} }
resetFull(); resetFull();
HashSet set = new HashSet(elements); HashSet set = new HashSet(elements);
size = collection.size(); size = collection.size();
assertTrue(!collection.retainAll(set), assertTrue("Collection shouldn't change from retainAll without " +
"Collection shouldn't change from retainAll without duplicate elements"); "duplicate elements", !collection.retainAll(set));
verifyAll(); verifyAll();
assertEquals( size, collection.size(), assertEquals("Collection size didn't change from nonduplicate " +
"Collection size didn't change from nonduplicate retainAll"); "retainAll", size, collection.size());
} }
@@ -1024,10 +1060,11 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testCollectionSize() { public void testCollectionSize() {
resetEmpty(); resetEmpty();
assertEquals(0, collection.size(), "Size of new Collection is 0."); assertEquals("Size of new Collection is 0.", 0, collection.size());
resetFull(); resetFull();
assertTrue(collection.size() > 0, "Size of full collection should be greater than zero"); assertTrue("Size of full collection should be greater than zero",
collection.size() > 0);
} }
@@ -1036,18 +1073,22 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
*/ */
public void testCollectionToArray() { public void testCollectionToArray() {
resetEmpty(); resetEmpty();
assertEquals(0, collection.toArray().length, "Empty Collection should return empty array for toArray"); assertEquals("Empty Collection should return empty array for toArray",
0, collection.toArray().length);
resetFull(); resetFull();
Object[] array = collection.toArray(); Object[] array = collection.toArray();
assertEquals(array.length, collection.size(), "Full collection toArray should be same size as collection"); assertEquals("Full collection toArray should be same size as " +
"collection", array.length, collection.size());
Object[] confirmedArray = confirmed.toArray(); Object[] confirmedArray = confirmed.toArray();
assertEquals(confirmedArray.length, array.length, assertEquals("length of array from confirmed collection should " +
"length of array from confirmed collection should match the length of the collection's array"); "match the length of the collection's array",
confirmedArray.length, array.length);
boolean[] matched = new boolean[array.length]; boolean[] matched = new boolean[array.length];
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
assertTrue(collection.contains(array[i]), "Collection should contain element in toArray"); assertTrue("Collection should contain element in toArray",
collection.contains(array[i]));
boolean match = false; boolean match = false;
// find a match in the confirmed array // find a match in the confirmed array
@@ -1067,7 +1108,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
} }
} }
for(int i = 0; i < matched.length; i++) { for(int i = 0; i < matched.length; i++) {
assertEquals(true, matched[i], "Collection should return all its elements in toArray"); assertEquals("Collection should return all its elements in " +
"toArray", true, matched[i]);
} }
} }
@@ -1081,8 +1123,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
resetEmpty(); resetEmpty();
Object[] a = new Object[] { new Object(), null, null }; Object[] a = new Object[] { new Object(), null, null };
Object[] array = collection.toArray(a); Object[] array = collection.toArray(a);
assertArrayEquals(array, a, "Given array shouldn't shrink"); assertArrayEquals("Given array shouldn't shrink", array, a);
assertNull(a[0], "Last element should be set to null"); assertNull("Last element should be set to null", a[0]);
verifyAll(); verifyAll();
resetFull(); resetFull();
@@ -1104,7 +1146,8 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
array = collection.toArray(new Object[0]); array = collection.toArray(new Object[0]);
a = collection.toArray(); a = collection.toArray();
assertEquals(Arrays.asList(array), Arrays.asList(a), "toArrays should be equal"); assertEquals("toArrays should be equal",
Arrays.asList(array), Arrays.asList(a));
// Figure out if they're all the same class // Figure out if they're all the same class
// TODO: It'd be nicer to detect a common superclass // TODO: It'd be nicer to detect a common superclass
@@ -1120,10 +1163,11 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
} }
a = (Object[])Array.newInstance(cl, 0); a = (Object[])Array.newInstance(cl, 0);
array = collection.toArray(a); array = collection.toArray(a);
assertEquals(a.getClass(), array.getClass(), "toArray(Object[]) should return correct array type"); assertEquals("toArray(Object[]) should return correct array type",
assertEquals(Arrays.asList(array), a.getClass(), array.getClass());
Arrays.asList(collection.toArray()), assertEquals("type-specific toArrays should be equal",
"type-specific toArrays should be equal"); Arrays.asList(array),
Arrays.asList(collection.toArray()));
verifyAll(); verifyAll();
} }
@@ -1134,10 +1178,12 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testCollectionToString() { public void testCollectionToString() {
resetEmpty(); resetEmpty();
assertTrue(collection.toString() != null, "toString shouldn't return null"); assertTrue("toString shouldn't return null",
collection.toString() != null);
resetFull(); resetFull();
assertTrue(collection.toString() != null, "toString shouldn't return null"); assertTrue("toString shouldn't return null",
collection.toString() != null);
} }
@@ -30,11 +30,13 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Ignore;
import org.junit.Test;
import java.util.*; import java.util.*;
import org.junit.jupiter.api.Disabled; import static org.junit.Assert.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* CollectionUtilTest * CollectionUtilTest
* *
@@ -59,60 +61,44 @@ public class CollectionUtilTest {
assertArrayEquals(new Object[] {"bar", "baz", 3}, merged); assertArrayEquals(new Object[] {"bar", "baz", 3}, merged);
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectBadOffset() { public void testMergeArraysObjectBadOffset() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, 4, 2, integerObjects, 2, 1);
CollectionUtil.mergeArrays(stringObjects, 4, 2, integerObjects, 2, 1);
});
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectBadSecondOffset() { public void testMergeArraysObjectBadSecondOffset() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 4, 1);
CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 4, 1);
});
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectBadLength() { public void testMergeArraysObjectBadLength() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, 1, 4, integerObjects, 2, 1);
CollectionUtil.mergeArrays(stringObjects, 1, 4, integerObjects, 2, 1);
});
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectBadSecondLength() { public void testMergeArraysObjectBadSecondLength() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 2, 2);
CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 2, 2);
});
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectNegativeOffset() { public void testMergeArraysObjectNegativeOffset() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, -1, 2, integerObjects, 2, 1);
CollectionUtil.mergeArrays(stringObjects, -1, 2, integerObjects, 2, 1);
});
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectNegativeSecondOffset() { public void testMergeArraysObjectNegativeSecondOffset() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, -1, 1);
CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, -1, 1);
});
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectNegativeLength() { public void testMergeArraysObjectNegativeLength() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, 1, -1, integerObjects, 2, 1);
CollectionUtil.mergeArrays(stringObjects, 1, -1, integerObjects, 2, 1);
});
} }
@Test @Test(expected = IndexOutOfBoundsException.class)
public void testMergeArraysObjectNegativeSecondLength() { public void testMergeArraysObjectNegativeSecondLength() {
assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 2, -1);
CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 2, -1);
});
} }
@Test @Test
@@ -123,24 +109,20 @@ public class CollectionUtilTest {
assertArrayEquals(new Object[] {"foo", "bar", "baz", 1, 2, 3}, merged); assertArrayEquals(new Object[] {"foo", "bar", "baz", 1, 2, 3}, merged);
} }
@Test @Test(expected = ArrayStoreException.class)
public void testMergeArraysObjectIllegalType() { public void testMergeArraysObjectIllegalType() {
String[] strings = {"foo", "bar", "baz"}; String[] strings = {"foo", "bar", "baz"};
Integer[] integers = {1, 2, 3}; // Integer not assignable to String Integer[] integers = {1, 2, 3}; // Integer not assignable to String
assertThrows(ArrayStoreException.class, () -> { CollectionUtil.mergeArrays(strings, integers);
CollectionUtil.mergeArrays(strings, integers);
});
} }
@Test @Test(expected = ArrayStoreException.class)
public void testMergeArraysNativeIllegalType() { public void testMergeArraysNativeIllegalType() {
char[] chars = {'a', 'b', 'c'}; char[] chars = {'a', 'b', 'c'};
int[] integers = {1, 2, 3}; // Integer not assignable to String int[] integers = {1, 2, 3}; // Integer not assignable to String
assertThrows(ArrayStoreException.class, () -> { CollectionUtil.mergeArrays(chars, integers);
CollectionUtil.mergeArrays(chars, integers);
});
} }
@@ -165,11 +147,9 @@ public class CollectionUtilTest {
assertArrayEquals(new int[] {2, 3, 4}, numbers); assertArrayEquals(new int[] {2, 3, 4}, numbers);
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testEnumIteratorNull() { public void testEnumIteratorNull() {
assertThrows(IllegalArgumentException.class, () -> { CollectionUtil.iterator((Enumeration<Object>) null);
CollectionUtil.iterator((Enumeration<Object>) null);
});
} }
@Test @Test
@@ -203,11 +183,9 @@ public class CollectionUtilTest {
} }
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testArrayIteratorNull() { public void testArrayIteratorNull() {
assertThrows(IllegalArgumentException.class, () -> { CollectionUtil.iterator((Object[]) null);
CollectionUtil.iterator((Object[]) null);
});
} }
@Test @Test
@@ -284,7 +262,7 @@ public class CollectionUtilTest {
int count = 0; int count = 0;
for (Object element : elements) { for (Object element : elements) {
assertTrue(iterator.hasNext(), "No next element for element '" + element + "' at index: " + count); assertTrue("No next element for element '" + element + "' at index: " + count, iterator.hasNext());
assertEquals(count > 0, iterator.hasPrevious()); assertEquals(count > 0, iterator.hasPrevious());
assertEquals(count, iterator.nextIndex()); assertEquals(count, iterator.nextIndex());
assertEquals(count - 1, iterator.previousIndex()); assertEquals(count - 1, iterator.previousIndex());
@@ -340,7 +318,7 @@ public class CollectionUtilTest {
assertEquals(elements.length, iterator.nextIndex()); assertEquals(elements.length, iterator.nextIndex());
for (int i = count; i > 0; i--) { for (int i = count; i > 0; i--) {
assertTrue(iterator.hasPrevious(), "No previous element for element '" + elements[i - 1] + "' at index: " + (i - 1)); assertTrue("No previous element for element '" + elements[i - 1] + "' at index: " + (i - 1), iterator.hasPrevious());
assertEquals(i < elements.length, iterator.hasNext()); assertEquals(i < elements.length, iterator.hasNext());
assertEquals(i - 1, iterator.previousIndex()); assertEquals(i - 1, iterator.previousIndex());
assertEquals(i, iterator.nextIndex()); assertEquals(i, iterator.nextIndex());
@@ -361,24 +339,18 @@ public class CollectionUtilTest {
} }
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testArrayIteratorRangeNull() { public void testArrayIteratorRangeNull() {
assertThrows(IllegalArgumentException.class, () -> { CollectionUtil.iterator(null, 0, 0);
CollectionUtil.iterator(null, 0, 0);
});
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testArrayIteratorRangeBadStart() { public void testArrayIteratorRangeBadStart() {
assertThrows(IllegalArgumentException.class, () -> { CollectionUtil.iterator(stringObjects, stringObjects.length + 1, 2);
CollectionUtil.iterator(stringObjects, stringObjects.length + 1, 2);
});
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testArrayIteratorRangeBadLength() { public void testArrayIteratorRangeBadLength() {
assertThrows(IllegalArgumentException.class, () -> { CollectionUtil.iterator(stringObjects, 1, stringObjects.length);
CollectionUtil.iterator(stringObjects, 1, stringObjects.length);
});
} }
@Test @Test
@@ -407,11 +379,9 @@ public class CollectionUtilTest {
} }
} }
@Test @Test(expected = IllegalArgumentException.class)
public void testReverseOrderNull() { public void testReverseOrderNull() {
assertThrows(IllegalArgumentException.class, () -> { CollectionUtil.reverseOrder(null);
CollectionUtil.reverseOrder(null);
});
} }
@Test @Test
@@ -461,7 +431,7 @@ public class CollectionUtilTest {
} }
} }
@Disabled("For development only") @Ignore("For development only")
@Test @Test
@SuppressWarnings({"UnusedDeclaration"}) @SuppressWarnings({"UnusedDeclaration"})
public void testGenerify() { public void testGenerify() {
@@ -45,10 +45,11 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.util.*; import java.util.*;
import org.junit.jupiter.api.*; import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* Tests LRUMap. * Tests LRUMap.
@@ -80,8 +81,8 @@ public class LRUMapTest extends LinkedMapTest {
map2.put(4,"foo"); // removes 1 since max size exceeded map2.put(4,"foo"); // removes 1 since max size exceeded
map2.removeLRU(); // should be Integer(2) map2.removeLRU(); // should be Integer(2)
assertTrue(map2.get(new Integer(3)).equals("foo"), "Second to last value should exist"); assertTrue("Second to last value should exist",map2.get(new Integer(3)).equals("foo"));
assertTrue(map2.get(new Integer(1)) == null, "First value inserted should not exist"); assertTrue("First value inserted should not exist", map2.get(new Integer(1)) == null);
} }
@Test @Test
@@ -92,8 +93,8 @@ public class LRUMapTest extends LinkedMapTest {
map2.put(3,"foo"); map2.put(3,"foo");
map2.put(4,"bar"); map2.put(4,"bar");
assertTrue(map2.get(new Integer(4)).equals("bar"), "last value should exist"); assertTrue("last value should exist",map2.get(new Integer(4)).equals("bar"));
assertTrue(map2.get(new Integer(1)) == null, "LRU should not exist"); assertTrue("LRU should not exist", map2.get(new Integer(1)) == null);
} }
/** /**
@@ -112,8 +113,10 @@ public class LRUMapTest extends LinkedMapTest {
map2.putAll(hashMap); map2.putAll(hashMap);
assertTrue(map2.size() == 3, "max size is 3, but actual size is " + map2.size()); assertTrue("max size is 3, but actual size is " + map2.size(),
assertTrue(map2.containsKey(new Integer(4)), "map should contain the Integer(4) object"); map2.size() == 3);
assertTrue("map should contain the Integer(4) object",
map2.containsKey(new Integer(4)));
} }
/** /**
@@ -131,7 +134,8 @@ public class LRUMapTest extends LinkedMapTest {
map.put("6","6"); map.put("6","6");
map.setMaxSize(3); map.setMaxSize(3);
assertTrue(map.size() == 3, "map should have size = 3, but actually = " + map.size()); assertTrue("map should have size = 3, but actually = " + map.size(),
map.size() == 3);
} }
@Test @Test
@@ -156,9 +160,9 @@ public class LRUMapTest extends LinkedMapTest {
keys[i] = keyIterator.next(); keys[i] = keyIterator.next();
} }
assertTrue(keys[0].equals("3"), "first evicted should be 3, was " + keys[0]); assertTrue("first evicted should be 3, was " + keys[0], keys[0].equals("3"));
assertTrue(keys[1].equals("1"), "second evicted should be 1, was " + keys[1]); assertTrue("second evicted should be 1, was " + keys[1], keys[1].equals("1"));
assertTrue(keys[2].equals("4"), "third evicted should be 4, was " + keys[2]); assertTrue("third evicted should be 4, was " + keys[2], keys[2].equals("4"));
} }
@@ -188,12 +192,13 @@ public class LRUMapTest extends LinkedMapTest {
// 4 2 // 4 2
counter.remove("5"); counter.remove("5");
assertTrue(counter.size() == 2, "size should be 2, but was " + counter.size()); assertTrue("size should be 2, but was " + counter.size(), counter.size() == 2);
assertTrue(counter.removedCount == 3, "removedCount should be 3 but was " + counter.removedCount); assertTrue("removedCount should be 3 but was " + counter.removedCount,
counter.removedCount == 3);
assertTrue(counter.list.get(0).equals("2"), "first removed was '2'"); assertTrue("first removed was '2'",counter.list.get(0).equals("2"));
assertTrue(counter.list.get(1).equals("3"), "second removed was '3'"); assertTrue("second removed was '3'",counter.list.get(1).equals("3"));
assertTrue(counter.list.get(2).equals("1"), "third removed was '1'"); assertTrue("third removed was '1'",counter.list.get(2).equals("1"));
//assertTrue("oldest key is '4'",counter.get(0).equals("4")); //assertTrue("oldest key is '4'",counter.get(0).equals("4"));
//assertTrue("newest key is '2'",counter.get(1).equals("2")); //assertTrue("newest key is '2'",counter.get(1).equals("2"));
@@ -45,11 +45,15 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.*; import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.assertTrue;
/** /**
* Unit tests * Unit tests
@@ -70,7 +74,7 @@ public class LinkedMapTest extends MapAbstractTest {
*/ */
protected LinkedMap labRat; protected LinkedMap labRat;
@BeforeEach @Before
public void setUp() throws Exception { public void setUp() throws Exception {
// use makeMap and cast the result to a SeqHashMap // use makeMap and cast the result to a SeqHashMap
// so that subclasses of SeqHashMap can share these tests // so that subclasses of SeqHashMap can share these tests
@@ -99,21 +103,27 @@ public class LinkedMapTest extends MapAbstractTest {
} }
// Test size(). // Test size().
assertEquals(expectedSize, labRat.size(), "size() does not match expected size"); assertEquals("size() does not match expected size",
expectedSize, labRat.size());
// Test clone(), iterator(), and get(Object). // Test clone(), iterator(), and get(Object).
LinkedMap clone = (LinkedMap) labRat.clone(); LinkedMap clone = (LinkedMap) labRat.clone();
assertEquals(labRat.size(), clone.size(), "Size of clone does not match original"); assertEquals("Size of clone does not match original",
labRat.size(), clone.size());
Iterator origEntries = labRat.entrySet().iterator(); Iterator origEntries = labRat.entrySet().iterator();
Iterator copiedEntries = clone.entrySet().iterator(); Iterator copiedEntries = clone.entrySet().iterator();
while (origEntries.hasNext()) { while (origEntries.hasNext()) {
Map.Entry origEntry = (Map.Entry)origEntries.next(); Map.Entry origEntry = (Map.Entry)origEntries.next();
Map.Entry copiedEntry = (Map.Entry)copiedEntries.next(); Map.Entry copiedEntry = (Map.Entry)copiedEntries.next();
assertEquals(origEntry.getKey(), copiedEntry.getKey(), "Cloned key does not match original"); assertEquals("Cloned key does not match original",
assertEquals(origEntry.getValue(), copiedEntry.getValue(), "Cloned value does not match original"); origEntry.getKey(), copiedEntry.getKey());
assertEquals(origEntry, copiedEntry, "Cloned entry does not match original"); assertEquals("Cloned value does not match original",
origEntry.getValue(), copiedEntry.getValue());
assertEquals("Cloned entry does not match original",
origEntry, copiedEntry);
} }
assertTrue(!copiedEntries.hasNext(), "iterator() returned different number of elements than keys()"); assertTrue("iterator() returned different number of elements than keys()",
!copiedEntries.hasNext());
// Test sequence() // Test sequence()
/* /*
@@ -197,7 +207,7 @@ public class LinkedMapTest extends MapAbstractTest {
} }
*/ */
@AfterEach @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
labRat = null; labRat = null;
} }
@@ -45,10 +45,12 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.After;
import org.junit.Test;
import java.util.*; import java.util.*;
import org.junit.jupiter.api.*; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* Abstract test class for {@link java.util.Map} methods and contracts. * Abstract test class for {@link java.util.Map} methods and contracts.
@@ -388,19 +390,19 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
m.put(keys[i], values[i]); m.put(keys[i], values[i]);
} }
catch (NullPointerException exception) { catch (NullPointerException exception) {
assertTrue(keys[i] == null || values[i] == null, assertTrue("NullPointerException only allowed to be thrown if either the key or value is null.",
"NullPointerException only allowed to be thrown if either the key or value is null."); keys[i] == null || values[i] == null);
assertTrue(keys[i] == null || !isAllowNullKey(), assertTrue("NullPointerException on null key, but isAllowNullKey is not overridden to return false.",
"NullPointerException on null key, but isAllowNullKey is not overridden to return false."); keys[i] == null || !isAllowNullKey());
assertTrue(values[i] == null || !isAllowNullValue(), assertTrue("NullPointerException on null value, but isAllowNullValue is not overridden to return false.",
"NullPointerException on null value, but isAllowNullValue is not overridden to return false."); values[i] == null || !isAllowNullValue());
fail("Unknown reason for NullPointer."); assertTrue("Unknown reason for NullPointer.", false);
} }
} }
assertEquals(keys.length, m.size(), "size must reflect number of mappings added."); assertEquals("size must reflect number of mappings added.", keys.length, m.size());
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@@ -479,26 +481,27 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
Object[] values = getSampleValues(); Object[] values = getSampleValues();
Object[] newValues = getNewSampleValues(); Object[] newValues = getNewSampleValues();
assertTrue(keys != null, "failure in test: Must have keys returned from getSampleKeys."); assertTrue("failure in test: Must have keys returned from getSampleKeys.", keys != null);
assertTrue(values != null, "failure in test: Must have values returned from getSampleValues."); assertTrue("failure in test: Must have values returned from getSampleValues.", values != null);
// verify keys and values have equivalent lengths (in case getSampleX are // verify keys and values have equivalent lengths (in case getSampleX are
// overridden) // overridden)
assertEquals(keys.length, values.length, "failure in test: not the same number of sample keys and values."); assertEquals("failure in test: not the same number of sample keys and values.", keys.length, values.length);
assertEquals(values.length, newValues.length, "failure in test: not the same number of values and new values."); assertEquals("failure in test: not the same number of values and new values.", values.length, newValues.length);
// verify there aren't duplicate keys, and check values // verify there aren't duplicate keys, and check values
for (int i = 0; i < keys.length - 1; i++) { for (int i = 0; i < keys.length - 1; i++) {
for (int j = i + 1; j < keys.length; j++) { for (int j = i + 1; j < keys.length; j++) {
assertTrue((keys[i] != null || keys[j] != null), "failure in test: duplicate null keys."); assertTrue("failure in test: duplicate null keys.", (keys[i] != null || keys[j] != null));
assertTrue((keys[i] == null || keys[j] == null || (!keys[i].equals(keys[j]) && !keys[j].equals(keys[i]))), assertTrue("failure in test: duplicate non-null key.",
"failure in test: duplicate non-null key."); (keys[i] == null || keys[j] == null || (!keys[i].equals(keys[j]) && !keys[j].equals(keys[i]))));
} }
assertTrue(keys[i] != null || isAllowNullKey(),"failure in test: found null key, but isNullKeySupported is false."); assertTrue("failure in test: found null key, but isNullKeySupported is false.", keys[i] != null || isAllowNullKey());
assertTrue(values[i] != null || isAllowNullValue(),"failure in test: found null value, but isNullValueSupported is false."); assertTrue("failure in test: found null value, but isNullValueSupported is false.", values[i] != null || isAllowNullValue());
assertTrue(newValues[i] != null || isAllowNullValue(), "failure in test: found null new value, but isNullValueSupported is false."); assertTrue("failure in test: found null new value, but isNullValueSupported is false.", newValues[i] != null || isAllowNullValue());
assertTrue(values[i] != newValues[i] && (values[i] == null || !values[i].equals(newValues[i])), "failure in test: values should not be the same as new value"); assertTrue("failure in test: values should not be the same as new value",
values[i] != newValues[i] && (values[i] == null || !values[i].equals(newValues[i])));
} }
} }
@@ -514,18 +517,18 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testMakeMap() { public void testMakeMap() {
Map em = makeEmptyMap(); Map em = makeEmptyMap();
assertTrue(em != null, "failure in test: makeEmptyMap must return a non-null map."); assertTrue("failure in test: makeEmptyMap must return a non-null map.", em != null);
Map em2 = makeEmptyMap(); Map em2 = makeEmptyMap();
assertTrue(em2 != null, "failure in test: makeEmptyMap must return a non-null map."); assertTrue("failure in test: makeEmptyMap must return a non-null map.", em2 != null);
assertTrue(em != em2, "failure in test: makeEmptyMap must return a new map with each invocation."); assertTrue("failure in test: makeEmptyMap must return a new map with each invocation.", em != em2);
Map fm = makeFullMap(); Map fm = makeFullMap();
assertTrue(fm != null, "failure in test: makeFullMap must return a non-null map."); assertTrue("failure in test: makeFullMap must return a non-null map.", fm != null);
Map fm2 = makeFullMap(); Map fm2 = makeFullMap();
assertTrue(fm2 != null, "failure in test: makeFullMap must return a non-null map."); assertTrue("failure in test: makeFullMap must return a non-null map.", fm2 != null);
assertTrue(fm != fm2, "failure in test: makeFullMap must return a new map with each invocation."); assertTrue("failure in test: makeFullMap must return a new map with each invocation.", fm != fm2);
} }
/** /**
@@ -534,11 +537,11 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testMapIsEmpty() { public void testMapIsEmpty() {
resetEmpty(); resetEmpty();
assertEquals(true, map.isEmpty(), "Map.isEmpty() should return true with an empty map"); assertEquals("Map.isEmpty() should return true with an empty map", true, map.isEmpty());
verifyAll(); verifyAll();
resetFull(); resetFull();
assertEquals(false, map.isEmpty(), "Map.isEmpty() should return false with a non-empty map"); assertEquals("Map.isEmpty() should return false with a non-empty map", false, map.isEmpty());
verifyAll(); verifyAll();
} }
@@ -548,11 +551,11 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testMapSize() { public void testMapSize() {
resetEmpty(); resetEmpty();
assertEquals(0, map.size(), "Map.size() should be 0 with an empty map"); assertEquals("Map.size() should be 0 with an empty map", 0, map.size());
verifyAll(); verifyAll();
resetFull(); resetFull();
assertEquals(getSampleKeys().length, map.size(), "Map.size() should equal the number of entries in the map"); assertEquals("Map.size() should equal the number of entries in the map", getSampleKeys().length, map.size());
verifyAll(); verifyAll();
} }
@@ -599,13 +602,13 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
resetEmpty(); resetEmpty();
for (Object key : keys) { for (Object key : keys) {
assertTrue(!map.containsKey(key), "Map must not contain key when map is empty"); assertTrue("Map must not contain key when map is empty", !map.containsKey(key));
} }
verifyAll(); verifyAll();
resetFull(); resetFull();
for (Object key : keys) { for (Object key : keys) {
assertTrue(map.containsKey(key), "Map must contain key for a mapping in the map. Missing: " + key); assertTrue("Map must contain key for a mapping in the map. Missing: " + key, map.containsKey(key));
} }
verifyAll(); verifyAll();
} }
@@ -621,13 +624,13 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
resetEmpty(); resetEmpty();
for (Object value : values) { for (Object value : values) {
assertTrue(!map.containsValue(value), "Empty map must not contain value"); assertTrue("Empty map must not contain value", !map.containsValue(value));
} }
verifyAll(); verifyAll();
resetFull(); resetFull();
for (Object value : values) { for (Object value : values) {
assertTrue(map.containsValue(value), "Map must contain value for a mapping in the map."); assertTrue("Map must contain value for a mapping in the map.", map.containsValue(value));
} }
verifyAll(); verifyAll();
} }
@@ -638,11 +641,11 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testMapEquals() { public void testMapEquals() {
resetEmpty(); resetEmpty();
assertTrue(map.equals(confirmed), "Empty maps unequal."); assertTrue("Empty maps unequal.", map.equals(confirmed));
verifyAll(); verifyAll();
resetFull(); resetFull();
assertTrue(map.equals(confirmed), "Full maps unequal."); assertTrue("Full maps unequal.", map.equals(confirmed));
verifyAll(); verifyAll();
resetFull(); resetFull();
@@ -651,11 +654,11 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
Iterator iter = confirmed.keySet().iterator(); Iterator iter = confirmed.keySet().iterator();
iter.next(); iter.next();
iter.remove(); iter.remove();
assertTrue(!map.equals(confirmed), "Different maps equal."); assertTrue("Different maps equal.", !map.equals(confirmed));
resetFull(); resetFull();
assertTrue(!map.equals(null), "equals(null) returned true."); assertTrue("equals(null) returned true.", !map.equals(null));
assertTrue(!map.equals(new Object()), "equals(new Object()) returned true."); assertTrue("equals(new Object()) returned true.", !map.equals(new Object()));
verifyAll(); verifyAll();
} }
@@ -670,14 +673,14 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
Object[] values = getSampleValues(); Object[] values = getSampleValues();
for (Object key : keys) { for (Object key : keys) {
assertTrue(map.get(key) == null, "Empty map.get() should return null."); assertTrue("Empty map.get() should return null.", map.get(key) == null);
} }
verifyAll(); verifyAll();
resetFull(); resetFull();
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
assertEquals(values[i], map.get(keys[i]), "Full map.get() should return value from mapping."); assertEquals("Full map.get() should return value from mapping.", values[i], map.get(keys[i]));
} }
} }
@@ -687,10 +690,10 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testMapHashCode() { public void testMapHashCode() {
resetEmpty(); resetEmpty();
assertTrue(map.hashCode() == confirmed.hashCode(), "Empty maps have different hashCodes."); assertTrue("Empty maps have different hashCodes.", map.hashCode() == confirmed.hashCode());
resetFull(); resetFull();
assertTrue(map.hashCode() == confirmed.hashCode(), "Equal maps have different hashCodes."); assertTrue("Equal maps have different hashCodes.", map.hashCode() == confirmed.hashCode());
} }
/** /**
@@ -705,11 +708,11 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
@Test @Test
public void testMapToString() { public void testMapToString() {
resetEmpty(); resetEmpty();
assertTrue(map.toString() != null, "Empty map toString() should not return null"); assertTrue("Empty map toString() should not return null", map.toString() != null);
verifyAll(); verifyAll();
resetFull(); resetFull();
assertTrue(map.toString() != null, "Empty map toString() should not return null"); assertTrue("Empty map toString() should not return null", map.toString() != null);
verifyAll(); verifyAll();
} }
@@ -773,23 +776,29 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
Object o = map.put(keys[i], values[i]); Object o = map.put(keys[i], values[i]);
confirmed.put(keys[i], values[i]); confirmed.put(keys[i], values[i]);
verifyAll(); verifyAll();
assertTrue(o == null, "First map.put should return null"); assertTrue("First map.put should return null", o == null);
assertTrue(map.containsKey(keys[i]), "Map should contain key after put"); assertTrue("Map should contain key after put",
assertTrue(map.containsValue(values[i]), "Map should contain value after put"); map.containsKey(keys[i]));
assertTrue("Map should contain value after put",
map.containsValue(values[i]));
} }
if (isPutChangeSupported()) { if (isPutChangeSupported()) {
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
Object o = map.put(keys[i], newValues[i]); Object o = map.put(keys[i], newValues[i]);
confirmed.put(keys[i], newValues[i]); confirmed.put(keys[i], newValues[i]);
verifyAll(); verifyAll();
assertEquals(values[i], o, "Map.put should return previous value when changed"); assertEquals("Map.put should return previous value when changed",
assertTrue(map.containsKey(keys[i]), "Map should still contain key after put when changed"); values[i], o);
assertTrue(map.containsValue(newValues[i]), "Map should contain new value after put when changed"); assertTrue("Map should still contain key after put when changed",
map.containsKey(keys[i]));
assertTrue("Map should contain new value after put when changed",
map.containsValue(newValues[i]));
// if duplicates are allowed, we're not guaranteed that the value // if duplicates are allowed, we're not guaranteed that the value
// no longer exists, so don't try checking that. // no longer exists, so don't try checking that.
if (!isAllowDuplicateValues()) { if (!isAllowDuplicateValues()) {
assertTrue(!map.containsValue(values[i]), "Map should not contain old value after put when changed"); assertTrue("Map should not contain old value after put when changed",
!map.containsValue(values[i]));
} }
} }
} }
@@ -823,14 +832,18 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
Object o = map.put(key, newValues[i]); Object o = map.put(key, newValues[i]);
Object value = confirmed.put(key, newValues[i]); Object value = confirmed.put(key, newValues[i]);
verifyAll(); verifyAll();
assertEquals(value, o, "Map.put should return previous value when changed"); assertEquals("Map.put should return previous value when changed",
assertTrue(map.containsKey(key), "Map should still contain key after put when changed"); value, o);
assertTrue(map.containsValue(newValues[i]), "Map should contain new value after put when changed"); assertTrue("Map should still contain key after put when changed",
map.containsKey(key));
assertTrue("Map should contain new value after put when changed",
map.containsValue(newValues[i]));
// if duplicates are allowed, we're not guaranteed that the value // if duplicates are allowed, we're not guaranteed that the value
// no longer exists, so don't try checking that. // no longer exists, so don't try checking that.
if (!isAllowDuplicateValues()) { if (!isAllowDuplicateValues()) {
assertTrue(!map.containsValue(values[i]), "Map should not contain old value after put when changed"); assertTrue("Map should not contain old value after put when changed",
!map.containsValue(values[i]));
} }
} }
} }
@@ -957,7 +970,7 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
Object[] values = getSampleValues(); Object[] values = getSampleValues();
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
Object o = map.remove(keys[i]); Object o = map.remove(keys[i]);
assertTrue(o == null, "First map.remove should return null"); assertTrue("First map.remove should return null", o == null);
} }
verifyAll(); verifyAll();
@@ -968,7 +981,8 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
confirmed.remove(keys[i]); confirmed.remove(keys[i]);
verifyAll(); verifyAll();
assertEquals(values[i], o, "map.remove with valid key should return value"); assertEquals("map.remove with valid key should return value",
values[i], o);
} }
Object[] other = getOtherKeys(); Object[] other = getOtherKeys();
@@ -977,8 +991,10 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
int size = map.size(); int size = map.size();
for (int i = 0; i < other.length; i++) { for (int i = 0; i < other.length; i++) {
Object o = map.remove(other[i]); Object o = map.remove(other[i]);
assertEquals(o, null, "map.remove for nonexistent key should return null"); assertEquals("map.remove for nonexistent key should return null",
assertEquals(size, map.size(), "map.remove for nonexistent key should not shrink map"); o, null);
assertEquals("map.remove for nonexistent key should not " +
"shrink map", size, map.size());
} }
verifyAll(); verifyAll();
} }
@@ -1188,9 +1204,10 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
} }
j++; j++;
} }
assertTrue(j < 10000, "values().remove(obj) is broken"); assertTrue("values().remove(obj) is broken", j < 10000);
assertTrue(!map.containsValue(sampleValues[i]), assertTrue(
"Value should have been removed from the underlying map."); "Value should have been removed from the underlying map.",
!map.containsValue(sampleValues[i]));
} }
} }
} }
@@ -1213,8 +1230,9 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
// if key.remove is unsupported, just skip this test // if key.remove is unsupported, just skip this test
return; return;
} }
assertTrue(!map.containsKey(sampleKeys[i]), assertTrue(
"Key should have been removed from the underlying map."); "Key should have been removed from the underlying map.",
!map.containsKey(sampleKeys[i]));
} }
} }
@@ -1395,7 +1413,7 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
break; break;
} }
} }
assertNotNull(entry, "No matching entry in map for key '" + key + "'"); assertNotNull("No matching entry in map for key '" + key + "'", entry);
return entry; return entry;
} }
@@ -1620,14 +1638,14 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
public void verifyMap() { public void verifyMap() {
int size = confirmed.size(); int size = confirmed.size();
boolean empty = confirmed.isEmpty(); boolean empty = confirmed.isEmpty();
assertEquals(size, map.size(), "Map should be same size as HashMap"); assertEquals("Map should be same size as HashMap", size, map.size());
assertEquals(empty, map.isEmpty(), "Map should be empty if HashMap is"); assertEquals("Map should be empty if HashMap is", empty, map.isEmpty());
assertEquals(confirmed.hashCode(), map.hashCode(), "hashCodes should be the same"); assertEquals("hashCodes should be the same", confirmed.hashCode(), map.hashCode());
// this fails for LRUMap because confirmed.equals() somehow modifies // this fails for LRUMap because confirmed.equals() somehow modifies
// map, causing concurrent modification exceptions. // map, causing concurrent modification exceptions.
//assertEquals("Map should still equal HashMap", confirmed, map); //assertEquals("Map should still equal HashMap", confirmed, map);
// this works though and performs the same verification: // this works though and performs the same verification:
assertTrue(map.equals(confirmed), "Map should still equal HashMap"); assertTrue("Map should still equal HashMap", map.equals(confirmed));
// TODO: this should really be reexamined to figure out why LRU map // TODO: this should really be reexamined to figure out why LRU map
// behaves like it does (the equals shouldn't modify since all accesses // behaves like it does (the equals shouldn't modify since all accesses
// by the confirmed collection should be through an iterator, thus not // by the confirmed collection should be through an iterator, thus not
@@ -1637,29 +1655,29 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
public void verifyEntrySet() { public void verifyEntrySet() {
int size = confirmed.size(); int size = confirmed.size();
boolean empty = confirmed.isEmpty(); boolean empty = confirmed.isEmpty();
assertEquals(size, entrySet.size(), assertEquals("entrySet should be same size as HashMap's\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(),
"entrySet should be same size as HashMap's\nTest: " + entrySet + "\nReal: " + confirmed.entrySet()); size, entrySet.size());
assertEquals(empty, entrySet.isEmpty(), assertEquals("entrySet should be empty if HashMap is\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(),
"entrySet should be empty if HashMap is\nTest: " + entrySet + "\nReal: " + confirmed.entrySet()); empty, entrySet.isEmpty());
assertTrue(entrySet.containsAll(confirmed.entrySet()), assertTrue("entrySet should contain all HashMap's elements\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(),
"entrySet should contain all HashMap's elements\nTest: " + entrySet + "\nReal: " + confirmed.entrySet()); entrySet.containsAll(confirmed.entrySet()));
assertEquals(confirmed.entrySet().hashCode(), entrySet.hashCode(), assertEquals("entrySet hashCodes should be the same\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(),
"entrySet hashCodes should be the same\nTest: " + entrySet + "\nReal: " + confirmed.entrySet()); confirmed.entrySet().hashCode(), entrySet.hashCode());
assertEquals(confirmed.entrySet(), entrySet,"Map's entry set should still equal HashMap's"); assertEquals("Map's entry set should still equal HashMap's", confirmed.entrySet(), entrySet);
} }
public void verifyKeySet() { public void verifyKeySet() {
int size = confirmed.size(); int size = confirmed.size();
boolean empty = confirmed.isEmpty(); boolean empty = confirmed.isEmpty();
assertEquals(size, keySet.size(), assertEquals("keySet should be same size as HashMap's\nTest: " + keySet + "\nReal: " + confirmed.keySet(),
"keySet should be same size as HashMap's\nTest: " + keySet + "\nReal: " + confirmed.keySet()); size, keySet.size());
assertEquals(empty, keySet.isEmpty(), assertEquals("keySet should be empty if HashMap is\nTest: " + keySet + "\nReal: " + confirmed.keySet(),
"keySet should be empty if HashMap is\nTest: " + keySet + "\nReal: " + confirmed.keySet()); empty, keySet.isEmpty());
assertTrue(keySet.containsAll(confirmed.keySet()), assertTrue("keySet should contain all HashMap's elements\nTest: " + keySet + "\nReal: " + confirmed.keySet(),
"keySet should contain all HashMap's elements\nTest: " + keySet + "\nReal: " + confirmed.keySet()); keySet.containsAll(confirmed.keySet()));
assertEquals(confirmed.keySet().hashCode(), keySet.hashCode(), assertEquals("keySet hashCodes should be the same\nTest: " + keySet + "\nReal: " + confirmed.keySet(),
"keySet hashCodes should be the same\nTest: " + keySet + "\nReal: " + confirmed.keySet()); confirmed.keySet().hashCode(), keySet.hashCode());
assertEquals(confirmed.keySet(), keySet, "Map's key set should still equal HashMap's"); assertEquals("Map's key set should still equal HashMap's", confirmed.keySet(), keySet);
} }
public void verifyValues() { public void verifyValues() {
@@ -1669,23 +1687,23 @@ public abstract class MapAbstractTest extends ObjectAbstractTest {
int size = confirmed.size(); int size = confirmed.size();
boolean empty = confirmed.isEmpty(); boolean empty = confirmed.isEmpty();
assertEquals(size, values.size(), "values should be same size as HashMap's\nTest: " + test + "\nReal: " + known); assertEquals("values should be same size as HashMap's\nTest: " + test + "\nReal: " + known, size, values.size());
assertEquals(empty, values.isEmpty(), "values should be empty if HashMap is\nTest: " + test + "\nReal: " + known); assertEquals("values should be empty if HashMap is\nTest: " + test + "\nReal: " + known, empty, values.isEmpty());
assertTrue(test.containsAll(known), "values should contain all HashMap's elements\nTest: " + test + "\nReal: " + known); assertTrue("values should contain all HashMap's elements\nTest: " + test + "\nReal: " + known, test.containsAll(known));
assertTrue(known.containsAll(test), "values should contain all HashMap's elements\nTest: " + test + "\nReal: " + known); assertTrue("values should contain all HashMap's elements\nTest: " + test + "\nReal: " + known, known.containsAll(test));
for (Object aKnown : known) { for (Object aKnown : known) {
boolean removed = test.remove(aKnown); boolean removed = test.remove(aKnown);
assertTrue(removed, "Map's values should still equal HashMap's"); assertTrue("Map's values should still equal HashMap's", removed);
} }
assertTrue(test.isEmpty(), "Map's values should still equal HashMap's"); assertTrue("Map's values should still equal HashMap's", test.isEmpty());
} }
/** /**
* Erases any leftover instance variables by setting them to null. * Erases any leftover instance variables by setting them to null.
*/ */
@AfterEach @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
map = null; map = null;
keySet = null; keySet = null;
@@ -30,11 +30,13 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.*; import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.assertTrue;
/** /**
* NOTE: This TestCase is written especially for NullMap, and is full of dirty * NOTE: This TestCase is written especially for NullMap, and is full of dirty
@@ -81,12 +83,12 @@ public class NullMapTest extends MapAbstractTest {
@Override @Override
public void testMapIsEmpty() { public void testMapIsEmpty() {
resetEmpty(); resetEmpty();
assertEquals(true, map.isEmpty(), assertEquals("Map.isEmpty() should return true with an empty map",
"Map.isEmpty() should return true with an empty map"); true, map.isEmpty());
verifyAll(); verifyAll();
resetFull(); resetFull();
assertEquals(true, map.isEmpty(), assertEquals("Map.isEmpty() should return true with a full map",
"Map.isEmpty() should return true with a full map"); true, map.isEmpty());
} }
// Overriden, as this map is always empty // Overriden, as this map is always empty
@@ -94,13 +96,13 @@ public class NullMapTest extends MapAbstractTest {
@Override @Override
public void testMapSize() { public void testMapSize() {
resetEmpty(); resetEmpty();
assertEquals(0, map.size(), assertEquals("Map.size() should be 0 with an empty map",
"Map.size() should be 0 with an empty map"); 0, map.size());
verifyAll(); verifyAll();
resetFull(); resetFull();
assertEquals(0, map.size(), assertEquals("Map.size() should equal the number of entries " +
"Map.size() should equal the number of entries in the map"); "in the map", 0, map.size());
} }
@Test @Test
@@ -110,7 +112,7 @@ public class NullMapTest extends MapAbstractTest {
resetEmpty(); resetEmpty();
for (Object key : keys) { for (Object key : keys) {
assertTrue(!map.containsKey(key),"Map must not contain key when map is empty"); assertTrue("Map must not contain key when map is empty", !map.containsKey(key));
} }
verifyAll(); verifyAll();
} }
@@ -122,7 +124,7 @@ public class NullMapTest extends MapAbstractTest {
resetEmpty(); resetEmpty();
for (Object value : values) { for (Object value : values) {
assertTrue(!map.containsValue(value), "Empty map must not contain value"); assertTrue("Empty map must not contain value", !map.containsValue(value));
} }
verifyAll(); verifyAll();
} }
@@ -131,7 +133,7 @@ public class NullMapTest extends MapAbstractTest {
@Override @Override
public void testMapEquals() { public void testMapEquals() {
resetEmpty(); resetEmpty();
assertTrue(map.equals(confirmed), "Empty maps unequal."); assertTrue("Empty maps unequal.", map.equals(confirmed));
verifyAll(); verifyAll();
} }
@@ -139,7 +141,8 @@ public class NullMapTest extends MapAbstractTest {
@Override @Override
public void testMapHashCode() { public void testMapHashCode() {
resetEmpty(); resetEmpty();
assertTrue(map.hashCode() == confirmed.hashCode(), "Empty maps have different hashCodes."); assertTrue("Empty maps have different hashCodes.",
map.hashCode() == confirmed.hashCode());
} }
@Test @Test
@@ -150,7 +153,7 @@ public class NullMapTest extends MapAbstractTest {
Object[] keys = getSampleKeys(); Object[] keys = getSampleKeys();
for (Object key : keys) { for (Object key : keys) {
assertTrue(map.get(key) == null, "Empty map.get() should return null."); assertTrue("Empty map.get() should return null.", map.get(key) == null);
} }
verifyAll(); verifyAll();
} }
@@ -167,7 +170,7 @@ public class NullMapTest extends MapAbstractTest {
Object o = map.put(keys[i], values[i]); Object o = map.put(keys[i], values[i]);
//confirmed.put(keys[i], values[i]); //confirmed.put(keys[i], values[i]);
verifyAll(); verifyAll();
assertTrue(o == null, "First map.put should return null"); assertTrue("First map.put should return null", o == null);
} }
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
map.put(keys[i], newValues[i]); map.put(keys[i], newValues[i]);
@@ -180,8 +183,8 @@ public class NullMapTest extends MapAbstractTest {
@Override @Override
public void testMapToString() { public void testMapToString() {
resetEmpty(); resetEmpty();
assertTrue(map.toString() != null, assertTrue("Empty map toString() should not return null",
"Empty map toString() should not return null"); map.toString() != null);
verifyAll(); verifyAll();
} }
@@ -199,7 +202,7 @@ public class NullMapTest extends MapAbstractTest {
Object[] keys = getSampleKeys(); Object[] keys = getSampleKeys();
for(int i = 0; i < keys.length; i++) { for(int i = 0; i < keys.length; i++) {
Object o = map.remove(keys[i]); Object o = map.remove(keys[i]);
assertTrue(o == null, "First map.remove should return null"); assertTrue("First map.remove should return null", o == null);
} }
verifyAll(); verifyAll();
} }
@@ -44,10 +44,12 @@
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.io.*; import java.io.*;
import org.junit.jupiter.api.*; import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.assertTrue;
/** /**
* Abstract test class for {@link Object} methods and contracts. * Abstract test class for {@link Object} methods and contracts.
@@ -117,7 +119,7 @@ public abstract class ObjectAbstractTest {
@Test @Test
public void testObjectEqualsSelf() { public void testObjectEqualsSelf() {
Object obj = makeObject(); Object obj = makeObject();
assertEquals(obj, obj, "A Object should equal itself"); assertEquals("A Object should equal itself", obj, obj);
} }
@Test @Test
@@ -129,24 +131,25 @@ public abstract class ObjectAbstractTest {
@Test @Test
public void testObjectHashCodeEqualsSelfHashCode() { public void testObjectHashCodeEqualsSelfHashCode() {
Object obj = makeObject(); Object obj = makeObject();
assertEquals(obj.hashCode(), obj.hashCode(), "hashCode should be repeatable"); assertEquals("hashCode should be repeatable", obj.hashCode(), obj.hashCode());
} }
@Test @Test
public void testObjectHashCodeEqualsContract() { public void testObjectHashCodeEqualsContract() {
Object obj1 = makeObject(); Object obj1 = makeObject();
if (obj1.equals(obj1)) { if (obj1.equals(obj1)) {
assertEquals(obj1.hashCode(), obj1.hashCode(), assertEquals(
"[1] When two objects are equal, their hashCodes should be also."); "[1] When two objects are equal, their hashCodes should be also.",
obj1.hashCode(), obj1.hashCode());
} }
Object obj2 = makeObject(); Object obj2 = makeObject();
if (obj1.equals(obj2)) { if (obj1.equals(obj2)) {
assertEquals( assertEquals(
obj1.hashCode(), obj2.hashCode(), "[2] When two objects are equal, their hashCodes should be also.",
"[2] When two objects are equal, their hashCodes should be also."); obj1.hashCode(), obj2.hashCode());
assertTrue( assertTrue(
obj2.equals(obj1), "When obj1.equals(obj2) is true, then obj2.equals(obj1) should also be true",
"When obj1.equals(obj2) is true, then obj2.equals(obj1) should also be true"); obj2.equals(obj1));
} }
} }
@@ -163,7 +166,7 @@ public abstract class ObjectAbstractTest {
Object dest = in.readObject(); Object dest = in.readObject();
in.close(); in.close();
if (isEqualsCheckable()) { if (isEqualsCheckable()) {
assertEquals(obj, dest, "obj != deserialize(serialize(obj))"); assertEquals("obj != deserialize(serialize(obj))", obj, dest);
} }
} }
} }
@@ -196,8 +199,8 @@ public abstract class ObjectAbstractTest {
if (object instanceof Serializable) { if (object instanceof Serializable) {
String name = getCanonicalEmptyCollectionName(object); String name = getCanonicalEmptyCollectionName(object);
assertTrue( assertTrue(
new File(name).exists(), "Canonical empty collection (" + name + ") is not in CVS",
"Canonical empty collection (" + name + ") is not in CVS"); new File(name).exists());
} }
} }
} }
@@ -213,8 +216,8 @@ public abstract class ObjectAbstractTest {
if (object instanceof Serializable) { if (object instanceof Serializable) {
String name = getCanonicalFullCollectionName(object); String name = getCanonicalFullCollectionName(object);
assertTrue( assertTrue(
new File(name).exists(), "Canonical full collection (" + name + ") is not in CVS",
"Canonical full collection (" + name + ") is not in CVS"); new File(name).exists());
} }
} }
} }
@@ -44,10 +44,12 @@
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.util.*; import java.util.*;
import org.junit.jupiter.api.*; import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.assertTrue;
/** /**
* Abstract test class for {@link Set} methods and contracts. * Abstract test class for {@link Set} methods and contracts.
@@ -77,12 +79,14 @@ public abstract class SetAbstractTest extends CollectionAbstractTest {
public void verifyAll() { public void verifyAll() {
super.verifyAll(); super.verifyAll();
assertEquals(confirmed, collection, "Sets should be equal"); assertEquals("Sets should be equal", confirmed, collection);
assertEquals(confirmed.hashCode(), collection.hashCode(), "Sets should have equal hashCodes"); assertEquals("Sets should have equal hashCodes",
confirmed.hashCode(), collection.hashCode());
Collection set = makeConfirmedCollection(); Collection set = makeConfirmedCollection();
Iterator iterator = collection.iterator(); Iterator iterator = collection.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
assertTrue(set.add(iterator.next()), "Set.iterator should only return unique elements"); assertTrue("Set.iterator should only return unique elements",
set.add(iterator.next()));
} }
} }
@@ -176,20 +180,23 @@ public abstract class SetAbstractTest extends CollectionAbstractTest {
@Test @Test
public void testSetEquals() { public void testSetEquals() {
resetEmpty(); resetEmpty();
assertEquals(getSet(), getConfirmedSet(), "Empty sets should be equal"); assertEquals("Empty sets should be equal",
getSet(), getConfirmedSet());
verifyAll(); verifyAll();
Collection set2 = makeConfirmedCollection(); Collection set2 = makeConfirmedCollection();
set2.add("foo"); set2.add("foo");
assertTrue(!getSet().equals(set2), "Empty set shouldn't equal nonempty set"); assertTrue("Empty set shouldn't equal nonempty set",
!getSet().equals(set2));
resetFull(); resetFull();
assertEquals(getSet(), getConfirmedSet(), "Full sets should be equal"); assertEquals("Full sets should be equal", getSet(), getConfirmedSet());
verifyAll(); verifyAll();
set2.clear(); set2.clear();
set2.addAll(Arrays.asList(getOtherElements())); set2.addAll(Arrays.asList(getOtherElements()));
assertTrue(!getSet().equals(set2), "Sets with different contents shouldn't be equal"); assertTrue("Sets with different contents shouldn't be equal",
!getSet().equals(set2));
} }
/** /**
@@ -198,9 +205,11 @@ public abstract class SetAbstractTest extends CollectionAbstractTest {
@Test @Test
public void testSetHashCode() { public void testSetHashCode() {
resetEmpty(); resetEmpty();
assertEquals(getSet().hashCode(), getConfirmedSet().hashCode(), "Empty sets have equal hashCodes"); assertEquals("Empty sets have equal hashCodes",
getSet().hashCode(), getConfirmedSet().hashCode());
resetFull(); resetFull();
assertEquals(getSet().hashCode(), getConfirmedSet().hashCode(), "Equal sets have equal hashCodes"); assertEquals("Equal sets have equal hashCodes",
getSet().hashCode(), getConfirmedSet().hashCode());
} }
} }
@@ -30,10 +30,12 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.util.Iterator; import java.util.Iterator;
import org.junit.jupiter.api.*; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* StringTokenIteratorTestCase * StringTokenIteratorTestCase
@@ -54,88 +56,88 @@ public class StringTokenIteratorTest extends TokenIteratorAbstractTest {
@Test @Test
public void testEmptyDelimiter() { public void testEmptyDelimiter() {
Iterator iterator = createTokenIterator("", ""); Iterator iterator = createTokenIterator("", "");
assertFalse(iterator.hasNext(), "Empty string has elements"); assertFalse("Empty string has elements", iterator.hasNext());
} }
@Test @Test
public void testSingleToken() { public void testSingleToken() {
Iterator iterator = createTokenIterator("A"); Iterator iterator = createTokenIterator("A");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testSingleTokenEmptyDelimiter() { public void testSingleTokenEmptyDelimiter() {
Iterator iterator = createTokenIterator("A", ""); Iterator iterator = createTokenIterator("A", "");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testSingleTokenSingleDelimiter() { public void testSingleTokenSingleDelimiter() {
Iterator iterator = createTokenIterator("A", ","); Iterator iterator = createTokenIterator("A", ",");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testSingleSeparatorDefaultDelimiter() { public void testSingleSeparatorDefaultDelimiter() {
Iterator iterator = createTokenIterator("A B C D"); Iterator iterator = createTokenIterator("A B C D");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("D", iterator.next()); assertEquals("D", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testSingleSeparator() { public void testSingleSeparator() {
Iterator iterator = createTokenIterator("A,B,C", ","); Iterator iterator = createTokenIterator("A,B,C", ",");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testMultipleSeparatorDefaultDelimiter() { public void testMultipleSeparatorDefaultDelimiter() {
Iterator iterator = createTokenIterator("A B C\nD\t\t \nE"); Iterator iterator = createTokenIterator("A B C\nD\t\t \nE");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("D", iterator.next()); assertEquals("D", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("E", iterator.next()); assertEquals("E", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testMultipleSeparator() { public void testMultipleSeparator() {
Iterator iterator = createTokenIterator("A,B,;,C...D, ., ,E", " ,.;:"); Iterator iterator = createTokenIterator("A,B,;,C...D, ., ,E", " ,.;:");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("D", iterator.next()); assertEquals("D", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("E", iterator.next()); assertEquals("E", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
} }
@@ -30,10 +30,12 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.util.*; import java.util.*;
import org.junit.jupiter.api.*; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* TimeoutMapTest * TimeoutMapTest
* <p/> * <p/>
@@ -539,7 +541,7 @@ public class TimeoutMapTest extends MapAbstractTest {
} }
} }
assertTrue(count > 0, "Elements expired too early, test did not run as expected."); assertTrue("Elements expired too early, test did not run as expected.", count > 0);
//assertEquals("Elements did not expire as expected.", 1, count); //assertEquals("Elements did not expire as expected.", 1, count);
} }
@@ -555,7 +557,7 @@ public class TimeoutMapTest extends MapAbstractTest {
// NOTE: Only wait fist time, to avoid slooow tests // NOTE: Only wait fist time, to avoid slooow tests
synchronized (this) { synchronized (this) {
try { try {
wait(60L); wait(60l);
} }
catch (InterruptedException e) { catch (InterruptedException e) {
} }
@@ -571,7 +573,7 @@ public class TimeoutMapTest extends MapAbstractTest {
} }
} }
assertTrue(count > 0, "Elements expired too early, test did not run as expected."); assertTrue("Elements expired too early, test did not run as expected.", count > 0);
//assertEquals("Elements did not expire as expected.", 1, count); //assertEquals("Elements did not expire as expected.", 1, count);
} }
@@ -589,7 +591,7 @@ public class TimeoutMapTest extends MapAbstractTest {
try { try {
wait(60l); wait(60l);
} }
catch (InterruptedException ignore) { catch (InterruptedException e) {
} }
} }
} }
@@ -611,7 +613,7 @@ public class TimeoutMapTest extends MapAbstractTest {
} }
} }
assertTrue(count > 0, "Elements expired too early, test did not run as expected."); assertTrue("Elements expired too early, test did not run as expected.", count > 0);
//assertEquals("Elements did not expire as expected.", 1, count); //assertEquals("Elements did not expire as expected.", 1, count);
} }
@@ -628,7 +630,7 @@ public class TimeoutMapTest extends MapAbstractTest {
Object removedKey = null; Object removedKey = null;
Object otherKey = null; Object otherKey = null;
Iterator iterator = map.entrySet().iterator(); Iterator iterator = map.entrySet().iterator();
assertTrue(iterator.hasNext(), "Iterator was empty"); assertTrue("Iterator was empty", iterator.hasNext());
try { try {
Map.Entry entry = (Map.Entry) iterator.next(); Map.Entry entry = (Map.Entry) iterator.next();
assertNotNull(entry); assertNotNull(entry);
@@ -646,27 +648,8 @@ public class TimeoutMapTest extends MapAbstractTest {
fail("Elements expired between Interator.hasNext() and Iterator.remove()"); fail("Elements expired between Interator.hasNext() and Iterator.remove()");
} }
assertTrue(!map.containsKey(removedKey), "Wrong entry removed, keySet().iterator() is broken."); assertTrue("Wrong entry removed, keySet().iterator() is broken.", !map.containsKey(removedKey));
assertTrue(map.containsKey(otherKey), "Wrong entry removed, keySet().iterator() is broken."); assertTrue("Wrong entry removed, keySet().iterator() is broken.", map.containsKey(otherKey));
}
@Test
public void testContainsKeyOnEmptyMap() {
// See #600
Map<String, String> timeoutMap = new TimeoutMap<>(30);
assertFalse(timeoutMap.containsKey("xyz"));
timeoutMap.put("xyz", "xyz");
assertTrue(timeoutMap.containsKey("xyz"));
try {
Thread.sleep(50); // Let the item expire
}
catch (InterruptedException ignore) {
}
assertFalse(timeoutMap.containsKey("xyz"));
assertNull(timeoutMap.get("xyz"));
} }
} }
@@ -30,10 +30,12 @@
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import org.junit.Test;
import java.util.Iterator; import java.util.Iterator;
import org.junit.jupiter.api.*; import static org.junit.Assert.assertFalse;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.Assert.fail;
/** /**
* TokenIteratorAbstractTestCase * TokenIteratorAbstractTestCase
@@ -78,7 +80,7 @@ public abstract class TokenIteratorAbstractTest {
@Test @Test
public void testEmptyString() { public void testEmptyString() {
Iterator iterator = createTokenIterator(""); Iterator iterator = createTokenIterator("");
assertFalse(iterator.hasNext(), "Empty string has elements"); assertFalse("Empty string has elements", iterator.hasNext());
} }
} }
@@ -30,8 +30,8 @@
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import org.junit.jupiter.api.Disabled; import org.junit.Ignore;
import org.junit.jupiter.api.Test; import org.junit.Test;
/** /**
* ConverterTest * ConverterTest
@@ -43,7 +43,7 @@ import org.junit.jupiter.api.Test;
*/ */
public class ConverterTest { public class ConverterTest {
@Disabled("Not implemented") @Ignore("Not implemented")
@Test @Test
public void testMe() { public void testMe() {
// TODO: Implement tests // TODO: Implement tests
@@ -31,7 +31,7 @@
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.DateUtil; import com.twelvemonkeys.lang.DateUtil;
import org.junit.jupiter.api.Test; import org.junit.Test;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.Date; import java.util.Date;
@@ -31,13 +31,13 @@
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File; import java.io.File;
import java.net.URI; import java.net.URI;
import org.junit.jupiter.api.Disabled; import static org.junit.Assert.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* DefaultConverterTest * DefaultConverterTest
@@ -138,7 +138,7 @@ public class DefaultConverterTest extends PropertyConverterAbstractTest {
assertEquals(-2.3456, (Double) converter.toObject("-2.3456", Double.TYPE, null), 0); assertEquals(-2.3456, (Double) converter.toObject("-2.3456", Double.TYPE, null), 0);
} }
@Disabled("Known issue. Why would anyone do something like this?") @Ignore("Known issue. Why would anyone do something like this?")
@Test @Test
public void testConvertCharPrimitive() { public void testConvertCharPrimitive() {
PropertyConverter converter = makePropertyConverter(); PropertyConverter converter = makePropertyConverter();
@@ -32,11 +32,11 @@ package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.ObjectAbstractTest; import com.twelvemonkeys.lang.ObjectAbstractTest;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import org.junit.Test;
import java.util.Arrays; import java.util.Arrays;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* PropertyConverterAbstractTest * PropertyConverterAbstractTest
@@ -66,26 +66,26 @@ public abstract class PropertyConverterAbstractTest extends ObjectAbstractTest {
try { try {
obj = converter.toObject(test.original(), test.type(), test.format()); obj = converter.toObject(test.original(), test.type(), test.format());
assertEquals(test.type(), obj.getClass(), String.format("'%s' converted to incorrect type", test.original())); assertEquals(String.format("'%s' converted to incorrect type", test.original()), test.type(), obj.getClass());
if (test.type().isArray()) { if (test.type().isArray()) {
assertArrayEquals0(String.format("'%s' not converted", test.original()), test.value(), obj); assertArrayEquals0(String.format("'%s' not converted", test.original()), test.value(), obj);
} }
else { else {
assertEquals(test.value(), obj, String.format("'%s' not converted", test.original())); assertEquals(String.format("'%s' not converted", test.original()), test.value(), obj);
} }
String result = converter.toString(test.value(), test.format()); String result = converter.toString(test.value(), test.format());
assertEquals(test.converted(), result, String.format("'%s' does not match", test.converted())); assertEquals(String.format("'%s' does not match", test.converted()), test.converted(), result);
obj = converter.toObject(result, test.type(), test.format()); obj = converter.toObject(result, test.type(), test.format());
assertEquals(test.type(), obj.getClass(), String.format("'%s' converted to incorrect type", test.original())); assertEquals(String.format("'%s' converted to incorrect type", test.original()), test.type(), obj.getClass());
if (test.type().isArray()) { if (test.type().isArray()) {
assertArrayEquals0(String.format("'%s' did not survive round trip conversion", test.original()), test.value(), obj); assertArrayEquals0(String.format("'%s' did not survive round trip conversion", test.original()), test.value(), obj);
} }
else { else {
assertEquals(test.value(), obj, String.format("'%s' did not survive round trip conversion", test.original())); assertEquals(String.format("'%s' did not survive round trip conversion", test.original()), test.value(), obj);
} }
} }
catch (ConversionException e) { catch (ConversionException e) {
@@ -98,35 +98,35 @@ public abstract class PropertyConverterAbstractTest extends ObjectAbstractTest {
Class<?> componentType = left.getClass().getComponentType(); Class<?> componentType = left.getClass().getComponentType();
if (componentType.isPrimitive()) { if (componentType.isPrimitive()) {
if (int.class == componentType) { if (int.class == componentType) {
assertArrayEquals((int[]) left, (int[]) right, message); assertArrayEquals(message, (int[]) left, (int[]) right);
} }
else if (short.class == componentType) { else if (short.class == componentType) {
assertArrayEquals((short[]) left, (short[]) right, message); assertArrayEquals(message, (short[]) left, (short[]) right);
} }
else if (long.class == componentType) { else if (long.class == componentType) {
assertArrayEquals((long[]) left, (long[]) right, message); assertArrayEquals(message, (long[]) left, (long[]) right);
} }
else if (float.class == componentType) { else if (float.class == componentType) {
assertArrayEquals((float[]) left, (float[]) right, 0f, message); assertArrayEquals(message, (float[]) left, (float[]) right, 0f);
} }
else if (double.class == componentType) { else if (double.class == componentType) {
assertArrayEquals((double[]) left, (double[]) right, 0d, message); assertArrayEquals(message, (double[]) left, (double[]) right, 0d);
} }
else if (boolean.class == componentType) { else if (boolean.class == componentType) {
assertTrue(Arrays.equals((boolean[]) left, (boolean[]) right), message); assertTrue(message, Arrays.equals((boolean[]) left, (boolean[]) right));
} }
else if (byte.class == componentType) { else if (byte.class == componentType) {
assertArrayEquals((byte[]) left, (byte[]) right, message); assertArrayEquals(message, (byte[]) left, (byte[]) right);
} }
else if (char.class == componentType) { else if (char.class == componentType) {
assertArrayEquals((char[]) left, (char[]) right, message); assertArrayEquals(message, (char[]) left, (char[]) right);
} }
else { else {
fail(String.format("Unknown primitive type: %s", componentType)); fail(String.format("Unknown primitive type: %s", componentType));
} }
} }
else { else {
assertArrayEquals((Object[]) left, (Object[]) right, message); assertArrayEquals(message, (Object[]) left, (Object[]) right);
} }
} }
@@ -32,11 +32,12 @@ package com.twelvemonkeys.util.regex;
import com.twelvemonkeys.util.TokenIterator; import com.twelvemonkeys.util.TokenIterator;
import com.twelvemonkeys.util.TokenIteratorAbstractTest; import com.twelvemonkeys.util.TokenIteratorAbstractTest;
import org.junit.Test;
import java.util.Iterator; import java.util.Iterator;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* StringTokenIteratorTestCase * StringTokenIteratorTestCase
* <p/> * <p/>
@@ -67,9 +68,9 @@ public class RegExTokenIteratorTest extends TokenIteratorAbstractTest {
@Test @Test
public void testSingleToken() { public void testSingleToken() {
Iterator iterator = createTokenIterator("A"); Iterator iterator = createTokenIterator("A");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
@@ -86,67 +87,67 @@ public class RegExTokenIteratorTest extends TokenIteratorAbstractTest {
@Test @Test
public void testSingleTokenSingleDelimiter() { public void testSingleTokenSingleDelimiter() {
Iterator iterator = createTokenIterator("A", "[^,]+"); Iterator iterator = createTokenIterator("A", "[^,]+");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testSingleSeparatorDefaultDelimiter() { public void testSingleSeparatorDefaultDelimiter() {
Iterator iterator = createTokenIterator("A B C D"); Iterator iterator = createTokenIterator("A B C D");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("D", iterator.next()); assertEquals("D", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testSingleSeparator() { public void testSingleSeparator() {
Iterator iterator = createTokenIterator("A,B,C", "[^,]+"); Iterator iterator = createTokenIterator("A,B,C", "[^,]+");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testMultipleSeparatorDefaultDelimiter() { public void testMultipleSeparatorDefaultDelimiter() {
Iterator iterator = createTokenIterator("A B C\nD\t\t \nE"); Iterator iterator = createTokenIterator("A B C\nD\t\t \nE");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("A", iterator.next()); assertEquals("A", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("D", iterator.next()); assertEquals("D", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("E", iterator.next()); assertEquals("E", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
@Test @Test
public void testMultipleSeparator() { public void testMultipleSeparator() {
Iterator iterator = createTokenIterator("A,B,;,C...D, ., ,E", "[^ ,.;:]+"); Iterator iterator = createTokenIterator("A,B,;,C...D, ., ,E", "[^ ,.;:]+");
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
Object o = iterator.next(); Object o = iterator.next();
assertEquals("A", o); assertEquals("A", o);
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("B", iterator.next()); assertEquals("B", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("C", iterator.next()); assertEquals("C", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("D", iterator.next()); assertEquals("D", iterator.next());
assertTrue(iterator.hasNext(), "String has no elements"); assertTrue("String has no elements", iterator.hasNext());
assertEquals("E", iterator.next()); assertEquals("E", iterator.next());
assertFalse(iterator.hasNext(), "String has more than one element"); assertFalse("String has more than one element", iterator.hasNext());
} }
} }
@@ -30,13 +30,12 @@
package com.twelvemonkeys.util.service; package com.twelvemonkeys.util.service;
import com.twelvemonkeys.lang.Validate;
import com.twelvemonkeys.util.CollectionUtil; import com.twelvemonkeys.util.CollectionUtil;
import org.junit.Test;
import java.util.*; import java.util.*;
import org.junit.jupiter.api.Test; import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* ServiceRegistryTest * ServiceRegistryTest
@@ -49,11 +48,9 @@ public class ServiceRegistryTest {
private final TestRegistry registry = new TestRegistry(); private final TestRegistry registry = new TestRegistry();
@Test @Test(expected = IllegalArgumentException.class)
public void testCreateNull() { public void testCreateNull() {
assertThrows(IllegalArgumentException.class, () -> { new ServiceRegistry(null);
new ServiceRegistry(null);
});
} }
@Test @Test
@@ -67,12 +64,11 @@ public class ServiceRegistryTest {
} }
} }
@Test @Test(expected = ServiceConfigurationError.class)
public void testCreateBadConfig() { public void testCreateBadConfig() {
assertThrows(ServiceConfigurationError.class, () -> { @SuppressWarnings("unchecked")
ServiceRegistry registry = new ServiceRegistry(Arrays.asList(BadSPI.class).iterator()); ServiceRegistry registry = new ServiceRegistry(Arrays.asList(BadSPI.class).iterator());
registry.registerApplicationClasspathSPIs(); registry.registerApplicationClasspathSPIs();
});
// DONE: Test non-class // DONE: Test non-class
+4 -15
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.13.2-SNAPSHOT</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
@@ -20,10 +20,6 @@
<module>common-image</module> <module>common-image</module>
</modules> </modules>
<properties>
<junit.jupiter.version>5.14.3</junit.jupiter.version>
</properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -47,18 +43,11 @@
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>junit</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit</artifactId>
<version>${junit.jupiter.version}</version> <version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
+6 -25
View File
@@ -4,20 +4,15 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.13.2-SNAPSHOT</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.contrib</groupId> <groupId>com.twelvemonkeys.contrib</groupId>
<artifactId>contrib</artifactId> <artifactId>contrib</artifactId>
<name>TwelveMonkeys :: Contrib</name> <name>TwelveMonkeys :: Contrib</name>
<description> <description>
Contributions to TwelveMonkeys and code that doesn't fit anywhere else. Contributions to TwelveMonkeys which are not matching into the ImageIO plug-ins.
</description> </description>
<properties>
<junit.jupiter.version>5.14.3</junit.jupiter.version>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
@@ -66,26 +61,12 @@
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>junit</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit</artifactId>
<version>${junit.jupiter.version}</version> <version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> </project>
@@ -30,14 +30,9 @@ import static com.twelvemonkeys.contrib.tiff.TIFFUtilities.applyOrientation;
public class EXIFUtilities { public class EXIFUtilities {
/** /**
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}. * Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
* The returned {@code IIOImage} will always contain an image and no raster, and
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
*
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
* *
* @param input a {@code URL} * @param input a {@code URL}
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or * @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
* {@code null}.
* @throws IOException if an error occurs during reading. * @throws IOException if an error occurs during reading.
*/ */
public static IIOImage readWithOrientation(final URL input) throws IOException { public static IIOImage readWithOrientation(final URL input) throws IOException {
@@ -48,14 +43,9 @@ public class EXIFUtilities {
/** /**
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}. * Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
* The returned {@code IIOImage} will always contain an image and no raster, and
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
*
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
* *
* @param input an {@code InputStream} * @param input an {@code InputStream}
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or * @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
* {@code null}.
* @throws IOException if an error occurs during reading. * @throws IOException if an error occurs during reading.
*/ */
public static IIOImage readWithOrientation(final InputStream input) throws IOException { public static IIOImage readWithOrientation(final InputStream input) throws IOException {
@@ -66,14 +56,9 @@ public class EXIFUtilities {
/** /**
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}. * Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
* The returned {@code IIOImage} will always contain an image and no raster, and
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
*
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
* *
* @param input a {@code File} * @param input a {@code File}
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info or * @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
* {@code null}.
* @throws IOException if an error occurs during reading. * @throws IOException if an error occurs during reading.
*/ */
public static IIOImage readWithOrientation(final File input) throws IOException { public static IIOImage readWithOrientation(final File input) throws IOException {
@@ -84,14 +69,9 @@ public class EXIFUtilities {
/** /**
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}. * Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
* The returned {@code IIOImage} will always contain an image and no raster, and
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
*
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
* *
* @param input an {@code ImageInputStream} * @param input an {@code ImageInputStream}
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or * @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
* {@code null}.
* @throws IOException if an error occurs during reading. * @throws IOException if an error occurs during reading.
*/ */
public static IIOImage readWithOrientation(final ImageInputStream input) throws IOException { public static IIOImage readWithOrientation(final ImageInputStream input) throws IOException {
@@ -1,9 +1,9 @@
package com.twelvemonkeys.contrib.exif; package com.twelvemonkeys.contrib.exif;
import static com.twelvemonkeys.contrib.exif.Orientation.*; import org.junit.Test;
import org.junit.jupiter.api.Test; import static com.twelvemonkeys.contrib.exif.Orientation.*;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* OrientationTest. * OrientationTest.
@@ -31,9 +31,10 @@
package com.twelvemonkeys.contrib.tiff; package com.twelvemonkeys.contrib.tiff;
import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension; import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat; import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -52,9 +53,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* TIFFUtilitiesTest * TIFFUtilitiesTest
* *
@@ -96,7 +94,7 @@ public class TIFFUtilitiesTest {
ImageInputStream iis = ImageIO.createImageInputStream(output); ImageInputStream iis = ImageIO.createImageInputStream(output);
ImageReader reader = ImageIO.getImageReaders(iis).next(); ImageReader reader = ImageIO.getImageReaders(iis).next();
reader.setInput(iis); reader.setInput(iis);
assertEquals(3, reader.getNumImages(true)); Assert.assertEquals(3, reader.getNumImages(true));
iis.close(); iis.close();
output.delete(); output.delete();
@@ -120,11 +118,11 @@ public class TIFFUtilitiesTest {
ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next(); ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next();
File[] outputFiles = outputDirectory.listFiles(); File[] outputFiles = outputDirectory.listFiles();
assertEquals(3, outputFiles.length); Assert.assertEquals(3, outputFiles.length);
for (File outputFile : outputFiles) { for (File outputFile : outputFiles) {
ImageInputStream iis = ImageIO.createImageInputStream(outputFile); ImageInputStream iis = ImageIO.createImageInputStream(outputFile);
reader.setInput(iis); reader.setInput(iis);
assertEquals(1, reader.getNumImages(true)); Assert.assertEquals(1, reader.getNumImages(true));
iis.close(); iis.close();
outputFile.delete(); outputFile.delete();
} }
@@ -156,9 +154,9 @@ public class TIFFUtilitiesTest {
reader.setInput(checkTest1); reader.setInput(checkTest1);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(i) Node metaData = reader.getImageMetadata(i)
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME); .getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue(); short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP); Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
} }
checkTest1.close(); checkTest1.close();
@@ -173,9 +171,9 @@ public class TIFFUtilitiesTest {
reader.setInput(checkTest2); reader.setInput(checkTest2);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(i) Node metaData = reader.getImageMetadata(i)
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME); .getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue(); short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
assertEquals(orientation, i == 1 Assert.assertEquals(orientation, i == 1
? TIFFExtension.ORIENTATION_BOTRIGHT ? TIFFExtension.ORIENTATION_BOTRIGHT
: TIFFExtension.ORIENTATION_RIGHTTOP); : TIFFExtension.ORIENTATION_RIGHTTOP);
} }
@@ -200,7 +198,7 @@ public class TIFFUtilitiesTest {
byte[] original = ((DataBufferByte) image.getData().getDataBuffer()).getData(); byte[] original = ((DataBufferByte) image.getData().getDataBuffer()).getData();
byte[] rotated = ((DataBufferByte) image360.getData().getDataBuffer()).getData(); byte[] rotated = ((DataBufferByte) image360.getData().getDataBuffer()).getData();
assertArrayEquals(original, rotated); Assert.assertArrayEquals(original, rotated);
} }
@Test @Test
+15 -29
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.13.2-SNAPSHOT</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-batik</artifactId> <artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name> <name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
@@ -14,12 +14,6 @@
See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a> See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a>
for more information.]]> for more information.]]>
</description> </description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.batik</project.jpms.module.name>
<batik.version>1.19</batik.version>
</properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@@ -27,24 +21,12 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<systemPropertyVariables> <systemPropertyVariables>
<com.twelvemonkeys.imageio.plugins.svg.allowExternalResources> <com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
true true
</com.twelvemonkeys.imageio.plugins.svg.allowExternalResources> </com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Provide-Capability>
osgi.serviceloader;
osgi.serviceloader=javax.imageio.spi.ImageReaderSpi
</Provide-Capability>
</instructions>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
@@ -60,13 +42,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.21.0</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.apache.xmlgraphics</groupId> <groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-rasterizer-ext</artifactId> <artifactId>batik-rasterizer-ext</artifactId>
@@ -87,6 +62,13 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>xmlgraphics-commons</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.apache.xmlgraphics</groupId> <groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-anim</artifactId> <artifactId>batik-anim</artifactId>
@@ -110,7 +92,7 @@
<!-- <!--
There seems to be some weirdness in the There seems to be some weirdness in the
Batik/FOP poms (Batik depends on FOP 0.20-5) that screws things up, Batik/FOP poms (Batik depends on FOP 0.20-5) that screws things up,
making everything end up depending on Batik 1.5, not the specified version making everything end up depending on Batik 1.5, not 1.6
--> -->
<exclusions> <exclusions>
<exclusion> <exclusion>
@@ -120,4 +102,8 @@
</exclusions> </exclusions>
</dependency> </dependency>
</dependencies> </dependencies>
<properties>
<batik.version>1.14</batik.version>
</properties>
</project> </project>
@@ -33,12 +33,11 @@ package com.twelvemonkeys.imageio.plugins.svg;
import com.twelvemonkeys.image.ImageUtil; import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase; import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import org.apache.batik.anim.dom.SVGDOMImplementation; import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.anim.dom.SVGOMDocument; import org.apache.batik.anim.dom.SVGOMDocument;
import org.apache.batik.bridge.*; import org.apache.batik.bridge.*;
import org.apache.batik.css.parser.CSSLexicalUnit;
import org.apache.batik.dom.util.DOMUtilities; import org.apache.batik.dom.util.DOMUtilities;
import org.apache.batik.ext.awt.image.GraphicsUtil; import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.gvt.CanvasGraphicsNode; import org.apache.batik.gvt.CanvasGraphicsNode;
@@ -46,14 +45,11 @@ import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory; import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory;
import org.apache.batik.gvt.renderer.ImageRenderer; import org.apache.batik.gvt.renderer.ImageRenderer;
import org.apache.batik.gvt.renderer.ImageRendererFactory; import org.apache.batik.gvt.renderer.ImageRendererFactory;
import org.apache.batik.transcoder.SVGAbstractTranscoder; import org.apache.batik.transcoder.*;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder; import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.util.ParsedURL; import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.SVGConstants; import org.apache.batik.util.SVGConstants;
import org.apache.batik.xml.LexicalUnits;
import org.w3c.dom.DOMImplementation; import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGSVGElement; import org.w3c.dom.svg.SVGSVGElement;
@@ -63,8 +59,10 @@ import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderSpi;
import java.awt.*; import java.awt.*;
import java.awt.geom.*; import java.awt.geom.AffineTransform;
import java.awt.image.*; import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@@ -78,13 +76,12 @@ import java.util.Map;
* @author Harald Kuhr * @author Harald Kuhr
* @author Inpspired by code from the Batik Team * @author Inpspired by code from the Batik Team
* @version $Id: $ * @version $Id: $
* @see <a href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</a> * @see <A href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</A>
*/ */
public class SVGImageReader extends ImageReaderBase { public class SVGImageReader extends ImageReaderBase {
final static boolean DEFAULT_ALLOW_EXTERNAL_RESOURCES = final static boolean DEFAULT_ALLOW_EXTERNAL_RESOURCES =
"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowExternalResources", "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources"));
System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources")));
private Rasterizer rasterizer; private Rasterizer rasterizer;
private boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES; private boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES;
@@ -92,10 +89,10 @@ public class SVGImageReader extends ImageReaderBase {
/** /**
* Creates an {@code SVGImageReader}. * Creates an {@code SVGImageReader}.
* *
* @param provider the provider * @param pProvider the provider
*/ */
public SVGImageReader(final ImageReaderSpi provider) { public SVGImageReader(final ImageReaderSpi pProvider) {
super(provider); super(pProvider);
} }
protected void resetMembers() { protected void resetMembers() {
@@ -109,20 +106,20 @@ public class SVGImageReader extends ImageReaderBase {
} }
@Override @Override
public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { public void setInput(Object pInput, boolean seekForwardOnly, boolean ignoreMetadata) {
super.setInput(input, seekForwardOnly, ignoreMetadata); super.setInput(pInput, seekForwardOnly, ignoreMetadata);
if (imageInput != null) { if (imageInput != null) {
TranscoderInput transcoderInput = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput)); TranscoderInput input = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput));
rasterizer.setInput(transcoderInput); rasterizer.setInput(input);
} }
} }
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException { public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
checkBounds(imageIndex); checkBounds(pIndex);
if (param instanceof SVGReadParam) { if (pParam instanceof SVGReadParam) {
SVGReadParam svgParam = (SVGReadParam) param; SVGReadParam svgParam = (SVGReadParam) pParam;
// set the external-resource-resolution preference // set the external-resource-resolution preference
allowExternalResources = svgParam.isAllowExternalResources(); allowExternalResources = svgParam.isAllowExternalResources();
@@ -135,38 +132,43 @@ public class SVGImageReader extends ImageReaderBase {
// Set ImageReadParams as hints // Set ImageReadParams as hints
// Note: The cast to Map invokes a different method that preserves // Note: The cast to Map invokes a different method that preserves
// unset defaults, DO NOT REMOVE! // unset defaults, DO NOT REMOVE!
//noinspection rawtypes
rasterizer.setTranscodingHints((Map) paramsToHints(svgParam)); rasterizer.setTranscodingHints((Map) paramsToHints(svgParam));
} }
Dimension size = null; Dimension size = null;
if (param != null) { if (pParam != null) {
size = param.getSourceRenderSize(); size = pParam.getSourceRenderSize();
} }
if (size == null) { if (size == null) {
size = new Dimension(getWidth(imageIndex), getHeight(imageIndex)); size = new Dimension(getWidth(pIndex), getHeight(pIndex));
} }
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), size.width, size.height); BufferedImage destination = getDestination(pParam, getImageTypes(pIndex), size.width, size.height);
// Read in the image, using the Batik Transcoder // Read in the image, using the Batik Transcoder
processImageStarted(imageIndex);
BufferedImage image = rasterizer.getImage();
Graphics2D g = destination.createGraphics();
try { try {
g.setComposite(AlphaComposite.Src); processImageStarted(pIndex);
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g.drawImage(image, 0, 0, null); // TODO: Dest offset?
}
finally {
g.dispose();
}
processImageComplete(); BufferedImage image = rasterizer.getImage();
return destination; Graphics2D g = destination.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g.drawImage(image, 0, 0, null); // TODO: Dest offset?
}
finally {
g.dispose();
}
processImageComplete();
return destination;
}
catch (TranscoderException e) {
Throwable cause = unwrapException(e);
throw new IIOException(cause.getMessage(), cause);
}
} }
private static Throwable unwrapException(TranscoderException ex) { private static Throwable unwrapException(TranscoderException ex) {
@@ -174,18 +176,18 @@ public class SVGImageReader extends ImageReaderBase {
return ex.getException() != null ? ex.getException() : ex; return ex.getException() != null ? ex.getException() : ex;
} }
private TranscodingHints paramsToHints(SVGReadParam param) throws IOException { private TranscodingHints paramsToHints(SVGReadParam pParam) throws IOException {
TranscodingHints hints = new TranscodingHints(); TranscodingHints hints = new TranscodingHints();
// Note: We must allow generic ImageReadParams, so converting to // Note: We must allow generic ImageReadParams, so converting to
// TanscodingHints should be done outside the SVGReadParam class. // TanscodingHints should be done outside the SVGReadParam class.
// Set dimensions // Set dimensions
Dimension size = param.getSourceRenderSize(); Dimension size = pParam.getSourceRenderSize();
Rectangle viewBox = rasterizer.getViewBox(); Dimension origSize = new Dimension(getWidth(0), getHeight(0));
if (size == null) { if (size == null) {
// SVG is not a pixel based format, but we'll scale it, according to // SVG is not a pixel based format, but we'll scale it, according to
// the subsampling for compatibility // the subsampling for compatibility
size = getSourceRenderSizeFromSubsamping(param, viewBox.getSize()); size = getSourceRenderSizeFromSubsamping(pParam, origSize);
} }
if (size != null) { if (size != null) {
@@ -194,7 +196,7 @@ public class SVGImageReader extends ImageReaderBase {
} }
// Set area of interest // Set area of interest
Rectangle region = param.getSourceRegion(); Rectangle region = pParam.getSourceRegion();
if (region != null) { if (region != null) {
hints.put(ImageTranscoder.KEY_AOI, region); hints.put(ImageTranscoder.KEY_AOI, region);
@@ -205,8 +207,8 @@ public class SVGImageReader extends ImageReaderBase {
} }
else { else {
// Need to resize here... // Need to resize here...
double xScale = size.getWidth() / viewBox.getWidth(); double xScale = size.getWidth() / origSize.getWidth();
double yScale = size.getHeight() / viewBox.getHeight(); double yScale = size.getHeight() / origSize.getHeight();
hints.put(ImageTranscoder.KEY_WIDTH, (float) (region.getWidth() * xScale)); hints.put(ImageTranscoder.KEY_WIDTH, (float) (region.getWidth() * xScale));
hints.put(ImageTranscoder.KEY_HEIGHT, (float) (region.getHeight() * yScale)); hints.put(ImageTranscoder.KEY_HEIGHT, (float) (region.getHeight() * yScale));
@@ -214,11 +216,11 @@ public class SVGImageReader extends ImageReaderBase {
} }
else if (size != null) { else if (size != null) {
// Allow non-uniform scaling // Allow non-uniform scaling
hints.put(ImageTranscoder.KEY_AOI, viewBox); hints.put(ImageTranscoder.KEY_AOI, new Rectangle(origSize));
} }
// Background color // Background color
Paint bg = param.getBackgroundColor(); Paint bg = pParam.getBackgroundColor();
if (bg != null) { if (bg != null) {
hints.put(ImageTranscoder.KEY_BACKGROUND_COLOR, bg); hints.put(ImageTranscoder.KEY_BACKGROUND_COLOR, bg);
} }
@@ -226,10 +228,10 @@ public class SVGImageReader extends ImageReaderBase {
return hints; return hints;
} }
private Dimension getSourceRenderSizeFromSubsamping(ImageReadParam param, Dimension origSize) { private Dimension getSourceRenderSizeFromSubsamping(ImageReadParam pParam, Dimension pOrigSize) {
if (param.getSourceXSubsampling() > 1 || param.getSourceYSubsampling() > 1) { if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) {
return new Dimension((int) (origSize.width / (float) param.getSourceXSubsampling()), return new Dimension((int) (pOrigSize.width / (float) pParam.getSourceXSubsampling()),
(int) (origSize.height / (float) param.getSourceYSubsampling())); (int) (pOrigSize.height / (float) pParam.getSourceYSubsampling()));
} }
return null; return null;
} }
@@ -238,19 +240,28 @@ public class SVGImageReader extends ImageReaderBase {
return new SVGReadParam(); return new SVGReadParam();
} }
public int getWidth(int imageIndex) throws IOException { public int getWidth(int pIndex) throws IOException {
checkBounds(imageIndex); checkBounds(pIndex);
try {
return rasterizer.getDefaultWidth(); return rasterizer.getDefaultWidth();
}
catch (TranscoderException e) {
throw new IIOException(e.getMessage(), e);
}
} }
public int getHeight(int imageIndex) throws IOException { public int getHeight(int pIndex) throws IOException {
checkBounds(imageIndex); checkBounds(pIndex);
return rasterizer.getDefaultHeight(); try {
return rasterizer.getDefaultHeight();
}
catch (TranscoderException e) {
throw new IIOException(e.getMessage(), e);
}
} }
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) { public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
return Collections.singleton(ImageTypeSpecifiers.createFromRenderedImage(rasterizer.createImage(1, 1))).iterator(); return Collections.singleton(ImageTypeSpecifier.createFromRenderedImage(rasterizer.createImage(1, 1))).iterator();
} }
/** /**
@@ -260,11 +271,12 @@ public class SVGImageReader extends ImageReaderBase {
* and needs major refactoring! * and needs major refactoring!
* </p> * </p>
*/ */
private class Rasterizer extends SVGAbstractTranscoder { private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ {
private BufferedImage image; private BufferedImage image;
private TranscoderInput transcoderInput; private TranscoderInput transcoderInput;
private final Rectangle2D viewBox = new Rectangle2D.Float(); private float defaultWidth;
private final Dimension defaultSize = new Dimension(); private float defaultHeight;
private boolean initialized = false; private boolean initialized = false;
private SVGOMDocument document; private SVGOMDocument document;
private String uri; private String uri;
@@ -277,7 +289,7 @@ public class SVGImageReader extends ImageReaderBase {
} }
// This is cheating... We don't fully transcode after all // This is cheating... We don't fully transcode after all
protected void transcode(Document document, final String uri, final TranscoderOutput output) { protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException {
// Sets up root, curTxf & curAoi // Sets up root, curTxf & curAoi
// ---- // ----
if (document != null) { if (document != null) {
@@ -325,66 +337,54 @@ public class SVGImageReader extends ImageReaderBase {
// ---- // ----
SVGSVGElement rootElement = svgDoc.getRootElement(); SVGSVGElement rootElement = svgDoc.getRootElement();
// Get the viewBox // get the 'width' and 'height' attributes of the SVG document
String viewBoxStr = rootElement.getAttributeNS(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE); UnitProcessor.Context uctx
if (viewBoxStr.length() != 0) { = UnitProcessor.createContext(ctx, rootElement);
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
viewBox.setFrame(rect[0], rect[1], rect[2], rect[3]);
}
// Get the 'width' and 'height' attributes of the SVG document
double width = 0;
double height = 0;
UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, rootElement);
String widthStr = rootElement.getAttributeNS(null, SVGConstants.SVG_WIDTH_ATTRIBUTE); String widthStr = rootElement.getAttributeNS(null, SVGConstants.SVG_WIDTH_ATTRIBUTE);
String heightStr = rootElement.getAttributeNS(null, SVGConstants.SVG_HEIGHT_ATTRIBUTE); String heightStr = rootElement.getAttributeNS(null, SVGConstants.SVG_HEIGHT_ATTRIBUTE);
if (!StringUtil.isEmpty(widthStr)) { if (!StringUtil.isEmpty(widthStr)) {
width = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx); defaultWidth = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx);
} }
if (!StringUtil.isEmpty(heightStr)) { if(!StringUtil.isEmpty(heightStr)){
height = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx); defaultHeight = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
} }
boolean hasWidth = width > 0.0; boolean hasWidth = defaultWidth > 0.0;
boolean hasHeight = height > 0.0; boolean hasHeight = defaultHeight > 0.0;
if (!hasWidth || !hasHeight) { if (!hasWidth || !hasHeight) {
if (!viewBox.isEmpty()) { String viewBoxStr = rootElement.getAttributeNS
// If one dimension is given, calculate other by aspect ratio in viewBox (null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
if (viewBoxStr.length() != 0) {
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
// if one dimension is given, calculate other by aspect ratio in viewBox
// or use viewBox if no dimension is given
if (hasWidth) { if (hasWidth) {
height = width * viewBox.getHeight() / viewBox.getWidth(); defaultHeight = defaultWidth * rect[3] / rect[2];
} }
else if (hasHeight) { else if (hasHeight) {
width = height * viewBox.getWidth() / viewBox.getHeight(); defaultWidth = defaultHeight * rect[2] / rect[3];
} }
else { else {
// ...or use viewBox if no dimension is given defaultWidth = rect[2];
width = viewBox.getWidth(); defaultHeight = rect[3];
height = viewBox.getHeight();
} }
} }
else { else {
// No viewBox, just assume square size
if (hasHeight) { if (hasHeight) {
width = height; defaultWidth = defaultHeight;
} }
else if (hasWidth) { else if (hasWidth) {
height = width; defaultHeight = defaultWidth;
} }
else { else {
// ...or finally fall back to Batik default sizes // fallback to batik default sizes
width = 400; defaultWidth = 400;
height = 400; defaultHeight = 400;
} }
} }
} }
// We now have a size, in the rare case we don't have a viewBox; set it to this size
defaultSize.setSize(width, height);
if (viewBox.isEmpty()) {
viewBox.setRect(0, 0, width, height);
}
// Hack to work around exception above // Hack to work around exception above
if (root != null) { if (root != null) {
gvtRoot = root; gvtRoot = root;
@@ -397,7 +397,7 @@ public class SVGImageReader extends ImageReaderBase {
ctx = null; ctx = null;
} }
private BufferedImage readImage() throws IOException { private BufferedImage readImage() throws TranscoderException {
init(); init();
if (abortRequested()) { if (abortRequested()) {
@@ -422,8 +422,7 @@ public class SVGImageReader extends ImageReaderBase {
} }
if (gvtRoot == null) { if (gvtRoot == null) {
Throwable cause = unwrapException(exception); throw exception;
throw new IIOException(cause.getMessage(), cause);
} }
} }
ctx = context; ctx = context;
@@ -441,7 +440,7 @@ public class SVGImageReader extends ImageReaderBase {
// ---- // ----
setImageSize(defaultSize.width, defaultSize.height); setImageSize(defaultWidth, defaultHeight);
if (abortRequested()) { if (abortRequested()) {
processReadAborted(); processReadAborted();
@@ -455,17 +454,18 @@ public class SVGImageReader extends ImageReaderBase {
try { try {
Px = ViewBox.getViewTransform(ref, root, width, height, null); Px = ViewBox.getViewTransform(ref, root, width, height, null);
} }
catch (BridgeException ex) { catch (BridgeException ex) {
throw new IIOException(ex.getMessage(), ex); throw new TranscoderException(ex);
} }
if (Px.isIdentity() && (width != defaultSize.width || height != defaultSize.height)) { if (Px.isIdentity() && (width != defaultWidth || height != defaultHeight)) {
// The document has no viewBox, we need to resize it by hand. // The document has no viewBox, we need to resize it by hand.
// we want to keep the document size ratio // we want to keep the document size ratio
float xscale, yscale; float xscale, yscale;
xscale = width / defaultSize.width; xscale = width / defaultWidth;
yscale = height / defaultSize.height; yscale = height / defaultHeight;
float scale = Math.min(xscale, yscale); float scale = Math.min(xscale, yscale);
Px = AffineTransform.getScaleInstance(scale, scale); Px = AffineTransform.getScaleInstance(scale, scale);
} }
@@ -515,7 +515,7 @@ public class SVGImageReader extends ImageReaderBase {
} }
} }
catch (BridgeException ex) { catch (BridgeException ex) {
throw new IIOException(ex.getMessage(), ex); throw new TranscoderException(ex);
} }
this.root = gvtRoot; this.root = gvtRoot;
@@ -584,7 +584,9 @@ public class SVGImageReader extends ImageReaderBase {
return dest; return dest;
} }
catch (Exception ex) { catch (Exception ex) {
throw new IIOException(ex.getMessage(), ex); TranscoderException exception = new TranscoderException(ex.getMessage());
exception.initCause(ex);
throw exception;
} }
finally { finally {
if (context != null) { if (context != null) {
@@ -593,7 +595,7 @@ public class SVGImageReader extends ImageReaderBase {
} }
} }
private synchronized void init() throws IIOException { private synchronized void init() throws TranscoderException {
if (!initialized) { if (!initialized) {
if (transcoderInput == null) { if (transcoderInput == null) {
throw new IllegalStateException("input == null"); throw new IllegalStateException("input == null");
@@ -601,18 +603,11 @@ public class SVGImageReader extends ImageReaderBase {
initialized = true; initialized = true;
try { super.transcode(transcoderInput, null);
super.addTranscodingHint(SVGAbstractTranscoder.KEY_ALLOW_EXTERNAL_RESOURCES, allowExternalResources);
super.transcode(transcoderInput, null);
}
catch (TranscoderException e) {
Throwable cause = unwrapException(e);
throw new IIOException(cause.getMessage(), cause);
}
} }
} }
private BufferedImage getImage() throws IOException { private BufferedImage getImage() throws TranscoderException {
if (image == null) { if (image == null) {
image = readImage(); image = readImage();
} }
@@ -620,23 +615,18 @@ public class SVGImageReader extends ImageReaderBase {
return image; return image;
} }
int getDefaultWidth() throws IOException { int getDefaultWidth() throws TranscoderException {
init(); init();
return defaultSize.width; return (int) Math.ceil(defaultWidth);
} }
int getDefaultHeight() throws IOException { int getDefaultHeight() throws TranscoderException {
init(); init();
return defaultSize.height; return (int) Math.ceil(defaultHeight);
} }
Rectangle getViewBox() throws IOException { void setInput(final TranscoderInput pInput) {
init(); transcoderInput = pInput;
return viewBox.getBounds();
}
void setInput(final TranscoderInput input) {
transcoderInput = input;
} }
@Override @Override
@@ -665,7 +655,7 @@ public class SVGImageReader extends ImageReaderBase {
if (allowExternalResources) { if (allowExternalResources) {
return super.getExternalResourceSecurity(resourceURL, docURL); return super.getExternalResourceSecurity(resourceURL, docURL);
} }
return new EmbededExternalResourceSecurity(resourceURL); return new NoLoadExternalResourceSecurity();
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More