mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-23 00:00:05 -04:00
Compare commits
1064 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1dc6045079 | |||
| b9b3c364be | |||
| 26ecf18c68 | |||
| 3f356a8197 | |||
| dc59c66209 | |||
| 2a0b15f33f | |||
| 9ca9569537 | |||
| 0a717ea0af | |||
| 7f2eb517ef | |||
| a7a4445ce8 | |||
| 4c1b268325 | |||
| e3c3f640a4 | |||
| ecc938a666 | |||
| 25fdfb1947 | |||
| 0c05918d8a | |||
| 4de2a38dd8 | |||
| 5b4fa64dc0 | |||
| 2ad522a9fa | |||
| 20af575fbd | |||
| 02a6ed1ac1 | |||
| 146d5926bb | |||
| 561b25022c | |||
| 4cd6b893de | |||
| dabff3abce | |||
| 1b889b1b4b | |||
| 29a3bd591d | |||
| cd79ef4409 | |||
| a604cb83fb | |||
| eb1735ab33 | |||
| 357eeb2236 | |||
| d634b9d93f | |||
| 7e3241d64b | |||
| b86d82720d | |||
| 47e90a657a | |||
| bdd8b2f1fb | |||
| 8d08c9565b | |||
| c11f61d132 | |||
| e6b38cb547 | |||
| e11d888809 | |||
| 46a399ff02 | |||
| a09629be32 | |||
| 57fb1ca1bb | |||
| c9063ca02d | |||
| 340e79eb8a | |||
| 355a916225 | |||
| 8da45b5f05 | |||
| 6c8b0cdc2f | |||
| 8af219e669 | |||
| 7e10b9242d | |||
| 36103046a5 | |||
| 95787449ea | |||
| 3223743f2c | |||
| 234c7f59d4 | |||
| 1cb0da2967 | |||
| d1525828fa | |||
| 194fd5a446 | |||
| 21e5a465b9 | |||
| 6309bc5765 | |||
| 5011e98184 | |||
| a9aa5cc66e | |||
| ac107196eb | |||
| 489a419df1 | |||
| 51891b45ea | |||
| 90e60f509d | |||
| 53e5eff0c9 | |||
| 8670e1fd8a | |||
| 6395fd33e9 | |||
| 0834a2b1be | |||
| 6c75661ab4 | |||
| 9c25c962cc | |||
| 1328dbf763 | |||
| 8bccdef371 | |||
| 2d6f7d89fa | |||
| 1afa35ccd6 | |||
| 5b184a2a8a | |||
| 39bd218085 | |||
| 87af2f6212 | |||
| 598ea1e486 | |||
| 93180e7a2d | |||
| 2cc6d1d28e | |||
| 8ea9d6f318 | |||
| 029fa843f6 | |||
| 2cb4c0a661 | |||
| bf40c81123 | |||
| 78a90fab95 | |||
| b03f7a5582 | |||
| 96470c3897 | |||
| 365b19998f | |||
| b1846e1aed | |||
| ec63fbbd85 | |||
| fbea929bc5 | |||
| 18372944f1 | |||
| 488608b312 | |||
| b3ae8a8ba6 | |||
| fc3455d2c6 | |||
| f653279c62 | |||
| 0eff483d72 | |||
| 9c044b132b | |||
| 1ec4088e49 | |||
| 5ac415d019 | |||
| 133eafc8c2 | |||
| 85346a82e1 | |||
| e718e58b65 | |||
| 13a64ed3bb | |||
| 3c6df6ad1b | |||
| 9abe154ed3 | |||
| 0e752a2d2c | |||
| 867a46d281 | |||
| 9972f966f7 | |||
| f066a48d55 | |||
| 5af87372aa | |||
| defadbbbcd | |||
| 66363f8d09 | |||
| 1bef35daba | |||
| 0be5efc534 | |||
| 35da57dfcf | |||
| 5d09bb664e | |||
| 5590072cc7 | |||
| 31117c7cea | |||
| ae0899fe76 | |||
| df51ef340d | |||
| 71d8295042 | |||
| a8f77a9a45 | |||
| eb3176b447 | |||
| 20e308dd8f | |||
| 9fa9bc5fe0 | |||
| 4da3e2e441 | |||
| 4516d84e54 | |||
| a815e5f180 | |||
| 35658a888f | |||
| 6cd6b6113d | |||
| f8b919ee58 | |||
| 8989ba07d8 | |||
| bc38ec1034 | |||
| e37c1fbb50 | |||
| 75b6de7102 | |||
| 22a16db463 | |||
| 3e93753073 | |||
| ad3cbbd86d | |||
| 076e417010 | |||
| 0b84a99954 | |||
| e2f4241a99 | |||
| 6eedaa129d | |||
| f5dbc0c2f4 | |||
| 7d79185fb2 | |||
| c885ed6dbb | |||
| e116c98db2 | |||
| be9493af20 | |||
| 5ffe3087ad | |||
| ffc7dad821 | |||
| 2a4ff2aa12 | |||
| 1e9669d6c3 | |||
| caca51d5ce | |||
| 015728a57d | |||
| 4272c6c8ed | |||
| b5582e32f6 | |||
| 76045ab0d9 | |||
| 4fc8f28cc6 | |||
| 7198a92a2e | |||
| e2fbd80c36 | |||
| 6b28cbe4a1 | |||
| 76198b0655 | |||
| f81b14df71 | |||
| da3f79f0c9 | |||
| d39418eca2 | |||
| cbbc2bc6fe | |||
| b0b423de4f | |||
| 1c4ec8d20e | |||
| ec99a8ce1f | |||
| c49a887cd3 | |||
| 14af75d191 | |||
| 0ce205638e | |||
| fe4119ca1c | |||
| c927f4c1ec | |||
| 161368084a | |||
| d958aba69e | |||
| 931429724c | |||
| dcd21db2ef | |||
| 7a3022ebf2 | |||
| 274886b1d9 | |||
| 0cf784ee60 | |||
| a797f1ea26 | |||
| baff849b6c | |||
| 408977d896 | |||
| d78e6373a8 | |||
| 9610256ddb | |||
| 34c9b9b4b5 | |||
| 9d5823d982 | |||
| 10e824df36 | |||
| 3bc90bfad1 | |||
| 543acce8a3 | |||
| a67fdd4b80 | |||
| 9c936d3f11 | |||
| fb2bff1404 | |||
| 3c605f8796 | |||
| 7817af91a3 | |||
| 1ed1076714 | |||
| a90d375422 | |||
| 815007ab2b | |||
| b3c8f53512 | |||
| d2977a18ac | |||
| 7e5e905d18 | |||
| 0fcba279f1 | |||
| d7d9552216 | |||
| e48a78aa77 | |||
| bf351c01a6 | |||
| 9bd4389142 | |||
| 984383bd56 | |||
| 77247772d0 | |||
| ae482bc7e5 | |||
| 119700487b | |||
| 5e5d530498 | |||
| 3dfc0850cc | |||
| 8f285327fc | |||
| 734b90863a | |||
| 18f31f2dd4 | |||
| a7294af89c | |||
| ea63a5bdc1 | |||
| 2cc50bbdad | |||
| f4a5f57d52 | |||
| 7fc47a338c | |||
| f8311164af | |||
| 84c10ed96d | |||
| 9c8ae4ac3e | |||
| aab7b6f7f5 | |||
| 3c01071452 | |||
| 4fc9c3513b | |||
| 59b08bfc79 | |||
| 743f1f9356 | |||
| d90b1c984c | |||
| 1160be4995 | |||
| 2978c901ae | |||
| e4b465a0d4 | |||
| 297a27c5be | |||
| 9d9593dba2 | |||
| 66683a2ead | |||
| 4be4f4e2b6 | |||
| 7f59d41b2a | |||
| a060c105e1 | |||
| f3318ee818 | |||
| afa306a787 | |||
| ed032b6c7a | |||
| 894e5dc30a | |||
| 8e02fb36e9 | |||
| 88a2e226ca | |||
| 203ad0258f | |||
| 2a1ecf80fa | |||
| 4239ce5e31 | |||
| 61ca2fc3e0 | |||
| 9e502899ac | |||
| 16fd8d1453 | |||
| 81ae64b65c | |||
| b0d5480445 | |||
| 9e989680c6 | |||
| dd174c7f7e | |||
| 72ef50b677 | |||
| a7349278b2 | |||
| 858f519f0b | |||
| d797ff0bb3 | |||
| fef4c4dd7a | |||
| a99762b5de | |||
| c7d0c902db | |||
| e8e2026d48 | |||
| f1dc50c276 | |||
| 0e615192e7 | |||
| d9b82f80ef | |||
| 41f316e621 | |||
| 5f2b0c0ccf | |||
| ee7d4ba724 | |||
| f7d4557c57 | |||
| 20d8c34dae | |||
| eaee82f3e7 | |||
| a0d87ec6d8 | |||
| ac5d4a02a9 | |||
| a26932cd70 | |||
| 7b8e5433dd | |||
| 1471af3e68 | |||
| 4f0dd84cf6 | |||
| 143e25c39c | |||
| 3d0ba82422 | |||
| 34f626914a | |||
| d6587ad292 | |||
| 46eaa72a13 | |||
| b3d48bdc50 | |||
| 981d2da1e6 | |||
| cf40b5f4a0 | |||
| 34128c66a4 | |||
| 1aa02477a7 | |||
| fa49dd93a7 | |||
| 83cff35076 | |||
| 93e3ae5ef8 | |||
| d8857c2997 | |||
| 7b119ce7dc | |||
| 3e7ad05973 | |||
| 1e574ca429 | |||
| dce193b113 | |||
| 05517e1fed | |||
| d707793211 | |||
| 39cfeedc6b | |||
| 1dcbbc39e3 | |||
| c933dbf69f | |||
| 5fc7dc1963 | |||
| f42d5e0a32 | |||
| d1c3a9bacb | |||
| 21ad54b820 | |||
| e11fc63730 | |||
| 2a70e744e0 | |||
| 60550ba8a8 | |||
| d3b16cdd35 | |||
| add7b06fa4 | |||
| b99391854d | |||
| a22188bd72 | |||
| d5f5f295f5 | |||
| d95de31908 | |||
| 4fda5af141 | |||
| b54827ae1b | |||
| 2762b9705a | |||
| 2d6248e293 | |||
| 8a43e4539c | |||
| 8ecf203263 | |||
| 8e50af2804 | |||
| 58e31aebd1 | |||
| b8e3e7825c | |||
| 155e9850fb | |||
| 0c4adf36ba | |||
| fd32bc9089 | |||
| 6ad010dc44 | |||
| c9b14ea4af | |||
| f162c873ed | |||
| c21b971655 | |||
| d1b0247055 | |||
| 6d3605c080 | |||
| a188dcf9fd | |||
| 08938c6b4d | |||
| 016ef952ce | |||
| 2844c396c8 | |||
| 2f95716b8a | |||
| cf7fce647d | |||
| 4014ae4644 | |||
| 4e46e81e3f | |||
| ec43410fa6 | |||
| 721fc5b5a5 | |||
| 0c71981158 | |||
| ac0ed5ed8f | |||
| d1b11ee1d1 | |||
| 210eeccb39 | |||
| e07e7c3685 | |||
| 611cb6cb4d | |||
| 54d9feef94 | |||
| 54b4ded602 | |||
| e360ed2a6a | |||
| 2325cef5a3 | |||
| 465eb09dd3 | |||
| feae2a0f79 | |||
| 860cb32a58 | |||
| b9bd19b950 | |||
| 3742e6ef08 | |||
| 52aa582ccd | |||
| adda52053b | |||
| 6501321fac | |||
| 57d9bfda59 | |||
| 1226375ba6 | |||
| 9c2d3058fd | |||
| e89cadd95f | |||
| 0ec299c2e6 | |||
| b91d02a562 | |||
| 2b5f897841 | |||
| e65078bdb7 | |||
| 36d8555172 | |||
| 80e3170820 | |||
| 90bc04b030 | |||
| dcba9b0667 | |||
| dc755c4c3c | |||
| 6e4f1e0739 | |||
| 7117cd1fd6 | |||
| 2b954c1067 | |||
| 77a1745106 | |||
| 0378f504e7 | |||
| 1d3a7fe812 | |||
| 8715b6b696 | |||
| a95235b422 | |||
| 9f4b09fc7d | |||
| 03455f0132 | |||
| ac5779d8d6 | |||
| fef8ff3aab | |||
| a9e4b2e262 | |||
| 7147e2dfb1 | |||
| 1680fadf83 | |||
| d1df8c13ed | |||
| 031937fe99 | |||
| 24c473ae45 | |||
| 57941cb638 | |||
| d325b6deec | |||
| 3f67430b2d | |||
| 314071dde8 | |||
| 83ad25c2dd | |||
| d7dae90e2e | |||
| ad437c2470 | |||
| dabc26bdb5 | |||
| b441298a9a | |||
| 345ca0ac13 | |||
| 9720a931c5 | |||
| 4d3f691e6a | |||
| 2e0e575183 | |||
| 2c7c47b158 | |||
| 57680f1bec | |||
| 8dc83c4e9e | |||
| 929bea6b6b | |||
| 699662d054 | |||
| 1bebee1851 | |||
| b0e8dd86bb | |||
| 5bdb0b5502 | |||
| 85d47528a3 | |||
| a7ebd66149 | |||
| f0a032a7b9 | |||
| 690cb064e7 | |||
| e65f471a8f | |||
| f2cc9faaf8 | |||
| 8bd3f4f34a | |||
| be348543d8 | |||
| 80229b8c3c | |||
| a5ee53569d | |||
| c22ada03cd | |||
| b61341ce3b | |||
| e73b0a7bcf | |||
| ea9d1c0dc5 | |||
| e0563ee7dd | |||
| 44bcd202e8 | |||
| 5b57b51ff1 | |||
| 0b5f8a071e | |||
| d3dc578936 | |||
| 2ae937b870 | |||
| 2a7763299a | |||
| a57a1d35f9 | |||
| e618805786 | |||
| e3cb923d37 | |||
| 4513b0c166 | |||
| b55d4e8c1b | |||
| 399c75f59e | |||
| a7f7d73e2d | |||
| 41a83191b7 | |||
| 49e805eeb7 | |||
| f426637fdd | |||
| 4081ff1545 | |||
| ae4fbcc726 | |||
| 66208bfa03 | |||
| e72255805e | |||
| 780ccd7916 | |||
| 957e917281 | |||
| 9daf8337ee | |||
| 89c104d59f | |||
| 8608ee76c5 | |||
| aab2d36e92 | |||
| 608b37232d | |||
| e3ebf8e0fa | |||
| 9af10625a2 | |||
| ccc4e7b411 | |||
| 439323c5e5 | |||
| 332e191d0d | |||
| 58d1a89028 | |||
| 72fe799cc8 | |||
| 18af213e27 | |||
| 9d267f352f | |||
| f8f6f9f1e4 | |||
| 2841d26785 | |||
| 98ae5967ca | |||
| da9d87a561 | |||
| d472191926 | |||
| a8472170c4 | |||
| b2f7cada21 | |||
| 2a4c152c3d | |||
| 660932b7e7 | |||
| d0a17ff3b3 | |||
| 4259903bdd | |||
| 3018d2a342 | |||
| 8f94318f28 | |||
| 60b7151eb6 | |||
| 8ad21307e8 | |||
| 8c854f1e20 | |||
| 1445ff2533 | |||
| e8f1e80d4e | |||
| 18e29e9ed5 | |||
| ec4003b1c1 | |||
| 164b8db988 | |||
| 21feace385 | |||
| ba1f754611 | |||
| 822b5da631 | |||
| c785f6932f | |||
| 43e2a27c7f | |||
| 9d50acd2fe | |||
| ba73a308d6 | |||
| 20cd259abd | |||
| 033a1423ff | |||
| 7c8c520006 | |||
| 9162458e36 | |||
| d38af3cb16 | |||
| c57e8f80fa | |||
| 4750359ada | |||
| cbe8038619 | |||
| 2d8125e69c | |||
| 57a664c093 | |||
| 7f82377fd7 | |||
| 72b9f19a51 | |||
| 0083b8e77e | |||
| 79982cd493 | |||
| f0db338f3b | |||
| 9db4e0b3ed | |||
| cd4cbdcb82 | |||
| 15dc4b3852 | |||
| a3534ecd59 | |||
| 7bb5fee23b | |||
| 6cb7424bd0 | |||
| 9aa04d311e | |||
| 967e71dc92 | |||
| 628523ddc8 | |||
| 783c28ae0e | |||
| d35d67651f | |||
| 816d48efef | |||
| 25ee21d090 | |||
| 536ea7ba88 | |||
| d2687383f5 | |||
| 3ce35e059c | |||
| 1d5359dd35 | |||
| 2699b75b79 | |||
| 4e614dfc7e | |||
| 3a2efd9491 | |||
| c5dc2e4e53 | |||
| 41460bd32a | |||
| 13b37b3839 | |||
| 6dd74070f4 | |||
| 81b358b377 | |||
| 9715f4e74c | |||
| 38256c8be0 | |||
| bf3c1fad17 | |||
| b8488ae39b | |||
| d3bea0ae38 | |||
| 9435410b1e | |||
| f74e8c8ba1 | |||
| 0ae2c2f01d | |||
| 77c81a06bc | |||
| 2bbcd88798 | |||
| 829fbe7547 | |||
| 078425eed9 | |||
| 8ddcbbd2b2 | |||
| 507cca5fd7 | |||
| a4caac0c82 | |||
| 54c07b849c | |||
| c531d4f5d3 | |||
| aa2e8e5d7e | |||
| 76a35331b0 | |||
| 6b3f1c6ee3 | |||
| c731e10e8f | |||
| 4a8c3530f7 | |||
| e8996daa12 | |||
| 9196e60c74 | |||
| eabb8fd02b | |||
| 1794e336de | |||
| ac7612b3df | |||
| 606fd53823 | |||
| 614a07e040 | |||
| 34e8d88007 | |||
| 9b727df901 | |||
| f1f98bb4a4 | |||
| b34b26e08c | |||
| 993e07ee34 | |||
| a377712bdb | |||
| e5dc6aa878 | |||
| 46b1c1cf96 | |||
| d9300b1c90 | |||
| 0a2efb9eac | |||
| 3eabc591d8 | |||
| 5cefce2dbf | |||
| 4c645c0220 | |||
| 703848ca45 | |||
| 0ebd18fcb6 | |||
| 29f7547a99 | |||
| 25cd351eee | |||
| 77c98c917e | |||
| 4bbe946f46 | |||
| 78832ed923 | |||
| 164cc11592 | |||
| 8f5c1b409f | |||
| 26981513d8 | |||
| a3a30d54d4 | |||
| 102e9cff51 | |||
| da800be8c8 | |||
| 70493bd323 | |||
| 304d050bc3 | |||
| 0443172666 | |||
| cee2663f06 | |||
| 8f44cfc43c | |||
| 8a240aac68 | |||
| 61424f33b6 | |||
| c7b9b1fadd | |||
| ab08ec1e0d | |||
| cbe78dc67f | |||
| c9e11f171f | |||
| bc2c0c2301 | |||
| 6581e2e2a1 | |||
| c2873b1f27 | |||
| 35f2f0be9f | |||
| b5856fd110 | |||
| 1919d77a45 | |||
| 37afe24aac | |||
| 081f2efea2 | |||
| 627bb1bf1f | |||
| a77b62b6ba | |||
| 2500d8cc15 | |||
| c01336fb8a | |||
| 6f9b9bee01 | |||
| b9b1a35408 | |||
| 7ed5663633 | |||
| 6458fcdcbd | |||
| 9375bfda9a | |||
| 0160fb70f8 | |||
| 29dca0f124 | |||
| c21f14efe1 | |||
| 81ba43e1e8 | |||
| a1fcfc3958 | |||
| c60116a611 | |||
| 15e6ddc1fd | |||
| 49f4e5401e | |||
| e333c7d1b2 | |||
| cda34b704b | |||
| 7c4487be04 | |||
| 5a4525aaa1 | |||
| b766420e3e | |||
| c858454c5a | |||
| 67b48ce1e3 | |||
| 6608f61353 | |||
| 326b98d5e5 | |||
| fafa58b718 | |||
| 0ed0246762 | |||
| b3004a1227 | |||
| 7ab627a754 | |||
| 008e57a7ce | |||
| 28270b4d5b | |||
| 7382151db8 | |||
| b856ce07af | |||
| 190fe87ee9 | |||
| d1872ce94f | |||
| a5c52a99b4 | |||
| 4170b393fa | |||
| 53f9ba91e0 | |||
| be2d7d5f10 | |||
| 00aec2c90e | |||
| 2b04f7205c | |||
| 7d401d0194 | |||
| 48691139a3 | |||
| bcb87c09d2 | |||
| 84a8ceeb93 | |||
| 0cb99feedf | |||
| 91493c5145 | |||
| 6ddb799a95 | |||
| 8a187f6657 | |||
| b7d865f2cf | |||
| d50fb1a51e | |||
| 8992406f50 | |||
| 44eebff62f | |||
| 8c85c4ca96 | |||
| fa5c77bff0 | |||
| d87b80deea | |||
| ae138c3b4e | |||
| ab13fdd09d | |||
| aab5b062bd | |||
| 00d6acd1bf | |||
| 0f8a7ea482 | |||
| 9fe87fe10d | |||
| a33dbaf897 | |||
| 9e2f369459 | |||
| d34b0b7fcf | |||
| 5effcb1344 | |||
| b67d687761 | |||
| d0881c8b5c | |||
| 976928f48c | |||
| e1c2f2ee73 | |||
| 92632fa2a3 | |||
| 5a563e315f | |||
| c06d47d123 | |||
| fea6beb364 | |||
| 4b951c06cc | |||
| a3e6e52c95 | |||
| 5347015cbd | |||
| 4d190892df | |||
| 60eab81709 | |||
| b400b6b157 | |||
| 499b3ef120 | |||
| 92bc9c73f6 | |||
| 2a77558cac | |||
| 816cad60a8 | |||
| 7167f81c69 | |||
| f5cfa0e619 | |||
| 73ad024833 | |||
| 379449b621 | |||
| e17faad6fb | |||
| 1271a3d55e | |||
| 1cd594d113 | |||
| b76f74e79a | |||
| 78817a489b | |||
| b8f2a80ca6 | |||
| ac8a36db1c | |||
| 7e0d8922da | |||
| 9a6b8c9bfe | |||
| eced5b8efd | |||
| 74611e4e52 | |||
| b8614eca4d | |||
| efd24456ac | |||
| 191b2371c8 | |||
| 33419ef291 | |||
| 123f0bb7fc | |||
| 99b5f28a49 | |||
| b30fb4f8c3 | |||
| dc0bdcbd5b | |||
| 0cf29c167d | |||
| 98e4b76206 | |||
| aa4b5db054 | |||
| 433311c10d | |||
| f50178bc78 | |||
| e016e970e5 | |||
| 4223d13898 | |||
| 444aeabf21 | |||
| 05507a59d6 | |||
| c4c89a0a25 | |||
| b0ad6b2a4b | |||
| 25c703f4b2 | |||
| 529c59f93f | |||
| 584b1d9b21 | |||
| 312ce364cc | |||
| 7de8231471 | |||
| 0de9f79029 | |||
| eeb56acdde | |||
| a6862cfec8 | |||
| f8284700b4 | |||
| 38caeb22e0 | |||
| b2c5915db8 | |||
| 3911191b04 | |||
| bc328419ac | |||
| da4efe98bf | |||
| 6653f4a85d | |||
| 511a29beb9 | |||
| 5617b4323c | |||
| 16d0af357d | |||
| 74927d5396 | |||
| 7e809dd834 | |||
| 7aa95a08bc | |||
| c28963ae49 | |||
| 0327f5fc1a | |||
| 1c59057c30 | |||
| 3e1f85c4dc | |||
| 11227a68a0 | |||
| 62ba73a30e | |||
| 1f33afb5a1 | |||
| 9d3f271867 | |||
| 812e12acb0 | |||
| 060b6cf852 | |||
| e68ce7ffd1 | |||
| 778cdef69c | |||
| d46a76fca8 | |||
| 105a1ee466 | |||
| aa030f526c | |||
| 976e5d6210 | |||
| 6daca00fcd | |||
| ef05872934 | |||
| 1ddab866fd | |||
| ce997a6951 | |||
| 23bf5cb7b2 | |||
| 564778f415 | |||
| e28bf8fb44 | |||
| cf8d630d01 | |||
| 0ff7224912 | |||
| 196081a317 | |||
| ff50180d86 | |||
| 8f2c482167 | |||
| eab24890ca | |||
| cd42d81817 | |||
| ba5c667b6c | |||
| 4e9fa9442c | |||
| 4d2326c18d | |||
| 94eac2d6e5 | |||
| f63a33d541 | |||
| 00f8d87f36 | |||
| 4c2ab6da7b | |||
| b5088312e2 | |||
| f04f968f12 | |||
| 8896092e31 | |||
| 2f9768a1d4 | |||
| 06bcf22242 | |||
| 20c7f8e60e | |||
| 15a9ad0a9b | |||
| 7ae2d636dc | |||
| 12e756b23c | |||
| 4e2bf131d2 | |||
| d0c4a07556 | |||
| 21059c8d5a | |||
| fa7b530809 | |||
| 790cf3b32e | |||
| b1baaad23b | |||
| 7fa704ace5 | |||
| 8d07f4fe90 | |||
| 32bba6857b | |||
| ab7b08dfa9 | |||
| e0d6fa0d84 | |||
| 51bdd370da | |||
| ee2be3f88f | |||
| c5511833cc | |||
| 6ac8a5d8b4 | |||
| 3f7cb24407 | |||
| 8bf9f7a8f0 | |||
| 03ab9558a0 | |||
| 715bde8358 | |||
| 0151efb5f6 | |||
| bd796429c5 | |||
| b4ef5823f3 | |||
| 9adf0f4da3 | |||
| 01a4e55185 | |||
| 2e2ab11091 | |||
| 419ffc9373 | |||
| b67975eef7 | |||
| b32a861b2d | |||
| 6930168c93 | |||
| fac9f1a927 | |||
| 913a03608c | |||
| 46ce99e10f | |||
| 3e4460ac41 | |||
| 5b7fc25520 | |||
| 42196e8513 | |||
| bc07524e7a | |||
| 0011b9a480 | |||
| 7b09ec8919 | |||
| 9c8977062d | |||
| b01b820ec8 | |||
| b61f2c179c | |||
| 967f8e6984 | |||
| bb650e5280 | |||
| 3b34d6e7ce | |||
| db782cfe9e | |||
| 96223f9f9f | |||
| da45c5783d | |||
| 5b6c819ac4 | |||
| 6d41f2db86 | |||
| ba0bb7b903 | |||
| d03dc28764 | |||
| 20a785ea5e | |||
| 0286fa4268 | |||
| 85fb9e6af3 | |||
| 97a8806bfb | |||
| 970f4f3a7e | |||
| 6d192968d1 | |||
| f5959af2e1 | |||
| ea74ac2714 | |||
| 80c595cea8 | |||
| fbc738f2d4 | |||
| 3e3acf3332 | |||
| 0a77520d67 | |||
| 72cd3aade3 | |||
| 88bd9cd2ba | |||
| 5ee8678a29 | |||
| fb1937ae63 | |||
| de02e3d7e0 | |||
| ebaa69713f | |||
| 8a1a90dafd | |||
| 6f6e65be12 | |||
| 253f04066b | |||
| 74902b3fb4 | |||
| af1a6492d4 | |||
| 0da007ec8c | |||
| 9053fb3816 | |||
| c1d4e474f0 | |||
| 6bac13eb84 | |||
| 0e48ddd306 | |||
| 8682decbbc | |||
| bb615b90bf | |||
| cb0c320b45 | |||
| 73044bea58 | |||
| 3bb312e9e1 | |||
| c7d2f422b8 | |||
| 4dedf76ebc | |||
| 2376d16ffd | |||
| 1fe0bdd41f | |||
| 1b4d25342f | |||
| bc391550fb | |||
| b563f573de | |||
| 25150b421c | |||
| 94031a2913 | |||
| 64fb421b38 | |||
| 78af95d747 | |||
| 1d4f681b8f | |||
| eda2cd76db | |||
| 4adc60a6c6 | |||
| 0d5577a9a4 | |||
| 918f92aba7 | |||
| 7a24d55be7 | |||
| a84cc1c060 | |||
| 31cb79d2b9 | |||
| d995e7baa0 | |||
| e7fe6d5c22 | |||
| 918b698e50 | |||
| 2427b2323f | |||
| 0a8222fea3 | |||
| 60a00b89ae | |||
| 4c88efa19d | |||
| 17d65a1f6f | |||
| fcd03eb903 | |||
| 4e69efce28 | |||
| 16caec4a22 | |||
| 08282ea09d | |||
| c04fed1aff | |||
| 97e788883a | |||
| a16fce0749 | |||
| 26e2fa0168 | |||
| 120deb3ad4 | |||
| 0a9e2df5de | |||
| 6ffcb88872 | |||
| 960e764c7b | |||
| d88f27b251 | |||
| e5b3e9755e | |||
| 6c34fb211f | |||
| 9fdbc3b1fc | |||
| 622c6f40d4 | |||
| 107da17ca9 | |||
| f9871b73a3 | |||
| 7605b646fe | |||
| 19c62ac7da | |||
| a5e4412d1a | |||
| 651246566a | |||
| fe8f854b17 | |||
| a4d20a4af4 | |||
| 0643d5910a | |||
| c78a456985 | |||
| 27017576d3 | |||
| f1810be10a | |||
| 021aba1a98 | |||
| a0b68adff3 | |||
| fa4586663c | |||
| 623d13a517 | |||
| a7ebc1b52f | |||
| f54f4370c0 | |||
| 5040e9fe8a | |||
| fc72cd34a1 | |||
| 6d71a3d306 | |||
| 86f8cf52a5 | |||
| bda6544a5f | |||
| 49c7cd1979 | |||
| 9dae58d5a6 | |||
| ed14b97199 | |||
| b94135a91c | |||
| 7384118357 | |||
| 4d833a50e5 | |||
| 57b0fdac0b | |||
| e6bd94025f | |||
| 330a0414f0 | |||
| 5cc201b46d | |||
| 7e55d7765d | |||
| 8f942922fd | |||
| db5635e844 | |||
| 8bc952ba66 | |||
| 96cb3a07f4 | |||
| cd6a6258b6 | |||
| a0fa2c08ac | |||
| 6642b1647a | |||
| 5315caf830 | |||
| 872523b0f0 | |||
| 7bf99fb496 | |||
| a1047edddb | |||
| e956176872 | |||
| 6d2947b080 | |||
| fb304d6c27 | |||
| 903289caa4 | |||
| aff31ebd1b | |||
| b6773f6983 | |||
| 0d28eb31d2 | |||
| 4922ccf80b | |||
| f7ce772f84 | |||
| 3d4bc0e69d | |||
| c721291a78 | |||
| 5f9ea2e7c2 | |||
| c48af5acc7 | |||
| 8b86b57e63 | |||
| 420f78be88 | |||
| d2b58ed20e | |||
| 278ce6ef33 | |||
| 768bc30653 | |||
| d9d8419803 | |||
| 7a9523690c | |||
| e5c6832ec0 | |||
| 167686bdea | |||
| d3249dc3d5 | |||
| 5c1f51f3ca | |||
| fde3a5c2ab | |||
| ae213dcf5e | |||
| 51ace4ca7f | |||
| f15bcc7df9 | |||
| 6be86affd6 | |||
| eec8268eb9 | |||
| 859b232f64 | |||
| 5501c0e709 | |||
| 306d8ac5f6 | |||
| d41f6a4465 | |||
| 8ed5f06151 | |||
| 42e17f2063 | |||
| f449a50d64 | |||
| 0c2433dc9f | |||
| a5def1ba3d | |||
| 810b7197ab | |||
| 24a0786d64 | |||
| c1ce4b066d | |||
| b6aec25f00 | |||
| c1834dc78d | |||
| 9e23413456 | |||
| 7d2c692663 | |||
| 9ce1a86cae | |||
| b0b5de5fa4 | |||
| 64178bd636 | |||
| 7985cdd4a7 | |||
| 8247861095 | |||
| 8480c929c1 | |||
| e6f437f55b | |||
| ee049d9465 | |||
| f6aa810f8b | |||
| e8d1b999a2 | |||
| d7fbd6594e | |||
| caef9b6a9e | |||
| 7fad4d5cd8 | |||
| 3e4b14f984 | |||
| 5ade293445 | |||
| a0fc5f560d | |||
| 51b0734462 | |||
| 8e3cd5aedb | |||
| cbaa0dc688 | |||
| 61bac97933 | |||
| 3e9be32279 | |||
| d41acaee05 | |||
| a0955875d8 | |||
| 38ea0989d4 | |||
| 7e0de14783 | |||
| de34d4642e | |||
| b86bad2bf9 | |||
| cc5af0134c | |||
| edcac25f5e | |||
| 5a5c1f996b | |||
| f9b8cae0ea | |||
| 62538c3db9 | |||
| 48aa504be4 | |||
| ed2eb7642a | |||
| 24ec5ae34d | |||
| 0cd250646a | |||
| 586359e7ab | |||
| 5a3945c411 | |||
| a7b374e51a | |||
| 1a1067a6bd | |||
| d8d0131806 | |||
| 60180938d2 | |||
| c74630656f | |||
| 96901e020f | |||
| 5e3229c9b7 | |||
| fc8c891f07 | |||
| 632e5e6664 | |||
| 7af0cfaa78 | |||
| f4b154f6c8 | |||
| b09a8a5dbe | |||
| a651939274 | |||
| 08283299ba | |||
| 1228043d57 | |||
| c924f284a0 |
@@ -0,0 +1 @@
|
|||||||
|
github: haraldk
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
**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
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
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@74626db7353a25a20a72816467ebf035f674c5f8 # 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@74626db7353a25a20a72816467ebf035f674c5f8 # 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@74626db7353a25a20a72816467ebf035f674c5f8 # 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 }}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
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}}"
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# 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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.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
|
||||||
@@ -15,3 +15,4 @@ private
|
|||||||
profiles.xml
|
profiles.xml
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/.metadata/
|
||||||
|
|||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
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
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
BSD 3-Clause License
|
BSD 3-Clause License
|
||||||
|
|
||||||
Copyright (c) 2017, 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
|
||||||
|
|||||||
@@ -1,261 +1,85 @@
|
|||||||
## Latest
|
[](https://github.com/haraldk/TwelveMonkeys/actions/workflows/ci.yml)
|
||||||
|
[](https://github.com/haraldk/TwelveMonkeys/actions/workflows/codeql.yml)
|
||||||
|
[](https://securityscorecards.dev/viewer/?uri=github.com/haraldk/TwelveMonkeys)
|
||||||
|
[](https://www.bestpractices.dev/projects/7900)
|
||||||
|
|
||||||
Master branch build status: [](https://travis-ci.org/haraldk/TwelveMonkeys)
|
[](https://search.maven.org/search?q=g:com.twelvemonkeys.imageio)
|
||||||
|
[](https://central.sonatype.com/repository/maven-snapshots/com/twelvemonkeys/imageio/imageio/maven-metadata.xml)
|
||||||
|
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
||||||
|
[](https://paypal.me/haraldk76/100)
|
||||||
|
|
||||||
Latest release is TwelveMonkeys ImageIO [3.3.2](http://search.maven.org/#search%7Cga%7C1%7Cg%3Acom.twelvemonkeys*%20AND%20v%3A%223.3.2%22) (Feb. 2nd, 2017).
|

|
||||||
[Release notes](https://github.com/haraldk/TwelveMonkeys/releases/latest).
|
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
|
TwelveMonkeys ImageIO provides extended image file format support for the Java platform, through plugins for the `javax.imageio.*` package.
|
||||||
|
|
||||||
These plugins extends the number of image file formats supported in Java, using the javax.imageio.* package.
|
The main goal of this project is to provide support for file formats not covered by the JDK.
|
||||||
The main purpose of this project is to provide support for formats not covered by the JRE itself.
|
Support for these formats is important, to be able to read data found
|
||||||
|
|
||||||
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.
|
||||||
Because there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
|
As 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.
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Features
|
## File formats supported
|
||||||
|
|
||||||
Mainstream format support
|
| Plugin | Format | Description | R | W | Metadata | Notes |
|
||||||
|
| ------ | -------- |---------------------------------------------------------|:---:|:---:| -------- | ----- |
|
||||||
|
| Batik | **SVG** | Scalable Vector Graphics | ✔ | - | - | 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) |
|
||||||
|
| | CUR | MS Windows Cursor 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](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 | ✔ | ✔ | - |
|
||||||
|
| [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](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](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](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](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 QuickTime Picture Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||||
|
| | 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) |
|
||||||
|
| [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) |
|
||||||
|
| | 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) |
|
||||||
|
| | 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) |
|
||||||
|
| | 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) |
|
||||||
|
| | 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) |
|
||||||
|
| [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) |
|
||||||
|
| | 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) |
|
||||||
|
| [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) |
|
||||||
|
| [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) |
|
||||||
|
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | ✔ | - | - | OLE2 Compound Document based format only |
|
||||||
|
| [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) |
|
||||||
|
| | 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) |
|
||||||
|
| [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) |
|
||||||
|
|
||||||
#### BMP - MS Windows/IBM OS/2 Device Independent Bitmap
|
|
||||||
|
|
||||||
* Read support for all known versions of the DIB/BMP format
|
|
||||||
* Indexed color, 1, 4 and 8 bit, including 4 and 8 bit RLE
|
|
||||||
* RGB, 16, 24 and 32 bit
|
|
||||||
* Embedded PNG and JPEG data
|
|
||||||
* Windows and OS/2 versions
|
|
||||||
* Native and standard metadata format
|
|
||||||
|
|
||||||
#### JPEG
|
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
|
||||||
|
and make sure you use an updated and secure version.*
|
||||||
* Read support for the following JPEG "flavors":
|
|
||||||
* All JFIF compliant JPEGs
|
|
||||||
* All Exif compliant JPEGs
|
|
||||||
* YCbCr JPEGs without JFIF segment (converted to RGB, using embedded ICC profile)
|
|
||||||
* CMYK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
|
|
||||||
* Adobe YCCK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
|
|
||||||
* JPEGs containing ICC profiles with interpretation other than 'Perceptual' or class other than 'Display'
|
|
||||||
* JPEGs containing ICC profiles that are incompatible with stream data, corrupted ICC profiles or corrupted `ICC_PROFILE` segments
|
|
||||||
* JPEGs using non-standard color spaces, unsupported by Java 2D
|
|
||||||
* JPEGs with APP14/Adobe segments with length other than 14 bytes
|
|
||||||
* 8 bit JPEGs with 16 bit DQT segments
|
|
||||||
* Issues warnings instead of throwing exceptions in cases of corrupted or non-conformant data where ever the image
|
|
||||||
data can still be read in a reasonable way
|
|
||||||
* Thumbnail support:
|
|
||||||
* JFIF thumbnails (even if stream contains "inconsistent metadata")
|
|
||||||
* JFXX thumbnails (JPEG, Indexed and RGB)
|
|
||||||
* EXIF thumbnails (JPEG, RGB and YCbCr)
|
|
||||||
* Metadata support:
|
|
||||||
* JPEG metadata in both standard and native formats (even if stream contains "inconsistent metadata")
|
|
||||||
* `javax_imageio_jpeg_image_1.0` format (currently as native format, may change in the future)
|
|
||||||
* Non-conforming combinations of JFIF, Exif and Adobe markers, using "unknown" segments in the
|
|
||||||
"MarkerSequence" tag for the unsupported segments (for `javax_imageio_jpeg_image_1.0` format)
|
|
||||||
* Extended write support in progress:
|
|
||||||
* CMYK JPEGs
|
|
||||||
* YCCK JPEGs
|
|
||||||
|
|
||||||
#### JPEG-2000
|
|
||||||
|
|
||||||
* Possibly coming in the future, pending some license issues.
|
|
||||||
|
|
||||||
If you are one of the authors, or know one of the authors and/or the current license holders of either the original
|
|
||||||
jj2000 package or the JAI ImageIO project, please contact me (I've tried to get in touch in various ways,
|
|
||||||
without success so far).
|
|
||||||
|
|
||||||
Alternatively, if you have or know of a JPEG-2000 implementation in Java with a suitable license, get in touch. :-)
|
|
||||||
|
|
||||||
#### PNM - NetPBM Portable Any Map
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* PBM in 'P1' (ASCII) and 'P4' (binary) formats, 1 bit per pixel
|
|
||||||
* PGM in 'P2' (ASCII) and 'P5' (binary) formats, up to 16/32 bits per pixel
|
|
||||||
* PPM in 'P3' (ASCII) and 'P6' (binary) formats, up to 16/32 bits per pixel component
|
|
||||||
* PAM in 'P7' (binary) format up to 32 bits per pixel component
|
|
||||||
* Limited support for PFM in 'Pf' (gray) and 'PF' (RGB) formats, 32 bits floating point
|
|
||||||
* Write support for the following formats:
|
|
||||||
* PPM in 'P6' (binary) format
|
|
||||||
* PAM in 'P7' (binary) format
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### PSD - Adobe Photoshop Document
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* Monochrome, 1 channel, 1 bit
|
|
||||||
* Indexed, 1 channel, 8 bit
|
|
||||||
* Gray, 1 channel, 8, 16 and 32 bit
|
|
||||||
* Duotone, 1 channel, 8, 16 and 32 bit
|
|
||||||
* RGB, 3-4 channels, 8, 16 and 32 bit
|
|
||||||
* CMYK, 4-5 channels, 8, 16 and 32 bit
|
|
||||||
* Read support for the following compression types:
|
|
||||||
* Uncompressed
|
|
||||||
* RLE (PackBits)
|
|
||||||
* Layer support
|
|
||||||
* Image layers only, in all of the above types
|
|
||||||
* Thumbnail support
|
|
||||||
* JPEG
|
|
||||||
* RAW (RGB)
|
|
||||||
* Support for "Large Document Format" (PSB)
|
|
||||||
* Native and Standard metadata support
|
|
||||||
|
|
||||||
#### TIFF - Aldus/Adobe Tagged Image File Format
|
|
||||||
|
|
||||||
* Read support for the following "Baseline" TIFF file types:
|
|
||||||
* Class B (Bi-level), all relevant compression types, 1 bit per sample
|
|
||||||
* Class G (Gray), all relevant compression types, 2, 4, 8, 16 or 32 bits per sample, unsigned integer
|
|
||||||
* Class P (Palette/indexed color), all relevant compression types, 1, 2, 4, 8 or 16 bits per sample, unsigned integer
|
|
||||||
* Class R (RGB), all relevant compression types, 8 or 16 bits per sample, unsigned integer
|
|
||||||
* Read support for the following TIFF extensions:
|
|
||||||
* Tiling
|
|
||||||
* Class F (Facsimile), CCITT Modified Huffman RLE, T4 and T6 (type 2, 3 and 4) compressions.
|
|
||||||
* LZW Compression (type 5)
|
|
||||||
* "Old-style" JPEG Compression (type 6), as a best effort, as the spec is not well-defined
|
|
||||||
* JPEG Compression (type 7)
|
|
||||||
* ZLib (aka Adobe-style Deflate) Compression (type 8)
|
|
||||||
* Deflate Compression (type 32946)
|
|
||||||
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate and PackBits compression
|
|
||||||
* Alpha channel (ExtraSamples type 1/Associated Alpha and type 2/Unassociated Alpha)
|
|
||||||
* CMYK data (PhotometricInterpretation type 5/Separated)
|
|
||||||
* YCbCr data (PhotometricInterpretation type 6/YCbCr) for JPEG
|
|
||||||
* CIELab data in TIFF, ITU and ICC variants (PhotometricInterpretation type 9, 10 and 11)
|
|
||||||
* Planar data (PlanarConfiguration type 2/Planar)
|
|
||||||
* ICC profiles (ICCProfile)
|
|
||||||
* BitsPerSample values up to 16 for most PhotometricInterpretations
|
|
||||||
* Multiple images (pages) in one file
|
|
||||||
* Write support for most "Baseline" TIFF options
|
|
||||||
* Uncompressed, PackBits, ZLib and Deflate
|
|
||||||
* Additional support for CCITT T4 and and T6 compressions.
|
|
||||||
* Additional support for LZW and JPEG (type 7) compressions
|
|
||||||
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate
|
|
||||||
* Native and Standard metadata support
|
|
||||||
|
|
||||||
Legacy formats
|
|
||||||
|
|
||||||
#### HDR - Radiance High Dynamic Range RGBE Format
|
|
||||||
|
|
||||||
* Read support for the most common RGBE (.hdr) format
|
|
||||||
* Samples are converted to 32 bit floating point (`float`) and normalized using a global tone mapper by default.
|
|
||||||
* Support for custom global tone mappers
|
|
||||||
* Alternatively, use a "null-tone mapper", for unnormalized data (allows local tone mapping)
|
|
||||||
* Unconverted RGBE samples accessible using `readRaster`
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### IFF - Commodore Amiga/Electronic Arts Interchange File Format
|
|
||||||
|
|
||||||
* Legacy format, allows reading popular image format from the Commodore Amiga computer.
|
|
||||||
* Read support for the following file types:
|
|
||||||
* ILBM Indexed color, 1-8 interleaved bit planes, including 6 bit EHB
|
|
||||||
* ILBM Gray, 8 bit interleaved bit planes
|
|
||||||
* ILBM RGB, 24 and 32 bit interleaved bit planes
|
|
||||||
* ILBM HAM6 and HAM8
|
|
||||||
* PBM Indexed color, 1-8 bit,
|
|
||||||
* PBM Gray, 8 bit
|
|
||||||
* PBM RGB, 24 and 32 bit
|
|
||||||
* PBM HAM6 and HAM8
|
|
||||||
* Write support
|
|
||||||
* ILBM Indexed color, 1-8 bits per sample, 8 bit gray, 24 and 32 bit true color.
|
|
||||||
* Support for the following compression types (read/write):
|
|
||||||
* Uncompressed
|
|
||||||
* RLE (PackBits)
|
|
||||||
|
|
||||||
#### PCX - ZSoft Paintbrush Format
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* Indexed color, 1, 2, 4 or 8 bits per pixel, bit planes or interleaved
|
|
||||||
* Grayscale, 8 bits per pixel
|
|
||||||
* Color (RGB), 8 bits per pixel component
|
|
||||||
* Read support for DCX (multi-page) fax format, containing any of the above types
|
|
||||||
* Support for the following compression types:
|
|
||||||
* Uncompressed (experimental)
|
|
||||||
* RLE compressed
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### PICT - Apple Mac Paint Picture Format
|
|
||||||
|
|
||||||
* Legacy format, especially useful for reading OS X clipboard data.
|
|
||||||
* Read support for the following file types:
|
|
||||||
* QuickDraw (format support is not complete, but supports most OS X clipboard data as well as RGB pixel data)
|
|
||||||
* QuickDraw bitmap
|
|
||||||
* QuickDraw pixmap
|
|
||||||
* QuickTime stills
|
|
||||||
* Write support for RGB pixel data:
|
|
||||||
* QuickDraw pixmap
|
|
||||||
|
|
||||||
#### SGI - Silicon Graphics Image Format
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* 1, 2, 3 or 4 channel image data
|
|
||||||
* 8 or 16 bits per pixel component
|
|
||||||
* Support for the following compression types:
|
|
||||||
* Uncompressed
|
|
||||||
* RLE compressed
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### TGA - Truevision TGA Image Format
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* ColorMapped
|
|
||||||
* Monochrome
|
|
||||||
* TrueColor
|
|
||||||
* Support for the following compression types:
|
|
||||||
* Uncompressed
|
|
||||||
* RLE compressed
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
Icon/other formats
|
|
||||||
|
|
||||||
#### ICNS - Apple Icon Image
|
|
||||||
|
|
||||||
* Read support for the following icon types:
|
|
||||||
* All known "native" icon types
|
|
||||||
* Large PNG encoded icons
|
|
||||||
* Large JPEG 2000 encoded icons (requires JPEG 2000 ImageIO plugin or fallback to `sips` command line tool)
|
|
||||||
|
|
||||||
#### ICO & CUR - MS Windows Icon and Cursor Formats
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* ICO Indexed color, 1, 4 and 8 bit
|
|
||||||
* ICO RGB, 16, 24 and 32 bit
|
|
||||||
* CUR Indexed color, 1, 4 and 8 bit
|
|
||||||
* CUR RGB, 16, 24 and 32 bit
|
|
||||||
* *3.1* Note: These formats are now part of the BMP plugin
|
|
||||||
|
|
||||||
#### Thumbs.db - MS Windows Thumbs DB
|
|
||||||
|
|
||||||
* Read support
|
|
||||||
|
|
||||||
Other formats, using 3rd party libraries
|
|
||||||
|
|
||||||
#### SVG - Scalable Vector Graphics
|
|
||||||
|
|
||||||
* Read-only support using Batik
|
|
||||||
|
|
||||||
#### WMF - MS Windows MetaFile
|
|
||||||
|
|
||||||
* Limited read-only support using Batik
|
|
||||||
|
|
||||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html), and make sure you use
|
|
||||||
either version 1.6.1, 1.7.1 or 1.8+.*
|
|
||||||
|
|
||||||
|
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).
|
||||||
|
For BMP, JPEG, and TIFF formats the TwelveMonkeys plugins provides extended format support and additional features.
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
Most of the time, all you need to do is simply include the plugins in your project and write:
|
Most of the time, all you need to do is simply include the plugins in your project and write:
|
||||||
|
|
||||||
BufferedImage image = ImageIO.read(file);
|
```java
|
||||||
|
BufferedImage image = ImageIO.read(file);
|
||||||
|
```
|
||||||
|
|
||||||
This will load the first image of the file, entirely into memory.
|
This will load the first image of the file, entirely into memory.
|
||||||
|
|
||||||
The basic and simplest form of writing is:
|
The basic and simplest form of writing is:
|
||||||
|
|
||||||
if (!ImageIO.write(image, format, file)) {
|
```java
|
||||||
// Handle image not written case
|
if (!ImageIO.write(image, format, file)) {
|
||||||
}
|
// Handle image not written case
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
This will write the entire image into a single file, using the default settings for the given format.
|
This will write the entire image into a single file, using the default settings for the given format.
|
||||||
|
|
||||||
@@ -265,50 +89,46 @@ The plugins are discovered automatically at run time. See the [FAQ](#faq) for mo
|
|||||||
|
|
||||||
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
|
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
|
||||||
|
|
||||||
// Create input stream
|
```java
|
||||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
// Create input stream (in try-with-resource block to avoid leaks)
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
|
||||||
|
// Get the reader
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||||
|
|
||||||
|
if (!readers.hasNext()) {
|
||||||
|
throw new IllegalArgumentException("No reader for: " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the reader
|
reader.setInput(input);
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
|
||||||
|
|
||||||
if (!readers.hasNext()) {
|
// Optionally, listen for read warnings, progress, etc.
|
||||||
throw new IllegalArgumentException("No reader for: " + file);
|
reader.addIIOReadWarningListener(...);
|
||||||
}
|
reader.addIIOReadProgressListener(...);
|
||||||
|
|
||||||
ImageReader reader = readers.next();
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
|
|
||||||
try {
|
// Optionally, control read settings like sub sampling, source region or destination etc.
|
||||||
reader.setInput(input);
|
param.setSourceSubsampling(...);
|
||||||
|
param.setSourceRegion(...);
|
||||||
|
param.setDestination(...);
|
||||||
|
// ...
|
||||||
|
|
||||||
// Optionally, listen for read warnings, progress, etc.
|
// Finally read the image, using settings from param
|
||||||
reader.addIIOReadWarningListener(...);
|
BufferedImage image = reader.read(0, param);
|
||||||
reader.addIIOReadProgressListener(...);
|
|
||||||
|
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
// Optionally, read thumbnails, meta data, etc...
|
||||||
|
int numThumbs = reader.getNumThumbnails(0);
|
||||||
// Optionally, control read settings like sub sampling, source region or destination etc.
|
// ...
|
||||||
param.setSourceSubsampling(...);
|
|
||||||
param.setSourceRegion(...);
|
|
||||||
param.setDestination(...);
|
|
||||||
// ...
|
|
||||||
|
|
||||||
// Finally read the image, using settings from param
|
|
||||||
BufferedImage image = reader.read(0, param);
|
|
||||||
|
|
||||||
// Optionally, read thumbnails, meta data, etc...
|
|
||||||
int numThumbs = reader.getNumThumbnails(0);
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Dispose reader in finally block to avoid memory leaks
|
|
||||||
reader.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// Close stream in finally block to avoid resource leaks
|
// Dispose reader in finally block to avoid memory leaks
|
||||||
input.close();
|
reader.dispose();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
||||||
entire image into memory first.
|
entire image into memory first.
|
||||||
@@ -318,129 +138,142 @@ It's also possible to read multiple images from the same file in a loop, using `
|
|||||||
|
|
||||||
If you need more control of write parameters and the writing process, the common idiom for writing is something like:
|
If you need more control of write parameters and the writing process, the common idiom for writing is something like:
|
||||||
|
|
||||||
// Get the writer
|
```java
|
||||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
// Get the writer
|
||||||
|
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
||||||
|
|
||||||
if (!writers.hasNext()) {
|
if (!writers.hasNext()) {
|
||||||
throw new IllegalArgumentException("No writer for: " + format);
|
throw new IllegalArgumentException("No writer for: " + format);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageWriter writer = writers.next();
|
ImageWriter writer = writers.next();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create output stream
|
// Create output stream (in try-with-resource block to avoid leaks)
|
||||||
ImageOutputStream output = ImageIO.createImageOutputStream(file);
|
try (ImageOutputStream output = ImageIO.createImageOutputStream(file)) {
|
||||||
|
writer.setOutput(output);
|
||||||
try {
|
|
||||||
writer.setOutput(output);
|
// Optionally, listen to progress, warnings, etc.
|
||||||
|
|
||||||
// Optionally, listen to progress, warnings, etc.
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||||
|
|
||||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
// Optionally, control format specific settings of param (requires casting), or
|
||||||
|
// control generic write settings like sub sampling, source region, output type etc.
|
||||||
// Optionally, control format specific settings of param (requires casting), or
|
|
||||||
// control generic write settings like sub sampling, source region, output type etc.
|
// Optionally, provide thumbnails and image/stream metadata
|
||||||
|
writer.write(..., new IIOImage(..., image, ...), param);
|
||||||
// Optionally, provide thumbnails and image/stream metadata
|
|
||||||
writer.write(..., new IIOImage(..., image, ...), param);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Close stream in finally block to avoid resource leaks
|
|
||||||
output.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Dispose writer in finally block to avoid memory leaks
|
|
||||||
writer.dispose();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Dispose writer in finally block to avoid memory leaks
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
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](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
[Java Image I/O API Guide](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
||||||
from Oracle.
|
from Oracle.
|
||||||
|
|
||||||
|
#### Adobe Clipping Path support
|
||||||
|
|
||||||
#### Deploying the plugins in a web app
|
```java
|
||||||
|
import com.twelvemonkeys.imageio.path.Paths;
|
||||||
|
|
||||||
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
|
...
|
||||||
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.
|
|
||||||
|
|
||||||
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
|
try (ImageInputStream stream = ImageIO.createImageInputStream(new File("image_with_path.jpg")) {
|
||||||
If you restart your application, old classes will by default remain in memory forever (because the next time
|
BufferedImage image = Paths.readClipped(stream);
|
||||||
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
|
|
||||||
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
|
|
||||||
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
|
|
||||||
for uninitialized inner classes) may occur.
|
|
||||||
|
|
||||||
To work around both the discovery problem and the resource leak,
|
// Do something with the clipped image...
|
||||||
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
|
}
|
||||||
dynamic loading and unloading of ImageIO plugins for web applications.
|
```
|
||||||
|
See [Adobe Clipping Path support on the Wiki](https://github.com/haraldk/TwelveMonkeys/wiki/Photoshop-Clipping-Path-support) for more details and example code.
|
||||||
|
|
||||||
<web-app ...>
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
<listener>
|
|
||||||
<display-name>ImageIO service provider loader/unloader</display-name>
|
|
||||||
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
</web-app>
|
|
||||||
|
|
||||||
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
|
|
||||||
|
|
||||||
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
|
|
||||||
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.
|
|
||||||
|
|
||||||
#### Using the ResampleOp
|
#### Using the ResampleOp
|
||||||
|
|
||||||
The library comes with a resampling (image resizing) operation, that contains many different algorithms
|
The library comes with a resampling (image resizing) operation, that contains many different algorithms
|
||||||
to provide excellent results at reasonable speed.
|
to provide excellent results at reasonable speed.
|
||||||
|
|
||||||
import com.twelvemonkeys.image.ResampleOp;
|
```java
|
||||||
|
import com.twelvemonkeys.image.ResampleOp;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
BufferedImage input = ...; // Image to resample
|
BufferedImage input = ...; // Image to resample
|
||||||
int width, height = ...; // new width/height
|
int width, height = ...; // new width/height
|
||||||
|
|
||||||
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
|
|
||||||
BufferedImage output = resampler.filter(input, null);
|
|
||||||
|
|
||||||
|
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
|
||||||
|
BufferedImage output = resampler.filter(input, null);
|
||||||
|
```
|
||||||
|
|
||||||
#### Using the DiffusionDither
|
#### Using the DiffusionDither
|
||||||
|
|
||||||
The library comes with a dithering operation, that can be used to convert `BufferedImage`s to `IndexColorModel` using
|
The library comes with a dithering operation, that can be used to convert `BufferedImage`s to `IndexColorModel` using
|
||||||
Floyd-Steinberg error-diffusion dither.
|
Floyd-Steinberg error-diffusion dither.
|
||||||
|
|
||||||
import com.twelvemonkeys.image.DiffusionDither;
|
```java
|
||||||
|
import com.twelvemonkeys.image.DiffusionDither;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
BufferedImage input = ...; // Image to dither
|
BufferedImage input = ...; // Image to dither
|
||||||
|
|
||||||
BufferedImageOp ditherer = new DiffusionDither();
|
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](http://git-scm.com/downloads)):
|
Download the project (using [Git](https://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](http://maven.apache.org/download.cgi)):
|
Build the project (using [Maven](https://maven.apache.org/download.cgi)):
|
||||||
|
|
||||||
$ mvn package
|
$ mvn package
|
||||||
|
|
||||||
Currently, the recommended JDK for making a build is Oracle JDK 7.x or 8.x.
|
Currently, the recommended JDK for making a build is Oracle JDK 8.x.
|
||||||
|
|
||||||
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
|
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
|
||||||
|
|
||||||
@@ -461,10 +294,12 @@ The ImageIO registry and service lookup mechanism will make sure the plugins are
|
|||||||
|
|
||||||
To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
|
To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
|
||||||
|
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
```java
|
||||||
while (readers.hasNext()) {
|
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||||
System.out.println("reader: " + readers.next());
|
while (readers.hasNext()) {
|
||||||
}
|
System.out.println("reader: " + readers.next());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The first line should print:
|
The first line should print:
|
||||||
|
|
||||||
@@ -474,143 +309,209 @@ The first line should print:
|
|||||||
|
|
||||||
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
|
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
...
|
||||||
|
<dependencies>
|
||||||
...
|
...
|
||||||
<dependencies>
|
<dependency>
|
||||||
...
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<dependency>
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<version>3.13.1</version>
|
||||||
<artifactId>imageio-jpeg</artifactId>
|
</dependency>
|
||||||
<version>3.3.2</version> <!-- Alternatively, build your own version -->
|
<dependency>
|
||||||
</dependency>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<dependency>
|
<artifactId>imageio-tiff</artifactId>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<version>3.13.1</version>
|
||||||
<artifactId>imageio-tiff</artifactId>
|
</dependency>
|
||||||
<version>3.3.2</version> <!-- Alternatively, build your own version -->
|
|
||||||
</dependency>
|
<!--
|
||||||
</dependencies>
|
Optional dependency. Needed only if you deploy ImageIO plugins as part of a web app.
|
||||||
|
Make sure you add the IIOProviderContextListener to your web.xml, see above.
|
||||||
|
-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||||
|
<artifactId>servlet</artifactId>
|
||||||
|
<version>3.13.1</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>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
#### Manual dependency example
|
#### Manual dependency example
|
||||||
|
|
||||||
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.3.2.jar
|
twelvemonkeys-common-lang-3.13.1.jar
|
||||||
twelvemonkeys-common-io-3.3.2.jar
|
twelvemonkeys-common-io-3.13.1.jar
|
||||||
twelvemonkeys-common-image-3.3.2.jar
|
twelvemonkeys-common-image-3.13.1.jar
|
||||||
twelvemonkeys-imageio-core-3.3.2.jar
|
twelvemonkeys-imageio-core-3.13.1.jar
|
||||||
twelvemonkeys-imageio-metadata-3.3.2.jar
|
twelvemonkeys-imageio-metadata-3.13.1.jar
|
||||||
twelvemonkeys-imageio-jpeg-3.3.2.jar
|
twelvemonkeys-imageio-jpeg-3.13.1.jar
|
||||||
twelvemonkeys-imageio-tiff-3.3.2.jar
|
twelvemonkeys-imageio-tiff-3.13.1.jar
|
||||||
|
|
||||||
|
#### Deploying the plugins in a web app
|
||||||
|
|
||||||
|
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it does not work well with
|
||||||
|
servlet contexts as-is. 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.
|
||||||
|
|
||||||
|
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
|
||||||
|
If you restart your application, old classes will by default remain in memory forever (because the next time
|
||||||
|
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
|
||||||
|
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
|
||||||
|
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
|
||||||
|
for uninitialized inner classes) may occur.
|
||||||
|
|
||||||
|
To work around both the discovery problem and the resource leak,
|
||||||
|
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
|
||||||
|
dynamic loading and unloading of ImageIO plugins for web applications.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<web-app ...>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
<listener>
|
||||||
|
<display-name>ImageIO service provider loader/unloader</display-name>
|
||||||
|
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
</web-app>
|
||||||
|
```
|
||||||
|
|
||||||
|
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
|
||||||
|
|
||||||
|
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
|
||||||
|
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.
|
||||||
|
|
||||||
|
##### 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
|
||||||
|
|
||||||
|
The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar.
|
||||||
|
Re-packaging is not necessary to use the library, and not recommended.
|
||||||
|
|
||||||
|
However, if you like to create a "fat"
|
||||||
|
JAR, or otherwise like to re-package the JARs for some reason, it's important to remember that automatic discovery of
|
||||||
|
the plugins by ImageIO depends on the
|
||||||
|
[Service Provider Interface (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) mechanism.
|
||||||
|
In short, each JAR contains a special folder, named `META-INF/services` containing one or more files,
|
||||||
|
typically `javax.imageio.spi.ImageReaderSpi` and `javax.imageio.spi.ImageWriterSpi`.
|
||||||
|
These files exist *with the same name in every JAR*,
|
||||||
|
so if you simply unpack everything to a single folder or create a JAR, files will be overwritten and behavior be
|
||||||
|
unspecified (most likely you will end up with a single plugin being installed).
|
||||||
|
|
||||||
|
The solution is to make sure all files with the same name, are merged to a single file,
|
||||||
|
containing all the SPI information of each type. If using the Maven Shade plugin, you should use the
|
||||||
|
[ServicesResourceTransformer](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ServicesResourceTransformer)
|
||||||
|
to properly merge these files. You may also want to use the
|
||||||
|
[ManifestResourceTransforme](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ManifestResourceTransformer)
|
||||||
|
to get the correct vendor name, version info etc.
|
||||||
|
Other "fat" JAR bundlers will probably have similar mechanisms to merge entries with the same name.
|
||||||
|
|
||||||
### Links to prebuilt binaries
|
### Links to prebuilt binaries
|
||||||
|
|
||||||
##### Latest version (3.2.x)
|
##### Latest version (3.13.1)
|
||||||
|
|
||||||
Requires Java 7 or later.
|
The latest version that will run on Java 7 is 3.9.4. Later versions will require Java 8 or later.
|
||||||
|
|
||||||
Common dependencies
|
Common dependencies
|
||||||
* [common-lang-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.3.2/common-lang-3.3.2.jar)
|
* [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-io-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.3.2/common-io-3.3.2.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-image-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.3.2/common-image-3.3.2.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)
|
||||||
|
|
||||||
ImageIO dependencies
|
ImageIO dependencies
|
||||||
* [imageio-core-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.3.2/imageio-core-3.3.2.jar)
|
* [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-metadata-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.3.2/imageio-metadata-3.3.2.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 plugins
|
ImageIO plugins
|
||||||
* [imageio-bmp-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.3.2/imageio-bmp-3.3.2.jar)
|
* [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-jpeg-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.3.2/imageio-jpeg-3.3.2.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-tiff-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.3.2/imageio-tiff-3.3.2.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-pnm-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.3.2/imageio-pnm-3.3.2.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-psd-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.3.2/imageio-psd-3.3.2.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-hdr-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.3.2/imageio-hdr-3.3.2.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-iff-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.3.2/imageio-iff-3.3.2.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-pcx-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.3.2/imageio-pcx-3.3.2.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-pict-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.3.2/imageio-pict-3.3.2.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-sgi-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.3.2/imageio-sgi-3.3.2.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-tga-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.3.2/imageio-tga-3.3.2.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-icns-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.3.2/imageio-icns-3.3.2.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.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.3.2/imageio-thumbsdb-3.3.2.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.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.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.3.2/imageio-batik-3.3.2.jar)
|
* [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)
|
||||||
|
|
||||||
Photoshop Path support for ImageIO
|
Photoshop Path support for ImageIO
|
||||||
* [imageio-clippath-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.3.2/imageio-clippath-3.3.2.jar)
|
* [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)
|
||||||
|
|
||||||
Servlet support
|
Servlet support
|
||||||
* [servlet-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.3.2/servlet-3.3.2.jar)
|
* [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.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
|
||||||
|
|
||||||
The project is distributed under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
|
This project is provided under the OSI approved [BSD license](https://opensource.org/licenses/BSD-3-Clause):
|
||||||
|
|
||||||
Copyright (c) 2008-2015, Harald Kuhr
|
Copyright (c) 2008-2022, 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
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
o Redistributions of source code must retain the above copyright
|
o Redistributions of source code must retain the above copyright notice, this
|
||||||
notice, this list of conditions and the following disclaimer.
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
o Redistributions in binary form must reproduce the above copyright
|
o Redistributions in binary form must reproduce the above copyright notice,
|
||||||
notice, this list of conditions and the following disclaimer in the
|
this list of conditions and the following disclaimer in the documentation
|
||||||
documentation and/or other materials provided with the distribution.
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
o Neither the name "TwelveMonkeys" nor the
|
o Neither the name of the copyright holder nor the names of its
|
||||||
names of its contributors may be used to endorse or promote products
|
contributors may be used to endorse or promote products derived from
|
||||||
derived from this software without specific prior written permission.
|
this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
q: How do I use it?
|
q: How do I use it?
|
||||||
|
|
||||||
a: The easiest way is to build your own project using Maven, and just add dependencies to the specific plug-ins you need.
|
a: The easiest way is to build your own project using Maven, Gradle or other build tool with dependency management,
|
||||||
If you don't use Maven, make sure you have all the necessary JARs in classpath. See the Install section above.
|
and just add dependencies to the specific plug-ins you need.
|
||||||
|
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?
|
||||||
@@ -628,28 +529,47 @@ 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 have to do, is to make sure you have the TwelveMonkeys JARs in your classpath.
|
All you have to do, is to make sure you have the TwelveMonkeys ImageIO JARs in your classpath.
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
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` and `BMPImageReader`, and the Apple provided `TIFFImageReader` on OS X,
|
the Sun/Oracle provided `JPEGImageReader`, `BMPImageReader` `TIFFImageReader`, 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?
|
||||||
|
|
||||||
|
a: The short answer is simply that the built-in support in ImageIO for these formats are considered 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).
|
||||||
|
|
||||||
|
|
||||||
|
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 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 issues has been fixed for years.
|
- It's not actively developed. No issue 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.
|
||||||
|
|
||||||
|
|
||||||
q: What about JMagick or IM4Java? Can't you just use what´s already available?
|
q: What about JMagick or IM4Java? Can't you just use what's already available?
|
||||||
|
|
||||||
a: While great libraries with a wide range of formats support, the ImageMagick-based libraries has some disadvantages
|
a: While great libraries with a wide range of formats support, the ImageMagick-based libraries has some disadvantages
|
||||||
compared to ImageIO.
|
compared to ImageIO.
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# 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.
|
||||||
+16
-1
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.4</version>
|
<version>3.13.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.twelvemonkeys.bom</groupId>
|
<groupId>com.twelvemonkeys.bom</groupId>
|
||||||
@@ -63,6 +63,11 @@
|
|||||||
<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>
|
||||||
@@ -123,6 +128,16 @@
|
|||||||
<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>
|
||||||
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
<artifactId>imageio-xwd</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- ImageIO 3rd party dependent plugins -->
|
<!-- ImageIO 3rd party dependent plugins -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -4,15 +4,19 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.4</version>
|
<version>3.13.2-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>
|
||||||
The TwelveMonkeys Common Image support
|
TwelveMonkeys Common image support classes.
|
||||||
</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>
|
||||||
@@ -24,12 +28,14 @@
|
|||||||
<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>
|
||||||
|
|||||||
+109
-110
@@ -1,110 +1,109 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import java.awt.image.ImageConsumer;
|
import java.awt.image.ImageConsumer;
|
||||||
import java.awt.image.ImageProducer;
|
import java.awt.image.ImageProducer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractImageSource
|
* AbstractImageSource
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/image/AbstractImageSource.java#1 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/AbstractImageSource.java#1 $
|
*/
|
||||||
*/
|
public abstract class AbstractImageSource implements ImageProducer {
|
||||||
public abstract class AbstractImageSource implements ImageProducer {
|
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>();
|
||||||
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>();
|
protected int width;
|
||||||
protected int width;
|
protected int height;
|
||||||
protected int height;
|
protected int xOff;
|
||||||
protected int xOff;
|
protected int yOff;
|
||||||
protected int yOff;
|
|
||||||
|
// ImageProducer interface
|
||||||
// ImageProducer interface
|
public void addConsumer(final ImageConsumer pConsumer) {
|
||||||
public void addConsumer(final ImageConsumer pConsumer) {
|
if (consumers.contains(pConsumer)) {
|
||||||
if (consumers.contains(pConsumer)) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
consumers.add(pConsumer);
|
||||||
consumers.add(pConsumer);
|
|
||||||
|
try {
|
||||||
try {
|
initConsumer(pConsumer);
|
||||||
initConsumer(pConsumer);
|
sendPixels(pConsumer);
|
||||||
sendPixels(pConsumer);
|
|
||||||
|
if (isConsumer(pConsumer)) {
|
||||||
if (isConsumer(pConsumer)) {
|
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
||||||
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
|
||||||
|
// Get rid of "sticky" consumers...
|
||||||
// Get rid of "sticky" consumers...
|
if (isConsumer(pConsumer)) {
|
||||||
if (isConsumer(pConsumer)) {
|
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
removeConsumer(pConsumer);
|
||||||
removeConsumer(pConsumer);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception e) {
|
||||||
catch (Exception e) {
|
e.printStackTrace();
|
||||||
e.printStackTrace();
|
|
||||||
|
if (isConsumer(pConsumer)) {
|
||||||
if (isConsumer(pConsumer)) {
|
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void removeConsumer(final ImageConsumer pConsumer) {
|
||||||
public void removeConsumer(final ImageConsumer pConsumer) {
|
consumers.remove(pConsumer);
|
||||||
consumers.remove(pConsumer);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This implementation silently ignores this instruction. If pixel data is
|
||||||
* This implementation silently ignores this instruction. If pixel data is
|
* not in TDLR order by default, subclasses must override this method.
|
||||||
* not in TDLR order by default, subclasses must override this method.
|
*
|
||||||
*
|
* @param pConsumer the consumer that requested the resend
|
||||||
* @param pConsumer the consumer that requested the resend
|
*
|
||||||
*
|
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
||||||
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
*/
|
||||||
*/
|
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) {
|
||||||
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) {
|
// ignore
|
||||||
// ignore
|
}
|
||||||
}
|
|
||||||
|
public void startProduction(final ImageConsumer pConsumer) {
|
||||||
public void startProduction(final ImageConsumer pConsumer) {
|
addConsumer(pConsumer);
|
||||||
addConsumer(pConsumer);
|
}
|
||||||
}
|
|
||||||
|
public boolean isConsumer(final ImageConsumer pConsumer) {
|
||||||
public boolean isConsumer(final ImageConsumer pConsumer) {
|
return consumers.contains(pConsumer);
|
||||||
return consumers.contains(pConsumer);
|
}
|
||||||
}
|
|
||||||
|
protected abstract void initConsumer(ImageConsumer pConsumer);
|
||||||
protected abstract void initConsumer(ImageConsumer pConsumer);
|
|
||||||
|
protected abstract void sendPixels(ImageConsumer pConsumer);
|
||||||
protected abstract void sendPixels(ImageConsumer pConsumer);
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,7 +34,13 @@ 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.*;
|
import java.awt.image.BufferedImage;
|
||||||
|
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}.
|
||||||
@@ -70,6 +76,7 @@ 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 {
|
||||||
@@ -80,10 +87,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
|||||||
dst = createCompatibleDestImage(src, src.getColorModel());
|
dst = createCompatibleDestImage(src, src.getColorModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
Graphics2D g2d = null;
|
Graphics2D g2d = dst.createGraphics();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
g2d = dst.createGraphics();
|
|
||||||
int interpolationType = delegate.getInterpolationType();
|
int interpolationType = delegate.getInterpolationType();
|
||||||
|
|
||||||
if (interpolationType > 0) {
|
if (interpolationType > 0) {
|
||||||
@@ -109,9 +115,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (g2d != null) {
|
g2d.dispose();
|
||||||
g2d.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+173
-170
@@ -1,170 +1,173 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import java.awt.image.RGBImageFilter;
|
import java.awt.image.RGBImageFilter;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts the contrast and brightness of an image.
|
* Adjusts the contrast and brightness of an image.
|
||||||
* <p/>
|
* <p>
|
||||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||||
* A value of {@code 0.0} means no change.
|
* A value of {@code 0.0} means no change.
|
||||||
* Negative values will make the pixels darker.
|
* Negative values will make the pixels darker.
|
||||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||||
* Positive values will make the pixels brighter.
|
* Positive values will make the pixels brighter.
|
||||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||||
* <p/>
|
* </p>
|
||||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
* <p>
|
||||||
* A value of {@code 0.0} means no change.
|
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||||
* Negative values will reduce contrast.
|
* A value of {@code 0.0} means no change.
|
||||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
* Negative values will reduce contrast.
|
||||||
* (no contrast).
|
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||||
* Positive values will increase contrast.
|
* (no contrast).
|
||||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
* Positive values will increase contrast.
|
||||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||||
*
|
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* </p>
|
||||||
* @author last modified by $Author: haku $
|
*
|
||||||
*
|
* @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/BrightnessContrastFilter.java#1 $
|
* @author last modified by $Author: haku $
|
||||||
*
|
*
|
||||||
* @todo consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BrightnessContrastFilter.java#1 $
|
||||||
*/
|
*/
|
||||||
|
// TODO: consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947
|
||||||
public class BrightnessContrastFilter extends RGBImageFilter {
|
public class BrightnessContrastFilter extends RGBImageFilter {
|
||||||
|
|
||||||
// TODO: Replace with RescaleOp?
|
// TODO: Replace with RescaleOp?
|
||||||
|
|
||||||
// This filter can filter IndexColorModel, as it is does not depend on
|
// This filter can filter IndexColorModel, as it is does not depend on
|
||||||
// the pixels' location
|
// the pixels' location
|
||||||
{
|
{
|
||||||
canFilterIndexColorModel = true;
|
canFilterIndexColorModel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a pre-calculated lookup table for performance
|
// Use a pre-calculated lookup table for performance
|
||||||
private final int[] LUT;
|
private final int[] LUT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a BrightnessContrastFilter with default values
|
* Creates a BrightnessContrastFilter with default values
|
||||||
* ({@code brightness=0.3, contrast=0.3}).
|
* ({@code brightness=0.3, contrast=0.3}).
|
||||||
* <p/>
|
* <p>
|
||||||
* This will slightly increase both brightness and contrast.
|
* This will slightly increase both brightness and contrast.
|
||||||
*/
|
* </p>
|
||||||
public BrightnessContrastFilter() {
|
*/
|
||||||
this(0.3f, 0.3f);
|
public BrightnessContrastFilter() {
|
||||||
}
|
this(0.3f, 0.3f);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Creates a BrightnessContrastFilter with the given values for brightness
|
/**
|
||||||
* and contrast.
|
* Creates a BrightnessContrastFilter with the given values for brightness
|
||||||
* <p/>
|
* and contrast.
|
||||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
* <p>
|
||||||
* A value of {@code 0.0} means no change.
|
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||||
* Negative values will make the pixels darker.
|
* A value of {@code 0.0} means no change.
|
||||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
* Negative values will make the pixels darker.
|
||||||
* Positive values will make the pixels brighter.
|
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
* Positive values will make the pixels brighter.
|
||||||
* <p/>
|
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
* </p>
|
||||||
* A value of {@code 0.0} means no change.
|
* <p>
|
||||||
* Negative values will reduce contrast.
|
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
* A value of {@code 0.0} means no change.
|
||||||
* (no contrast).
|
* Negative values will reduce contrast.
|
||||||
* Positive values will increase contrast.
|
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
* (no contrast).
|
||||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
* Positive values will increase contrast.
|
||||||
*
|
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||||
* @param pBrightness adjust the brightness of the image, in the range
|
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||||
* {@code -2.0,..,0.0,..,2.0}.
|
* </p>
|
||||||
* @param pContrast adjust the contrast of the image, in the range
|
*
|
||||||
* {@code -1.0,..,0.0,..,1.0}.
|
* @param pBrightness adjust the brightness of the image, in the range
|
||||||
*/
|
* {@code -2.0,..,0.0,..,2.0}.
|
||||||
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
* @param pContrast adjust the contrast of the image, in the range
|
||||||
LUT = createLUT(pBrightness, pContrast);
|
* {@code -1.0,..,0.0,..,1.0}.
|
||||||
}
|
*/
|
||||||
|
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
||||||
private static int[] createLUT(float pBrightness, float pContrast) {
|
LUT = createLUT(pBrightness, pContrast);
|
||||||
int[] lut = new int[256];
|
}
|
||||||
|
|
||||||
// Hmmm.. This approximates Photoshop values.. Not good though..
|
private static int[] createLUT(float pBrightness, float pContrast) {
|
||||||
double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast;
|
int[] lut = new int[256];
|
||||||
|
|
||||||
// Convert range [-1,..,0,..,1] -> [0,..,1,..,2]
|
// Hmmm.. This approximates Photoshop values.. Not good though..
|
||||||
double brightness = pBrightness + 1.0;
|
double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast;
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++) {
|
// Convert range [-1,..,0,..,1] -> [0,..,1,..,2]
|
||||||
lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0)));
|
double brightness = pBrightness + 1.0;
|
||||||
}
|
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
// Special case, to ensure only primary colors for max contrast
|
lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0)));
|
||||||
if (pContrast == 1f) {
|
}
|
||||||
lut[127] = lut[126];
|
|
||||||
}
|
// Special case, to ensure only primary colors for max contrast
|
||||||
|
if (pContrast == 1f) {
|
||||||
return lut;
|
lut[127] = lut[126];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int clamp(int i) {
|
return lut;
|
||||||
if (i < 0) {
|
}
|
||||||
return 0;
|
|
||||||
}
|
private static int clamp(int i) {
|
||||||
if (i > 255) {
|
if (i < 0) {
|
||||||
return 255;
|
return 0;
|
||||||
}
|
}
|
||||||
return i;
|
if (i > 255) {
|
||||||
}
|
return 255;
|
||||||
|
}
|
||||||
/**
|
return i;
|
||||||
* Filters one pixel, adjusting brightness and contrast according to this
|
}
|
||||||
* filter.
|
|
||||||
*
|
/**
|
||||||
* @param pX x
|
* Filters one pixel, adjusting brightness and contrast according to this
|
||||||
* @param pY y
|
* filter.
|
||||||
* @param pARGB pixel value in default color space
|
*
|
||||||
*
|
* @param pX x
|
||||||
* @return the filtered pixel value in the default color space
|
* @param pY y
|
||||||
*/
|
* @param pARGB pixel value in default color space
|
||||||
public int filterRGB(int pX, int pY, int pARGB) {
|
*
|
||||||
// Get color components
|
* @return the filtered pixel value in the default color space
|
||||||
int r = pARGB >> 16 & 0xFF;
|
*/
|
||||||
int g = pARGB >> 8 & 0xFF;
|
public int filterRGB(int pX, int pY, int pARGB) {
|
||||||
int b = pARGB & 0xFF;
|
// Get color components
|
||||||
|
int r = pARGB >> 16 & 0xFF;
|
||||||
// Scale to new contrast
|
int g = pARGB >> 8 & 0xFF;
|
||||||
r = LUT[r];
|
int b = pARGB & 0xFF;
|
||||||
g = LUT[g];
|
|
||||||
b = LUT[b];
|
// Scale to new contrast
|
||||||
|
r = LUT[r];
|
||||||
// Return ARGB pixel, leave transparency as is
|
g = LUT[g];
|
||||||
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
b = LUT[b];
|
||||||
}
|
|
||||||
}
|
// Return ARGB pixel, leave transparency as is
|
||||||
|
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+100
-88
@@ -44,14 +44,16 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
* A faster, lighter and easier way to convert an {@code Image} to a
|
* A faster, lighter and easier way to convert an {@code Image} to a
|
||||||
* {@code BufferedImage} than using a {@code PixelGrabber}.
|
* {@code BufferedImage} than using a {@code PixelGrabber}.
|
||||||
* Clients may provide progress listeners to monitor conversion progress.
|
* Clients may provide progress listeners to monitor conversion progress.
|
||||||
* <p/>
|
* <p>
|
||||||
* Supports source image subsampling and source region extraction.
|
* Supports source image subsampling and source region extraction.
|
||||||
* Supports source images with 16 bit {@link ColorModel} and
|
* Supports source images with 16 bit {@link ColorModel} and
|
||||||
* {@link DataBuffer#TYPE_USHORT} transfer type, without converting to
|
* {@link DataBuffer#TYPE_USHORT} transfer type, without converting to
|
||||||
* 32 bit/TYPE_INT.
|
* 32 bit/TYPE_INT.
|
||||||
* <p/>
|
* </p>
|
||||||
|
* <p>
|
||||||
* NOTE: Does not support images with more than one {@code ColorModel} or
|
* NOTE: Does not support images with more than one {@code ColorModel} or
|
||||||
* different types of pixel data. This is not very common.
|
* different types of pixel data. This is not very common.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @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/image/BufferedImageFactory.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageFactory.java#1 $
|
||||||
@@ -77,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;
|
||||||
|
|
||||||
@@ -89,21 +91,21 @@ public final class BufferedImageFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code BufferedImageFactory}.
|
* Creates a {@code BufferedImageFactory}.
|
||||||
* @param pSource the source image
|
* @param source the source image
|
||||||
* @throws IllegalArgumentException if {@code pSource == null}
|
* @throws IllegalArgumentException if {@code source == null}
|
||||||
*/
|
*/
|
||||||
public BufferedImageFactory(final Image pSource) {
|
public BufferedImageFactory(final Image source) {
|
||||||
this(pSource != null ? pSource.getSource() : null);
|
this(source != null ? source.getSource() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code BufferedImageFactory}.
|
* Creates a {@code BufferedImageFactory}.
|
||||||
* @param pSource the source image producer
|
* @param source the source image producer
|
||||||
* @throws IllegalArgumentException if {@code pSource == null}
|
* @throws IllegalArgumentException if {@code source == null}
|
||||||
*/
|
*/
|
||||||
public BufferedImageFactory(final ImageProducer pSource) {
|
public BufferedImageFactory(final ImageProducer source) {
|
||||||
Validate.notNull(pSource, "source");
|
Validate.notNull(source, "source");
|
||||||
producer = pSource;
|
producer = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,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 pRegion the source region
|
* @param region the source region
|
||||||
*/
|
*/
|
||||||
public void setSourceRegion(final Rectangle pRegion) {
|
public void setSourceRegion(final Rectangle region) {
|
||||||
// Re-fetch everything, if region changed
|
// Re-fetch everything, if region changed
|
||||||
if (x != pRegion.x || y != pRegion.y || width != pRegion.width || height != pRegion.height) {
|
if (x != region.x || y != region.y || width != region.width || height != region.height) {
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
x = pRegion.x;
|
x = region.x;
|
||||||
y = pRegion.y;
|
y = region.y;
|
||||||
width = pRegion.width;
|
width = region.width;
|
||||||
height = pRegion.height;
|
height = region.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the source subsampling for the new image.
|
* Sets the source subsampling for the new image.
|
||||||
*
|
*
|
||||||
* @param pXSub horizontal subsampling factor
|
* @param xSubsampling horizontal subsampling factor
|
||||||
* @param pYSub vertical subsampling factor
|
* @param ySubsampling vertical subsampling factor
|
||||||
*/
|
*/
|
||||||
public void setSourceSubsampling(int pXSub, int pYSub) {
|
public void setSourceSubsampling(int xSubsampling, int ySubsampling) {
|
||||||
// Re-fetch everything, if subsampling changed
|
// Re-fetch everything, if subsampling changed
|
||||||
if (xSub != pXSub || ySub != pYSub) {
|
if (xSub != xSubsampling || ySub != ySubsampling) {
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pXSub > 1) {
|
if (xSubsampling > 1) {
|
||||||
xSub = pXSub;
|
xSub = xSubsampling;
|
||||||
}
|
}
|
||||||
if (pYSub > 1) {
|
if (ySubsampling > 1) {
|
||||||
ySub = pYSub;
|
ySub = ySubsampling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void doFetch(boolean pColorModelOnly) throws ImageConversionException {
|
private synchronized void doFetch(final boolean colorModelOnly) throws ImageConversionException {
|
||||||
if (!fetching && (!pColorModelOnly && buffered == null || buffered == null && sourceColorModel == null)) {
|
if (!fetching && (!colorModelOnly && 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 (!pColorModelOnly && (xSub > 1 || ySub > 1)) {
|
if (!colorModelOnly && (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;
|
||||||
@@ -205,38 +207,41 @@ public final class BufferedImageFactory {
|
|||||||
|
|
||||||
// Start fetching
|
// Start fetching
|
||||||
fetching = true;
|
fetching = true;
|
||||||
readColorModelOnly = pColorModelOnly;
|
readColorModelOnly = colorModelOnly;
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consumerException != null) {
|
try {
|
||||||
throw new ImageConversionException("Image conversion failed: " + consumerException.getMessage(), consumerException);
|
if (consumerException != null) {
|
||||||
}
|
throw new ImageConversionException("Image conversion failed: " + consumerException.getMessage(), consumerException);
|
||||||
|
}
|
||||||
|
|
||||||
if (pColorModelOnly) {
|
if (colorModelOnly) {
|
||||||
createColorModel();
|
createColorModel();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
createBuffered();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
finally {
|
||||||
createBuffered();
|
// Clean up, in case any objects are copied/cloned, so we can free resources
|
||||||
|
freeResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
||||||
@@ -251,8 +256,9 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up, in case any objects are copied/cloned, so we can free resources
|
if (buffered == null) {
|
||||||
freeResources();
|
throw new ImageConversionException("Could not create BufferedImage");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void freeResources() {
|
private void freeResources() {
|
||||||
@@ -278,27 +284,27 @@ public final class BufferedImageFactory {
|
|||||||
/**
|
/**
|
||||||
* Adds a progress listener to this factory.
|
* Adds a progress listener to this factory.
|
||||||
*
|
*
|
||||||
* @param pListener the progress listener
|
* @param listener the progress listener
|
||||||
*/
|
*/
|
||||||
public void addProgressListener(ProgressListener pListener) {
|
public void addProgressListener(ProgressListener listener) {
|
||||||
if (pListener == null) {
|
if (listener == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listeners == null) {
|
if (listeners == null) {
|
||||||
listeners = new CopyOnWriteArrayList<ProgressListener>();
|
listeners = new CopyOnWriteArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.add(pListener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a progress listener from this factory.
|
* Removes a progress listener from this factory.
|
||||||
*
|
*
|
||||||
* @param pListener the progress listener
|
* @param listener the progress listener
|
||||||
*/
|
*/
|
||||||
public void removeProgressListener(ProgressListener pListener) {
|
public void removeProgressListener(ProgressListener listener) {
|
||||||
if (pListener == null) {
|
if (listener == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +312,7 @@ public final class BufferedImageFactory {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.remove(pListener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -322,21 +328,22 @@ 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 pPixels the pixel data to convert
|
* @param inputPixels the pixel data to convert
|
||||||
* @return an array of {@code short}s, same lenght as {@code pPixels}
|
* @return an array of {@code short}s, same length as {@code inputPixels}
|
||||||
*/
|
*/
|
||||||
private static short[] toShortPixels(int[] pPixels) {
|
private static short[] toShortPixels(int[] inputPixels) {
|
||||||
short[] pixels = new short[pPixels.length];
|
short[] pixels = new short[inputPixels.length];
|
||||||
|
|
||||||
for (int i = 0; i < pixels.length; i++) {
|
for (int i = 0; i < pixels.length; i++) {
|
||||||
pixels[i] = (short) (pPixels[i] & 0xffff);
|
pixels[i] = (short) (inputPixels[i] & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pixels;
|
return pixels;
|
||||||
@@ -349,17 +356,17 @@ public final class BufferedImageFactory {
|
|||||||
* @see BufferedImageFactory#addProgressListener
|
* @see BufferedImageFactory#addProgressListener
|
||||||
* @see BufferedImageFactory#removeProgressListener
|
* @see BufferedImageFactory#removeProgressListener
|
||||||
*/
|
*/
|
||||||
public static interface ProgressListener extends EventListener {
|
public 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 pFactory the factory reporting the progress
|
* @param factory the factory reporting the progress
|
||||||
* @param pPercentage the percentage of progress
|
* @param percentage the percentage of progress
|
||||||
*/
|
*/
|
||||||
void progress(BufferedImageFactory pFactory, float pPercentage);
|
void progress(BufferedImageFactory factory, float percentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Consumer implements ImageConsumer {
|
private class Consumer implements ImageConsumer {
|
||||||
@@ -444,18 +451,18 @@ public final class BufferedImageFactory {
|
|||||||
processProgress(pY + pHeight);
|
processProgress(pY + pHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPixels(int pX, int pY, int pWidth, int pHeight, ColorModel pModel, short[] pPixels, int pOffset, int pScanSize) {
|
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, short[] pixels, int offset, int scanSize) {
|
||||||
setPixelsImpl(pX, pY, pWidth, pHeight, pModel, pPixels, pOffset, pScanSize);
|
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setColorModelOnce(final ColorModel pModel) {
|
private void setColorModelOnce(final ColorModel colorModel) {
|
||||||
// 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 != pModel) {
|
if (sourceColorModel != colorModel) {
|
||||||
if (/*sourceColorModel == null ||*/ sourcePixels == null) {
|
if (sourcePixels == null) {
|
||||||
sourceColorModel = pModel;
|
sourceColorModel = colorModel;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("Change of ColorModel after pixel delivery not supported");
|
throw new IllegalStateException("Change of ColorModel after pixel delivery not supported");
|
||||||
@@ -468,17 +475,16 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void imageComplete(int pStatus) {
|
@Override
|
||||||
|
public void imageComplete(int status) {
|
||||||
fetching = false;
|
fetching = false;
|
||||||
|
|
||||||
if (producer != null) {
|
if (producer != null) {
|
||||||
producer.removeConsumer(this);
|
producer.removeConsumer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (pStatus) {
|
if (status == ImageConsumer.IMAGEERROR) {
|
||||||
case ImageConsumer.IMAGEERROR:
|
consumerException = new ImageConversionException("ImageConsumer.IMAGEERROR");
|
||||||
consumerException = new ImageConversionException("ImageConsumer.IMAGEERROR");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (BufferedImageFactory.this) {
|
synchronized (BufferedImageFactory.this) {
|
||||||
@@ -486,16 +492,18 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColorModel(ColorModel pModel) {
|
@Override
|
||||||
setColorModelOnce(pModel);
|
public void setColorModel(ColorModel colorModel) {
|
||||||
|
setColorModelOnce(colorModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDimensions(int pWidth, int pHeight) {
|
@Override
|
||||||
|
public void setDimensions(int w, int h) {
|
||||||
if (width < 0) {
|
if (width < 0) {
|
||||||
width = pWidth - x;
|
width = w - x;
|
||||||
}
|
}
|
||||||
if (height < 0) {
|
if (height < 0) {
|
||||||
height = pHeight - y;
|
height = h - y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hmm.. Special case, but is it a good idea?
|
// Hmm.. Special case, but is it a good idea?
|
||||||
@@ -504,27 +512,31 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHints(int pHintflags) {
|
@Override
|
||||||
|
public void setHints(int hintFlags) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPixels(int pX, int pY, int pWidth, int pHeight, ColorModel pModel, byte[] pPixels, int pOffset, int pScanSize) {
|
@Override
|
||||||
setPixelsImpl(pX, pY, pWidth, pHeight, pModel, pPixels, pOffset, pScanSize);
|
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, byte[] pixels, int offset, int scanSize) {
|
||||||
|
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPixels(int pX, int pY, int pWeigth, int pHeight, ColorModel pModel, int[] pPixels, int pOffset, int pScanSize) {
|
@Override
|
||||||
if (pModel.getTransferType() == DataBuffer.TYPE_USHORT) {
|
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, int[] pixels, int offset, int scanSize) {
|
||||||
|
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(pX, pY, pWeigth, pHeight, pModel, toShortPixels(pPixels), pOffset, pScanSize);
|
setPixelsImpl(x, y, width, height, colorModel, toShortPixels(pixels), offset, scanSize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setPixelsImpl(pX, pY, pWeigth, pHeight, pModel, pPixels, pOffset, pScanSize);
|
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProperties(Hashtable pProperties) {
|
@Override
|
||||||
sourceProperties = pProperties;
|
public void setProperties(Hashtable properties) {
|
||||||
|
sourceProperties = properties;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,92 +1,90 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/image/BufferedImageIcon.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageIcon.java#2 $
|
*/
|
||||||
*/
|
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) {
|
this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
|
||||||
this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
|
}
|
||||||
}
|
|
||||||
|
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
||||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight);
|
||||||
this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight);
|
}
|
||||||
}
|
|
||||||
|
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) {
|
||||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) {
|
image = Validate.notNull(pImage, "image");
|
||||||
image = Validate.notNull(pImage, "image");
|
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
|
||||||
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
|
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
|
||||||
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
|
|
||||||
|
fast = useFastRendering;
|
||||||
fast = useFastRendering;
|
}
|
||||||
}
|
|
||||||
|
public int getIconHeight() {
|
||||||
public int getIconHeight() {
|
return height;
|
||||||
return height;
|
}
|
||||||
}
|
|
||||||
|
public int getIconWidth() {
|
||||||
public int getIconWidth() {
|
return width;
|
||||||
return width;
|
}
|
||||||
}
|
|
||||||
|
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
if (fast || !(g instanceof Graphics2D)) {
|
||||||
if (fast || !(g instanceof Graphics2D)) {
|
//System.out.println("Scaling fast");
|
||||||
//System.out.println("Scaling fast");
|
g.drawImage(image, x, y, width, height, null);
|
||||||
g.drawImage(image, x, y, width, height, null);
|
}
|
||||||
}
|
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);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,483 +1,485 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This {@code BufferedImageOp/RasterOp} implements basic
|
* This {@code BufferedImageOp/RasterOp} implements basic
|
||||||
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
||||||
* <P/>
|
* <p>
|
||||||
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
||||||
* <!-- - -
|
* <!-- - -
|
||||||
* | |x|7|
|
* | |x|7|
|
||||||
* - - - -
|
* - - - -
|
||||||
* |3|5|1|
|
* |3|5|1|
|
||||||
* - - -->
|
* - - -->
|
||||||
* <P/>
|
* </p>
|
||||||
* <TABLE border="1" cellpadding="4" cellspacing="0">
|
* <table border="1">
|
||||||
* <TR><TD bgcolor="#000000"> </TD><TD class="TableHeadingColor"
|
* <caption>Floyd-Steinberg error-diffusion weights</caption>
|
||||||
* align="center">X</TD><TD>7/16</TD></TR>
|
* <tr><td style="background:#000000"> </td><td class="TableHeadingColor"
|
||||||
* <TR><TD>3/16</TD><TD>5/16</TD><TD>1/16</TD></TR>
|
* style="text-align:center">x</td><td>7/16</td></tr>
|
||||||
* </TABLE>
|
* <tr><td>3/16</td><td>5/16</td><td>1/16</td></tr>
|
||||||
* <P/>
|
* </table>
|
||||||
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
|
* <p>
|
||||||
* for more information.
|
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
|
||||||
*
|
* for more information.
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* </p>
|
||||||
* @author last modified by $Author: haku $
|
*
|
||||||
*
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: DiffusionDither.java#1 $
|
* @author last modified by $Author: haku $
|
||||||
*/
|
*
|
||||||
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
* @version $Id: DiffusionDither.java#1 $
|
||||||
|
*/
|
||||||
private static final int FS_SCALE = 1 << 8;
|
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
||||||
private static final Random RANDOM = new Random();
|
|
||||||
|
private static final int FS_SCALE = 1 << 8;
|
||||||
protected final IndexColorModel indexColorModel;
|
private static final Random RANDOM = new Random();
|
||||||
private boolean alternateScans = true;
|
|
||||||
|
protected final IndexColorModel indexColorModel;
|
||||||
/**
|
private boolean alternateScans = true;
|
||||||
* Creates a {@code DiffusionDither}, using the given
|
|
||||||
* {@code IndexColorModel} for dithering into.
|
/**
|
||||||
*
|
* Creates a {@code DiffusionDither}, using the given
|
||||||
* @param pICM an IndexColorModel.
|
* {@code IndexColorModel} for dithering into.
|
||||||
*/
|
*
|
||||||
public DiffusionDither(final IndexColorModel pICM) {
|
* @param pICM an IndexColorModel.
|
||||||
// Store color model
|
*/
|
||||||
indexColorModel = pICM;
|
public DiffusionDither(final IndexColorModel pICM) {
|
||||||
}
|
// Store color model
|
||||||
|
indexColorModel = pICM;
|
||||||
/**
|
}
|
||||||
* Creates a {@code DiffusionDither}, with no fixed
|
|
||||||
* {@code IndexColorModel}. The color model will be generated for each
|
/**
|
||||||
* filtering, unless the destination image already has an
|
* Creates a {@code DiffusionDither}, with no fixed
|
||||||
* {@code IndexColorModel}.
|
* {@code IndexColorModel}. The color model will be generated for each
|
||||||
*/
|
* filtering, unless the destination image already has an
|
||||||
public DiffusionDither() {
|
* {@code IndexColorModel}.
|
||||||
this(null);
|
*/
|
||||||
}
|
public DiffusionDither() {
|
||||||
|
this(null);
|
||||||
/**
|
}
|
||||||
* Sets the scan mode. If the parameter is true, error distribution for
|
|
||||||
* every even line will be left-to-right, while odd lines will be
|
/**
|
||||||
* right-to-left.
|
* Sets the scan mode. If the parameter is true, error distribution for
|
||||||
* The default is {@code true}.
|
* every even line will be left-to-right, while odd lines will be
|
||||||
*
|
* right-to-left.
|
||||||
* @param pUse {@code true} if scan mode should be alternating left/right
|
* The default is {@code true}.
|
||||||
*/
|
*
|
||||||
public void setAlternateScans(boolean pUse) {
|
* @param pUse {@code true} if scan mode should be alternating left/right
|
||||||
alternateScans = pUse;
|
*/
|
||||||
}
|
public void setAlternateScans(boolean pUse) {
|
||||||
|
alternateScans = pUse;
|
||||||
/**
|
}
|
||||||
* Creates a compatible {@code BufferedImage} to dither into.
|
|
||||||
* Only {@code IndexColorModel} allowed.
|
/**
|
||||||
*
|
* Creates a compatible {@code BufferedImage} to dither into.
|
||||||
* @return a compatible {@code BufferedImage}
|
* Only {@code IndexColorModel} allowed.
|
||||||
*
|
*
|
||||||
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
* @return a compatible {@code BufferedImage}
|
||||||
* an instance of {@code IndexColorModel}.
|
*
|
||||||
*/
|
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
||||||
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
* an instance of {@code IndexColorModel}.
|
||||||
if (pDestCM == null) {
|
*/
|
||||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
||||||
BufferedImage.TYPE_BYTE_INDEXED,
|
if (pDestCM == null) {
|
||||||
getICM(pSource));
|
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||||
}
|
BufferedImage.TYPE_BYTE_INDEXED,
|
||||||
else if (pDestCM instanceof IndexColorModel) {
|
getICM(pSource));
|
||||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
}
|
||||||
BufferedImage.TYPE_BYTE_INDEXED,
|
else if (pDestCM instanceof IndexColorModel) {
|
||||||
(IndexColorModel) pDestCM);
|
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||||
}
|
BufferedImage.TYPE_BYTE_INDEXED,
|
||||||
else {
|
(IndexColorModel) pDestCM);
|
||||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
}
|
||||||
}
|
else {
|
||||||
}
|
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Creates a compatible {@code Raster} to dither into.
|
|
||||||
* Only {@code IndexColorModel} allowed.
|
/**
|
||||||
*
|
* Creates a compatible {@code Raster} to dither into.
|
||||||
* @param pSrc the source raster
|
* Only {@code IndexColorModel} allowed.
|
||||||
*
|
*
|
||||||
* @return a {@code WritableRaster}
|
* @param pSrc the source raster
|
||||||
*/
|
*
|
||||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc) {
|
* @return a {@code WritableRaster}
|
||||||
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
*/
|
||||||
}
|
public final WritableRaster createCompatibleDestRaster(Raster pSrc) {
|
||||||
|
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
||||||
/**
|
}
|
||||||
* Creates a compatible {@code Raster} to dither into.
|
|
||||||
*
|
/**
|
||||||
* @param pSrc the source raster.
|
* Creates a compatible {@code Raster} to dither into.
|
||||||
* @param pIndexColorModel the index color model used to create a {@code Raster}.
|
*
|
||||||
*
|
* @param pSrc the source raster.
|
||||||
* @return a {@code WritableRaster}
|
* @param pIndexColorModel the index color model used to create a {@code Raster}.
|
||||||
*/
|
*
|
||||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
* @return a {@code WritableRaster}
|
||||||
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
*/
|
||||||
}
|
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
||||||
|
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns the bounding box of the filtered destination image. Since
|
|
||||||
* this is not a geometric operation, the bounding box does not
|
/**
|
||||||
* change.
|
* Returns the bounding box of the filtered destination image. Since
|
||||||
* @param pSrc the {@code BufferedImage} to be filtered
|
* this is not a geometric operation, the bounding box does not
|
||||||
* @return the bounds of the filtered definition image.
|
* change.
|
||||||
*/
|
* @param pSrc the {@code BufferedImage} to be filtered
|
||||||
public final Rectangle2D getBounds2D(BufferedImage pSrc) {
|
* @return the bounds of the filtered definition image.
|
||||||
return getBounds2D(pSrc.getRaster());
|
*/
|
||||||
}
|
public final Rectangle2D getBounds2D(BufferedImage pSrc) {
|
||||||
|
return getBounds2D(pSrc.getRaster());
|
||||||
/**
|
}
|
||||||
* Returns the bounding box of the filtered destination Raster. Since
|
|
||||||
* this is not a geometric operation, the bounding box does not
|
/**
|
||||||
* change.
|
* Returns the bounding box of the filtered destination Raster. Since
|
||||||
* @param pSrc the {@code Raster} to be filtered
|
* this is not a geometric operation, the bounding box does not
|
||||||
* @return the bounds of the filtered definition {@code Raster}.
|
* change.
|
||||||
*/
|
* @param pSrc the {@code Raster} to be filtered
|
||||||
public final Rectangle2D getBounds2D(Raster pSrc) {
|
* @return the bounds of the filtered definition {@code Raster}.
|
||||||
return pSrc.getBounds();
|
*/
|
||||||
}
|
public final Rectangle2D getBounds2D(Raster pSrc) {
|
||||||
|
return pSrc.getBounds();
|
||||||
/**
|
}
|
||||||
* Returns the location of the destination point given a
|
|
||||||
* point in the source. If {@code dstPt} is not
|
/**
|
||||||
* {@code null}, it will be used to hold the return value.
|
* Returns the location of the destination point given a
|
||||||
* Since this is not a geometric operation, the {@code srcPt}
|
* point in the source. If {@code dstPt} is not
|
||||||
* will equal the {@code dstPt}.
|
* {@code null}, it will be used to hold the return value.
|
||||||
* @param pSrcPt a {@code Point2D} that represents a point
|
* Since this is not a geometric operation, the {@code srcPt}
|
||||||
* in the source image
|
* will equal the {@code dstPt}.
|
||||||
* @param pDstPt a {@code Point2D}that represents the location
|
* @param pSrcPt a {@code Point2D} that represents a point
|
||||||
* in the destination
|
* in the source image
|
||||||
* @return the {@code Point2D} in the destination that
|
* @param pDstPt a {@code Point2D}that represents the location
|
||||||
* corresponds to the specified point in the source.
|
* in the destination
|
||||||
*/
|
* @return the {@code Point2D} in the destination that
|
||||||
public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) {
|
* corresponds to the specified point in the source.
|
||||||
// Create new Point, if needed
|
*/
|
||||||
if (pDstPt == null) {
|
public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) {
|
||||||
pDstPt = new Point2D.Float();
|
// Create new Point, if needed
|
||||||
}
|
if (pDstPt == null) {
|
||||||
|
pDstPt = new Point2D.Float();
|
||||||
// Copy location
|
}
|
||||||
pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
|
|
||||||
|
// Copy location
|
||||||
// Return dest
|
pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
|
||||||
return pDstPt;
|
|
||||||
}
|
// Return dest
|
||||||
|
return pDstPt;
|
||||||
/**
|
}
|
||||||
* Returns the rendering mHints for this op.
|
|
||||||
* @return the {@code RenderingHints} object associated
|
/**
|
||||||
* with this op.
|
* Returns the rendering mHints for this op.
|
||||||
*/
|
* @return the {@code RenderingHints} object associated
|
||||||
public final RenderingHints getRenderingHints() {
|
* with this op.
|
||||||
return null;
|
*/
|
||||||
}
|
public final RenderingHints getRenderingHints() {
|
||||||
|
return null;
|
||||||
/**
|
}
|
||||||
* Converts an int ARGB to int triplet.
|
|
||||||
*/
|
/**
|
||||||
private static int[] toRGBArray(int pARGB, int[] pBuffer) {
|
* Converts an int ARGB to int triplet.
|
||||||
pBuffer[0] = ((pARGB & 0x00ff0000) >> 16);
|
*/
|
||||||
pBuffer[1] = ((pARGB & 0x0000ff00) >> 8);
|
private static int[] toRGBArray(int pARGB, int[] pBuffer) {
|
||||||
pBuffer[2] = ((pARGB & 0x000000ff));
|
pBuffer[0] = ((pARGB & 0x00ff0000) >> 16);
|
||||||
//pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha
|
pBuffer[1] = ((pARGB & 0x0000ff00) >> 8);
|
||||||
|
pBuffer[2] = ((pARGB & 0x000000ff));
|
||||||
return pBuffer;
|
//pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha
|
||||||
}
|
|
||||||
|
return pBuffer;
|
||||||
/**
|
}
|
||||||
* Converts a int triplet to int ARGB.
|
|
||||||
*/
|
/**
|
||||||
private static int toIntARGB(int[] pRGB) {
|
* Converts a int triplet to int ARGB.
|
||||||
return 0xff000000 // All opaque
|
*/
|
||||||
| (pRGB[0] << 16)
|
private static int toIntARGB(int[] pRGB) {
|
||||||
| (pRGB[1] << 8)
|
return 0xff000000 // All opaque
|
||||||
| (pRGB[2]);
|
| (pRGB[0] << 16)
|
||||||
/*
|
| (pRGB[1] << 8)
|
||||||
| ((int) (pRGB[0] << 16) & 0x00ff0000)
|
| (pRGB[2]);
|
||||||
| ((int) (pRGB[1] << 8) & 0x0000ff00)
|
/*
|
||||||
| ((int) (pRGB[2] ) & 0x000000ff);
|
| ((int) (pRGB[0] << 16) & 0x00ff0000)
|
||||||
*/
|
| ((int) (pRGB[1] << 8) & 0x0000ff00)
|
||||||
}
|
| ((int) (pRGB[2] ) & 0x000000ff);
|
||||||
|
*/
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Performs a single-input/single-output dither operation, applying basic
|
|
||||||
* Floyd-Steinberg error-diffusion to the image.
|
/**
|
||||||
*
|
* Performs a single-input/single-output dither operation, applying basic
|
||||||
* @param pSource the source image
|
* Floyd-Steinberg error-diffusion to the image.
|
||||||
* @param pDest the destination image
|
*
|
||||||
*
|
* @param pSource the source image
|
||||||
* @return the destination image, or a new image, if {@code pDest} was
|
* @param pDest the destination image
|
||||||
* {@code null}.
|
*
|
||||||
*/
|
* @return the destination image, or a new image, if {@code pDest} was
|
||||||
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
* {@code null}.
|
||||||
// Create destination image, if none provided
|
*/
|
||||||
if (pDest == null) {
|
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
||||||
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
// Create destination image, if none provided
|
||||||
}
|
if (pDest == null) {
|
||||||
else if (!(pDest.getColorModel() instanceof IndexColorModel)) {
|
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
||||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
}
|
||||||
}
|
else if (!(pDest.getColorModel() instanceof IndexColorModel)) {
|
||||||
|
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||||
// Filter rasters
|
}
|
||||||
filter(pSource.getRaster(), pDest.getRaster(), (IndexColorModel) pDest.getColorModel());
|
|
||||||
|
// Filter rasters
|
||||||
return pDest;
|
filter(pSource.getRaster(), pDest.getRaster(), (IndexColorModel) pDest.getColorModel());
|
||||||
}
|
|
||||||
|
return pDest;
|
||||||
/**
|
}
|
||||||
* Performs a single-input/single-output dither operation, applying basic
|
|
||||||
* Floyd-Steinberg error-diffusion to the image.
|
/**
|
||||||
*
|
* Performs a single-input/single-output dither operation, applying basic
|
||||||
* @param pSource the source raster, assumed to be in sRGB
|
* Floyd-Steinberg error-diffusion to the image.
|
||||||
* @param pDest the destination raster, may be {@code null}
|
*
|
||||||
*
|
* @param pSource the source raster, assumed to be in sRGB
|
||||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
* @param pDest the destination raster, may be {@code null}
|
||||||
* {@code null}.
|
*
|
||||||
*/
|
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest) {
|
* {@code null}.
|
||||||
return filter(pSource, pDest, getICM(pSource));
|
*/
|
||||||
}
|
public final WritableRaster filter(final Raster pSource, WritableRaster pDest) {
|
||||||
|
return filter(pSource, pDest, getICM(pSource));
|
||||||
private IndexColorModel getICM(BufferedImage pSource) {
|
}
|
||||||
return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
|
|
||||||
}
|
private IndexColorModel getICM(BufferedImage pSource) {
|
||||||
private IndexColorModel getICM(Raster pSource) {
|
return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
|
||||||
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource));
|
}
|
||||||
}
|
private IndexColorModel getICM(Raster pSource) {
|
||||||
|
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource));
|
||||||
private IndexColorModel createIndexColorModel(Raster pSource) {
|
}
|
||||||
BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
|
||||||
BufferedImage.TYPE_INT_ARGB);
|
private IndexColorModel createIndexColorModel(Raster pSource) {
|
||||||
image.setData(pSource);
|
BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||||
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
|
BufferedImage.TYPE_INT_ARGB);
|
||||||
}
|
image.setData(pSource);
|
||||||
|
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
|
||||||
/**
|
}
|
||||||
* Performs a single-input/single-output dither operation, applying basic
|
|
||||||
* Floyd-Steinberg error-diffusion to the image.
|
/**
|
||||||
*
|
* Performs a single-input/single-output dither operation, applying basic
|
||||||
* @param pSource the source raster, assumed to be in sRGB
|
* Floyd-Steinberg error-diffusion to the image.
|
||||||
* @param pDest the destination raster, may be {@code null}
|
*
|
||||||
* @param pColorModel the indexed color model to use
|
* @param pSource the source raster, assumed to be in sRGB
|
||||||
*
|
* @param pDest the destination raster, may be {@code null}
|
||||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
* @param pColorModel the indexed color model to use
|
||||||
* {@code null}.
|
*
|
||||||
*/
|
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
* {@code null}.
|
||||||
int width = pSource.getWidth();
|
*/
|
||||||
int height = pSource.getHeight();
|
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
||||||
|
int width = pSource.getWidth();
|
||||||
// Create destination raster if needed
|
int height = pSource.getHeight();
|
||||||
if (pDest == null) {
|
|
||||||
pDest = createCompatibleDestRaster(pSource, pColorModel);
|
// Create destination raster if needed
|
||||||
}
|
if (pDest == null) {
|
||||||
|
pDest = createCompatibleDestRaster(pSource, pColorModel);
|
||||||
// Initialize Floyd-Steinberg error vectors.
|
}
|
||||||
// +2 to handle the previous pixel and next pixel case minimally
|
|
||||||
// When reference for column, add 1 to reference as this buffer is
|
// Initialize Floyd-Steinberg error vectors.
|
||||||
// offset from actual column position by one to allow FS to not check
|
// +2 to handle the previous pixel and next pixel case minimally
|
||||||
// left/right edge conditions
|
// When reference for column, add 1 to reference as this buffer is
|
||||||
int[][] currErr = new int[width + 2][3];
|
// offset from actual column position by one to allow FS to not check
|
||||||
int[][] nextErr = new int[width + 2][3];
|
// left/right edge conditions
|
||||||
|
int[][] currErr = new int[width + 2][3];
|
||||||
// Random errors in [-1 .. 1] - for first row
|
int[][] nextErr = new int[width + 2][3];
|
||||||
for (int i = 0; i < width + 2; i++) {
|
|
||||||
// Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE
|
// Random errors in [-1 .. 1] - for first row
|
||||||
currErr[i][0] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
for (int i = 0; i < width + 2; i++) {
|
||||||
currErr[i][1] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
// Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE
|
||||||
currErr[i][2] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
currErr[i][0] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||||
}
|
currErr[i][1] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||||
|
currErr[i][2] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||||
// Temp buffers
|
}
|
||||||
final int[] diff = new int[3]; // No alpha
|
|
||||||
final int[] inRGB = new int[4];
|
// Temp buffers
|
||||||
final int[] outRGB = new int[4];
|
final int[] diff = new int[3]; // No alpha
|
||||||
Object pixel = null;
|
final int[] inRGB = new int[4];
|
||||||
boolean forward = true;
|
final int[] outRGB = new int[4];
|
||||||
|
Object pixel = null;
|
||||||
// Loop through image data
|
boolean forward = true;
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
// Clear out next error rows for colour errors
|
// Loop through image data
|
||||||
for (int i = nextErr.length; --i >= 0;) {
|
for (int y = 0; y < height; y++) {
|
||||||
nextErr[i][0] = 0;
|
// Clear out next error rows for colour errors
|
||||||
nextErr[i][1] = 0;
|
for (int i = nextErr.length; --i >= 0;) {
|
||||||
nextErr[i][2] = 0;
|
nextErr[i][0] = 0;
|
||||||
}
|
nextErr[i][1] = 0;
|
||||||
|
nextErr[i][2] = 0;
|
||||||
// Set up start column and limit
|
}
|
||||||
int x;
|
|
||||||
int limit;
|
// Set up start column and limit
|
||||||
if (forward) {
|
int x;
|
||||||
x = 0;
|
int limit;
|
||||||
limit = width;
|
if (forward) {
|
||||||
}
|
x = 0;
|
||||||
else {
|
limit = width;
|
||||||
x = width - 1;
|
}
|
||||||
limit = -1;
|
else {
|
||||||
}
|
x = width - 1;
|
||||||
|
limit = -1;
|
||||||
// TODO: Use getPixels instead of getPixel for better performance?
|
}
|
||||||
|
|
||||||
// Loop over row
|
// TODO: Use getPixels instead of getPixel for better performance?
|
||||||
while (true) {
|
|
||||||
// Get RGB from original raster
|
// Loop over row
|
||||||
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES.
|
while (true) {
|
||||||
pSource.getPixel(x, y, inRGB);
|
// Get RGB from original raster
|
||||||
|
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES.
|
||||||
// Get error for this pixel & add error to rgb
|
pSource.getPixel(x, y, inRGB);
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
// Make a 28.4 FP number, add Error (with fraction),
|
// Get error for this pixel & add error to rgb
|
||||||
// rounding and truncate to int
|
for (int i = 0; i < 3; i++) {
|
||||||
inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4;
|
// Make a 28.4 FP number, add Error (with fraction),
|
||||||
|
// rounding and truncate to int
|
||||||
// Clamp
|
inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4;
|
||||||
if (inRGB[i] > 255) {
|
|
||||||
inRGB[i] = 255;
|
// Clamp
|
||||||
}
|
if (inRGB[i] > 255) {
|
||||||
else if (inRGB[i] < 0) {
|
inRGB[i] = 255;
|
||||||
inRGB[i] = 0;
|
}
|
||||||
}
|
else if (inRGB[i] < 0) {
|
||||||
}
|
inRGB[i] = 0;
|
||||||
|
}
|
||||||
// Get pixel value...
|
}
|
||||||
// It is VERY important that we are using a IndexColorModel that
|
|
||||||
// support reverse color lookup for speed.
|
// Get pixel value...
|
||||||
pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel);
|
// It is VERY important that we are using a IndexColorModel that
|
||||||
|
// support reverse color lookup for speed.
|
||||||
// ...set it...
|
pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel);
|
||||||
pDest.setDataElements(x, y, pixel);
|
|
||||||
|
// ...set it...
|
||||||
// ..and get back the closet match
|
pDest.setDataElements(x, y, pixel);
|
||||||
pDest.getPixel(x, y, outRGB);
|
|
||||||
|
// ..and get back the closet match
|
||||||
// Convert the value to default sRGB
|
pDest.getPixel(x, y, outRGB);
|
||||||
// Should work for all transfertypes supported by IndexColorModel
|
|
||||||
toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB);
|
// Convert the value to default sRGB
|
||||||
|
// Should work for all transfertypes supported by IndexColorModel
|
||||||
// Find diff
|
toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB);
|
||||||
diff[0] = inRGB[0] - outRGB[0];
|
|
||||||
diff[1] = inRGB[1] - outRGB[1];
|
// Find diff
|
||||||
diff[2] = inRGB[2] - outRGB[2];
|
diff[0] = inRGB[0] - outRGB[0];
|
||||||
|
diff[1] = inRGB[1] - outRGB[1];
|
||||||
// Apply F-S error diffusion
|
diff[2] = inRGB[2] - outRGB[2];
|
||||||
// Serpentine scan: left-right
|
|
||||||
if (forward) {
|
// Apply F-S error diffusion
|
||||||
// Row 1 (y)
|
// Serpentine scan: left-right
|
||||||
// Update error in this pixel (x + 1)
|
if (forward) {
|
||||||
currErr[x + 2][0] += diff[0] * 7;
|
// Row 1 (y)
|
||||||
currErr[x + 2][1] += diff[1] * 7;
|
// Update error in this pixel (x + 1)
|
||||||
currErr[x + 2][2] += diff[2] * 7;
|
currErr[x + 2][0] += diff[0] * 7;
|
||||||
|
currErr[x + 2][1] += diff[1] * 7;
|
||||||
// Row 2 (y + 1)
|
currErr[x + 2][2] += diff[2] * 7;
|
||||||
// Update error in this pixel (x - 1)
|
|
||||||
nextErr[x][0] += diff[0] * 3;
|
// Row 2 (y + 1)
|
||||||
nextErr[x][1] += diff[1] * 3;
|
// Update error in this pixel (x - 1)
|
||||||
nextErr[x][2] += diff[2] * 3;
|
nextErr[x][0] += diff[0] * 3;
|
||||||
// Update error in this pixel (x)
|
nextErr[x][1] += diff[1] * 3;
|
||||||
nextErr[x + 1][0] += diff[0] * 5;
|
nextErr[x][2] += diff[2] * 3;
|
||||||
nextErr[x + 1][1] += diff[1] * 5;
|
// Update error in this pixel (x)
|
||||||
nextErr[x + 1][2] += diff[2] * 5;
|
nextErr[x + 1][0] += diff[0] * 5;
|
||||||
// Update error in this pixel (x + 1)
|
nextErr[x + 1][1] += diff[1] * 5;
|
||||||
// TODO: Consider calculating this using
|
nextErr[x + 1][2] += diff[2] * 5;
|
||||||
// error term = error - sum(error terms 1, 2 and 3)
|
// Update error in this pixel (x + 1)
|
||||||
// See Computer Graphics (Foley et al.), p. 573
|
// TODO: Consider calculating this using
|
||||||
nextErr[x + 2][0] += diff[0]; // * 1;
|
// error term = error - sum(error terms 1, 2 and 3)
|
||||||
nextErr[x + 2][1] += diff[1]; // * 1;
|
// See Computer Graphics (Foley et al.), p. 573
|
||||||
nextErr[x + 2][2] += diff[2]; // * 1;
|
nextErr[x + 2][0] += diff[0]; // * 1;
|
||||||
|
nextErr[x + 2][1] += diff[1]; // * 1;
|
||||||
// Next
|
nextErr[x + 2][2] += diff[2]; // * 1;
|
||||||
x++;
|
|
||||||
|
// Next
|
||||||
// Done?
|
x++;
|
||||||
if (x >= limit) {
|
|
||||||
break;
|
// Done?
|
||||||
}
|
if (x >= limit) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Row 1 (y)
|
}
|
||||||
// Update error in this pixel (x - 1)
|
else {
|
||||||
currErr[x][0] += diff[0] * 7;
|
// Row 1 (y)
|
||||||
currErr[x][1] += diff[1] * 7;
|
// Update error in this pixel (x - 1)
|
||||||
currErr[x][2] += diff[2] * 7;
|
currErr[x][0] += diff[0] * 7;
|
||||||
|
currErr[x][1] += diff[1] * 7;
|
||||||
// Row 2 (y + 1)
|
currErr[x][2] += diff[2] * 7;
|
||||||
// Update error in this pixel (x + 1)
|
|
||||||
nextErr[x + 2][0] += diff[0] * 3;
|
// Row 2 (y + 1)
|
||||||
nextErr[x + 2][1] += diff[1] * 3;
|
// Update error in this pixel (x + 1)
|
||||||
nextErr[x + 2][2] += diff[2] * 3;
|
nextErr[x + 2][0] += diff[0] * 3;
|
||||||
// Update error in this pixel (x)
|
nextErr[x + 2][1] += diff[1] * 3;
|
||||||
nextErr[x + 1][0] += diff[0] * 5;
|
nextErr[x + 2][2] += diff[2] * 3;
|
||||||
nextErr[x + 1][1] += diff[1] * 5;
|
// Update error in this pixel (x)
|
||||||
nextErr[x + 1][2] += diff[2] * 5;
|
nextErr[x + 1][0] += diff[0] * 5;
|
||||||
// Update error in this pixel (x - 1)
|
nextErr[x + 1][1] += diff[1] * 5;
|
||||||
// TODO: Consider calculating this using
|
nextErr[x + 1][2] += diff[2] * 5;
|
||||||
// error term = error - sum(error terms 1, 2 and 3)
|
// Update error in this pixel (x - 1)
|
||||||
// See Computer Graphics (Foley et al.), p. 573
|
// TODO: Consider calculating this using
|
||||||
nextErr[x][0] += diff[0]; // * 1;
|
// error term = error - sum(error terms 1, 2 and 3)
|
||||||
nextErr[x][1] += diff[1]; // * 1;
|
// See Computer Graphics (Foley et al.), p. 573
|
||||||
nextErr[x][2] += diff[2]; // * 1;
|
nextErr[x][0] += diff[0]; // * 1;
|
||||||
|
nextErr[x][1] += diff[1]; // * 1;
|
||||||
// Previous
|
nextErr[x][2] += diff[2]; // * 1;
|
||||||
x--;
|
|
||||||
|
// Previous
|
||||||
// Done?
|
x--;
|
||||||
if (x <= limit) {
|
|
||||||
break;
|
// Done?
|
||||||
}
|
if (x <= limit) {
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Make next error info current for next iteration
|
}
|
||||||
int[][] temperr;
|
|
||||||
temperr = currErr;
|
// Make next error info current for next iteration
|
||||||
currErr = nextErr;
|
int[][] temperr;
|
||||||
nextErr = temperr;
|
temperr = currErr;
|
||||||
|
currErr = nextErr;
|
||||||
// Toggle direction
|
nextErr = temperr;
|
||||||
if (alternateScans) {
|
|
||||||
forward = !forward;
|
// Toggle direction
|
||||||
}
|
if (alternateScans) {
|
||||||
}
|
forward = !forward;
|
||||||
|
}
|
||||||
return pDest;
|
}
|
||||||
}
|
|
||||||
|
return pDest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,84 +1,86 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GraphicsUtil
|
* GraphicsUtil
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GraphicsUtil.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GraphicsUtil.java#1 $
|
||||||
*/
|
*/
|
||||||
public final class GraphicsUtil {
|
public final class GraphicsUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables anti-aliasing in the {@code Graphics} object.
|
* Enables anti-aliasing in the {@code Graphics} object.
|
||||||
* <p/>
|
* <p>
|
||||||
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
||||||
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
||||||
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
||||||
*
|
* </p>
|
||||||
* @param pGraphics the graphics object
|
*
|
||||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
* @param pGraphics the graphics object
|
||||||
* {@code Graphics2D}.
|
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||||
*
|
* {@code Graphics2D}.
|
||||||
* @see java.awt.RenderingHints#KEY_ANTIALIASING
|
*
|
||||||
*/
|
* @see java.awt.RenderingHints#KEY_ANTIALIASING
|
||||||
public static void enableAA(final Graphics pGraphics) {
|
*/
|
||||||
((Graphics2D) pGraphics).setRenderingHint(
|
public static void enableAA(final Graphics pGraphics) {
|
||||||
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
|
((Graphics2D) pGraphics).setRenderingHint(
|
||||||
);
|
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
|
||||||
}
|
);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Sets the alpha in the {@code Graphics} object.
|
/**
|
||||||
* <p/>
|
* Sets the alpha in the {@code Graphics} object.
|
||||||
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
* <p>
|
||||||
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
||||||
* alpha.
|
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
||||||
*
|
* alpha.
|
||||||
* @param pGraphics the graphics object
|
* </p>
|
||||||
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
*
|
||||||
* number in the inclusive range [0.0, 1.0].
|
* @param pGraphics the graphics object
|
||||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
||||||
* {@code Graphics2D}.
|
* number in the inclusive range [0.0, 1.0].
|
||||||
*
|
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||||
* @see java.awt.AlphaComposite#SRC_OVER
|
* {@code Graphics2D}.
|
||||||
* @see java.awt.AlphaComposite#getInstance(int, float)
|
*
|
||||||
*/
|
* @see java.awt.AlphaComposite#SRC_OVER
|
||||||
public static void setAlpha(final Graphics pGraphics, final float pAlpha) {
|
* @see java.awt.AlphaComposite#getInstance(int, float)
|
||||||
((Graphics2D) pGraphics).setComposite(
|
*/
|
||||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha)
|
public static void setAlpha(final Graphics pGraphics, final float pAlpha) {
|
||||||
);
|
((Graphics2D) pGraphics).setComposite(
|
||||||
}
|
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha)
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,131 +1,132 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import java.awt.image.RGBImageFilter;
|
import java.awt.image.RGBImageFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class can convert a color image to grayscale.
|
* This class can convert a color image to grayscale.
|
||||||
* <P/>
|
* <p>
|
||||||
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
|
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
|
||||||
*
|
* </p>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
*
|
||||||
* @author last modified by $Author: haku $
|
* @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/GrayFilter.java#1 $
|
*
|
||||||
*
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GrayFilter.java#1 $
|
||||||
*/
|
*
|
||||||
public class GrayFilter extends RGBImageFilter {
|
*/
|
||||||
|
public class GrayFilter extends RGBImageFilter {
|
||||||
// This filter can filter IndexColorModel
|
|
||||||
{
|
// This filter can filter IndexColorModel
|
||||||
canFilterIndexColorModel = true;
|
{
|
||||||
}
|
canFilterIndexColorModel = true;
|
||||||
|
}
|
||||||
private int low = 0;
|
|
||||||
private float range = 1.0f;
|
private int low = 0;
|
||||||
|
private float range = 1.0f;
|
||||||
/**
|
|
||||||
* Constructs a GrayFilter using ITU color-conversion.
|
/**
|
||||||
*/
|
* Constructs a GrayFilter using ITU color-conversion.
|
||||||
public GrayFilter() {
|
*/
|
||||||
}
|
public GrayFilter() {
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Constructs a GrayFilter using ITU color-conversion, and a dynamic range between
|
/**
|
||||||
* pLow and pHigh.
|
* Constructs a GrayFilter using ITU color-conversion, and a dynamic range between
|
||||||
*
|
* pLow and pHigh.
|
||||||
* @param pLow float in the range 0..1
|
*
|
||||||
* @param pHigh float in the range 0..1 and >= pLow
|
* @param pLow float in the range 0..1
|
||||||
*/
|
* @param pHigh float in the range 0..1 and >= pLow
|
||||||
public GrayFilter(float pLow, float pHigh) {
|
*/
|
||||||
if (pLow > pHigh) {
|
public GrayFilter(float pLow, float pHigh) {
|
||||||
pLow = 0f;
|
if (pLow > pHigh) {
|
||||||
}
|
pLow = 0f;
|
||||||
// Make sure high and low are inside range
|
}
|
||||||
if (pLow < 0f) {
|
// Make sure high and low are inside range
|
||||||
pLow = 0f;
|
if (pLow < 0f) {
|
||||||
}
|
pLow = 0f;
|
||||||
else if (pLow > 1f) {
|
}
|
||||||
pLow = 1f;
|
else if (pLow > 1f) {
|
||||||
}
|
pLow = 1f;
|
||||||
if (pHigh < 0f) {
|
}
|
||||||
pHigh = 0f;
|
if (pHigh < 0f) {
|
||||||
}
|
pHigh = 0f;
|
||||||
else if (pHigh > 1f) {
|
}
|
||||||
pHigh = 1f;
|
else if (pHigh > 1f) {
|
||||||
}
|
pHigh = 1f;
|
||||||
|
}
|
||||||
low = (int) (pLow * 255f);
|
|
||||||
range = pHigh - pLow;
|
low = (int) (pLow * 255f);
|
||||||
|
range = pHigh - pLow;
|
||||||
}
|
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Constructs a GrayFilter using ITU color-conversion, and a dynamic
|
/**
|
||||||
* range between pLow and pHigh.
|
* Constructs a GrayFilter using ITU color-conversion, and a dynamic
|
||||||
*
|
* range between pLow and pHigh.
|
||||||
* @param pLow integer in the range 0..255
|
*
|
||||||
* @param pHigh inteeger in the range 0..255 and >= pLow
|
* @param pLow integer in the range 0..255
|
||||||
*/
|
* @param pHigh integer in the range 0..255 and >= pLow
|
||||||
public GrayFilter(int pLow, int pHigh) {
|
*/
|
||||||
this(pLow / 255f, pHigh / 255f);
|
public GrayFilter(int pLow, int pHigh) {
|
||||||
}
|
this(pLow / 255f, pHigh / 255f);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Filters one pixel using ITU color-conversion.
|
/**
|
||||||
*
|
* Filters one pixel using ITU color-conversion.
|
||||||
* @param pX x
|
*
|
||||||
* @param pY y
|
* @param pX x
|
||||||
* @param pARGB pixel value in default color space
|
* @param pY y
|
||||||
*
|
* @param pARGB pixel value in default color space
|
||||||
* @return the filtered pixel value in the default color space
|
*
|
||||||
*/
|
* @return the filtered pixel value in the default color space
|
||||||
public int filterRGB(int pX, int pY, int pARGB) {
|
*/
|
||||||
// Get color components
|
public int filterRGB(int pX, int pY, int pARGB) {
|
||||||
int r = pARGB >> 16 & 0xFF;
|
// Get color components
|
||||||
int g = pARGB >> 8 & 0xFF;
|
int r = pARGB >> 16 & 0xFF;
|
||||||
int b = pARGB & 0xFF;
|
int g = pARGB >> 8 & 0xFF;
|
||||||
|
int b = pARGB & 0xFF;
|
||||||
// ITU standard: Gray scale=(222*Red+707*Green+71*Blue)/1000
|
|
||||||
int gray = (222 * r + 707 * g + 71 * b) / 1000;
|
// ITU standard: Gray scale=(222*Red+707*Green+71*Blue)/1000
|
||||||
|
int gray = (222 * r + 707 * g + 71 * b) / 1000;
|
||||||
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
|
||||||
|
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
||||||
if (range != 1.0f) {
|
|
||||||
// Apply range
|
if (range != 1.0f) {
|
||||||
gray = low + (int) (gray * range);
|
// Apply range
|
||||||
}
|
gray = low + (int) (gray * range);
|
||||||
|
}
|
||||||
// Return ARGB pixel
|
|
||||||
return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
|
// Return ARGB pixel
|
||||||
}
|
return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,213 +1,214 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inverse Colormap to provide efficient lookup of any given input color
|
* Inverse Colormap to provide efficient lookup of any given input color
|
||||||
* to the closest match to the given color map.
|
* to the closest match to the given color map.
|
||||||
* <p/>
|
* <p>
|
||||||
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
||||||
* in "Graphics Gems Volume II"
|
* in "Graphics Gems Volume II".
|
||||||
*
|
* </p>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
*
|
||||||
* @author Robin Luiten (Java port)
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author Spencer W. Thomas (original c version).
|
* @author Robin Luiten (Java port)
|
||||||
*
|
* @author Spencer W. Thomas (original c version).
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/InverseColorMap.java#1 $
|
*
|
||||||
*/
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/InverseColorMap.java#1 $
|
||||||
class InverseColorMap {
|
*/
|
||||||
/**
|
class InverseColorMap {
|
||||||
* Number of high bits of each color channel to use to lookup near match
|
/**
|
||||||
*/
|
* Number of high bits of each color channel to use to lookup near match
|
||||||
final static int QUANTBITS = 5;
|
*/
|
||||||
|
final static int QUANTBITS = 5;
|
||||||
/**
|
|
||||||
* Truncated bits of each color channel
|
/**
|
||||||
*/
|
* Truncated bits of each color channel
|
||||||
final static int TRUNCBITS = 8 - QUANTBITS;
|
*/
|
||||||
|
final static int TRUNCBITS = 8 - QUANTBITS;
|
||||||
/**
|
|
||||||
* BITMASK representing the bits for blue in the color lookup
|
/**
|
||||||
*/
|
* BITMASK representing the bits for blue in the color lookup
|
||||||
final static int QUANTMASK_BLUE = (1 << 5) - 1;
|
*/
|
||||||
|
final static int QUANTMASK_BLUE = (1 << 5) - 1;
|
||||||
/**
|
|
||||||
* BITMASK representing the bits for green in the color lookup
|
/**
|
||||||
*/
|
* BITMASK representing the bits for green in the color lookup
|
||||||
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS);
|
*/
|
||||||
|
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS);
|
||||||
/**
|
|
||||||
* BITMASK representing the bits for red in the color lookup
|
/**
|
||||||
*/
|
* BITMASK representing the bits for red in the color lookup
|
||||||
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS);
|
*/
|
||||||
|
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS);
|
||||||
/**
|
|
||||||
* Maximum value a quantised color channel can have
|
/**
|
||||||
*/
|
* Maximum value a quantised color channel can have
|
||||||
final static int MAXQUANTVAL = 1 << 5;
|
*/
|
||||||
|
final static int MAXQUANTVAL = 1 << 5;
|
||||||
byte[] rgbMapByte;
|
|
||||||
int[] rgbMapInt;
|
byte[] rgbMapByte;
|
||||||
int numColors;
|
int[] rgbMapInt;
|
||||||
int maxColor;
|
int numColors;
|
||||||
byte[] inverseRGB; // inverse rgb color map
|
int maxColor;
|
||||||
int transparentIndex = -1;
|
byte[] inverseRGB; // inverse rgb color map
|
||||||
|
int transparentIndex = -1;
|
||||||
/**
|
|
||||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
/**
|
||||||
*/
|
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||||
InverseColorMap(byte[] pRGBColorMap) {
|
*/
|
||||||
this(pRGBColorMap, -1);
|
InverseColorMap(byte[] pRGBColorMap) {
|
||||||
}
|
this(pRGBColorMap, -1);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
/**
|
||||||
*/
|
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||||
// HaraldK 20040801: Added support for int[]
|
*/
|
||||||
InverseColorMap(int[] pRGBColorMap) {
|
// HaraldK 20040801: Added support for int[]
|
||||||
this(pRGBColorMap, -1);
|
InverseColorMap(int[] pRGBColorMap) {
|
||||||
}
|
this(pRGBColorMap, -1);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
/**
|
||||||
* @param pTransparent the index of the transparent pixel in the map
|
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||||
*/
|
* @param pTransparent the index of the transparent pixel in the map
|
||||||
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
*/
|
||||||
rgbMapByte = pRGBColorMap;
|
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
||||||
numColors = rgbMapByte.length / 4;
|
rgbMapByte = pRGBColorMap;
|
||||||
transparentIndex = pTransparent;
|
numColors = rgbMapByte.length / 4;
|
||||||
|
transparentIndex = pTransparent;
|
||||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
|
||||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||||
}
|
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
/**
|
||||||
* @param pTransparent the index of the transparent pixel in the map
|
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||||
*/
|
* @param pTransparent the index of the transparent pixel in the map
|
||||||
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
*/
|
||||||
rgbMapInt = pRGBColorMap;
|
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
||||||
numColors = rgbMapInt.length;
|
rgbMapInt = pRGBColorMap;
|
||||||
transparentIndex = pTransparent;
|
numColors = rgbMapInt.length;
|
||||||
|
transparentIndex = pTransparent;
|
||||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
|
||||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||||
}
|
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple inverse color table creation method.
|
/**
|
||||||
* @param pTemp temp array
|
* Simple inverse color table creation method.
|
||||||
*/
|
* @param pTemp temp array
|
||||||
void initIRGB(int[] pTemp) {
|
*/
|
||||||
final int x = (1 << TRUNCBITS); // 8 the size of 1 Dimension of each quantized cell
|
void initIRGB(int[] pTemp) {
|
||||||
final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors
|
final int x = (1 << TRUNCBITS); // 8 the size of 1 Dimension of each quantized cell
|
||||||
final int xsqr2 = xsqr + xsqr;
|
final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors
|
||||||
|
final int xsqr2 = xsqr + xsqr;
|
||||||
for (int i = 0; i < numColors; ++i) {
|
|
||||||
if (i == transparentIndex) {
|
for (int i = 0; i < numColors; ++i) {
|
||||||
// Skip the transparent pixel
|
if (i == transparentIndex) {
|
||||||
continue;
|
// Skip the transparent pixel
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
int red, r, rdist, rinc, rxx;
|
|
||||||
int green, g, gdist, ginc, gxx;
|
int red, r, rdist, rinc, rxx;
|
||||||
int blue, b, bdist, binc, bxx;
|
int green, g, gdist, ginc, gxx;
|
||||||
|
int blue, b, bdist, binc, bxx;
|
||||||
// HaraldK 20040801: Added support for int[]
|
|
||||||
if (rgbMapByte != null) {
|
// HaraldK 20040801: Added support for int[]
|
||||||
red = rgbMapByte[i * 4] & 0xFF;
|
if (rgbMapByte != null) {
|
||||||
green = rgbMapByte[i * 4 + 1] & 0xFF;
|
red = rgbMapByte[i * 4] & 0xFF;
|
||||||
blue = rgbMapByte[i * 4 + 2] & 0xFF;
|
green = rgbMapByte[i * 4 + 1] & 0xFF;
|
||||||
}
|
blue = rgbMapByte[i * 4 + 2] & 0xFF;
|
||||||
else if (rgbMapInt != null) {
|
}
|
||||||
red = (rgbMapInt[i] >> 16) & 0xFF;
|
else if (rgbMapInt != null) {
|
||||||
green = (rgbMapInt[i] >> 8) & 0xFF;
|
red = (rgbMapInt[i] >> 16) & 0xFF;
|
||||||
blue = rgbMapInt[i] & 0xFF;
|
green = (rgbMapInt[i] >> 8) & 0xFF;
|
||||||
}
|
blue = rgbMapInt[i] & 0xFF;
|
||||||
else {
|
}
|
||||||
throw new IllegalStateException("colormap == null");
|
else {
|
||||||
}
|
throw new IllegalStateException("colormap == null");
|
||||||
|
}
|
||||||
rdist = red - x / 2; // distance of red to center of current cell
|
|
||||||
gdist = green - x / 2; // green
|
rdist = red - x / 2; // distance of red to center of current cell
|
||||||
bdist = blue - x / 2; // blue
|
gdist = green - x / 2; // green
|
||||||
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
|
bdist = blue - x / 2; // blue
|
||||||
|
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
|
||||||
rinc = 2 * (xsqr - (red << TRUNCBITS));
|
|
||||||
ginc = 2 * (xsqr - (green << TRUNCBITS));
|
rinc = 2 * (xsqr - (red << TRUNCBITS));
|
||||||
binc = 2 * (xsqr - (blue << TRUNCBITS));
|
ginc = 2 * (xsqr - (green << TRUNCBITS));
|
||||||
|
binc = 2 * (xsqr - (blue << TRUNCBITS));
|
||||||
int rgbI = 0;
|
|
||||||
for (r = 0, rxx = rinc; r < MAXQUANTVAL; rdist += rxx, ++r, rxx += xsqr2) {
|
int rgbI = 0;
|
||||||
for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) {
|
for (r = 0, rxx = rinc; r < MAXQUANTVAL; rdist += rxx, ++r, rxx += xsqr2) {
|
||||||
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) {
|
||||||
if (i == 0 || pTemp[rgbI] > bdist) {
|
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
||||||
pTemp[rgbI] = bdist;
|
if (i == 0 || pTemp[rgbI] > bdist) {
|
||||||
inverseRGB[rgbI] = (byte) i;
|
pTemp[rgbI] = bdist;
|
||||||
}
|
inverseRGB[rgbI] = (byte) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Gets the index of the nearest color to from the color map.
|
/**
|
||||||
*
|
* Gets the index of the nearest color to from the color map.
|
||||||
* @param pColor the color to get the nearest color to from color map
|
*
|
||||||
* color must be of format {@code 0x00RRGGBB} - standard default RGB
|
* @param pColor the color to get the nearest color to from color map
|
||||||
* @return index of color which closest matches input color by using the
|
* color must be of format {@code 0x00RRGGBB} - standard default RGB
|
||||||
* created inverse color map.
|
* @return index of color which closest matches input color by using the
|
||||||
*/
|
* created inverse color map.
|
||||||
public final int getIndexNearest(int pColor) {
|
*/
|
||||||
return inverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) +
|
public final int getIndexNearest(int pColor) {
|
||||||
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
return inverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) +
|
||||||
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||||
}
|
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Gets the index of the nearest color to from the color map.
|
/**
|
||||||
*
|
* Gets the index of the nearest color to from the color map.
|
||||||
* @param pRed red component of the color to get the nearest color to from color map
|
*
|
||||||
* @param pGreen green component of the color to get the nearest color to from color map
|
* @param pRed red component of the color to get the nearest color to from color map
|
||||||
* @param pBlue blue component of the color to get the nearest color to from color map
|
* @param pGreen green component of the color to get the nearest color to from color map
|
||||||
* @return index of color which closest matches input color by using the
|
* @param pBlue blue component of the color to get the nearest color to from color map
|
||||||
* created inverse color map.
|
* @return index of color which closest matches input color by using the
|
||||||
*/
|
* created inverse color map.
|
||||||
public final int getIndexNearest(int pRed, int pGreen, int pBlue) {
|
*/
|
||||||
// NOTE: the third line in expression for blue is shifting DOWN not UP.
|
public final int getIndexNearest(int pRed, int pGreen, int pBlue) {
|
||||||
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
// NOTE: the third line in expression for blue is shifting DOWN not UP.
|
||||||
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
||||||
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||||
}
|
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,616 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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>
|
|
||||||
*
|
|
||||||
* @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}:
|
|
||||||
* <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 <= 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 <= 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>
|
|
||||||
*
|
|
||||||
* @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}:
|
|
||||||
* <dl>
|
|
||||||
* <dt>{@code IndexColorModel} with 1 bit b/w</dt>
|
|
||||||
* <dd>{@code MagickImage} of type {@code ImageType.BilevelType}</dd>
|
|
||||||
* <dt>{@code IndexColorModel} > 1 bit,</dt>
|
|
||||||
* <dd>{@code MagickImage} of type {@code ImageType.PaletteType}
|
|
||||||
* or {@code MagickImage} of type {@code ImageType.PaletteMatteType}
|
|
||||||
* depending on <tt>ColorModel.getAlpha()</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()</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>
|
|
||||||
*
|
|
||||||
* @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 <= 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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,79 +1,80 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import java.awt.image.ReplicateScaleFilter;
|
import java.awt.image.ReplicateScaleFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code ImageFilter} class for subsampling images.
|
* An {@code ImageFilter} class for subsampling images.
|
||||||
* <p/>
|
* <p>
|
||||||
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
||||||
* object to produce subsampled versions of existing images.
|
* object to produce subsampled versions of existing images.
|
||||||
*
|
* </p>
|
||||||
* @see java.awt.image.FilteredImageSource
|
*
|
||||||
*
|
* @see java.awt.image.FilteredImageSource
|
||||||
* @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/SubsamplingFilter.java#1 $
|
* @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/SubsamplingFilter.java#1 $
|
||||||
public class SubsamplingFilter extends ReplicateScaleFilter {
|
*/
|
||||||
private int xSub;
|
public class SubsamplingFilter extends ReplicateScaleFilter {
|
||||||
private int ySub;
|
private int xSub;
|
||||||
|
private int ySub;
|
||||||
/**
|
|
||||||
* Creates a {@code SubsamplingFilter}.
|
/**
|
||||||
*
|
* Creates a {@code SubsamplingFilter}.
|
||||||
* @param pXSub
|
*
|
||||||
* @param pYSub
|
* @param pXSub
|
||||||
*
|
* @param pYSub
|
||||||
* @throws IllegalArgumentException if {@code pXSub} or {@code pYSub} is
|
*
|
||||||
* less than 1.
|
* @throws IllegalArgumentException if {@code pXSub} or {@code pYSub} is
|
||||||
*/
|
* less than 1.
|
||||||
public SubsamplingFilter(int pXSub, int pYSub) {
|
*/
|
||||||
super(1, 1); // These are NOT REAL values, but we have to defer setting
|
public SubsamplingFilter(int pXSub, int pYSub) {
|
||||||
// until w/h is available, in setDimensions below
|
super(1, 1); // These are NOT REAL values, but we have to defer setting
|
||||||
|
// until w/h is available, in setDimensions below
|
||||||
if (pXSub < 1 || pYSub < 1) {
|
|
||||||
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
if (pXSub < 1 || pYSub < 1) {
|
||||||
}
|
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
||||||
|
}
|
||||||
xSub = pXSub;
|
|
||||||
ySub = pYSub;
|
xSub = pXSub;
|
||||||
}
|
ySub = pYSub;
|
||||||
|
}
|
||||||
/** {@code ImageFilter} implementation, do not invoke. */
|
|
||||||
public void setDimensions(int pWidth, int pHeight) {
|
/** {@code ImageFilter} implementation, do not invoke. */
|
||||||
destWidth = (pWidth + xSub - 1) / xSub;
|
public void setDimensions(int pWidth, int pHeight) {
|
||||||
destHeight = (pHeight + ySub - 1) / ySub;
|
destWidth = (pWidth + xSub - 1) / xSub;
|
||||||
|
destHeight = (pHeight + ySub - 1) / ySub;
|
||||||
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
|
|
||||||
super.setDimensions(pWidth, pHeight);
|
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
|
||||||
}
|
super.setDimensions(pWidth, pHeight);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,8 +30,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes for image manipulation.
|
* Classes for image manipulation.
|
||||||
* <p/>
|
* <p>
|
||||||
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
|||||||
+42
-38
@@ -30,17 +30,24 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import org.junit.Test;
|
import static java.lang.Math.min;
|
||||||
|
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.*;
|
import java.awt.image.BufferedImage;
|
||||||
|
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 static org.junit.Assert.*;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AffineTransformOpTest.
|
* AffineTransformOpTest.
|
||||||
@@ -101,6 +108,7 @@ 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() {
|
||||||
@@ -128,33 +136,33 @@ public class AffineTransformOpTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateBIStandard() {
|
public void testFilterRotateBIStandard() {
|
||||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
BufferedImageOp jreOp = new java.awt.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);
|
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), 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("No result!", tmResult);
|
assertNotNull(tmResult, "No result!");
|
||||||
assertEquals("Bad type", jreResult.getType(), tmResult.getType());
|
assertEquals(jreResult.getType(), tmResult.getType(), "Bad type");
|
||||||
assertEquals("Incorrect color model", jreResult.getColorModel(), tmResult.getColorModel());
|
assertEquals(jreResult.getColorModel(), tmResult.getColorModel(), "Incorrect color model");
|
||||||
|
|
||||||
assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
||||||
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateBICustom() {
|
public void testFilterRotateBICustom() {
|
||||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
BufferedImageOp jreOp = new java.awt.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);
|
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), 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("No result!", tmResult);
|
assertNotNull(tmResult, "No result!");
|
||||||
|
|
||||||
BufferedImage jreResult = null;
|
BufferedImage jreResult = null;
|
||||||
|
|
||||||
@@ -166,18 +174,18 @@ public class AffineTransformOpTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jreResult != null) {
|
if (jreResult != null) {
|
||||||
assertEquals("Bad type", jreResult.getType(), tmResult.getType());
|
assertEquals(jreResult.getType(), tmResult.getType(), "Bad type");
|
||||||
assertEquals("Incorrect color model", jreResult.getColorModel(), tmResult.getColorModel());
|
assertEquals(jreResult.getColorModel(), tmResult.getColorModel(), "Incorrect color model");
|
||||||
|
|
||||||
assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
||||||
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertEquals("Bad type", spec.getBufferedImageType(), tmResult.getType());
|
assertEquals(spec.getBufferedImageType(), tmResult.getType(), "Bad type");
|
||||||
assertEquals("Incorrect color model", spec.getColorModel(), tmResult.getColorModel());
|
assertEquals(spec.getColorModel(), tmResult.getColorModel(), "Incorrect color model");
|
||||||
|
|
||||||
assertEquals("Incorrect width", height, tmResult.getWidth());
|
assertEquals(height, tmResult.getWidth(), "Incorrect width");
|
||||||
assertEquals("Incorrect height", width, tmResult.getHeight());
|
assertEquals(width, tmResult.getHeight(), "Incorrect height");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,8 +205,8 @@ public class AffineTransformOpTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateRasterStandard() {
|
public void testFilterRotateRasterStandard() {
|
||||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
RasterOp jreOp = new java.awt.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);
|
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), 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();
|
||||||
@@ -221,27 +229,25 @@ 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("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
||||||
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertEquals("Incorrect width", height, tmResult.getWidth());
|
assertEquals(height, tmResult.getWidth(), "Incorrect width");
|
||||||
assertEquals("Incorrect height", width, tmResult.getHeight());
|
assertEquals(width, tmResult.getHeight(), "Incorrect height");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateRasterCustom() {
|
public void testFilterRotateRasterCustom() {
|
||||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
RasterOp jreOp = new java.awt.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);
|
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||||
|
|
||||||
for (ImageTypeSpecifier spec : SPECS) {
|
for (ImageTypeSpecifier spec : SPECS) {
|
||||||
Raster raster = spec.createBufferedImage(width, height).getRaster();
|
Raster raster = spec.createBufferedImage(width, height).getRaster();
|
||||||
@@ -264,19 +270,17 @@ 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("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
||||||
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertEquals("Incorrect width", height, tmResult.getWidth());
|
assertEquals(height, tmResult.getWidth(), "Incorrect width");
|
||||||
assertEquals("Incorrect height", width, tmResult.getHeight());
|
assertEquals(width, tmResult.getHeight(), "Incorrect height");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-42
@@ -30,18 +30,16 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
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.Assert.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BufferedImageFactoryTestCase
|
* BufferedImageFactoryTestCase
|
||||||
@@ -51,50 +49,58 @@ import static org.junit.Assert.*;
|
|||||||
* @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(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void testCreateNullImage() {
|
public void testCreateNullImage() {
|
||||||
new BufferedImageFactory((Image) null);
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new BufferedImageFactory((Image) null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void testCreateNullProducer() {
|
public void testCreateNullProducer() {
|
||||||
new BufferedImageFactory((ImageProducer) null);
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new BufferedImageFactory((ImageProducer) null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// NPE in Toolkit, ok
|
// NPE in Toolkit, ok
|
||||||
@Test(expected = RuntimeException.class)
|
@Test
|
||||||
public void testGetBufferedImageErrorSourceByteArray() {
|
public void testGetBufferedImageErrorSourceByteArray() {
|
||||||
Image source = Toolkit.getDefaultToolkit().createImage((byte[]) null);
|
assertThrows(RuntimeException.class, () -> {
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage((byte[]) null);
|
||||||
new BufferedImageFactory(source);
|
new BufferedImageFactory(source);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
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
|
||||||
@Ignore("Bug in Toolkit")
|
@Disabled("Bug in Toolkit")
|
||||||
@Test(timeout = 1000, expected = ImageConversionException.class)
|
@Test
|
||||||
public void testGetBufferedImageErrorSourceString() {
|
public void testGetBufferedImageErrorSourceString() {
|
||||||
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
|
assertTimeoutPreemptively(Duration.ofMillis(1000), () -> {
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
|
||||||
BufferedImageFactory factory = new BufferedImageFactory(source);
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
factory.getBufferedImage();
|
assertThrows(ImageConversionException.class, 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(timeout = 1000, expected = ImageConversionException.class)
|
@Test
|
||||||
public void testGetBufferedImageErrorSourceURL() {
|
public void testGetBufferedImageErrorSourceURL() {
|
||||||
Image source = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/META-INF/MANIFEST.MF"));
|
assertTimeoutPreemptively(Duration.ofMillis(1000), () -> {
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
|
||||||
BufferedImageFactory factory = new BufferedImageFactory(source);
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
factory.getBufferedImage();
|
assertThrows(ImageConversionException.class, factory::getBufferedImage);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -166,7 +172,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());
|
||||||
assertTrue(colorModel instanceof IndexColorModel);
|
assertInstanceOf(IndexColorModel.class, colorModel);
|
||||||
|
|
||||||
assertTrue(colorModel.hasAlpha());
|
assertTrue(colorModel.hasAlpha());
|
||||||
assertEquals(4, colorModel.getNumComponents());
|
assertEquals(4, colorModel.getNumComponents());
|
||||||
@@ -197,7 +203,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("RGB[" + x + ", " + y + "]", original.getRGB(x * 2, y * 2), image.getRGB(x, y));
|
assertEquals(original.getRGB(x * 2, y * 2), image.getRGB(x, y), "RGB[" + x + ", " + y + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,7 +226,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("RGB[" + x + ", " + y + "]", original.getRGB(40 + x, 40 + y), image.getRGB(x, y));
|
assertEquals(original.getRGB(40 + x, 40 + y), image.getRGB(x, y), "RGB[" + x + ", " + y + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,7 +250,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("RGB[" + x + ", " + y + "]", original.getRGB(40 + x * 2, 40 + y * 2), image.getRGB(x, y));
|
assertEquals(original.getRGB(40 + x * 2, 40 + y * 2), image.getRGB(x, y), "RGB[" + x + ", " + y + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,9 +266,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 pFactory, float pPercentage) {
|
public void progress(BufferedImageFactory factory, float percentage) {
|
||||||
if (pPercentage > 5) {
|
if (percentage > 5) {
|
||||||
pFactory.abort();
|
factory.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -343,7 +349,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 pFactory, float pPercentage) {
|
public void progress(BufferedImageFactory factory, float percentage) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
factory.getBufferedImage();
|
factory.getBufferedImage();
|
||||||
@@ -380,11 +386,11 @@ public class BufferedImageFactoryTest {
|
|||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void progress(BufferedImageFactory pFactory, float pPercentage) {
|
public void progress(BufferedImageFactory factory, float percentage) {
|
||||||
assertEquals(factory, pFactory);
|
assertEquals(this.factory, factory);
|
||||||
assertTrue(pPercentage >= progress && pPercentage <= 100f);
|
assertTrue(percentage >= progress && percentage <= 100f);
|
||||||
|
|
||||||
progress = pPercentage;
|
progress = percentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.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.Assert.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
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(
|
||||||
"FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too.",
|
scaled instanceof BufferedImage,
|
||||||
scaled instanceof BufferedImage
|
"FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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("0 constrast should not change image", original.getRGB(x, y), notContrasted.getRGB(x, y));
|
assertEquals(original.getRGB(x, y), notContrasted.getRGB(x, y), "0 constrast should not change image");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,24 +275,24 @@ public class ImageUtilTest {
|
|||||||
|
|
||||||
// RED
|
// RED
|
||||||
if (oR < 127) {
|
if (oR < 127) {
|
||||||
assertTrue("Contrast should be decreased or same", oR >= cR && cR >= dR);
|
assertTrue(oR >= cR && cR >= dR, "Contrast should be decreased or same");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue("Contrast should be increased or same", oR <= cR && cR <= dR);
|
assertTrue(oR <= cR && cR <= dR, "Contrast should be increased or same");
|
||||||
}
|
}
|
||||||
// GREEN
|
// GREEN
|
||||||
if (oG < 127) {
|
if (oG < 127) {
|
||||||
assertTrue("Contrast should be decreased or same", oG >= cG && cG >= dG);
|
assertTrue(oG >= cG && cG >= dG, "Contrast should be decreased or same");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue("Contrast should be increased or same", oG <= cG && cG <= dG);
|
assertTrue(oG <= cG && cG <= dG, "Contrast should be increased or same");
|
||||||
}
|
}
|
||||||
// BLUE
|
// BLUE
|
||||||
if (oB < 127) {
|
if (oB < 127) {
|
||||||
assertTrue("Contrast should be decreased or same", oB >= cB && cB >= dB);
|
assertTrue(oB >= cB && cB >= dB, "Contrast should be decreased or same");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue("Contrast should be increased or same", oB <= cB && cB <= dB);
|
assertTrue(oB <= cB && cB <= dB, "Contrast should be increased or same");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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("Max contrast should only produce primary colors", r == 0 || r == 255);
|
assertTrue(r == 0 || r == 255, "Max contrast should only produce primary colors");
|
||||||
assertTrue("Max contrast should only produce primary colors", g == 0 || g == 255);
|
assertTrue(g == 0 || g == 255, "Max contrast should only produce primary colors");
|
||||||
assertTrue("Max contrast should only produce primary colors", b == 0 || b == 255);
|
assertTrue(b == 0 || b == 255, "Max contrast should only produce primary colors");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,24 +327,24 @@ public class ImageUtilTest {
|
|||||||
|
|
||||||
// RED
|
// RED
|
||||||
if (oR >= 127) {
|
if (oR >= 127) {
|
||||||
assertTrue("Contrast should be decreased or same", oR >= cR);
|
assertTrue(oR >= cR, "Contrast should be decreased or same");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue("Contrast should be increased or same", oR <= cR);
|
assertTrue(oR <= cR, "Contrast should be increased or same");
|
||||||
}
|
}
|
||||||
// GREEN
|
// GREEN
|
||||||
if (oG >= 127) {
|
if (oG >= 127) {
|
||||||
assertTrue("Contrast should be decreased or same", oG >= cG);
|
assertTrue(oG >= cG, "Contrast should be decreased or same");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue("Contrast should be increased or same", oG <= cG);
|
assertTrue(oG <= cG, "Contrast should be increased or same");
|
||||||
}
|
}
|
||||||
// BLUE
|
// BLUE
|
||||||
if (oB >= 127) {
|
if (oB >= 127) {
|
||||||
assertTrue("Contrast should be decreased or same", oB >= cB);
|
assertTrue(oB >= cB, "Contrast should be decreased or same");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue("Contrast should be increased or same", oB <= cB);
|
assertTrue(oB <= cB, "Contrast should be increased or same");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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("Minimum contrast should be all gray", r == 127 && g == 127 && b == 127);
|
assertTrue(r == 127 && g == 127 && b == 127, "Minimum contrast should be all gray");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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("0 sharpen should not change image", original.getRGB(x, y), notSharpened.getRGB(x, y));
|
assertEquals(original.getRGB(x, y), notSharpened.getRGB(x, y), "0 sharpen should not change image");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,13 +446,13 @@ public class ImageUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffSharpened);
|
// assertEquals("Difference should not change", diffOriginal, diffSharpened);
|
||||||
assertTrue("Abs difference should increase", absDiffOriginal < absDiffSharpened);
|
assertTrue(absDiffOriginal < absDiffSharpened, "Abs difference should increase");
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||||
assertTrue("Abs difference should increase", absDiffOriginal < absDiffDefault);
|
assertTrue(absDiffOriginal < absDiffDefault, "Abs difference should increase");
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
||||||
assertTrue("Abs difference should increase", absDiffOriginal < absDiffMore);
|
assertTrue(absDiffOriginal < absDiffMore, "Abs difference should increase");
|
||||||
// assertEquals("Difference should not change", diffSharpened, diffMore);
|
// assertEquals("Difference should not change", diffSharpened, diffMore);
|
||||||
assertTrue("Abs difference should increase", absDiffSharpened < absDiffMore);
|
assertTrue(absDiffSharpened < absDiffMore, "Abs difference should increase");
|
||||||
}
|
}
|
||||||
|
|
||||||
@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("0 blur should not change image", original.getRGB(x, y), notBlurred.getRGB(x, y));
|
assertEquals(original.getRGB(x, y), notBlurred.getRGB(x, y), "0 blur should not change image");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -512,13 +512,13 @@ public class ImageUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffBlurred);
|
// assertEquals("Difference should not change", diffOriginal, diffBlurred);
|
||||||
assertTrue(String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred), absDiffOriginal > absDiffBlurred);
|
assertTrue(absDiffOriginal > absDiffBlurred, String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred));
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||||
assertTrue("Abs difference should decrease", absDiffOriginal > absDiffDefault);
|
assertTrue(absDiffOriginal > absDiffDefault, "Abs difference should decrease");
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
||||||
assertTrue("Abs difference should decrease", absDiffOriginal > absDiffMore);
|
assertTrue(absDiffOriginal > absDiffMore, "Abs difference should decrease");
|
||||||
// assertEquals("Difference should not change", diffBlurred, diffMore);
|
// assertEquals("Difference should not change", diffBlurred, diffMore);
|
||||||
assertTrue("Abs difference should decrease", absDiffBlurred > absDiffMore);
|
assertTrue(absDiffBlurred > absDiffMore, "Abs difference should decrease");
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 was null", image);
|
assertNotNull(image, "Image was null");
|
||||||
assertTrue(image.getColorModel() instanceof IndexColorModel);
|
assertInstanceOf(IndexColorModel.class, image.getColorModel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.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.Assert.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResampleOpTestCase
|
* ResampleOpTestCase
|
||||||
@@ -124,7 +124,7 @@ public class ResampleOpTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals("Filter threw exceptions: ", Collections.EMPTY_LIST, exceptions);
|
assertEquals(Collections.EMPTY_LIST, exceptions, "Filter threw exceptions: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1x1
|
// 1x1
|
||||||
@@ -358,7 +358,7 @@ public class ResampleOpTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("Not for general unit testing")
|
@Disabled("Not for general unit testing")
|
||||||
@Test
|
@Test
|
||||||
public void testTime() {
|
public void testTime() {
|
||||||
int iterations = 1000;
|
int iterations = 1000;
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
TODO:
|
|
||||||
Remove compile-time dependency on JMagick:
|
|
||||||
- Extract interface for MagickAccelerator
|
|
||||||
- Move implementation to separate module
|
|
||||||
- Instantiate impl via reflection
|
|
||||||
DONE:
|
|
||||||
@@ -4,15 +4,19 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.4</version>
|
<version>3.13.2-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>
|
||||||
The TwelveMonkeys Common IO support
|
TwelveMonkeys Common I/O support classes.
|
||||||
</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>
|
||||||
@@ -27,4 +31,12 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+13
-7
@@ -186,7 +186,7 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void closeImpl() throws IOException {
|
protected void closeImpl() throws IOException {
|
||||||
cache.flush(position);
|
cache.close();
|
||||||
cache = null;
|
cache = null;
|
||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
@@ -198,12 +198,12 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/AbstractCachedSeekableStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/AbstractCachedSeekableStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public static abstract class StreamCache {
|
static abstract class StreamCache {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code StreamCache}.
|
* Creates a {@code StreamCache}.
|
||||||
*/
|
*/
|
||||||
protected StreamCache() {
|
StreamCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -218,9 +218,10 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
/**
|
/**
|
||||||
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
||||||
* {@code pLength}.
|
* {@code pLength}.
|
||||||
* <p/>
|
* <p>
|
||||||
* This implementation invokes {@link #write(int)} {@code pLength} times.
|
* This implementation invokes {@link #write(int)} {@code pLength} times.
|
||||||
* Subclasses may override this method for performance.
|
* Subclasses may override this method for performance.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param pBuffer the bytes to write.
|
* @param pBuffer the bytes to write.
|
||||||
* @param pOffset the starting offset into the buffer.
|
* @param pOffset the starting offset into the buffer.
|
||||||
@@ -246,9 +247,10 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
/**
|
/**
|
||||||
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
||||||
* {@code pLength}.
|
* {@code pLength}.
|
||||||
* <p/>
|
* <p>
|
||||||
* This implementation invokes {@link #read()} {@code pLength} times.
|
* This implementation invokes {@link #read()} {@code pLength} times.
|
||||||
* Subclasses may override this method for performance.
|
* Subclasses may override this method for performance.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param pBuffer the bytes to write
|
* @param pBuffer the bytes to write
|
||||||
* @param pOffset the starting offset into the buffer.
|
* @param pOffset the starting offset into the buffer.
|
||||||
@@ -283,12 +285,14 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally flushes any data prior to the given position.
|
* Optionally flushes any data prior to the given position.
|
||||||
* <p/>
|
* <p>
|
||||||
* Attempting to perform a seek operation, and/or a read or write operation to a position equal to or before
|
* Attempting to perform a seek operation, and/or a read or write operation to a position equal to or before
|
||||||
* the flushed position may result in exceptions or undefined behaviour.
|
* the flushed position may result in exceptions or undefined behaviour.
|
||||||
* <p/>
|
* </p>
|
||||||
|
* <p>
|
||||||
* Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources.
|
* Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources.
|
||||||
* This implementation does nothing.
|
* This implementation does nothing.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param pPosition the last position to flush.
|
* @param pPosition the last position to flush.
|
||||||
*/
|
*/
|
||||||
@@ -303,5 +307,7 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
* @throws IOException if the position can't be determined because of a problem in the cache backing mechanism.
|
* @throws IOException if the position can't be determined because of a problem in the cache backing mechanism.
|
||||||
*/
|
*/
|
||||||
abstract long getPosition() throws IOException;
|
abstract long getPosition() throws IOException;
|
||||||
|
|
||||||
|
abstract void close() throws IOException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,222 +1,221 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Reader implementation that can read from multiple sources.
|
* A Reader implementation that can read from multiple sources.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author last modified by $Author: haku $
|
||||||
* @author last modified by $Author: haku $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/CompoundReader.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/CompoundReader.java#2 $
|
*/
|
||||||
*/
|
public class CompoundReader extends Reader {
|
||||||
public class CompoundReader extends Reader {
|
|
||||||
|
private Reader current;
|
||||||
private Reader current;
|
private List<Reader> readers;
|
||||||
private List<Reader> readers;
|
|
||||||
|
protected final Object finalLock;
|
||||||
protected final Object finalLock;
|
|
||||||
|
protected final boolean markSupported;
|
||||||
protected final boolean markSupported;
|
|
||||||
|
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.
|
*
|
||||||
*
|
* @param pReaders {@code Iterator} containting {@code Reader}s,
|
||||||
* @param pReaders {@code Iterator} containting {@code Reader}s,
|
* providing the character stream.
|
||||||
* providing the character stream.
|
*
|
||||||
*
|
* @throws NullPointerException if {@code pReaders} is {@code null}, or
|
||||||
* @throws NullPointerException if {@code pReaders} is {@code null}, or
|
* any of the elements in the iterator is {@code null}.
|
||||||
* any of the elements in the iterator is {@code null}.
|
* @throws ClassCastException if any element of the iterator is not a
|
||||||
* @throws ClassCastException if any element of the iterator is not a
|
* {@code java.io.Reader}
|
||||||
* {@code java.io.Reader}
|
*/
|
||||||
*/
|
public CompoundReader(final Iterator<Reader> pReaders) {
|
||||||
public CompoundReader(final Iterator<Reader> pReaders) {
|
super(Validate.notNull(pReaders, "readers"));
|
||||||
super(Validate.notNull(pReaders, "readers"));
|
|
||||||
|
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()) {
|
Reader reader = pReaders.next();
|
||||||
Reader reader = pReaders.next();
|
if (reader == null) {
|
||||||
if (reader == null) {
|
throw new NullPointerException("readers cannot contain null-elements");
|
||||||
throw new NullPointerException("readers cannot contain null-elements");
|
}
|
||||||
}
|
readers.add(reader);
|
||||||
readers.add(reader);
|
markSupported = markSupported && reader.markSupported();
|
||||||
markSupported = markSupported && reader.markSupported();
|
}
|
||||||
}
|
this.markSupported = markSupported;
|
||||||
this.markSupported = markSupported;
|
|
||||||
|
current = nextReader();
|
||||||
current = nextReader();
|
}
|
||||||
}
|
|
||||||
|
protected final Reader nextReader() {
|
||||||
protected final Reader nextReader() {
|
if (currentReader >= readers.size()) {
|
||||||
if (currentReader >= readers.size()) {
|
current = new EmptyReader();
|
||||||
current = new EmptyReader();
|
}
|
||||||
}
|
else {
|
||||||
else {
|
current = readers.get(currentReader++);
|
||||||
current = readers.get(currentReader++);
|
}
|
||||||
}
|
|
||||||
|
// 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;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Check to make sure that the stream has not been closed
|
||||||
* Check to make sure that the stream has not been closed
|
*
|
||||||
*
|
* @throws IOException if the stream is closed
|
||||||
* @throws IOException if the stream is closed
|
*/
|
||||||
*/
|
protected final void ensureOpen() throws IOException {
|
||||||
protected final void ensureOpen() throws IOException {
|
if (readers == null) {
|
||||||
if (readers == null) {
|
throw new IOException("Stream closed");
|
||||||
throw new IOException("Stream closed");
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void close() throws IOException {
|
||||||
public void close() throws IOException {
|
// Close all readers
|
||||||
// Close all readers
|
for (Reader reader : readers) {
|
||||||
for (Reader reader : readers) {
|
reader.close();
|
||||||
reader.close();
|
}
|
||||||
}
|
|
||||||
|
readers = null;
|
||||||
readers = null;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public void mark(int pReadLimit) throws IOException {
|
||||||
public void mark(int pReadLimit) throws IOException {
|
if (pReadLimit < 0) {
|
||||||
if (pReadLimit < 0) {
|
throw new IllegalArgumentException("Read limit < 0");
|
||||||
throw new IllegalArgumentException("Read limit < 0");
|
}
|
||||||
}
|
|
||||||
|
// TODO: It would be nice if we could actually close some readers now
|
||||||
// TODO: It would be nice if we could actually close some readers now
|
|
||||||
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
ensureOpen();
|
||||||
ensureOpen();
|
mark = next;
|
||||||
mark = mNext;
|
markedReader = currentReader;
|
||||||
markedReader = currentReader;
|
|
||||||
|
current.mark(pReadLimit);
|
||||||
current.mark(pReadLimit);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public void reset() throws IOException {
|
||||||
public void reset() throws IOException {
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
ensureOpen();
|
||||||
ensureOpen();
|
|
||||||
|
if (currentReader != markedReader) {
|
||||||
if (currentReader != markedReader) {
|
// Reset any reader before this
|
||||||
// Reset any reader before this
|
for (int i = currentReader; i >= markedReader; i--) {
|
||||||
for (int i = currentReader; i >= markedReader; i--) {
|
readers.get(i).reset();
|
||||||
readers.get(i).reset();
|
}
|
||||||
}
|
|
||||||
|
currentReader = markedReader - 1;
|
||||||
currentReader = markedReader - 1;
|
nextReader();
|
||||||
nextReader();
|
}
|
||||||
}
|
current.reset();
|
||||||
current.reset();
|
|
||||||
|
next = mark;
|
||||||
mNext = mark;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public boolean markSupported() {
|
||||||
public boolean markSupported() {
|
return markSupported;
|
||||||
return markSupported;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public int read() throws IOException {
|
||||||
public int read() throws IOException {
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
int read = current.read();
|
||||||
int read = current.read();
|
|
||||||
|
if (read < 0 && currentReader < readers.size()) {
|
||||||
if (read < 0 && currentReader < readers.size()) {
|
nextReader();
|
||||||
nextReader();
|
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);
|
|
||||||
|
if (read < 0 && currentReader < readers.size()) {
|
||||||
if (read < 0 && currentReader < readers.size()) {
|
nextReader();
|
||||||
nextReader();
|
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;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public boolean ready() throws IOException {
|
||||||
public boolean ready() throws IOException {
|
return current.ready();
|
||||||
return current.ready();
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public long skip(long pChars) throws IOException {
|
||||||
public long skip(long pChars) throws IOException {
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
long skipped = current.skip(pChars);
|
||||||
long skipped = current.skip(pChars);
|
|
||||||
|
if (skipped == 0 && currentReader < readers.size()) {
|
||||||
if (skipped == 0 && currentReader < readers.size()) {
|
nextReader();
|
||||||
nextReader();
|
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;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,47 +1,46 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EmptyReader
|
* EmptyReader
|
||||||
* <p/>
|
*
|
||||||
*
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author last modified by $Author: haku $
|
||||||
* @author last modified by $Author: haku $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/EmptyReader.java#1 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/EmptyReader.java#1 $
|
*/
|
||||||
*/
|
final class EmptyReader extends StringReader {
|
||||||
final class EmptyReader extends StringReader {
|
public EmptyReader() {
|
||||||
public EmptyReader() {
|
super("");
|
||||||
super("");
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
+129
-135
@@ -1,136 +1,130 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
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.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
/**
|
|
||||||
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
/**
|
||||||
* also has a constructor that lets you create a stream with initial content.
|
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
||||||
* <p/>
|
* also has a constructor that lets you create a stream with initial content.
|
||||||
*
|
*
|
||||||
* @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
|
||||||
|
* size.
|
||||||
/**
|
*
|
||||||
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
* @param pSize initial buffer size
|
||||||
* size.
|
*/
|
||||||
*
|
public FastByteArrayOutputStream(int pSize) {
|
||||||
* @param pSize initial buffer size
|
super(pSize);
|
||||||
*/
|
}
|
||||||
public FastByteArrayOutputStream(int pSize) {
|
|
||||||
super(pSize);
|
/**
|
||||||
}
|
* Creates a {@code ByteArrayOutputStream} with the given initial content.
|
||||||
|
* <p>
|
||||||
/**
|
* Note that the buffer is not cloned, for maximum performance.
|
||||||
* Creates a {@code ByteArrayOutputStream} with the given initial content.
|
* </p>
|
||||||
* <p/>
|
*
|
||||||
* Note that the buffer is not cloned, for maximum performance.
|
* @param pBuffer initial buffer
|
||||||
*
|
*/
|
||||||
* @param pBuffer initial buffer
|
public FastByteArrayOutputStream(byte[] pBuffer) {
|
||||||
*/
|
super(0); // Don't allocate array
|
||||||
public FastByteArrayOutputStream(byte[] pBuffer) {
|
buf = pBuffer;
|
||||||
super(0); // Don't allocate array
|
count = pBuffer.length;
|
||||||
buf = pBuffer;
|
}
|
||||||
count = pBuffer.length;
|
|
||||||
}
|
@Override
|
||||||
|
public void write(byte[] pBytes, int pOffset, int pLength) {
|
||||||
@Override
|
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||||
public void write(byte pBytes[], int pOffset, int pLength) {
|
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||||
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
throw new IndexOutOfBoundsException();
|
||||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
}
|
||||||
throw new IndexOutOfBoundsException();
|
else if (pLength == 0) {
|
||||||
}
|
return;
|
||||||
else if (pLength == 0) {
|
}
|
||||||
return;
|
|
||||||
}
|
int newCount = count + pLength;
|
||||||
|
growIfNeeded(newCount);
|
||||||
int newCount = count + pLength;
|
System.arraycopy(pBytes, pOffset, buf, count, pLength);
|
||||||
growIfNeeded(newCount);
|
count = newCount;
|
||||||
System.arraycopy(pBytes, pOffset, buf, count, pLength);
|
}
|
||||||
count = newCount;
|
|
||||||
}
|
@Override
|
||||||
|
public void write(int pByte) {
|
||||||
@Override
|
int newCount = count + 1;
|
||||||
public void write(int pByte) {
|
growIfNeeded(newCount);
|
||||||
int newCount = count + 1;
|
buf[count] = (byte) pByte;
|
||||||
growIfNeeded(newCount);
|
count = newCount;
|
||||||
buf[count] = (byte) pByte;
|
}
|
||||||
count = newCount;
|
|
||||||
}
|
private void growIfNeeded(int pNewCount) {
|
||||||
|
if (pNewCount > buf.length) {
|
||||||
private void growIfNeeded(int pNewCount) {
|
int newSize = Math.max(buf.length << 1, pNewCount);
|
||||||
if (pNewCount > buf.length) {
|
buf = Arrays.copyOf(buf, newSize);
|
||||||
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
}
|
||||||
byte newBuf[] = new byte[newSize];
|
}
|
||||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
|
||||||
buf = newBuf;
|
// Non-synchronized version of writeTo
|
||||||
}
|
@Override
|
||||||
}
|
public void writeTo(OutputStream pOut) throws IOException {
|
||||||
|
pOut.write(buf, 0, count);
|
||||||
// Non-synchronized version of writeTo
|
}
|
||||||
@Override
|
|
||||||
public void writeTo(OutputStream pOut) throws IOException {
|
// Non-synchronized version of toByteArray
|
||||||
pOut.write(buf, 0, count);
|
@Override
|
||||||
}
|
public byte[] toByteArray() {
|
||||||
|
return Arrays.copyOf(buf, count);
|
||||||
// Non-synchronized version of toByteArray
|
}
|
||||||
@Override
|
|
||||||
public byte[] toByteArray() {
|
/**
|
||||||
byte newBuf[] = new byte[count];
|
* Creates a {@code ByteArrayInputStream} that reads directly from this
|
||||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
* {@code FastByteArrayOutputStream}'s byte buffer.
|
||||||
|
* The buffer is not cloned, for maximum performance.
|
||||||
return newBuf;
|
* <p>
|
||||||
}
|
* Note that care needs to be taken to avoid writes to
|
||||||
|
* this output stream after the input stream is created.
|
||||||
/**
|
* Failing to do so, may result in unpredictable behaviour.
|
||||||
* Creates a {@code ByteArrayInputStream} that reads directly from this
|
* </p>
|
||||||
* {@code FastByteArrayOutputStream}'s byte buffer.
|
*
|
||||||
* The buffer is not cloned, for maximum performance.
|
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
||||||
* <p/>
|
*/
|
||||||
* Note that care needs to be taken to avoid writes to
|
public ByteArrayInputStream createInputStream() {
|
||||||
* this output stream after the input stream is created.
|
return new ByteArrayInputStream(buf, 0, count);
|
||||||
* Failing to do so, may result in unpredictable behaviour.
|
}
|
||||||
*
|
|
||||||
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
|
||||||
*/
|
|
||||||
public ByteArrayInputStream createInputStream() {
|
|
||||||
return new ByteArrayInputStream(buf, 0, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+241
-232
@@ -1,232 +1,241 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
|
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
|
||||||
* <p/>
|
* <p>
|
||||||
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
|
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
|
||||||
*
|
* </p>
|
||||||
* @see MemoryCacheSeekableStream
|
*
|
||||||
* @see FileSeekableStream
|
* @see MemoryCacheSeekableStream
|
||||||
*
|
* @see FileSeekableStream
|
||||||
* @see File#createTempFile(String, String)
|
*
|
||||||
* @see RandomAccessFile
|
* @see File#createTempFile(String, String)
|
||||||
*
|
* @see RandomAccessFile
|
||||||
* @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/FileCacheSeekableStream.java#5 $
|
* @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/FileCacheSeekableStream.java#5 $
|
||||||
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
|
*/
|
||||||
|
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
|
||||||
private byte[] buffer;
|
|
||||||
|
private byte[] buffer;
|
||||||
/**
|
|
||||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
/**
|
||||||
* {@code InputStream}. Data will be cached in a temporary file.
|
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||||
*
|
* {@code InputStream}. Data will be cached in a temporary file.
|
||||||
* @param pStream the {@code InputStream} to read from
|
*
|
||||||
*
|
* @param pStream the {@code InputStream} to read from
|
||||||
* @throws IOException if the temporary file cannot be created,
|
*
|
||||||
* or cannot be opened for random access.
|
* @throws IOException if the temporary file cannot be created,
|
||||||
*/
|
* or cannot be opened for random access.
|
||||||
public FileCacheSeekableStream(final InputStream pStream) throws IOException {
|
*/
|
||||||
this(pStream, "iocache", null);
|
public FileCacheSeekableStream(final InputStream pStream) throws IOException {
|
||||||
}
|
this(pStream, "iocache", null);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
/**
|
||||||
* {@code InputStream}. Data will be cached in a temporary file, with
|
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||||
* the given base name.
|
* {@code InputStream}. Data will be cached in a temporary file, with
|
||||||
*
|
* the given base name.
|
||||||
* @param pStream the {@code InputStream} to read from
|
*
|
||||||
* @param pTempBaseName optional base name for the temporary file
|
* @param pStream the {@code InputStream} to read from
|
||||||
*
|
* @param pTempBaseName optional base name for the temporary file
|
||||||
* @throws IOException if the temporary file cannot be created,
|
*
|
||||||
* or cannot be opened for random access.
|
* @throws IOException if the temporary file cannot be created,
|
||||||
*/
|
* or cannot be opened for random access.
|
||||||
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName) throws IOException {
|
*/
|
||||||
this(pStream, pTempBaseName, null);
|
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName) throws IOException {
|
||||||
}
|
this(pStream, pTempBaseName, null);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
/**
|
||||||
* {@code InputStream}. Data will be cached in a temporary file, with
|
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||||
* the given base name, in the given directory
|
* {@code InputStream}. Data will be cached in a temporary file, with
|
||||||
*
|
* the given base name, in the given directory
|
||||||
* @param pStream the {@code InputStream} to read from
|
*
|
||||||
* @param pTempBaseName optional base name for the temporary file
|
* @param pStream the {@code InputStream} to read from
|
||||||
* @param pTempDir optional temp directory
|
* @param pTempBaseName optional base name for the temporary file
|
||||||
*
|
* @param pTempDir optional temp directory
|
||||||
* @throws IOException if the temporary file cannot be created,
|
*
|
||||||
* or cannot be opened for random access.
|
* @throws IOException if the temporary file cannot be created,
|
||||||
*/
|
* or cannot be opened for random access.
|
||||||
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName, final File pTempDir) throws IOException {
|
*/
|
||||||
// NOTE: We do validation BEFORE we create temp file, to avoid orphan files
|
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName, final File pTempDir) throws IOException {
|
||||||
this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir));
|
// NOTE: We do validation BEFORE we create temp file, to avoid orphan files
|
||||||
}
|
this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir));
|
||||||
|
}
|
||||||
/*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
|
|
||||||
Validate.notNull(pTempBaseName, "tempBaseName");
|
/*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
|
||||||
|
Validate.notNull(pTempBaseName, "tempBaseName");
|
||||||
File file = File.createTempFile(pTempBaseName, null, pTempDir);
|
|
||||||
file.deleteOnExit();
|
File file = File.createTempFile(pTempBaseName, null, pTempDir);
|
||||||
|
file.deleteOnExit();
|
||||||
return file;
|
|
||||||
}
|
return file;
|
||||||
|
}
|
||||||
// TODO: Consider exposing this for external use
|
|
||||||
/*protected*/ FileCacheSeekableStream(final InputStream pStream, final File pFile) throws FileNotFoundException {
|
// TODO: Consider exposing this for external use
|
||||||
super(pStream, new FileCache(pFile));
|
/*protected*/ FileCacheSeekableStream(final InputStream pStream, final File pFile) throws FileNotFoundException {
|
||||||
|
super(pStream, new FileCache(pFile));
|
||||||
// TODO: Allow for custom buffer sizes?
|
|
||||||
buffer = new byte[1024];
|
// TODO: Allow for custom buffer sizes?
|
||||||
}
|
buffer = new byte[1024];
|
||||||
|
}
|
||||||
public final boolean isCachedMemory() {
|
|
||||||
return false;
|
public final boolean isCachedMemory() {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
public final boolean isCachedFile() {
|
|
||||||
return true;
|
public final boolean isCachedFile() {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
protected void closeImpl() throws IOException {
|
@Override
|
||||||
super.closeImpl();
|
protected void closeImpl() throws IOException {
|
||||||
buffer = null;
|
// TODO: Close cache file
|
||||||
}
|
super.closeImpl();
|
||||||
|
|
||||||
@Override
|
buffer = null;
|
||||||
public int read() throws IOException {
|
}
|
||||||
checkOpen();
|
|
||||||
|
@Override
|
||||||
int read;
|
public int read() throws IOException {
|
||||||
if (position == streamPosition) {
|
checkOpen();
|
||||||
// Read ahead into buffer, for performance
|
|
||||||
read = readAhead(buffer, 0, buffer.length);
|
int read;
|
||||||
if (read >= 0) {
|
if (position == streamPosition) {
|
||||||
read = buffer[0] & 0xff;
|
// Read ahead into buffer, for performance
|
||||||
}
|
read = readAhead(buffer, 0, buffer.length);
|
||||||
|
if (read >= 0) {
|
||||||
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
|
read = buffer[0] & 0xff;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// ..or read byte from the cache
|
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
|
||||||
syncPosition();
|
}
|
||||||
read = getCache().read();
|
else {
|
||||||
|
// ..or read byte from the cache
|
||||||
//System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
|
syncPosition();
|
||||||
}
|
read = getCache().read();
|
||||||
|
|
||||||
// TODO: This field is not REALLY considered accessible.. :-P
|
//System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
|
||||||
if (read != -1) {
|
}
|
||||||
position++;
|
|
||||||
}
|
// TODO: This field is not REALLY considered accessible.. :-P
|
||||||
return read;
|
if (read != -1) {
|
||||||
}
|
position++;
|
||||||
|
}
|
||||||
@Override
|
return read;
|
||||||
public int read(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
}
|
||||||
checkOpen();
|
|
||||||
|
@Override
|
||||||
int length;
|
public int read(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||||
if (position == streamPosition) {
|
checkOpen();
|
||||||
// Read bytes from the stream
|
|
||||||
length = readAhead(pBytes, pOffset, pLength);
|
int length;
|
||||||
|
if (position == streamPosition) {
|
||||||
//System.out.println("Read " + length + " byte from stream");
|
// Read bytes from the stream
|
||||||
}
|
length = readAhead(pBytes, pOffset, pLength);
|
||||||
else {
|
|
||||||
// ...or read bytes from the cache
|
//System.out.println("Read " + length + " byte from stream");
|
||||||
syncPosition();
|
}
|
||||||
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position));
|
else {
|
||||||
|
// ...or read bytes from the cache
|
||||||
//System.out.println("Read " + length + " byte from cache");
|
syncPosition();
|
||||||
}
|
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position));
|
||||||
|
|
||||||
// TODO: This field is not REALLY considered accessible.. :-P
|
//System.out.println("Read " + length + " byte from cache");
|
||||||
if (length > 0) {
|
}
|
||||||
position += length;
|
|
||||||
}
|
// TODO: This field is not REALLY considered accessible.. :-P
|
||||||
return length;
|
if (length > 0) {
|
||||||
}
|
position += length;
|
||||||
|
}
|
||||||
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
return length;
|
||||||
int length;
|
}
|
||||||
length = stream.read(pBytes, pOffset, pLength);
|
|
||||||
|
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
if (length > 0) {
|
int length;
|
||||||
streamPosition += length;
|
length = stream.read(pBytes, pOffset, pLength);
|
||||||
getCache().write(pBytes, pOffset, length);
|
|
||||||
}
|
if (length > 0) {
|
||||||
return length;
|
streamPosition += length;
|
||||||
}
|
getCache().write(pBytes, pOffset, length);
|
||||||
|
}
|
||||||
final static class FileCache extends StreamCache {
|
return length;
|
||||||
private RandomAccessFile mCacheFile;
|
}
|
||||||
|
|
||||||
public FileCache(final File pFile) throws FileNotFoundException {
|
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
|
||||||
Validate.notNull(pFile, "file");
|
|
||||||
mCacheFile = new RandomAccessFile(pFile, "rw");
|
final static class FileCache extends StreamCache {
|
||||||
}
|
private RandomAccessFile cacheFile;
|
||||||
|
|
||||||
public void write(final int pByte) throws IOException {
|
public FileCache(final File pFile) throws FileNotFoundException {
|
||||||
mCacheFile.write(pByte);
|
Validate.notNull(pFile, "file");
|
||||||
}
|
cacheFile = new RandomAccessFile(pFile, "rw");
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
public void write(final int pByte) throws IOException {
|
||||||
mCacheFile.write(pBuffer, pOffset, pLength);
|
cacheFile.write(pByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
@Override
|
||||||
return mCacheFile.read();
|
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||||
}
|
cacheFile.write(pBuffer, pOffset, pLength);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
public int read() throws IOException {
|
||||||
return mCacheFile.read(pBuffer, pOffset, pLength);
|
return cacheFile.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void seek(final long pPosition) throws IOException {
|
@Override
|
||||||
mCacheFile.seek(pPosition);
|
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||||
}
|
return cacheFile.read(pBuffer, pOffset, pLength);
|
||||||
|
}
|
||||||
public long getPosition() throws IOException {
|
|
||||||
return mCacheFile.getFilePointer();
|
public void seek(final long pPosition) throws IOException {
|
||||||
}
|
cacheFile.seek(pPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public long getPosition() throws IOException {
|
||||||
|
return cacheFile.getFilePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void close() throws IOException {
|
||||||
|
cacheFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,135 +1,135 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
|
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
|
||||||
* <p/>
|
|
||||||
* @see FileCacheSeekableStream
|
* @see FileCacheSeekableStream
|
||||||
* @see MemoryCacheSeekableStream
|
* @see MemoryCacheSeekableStream
|
||||||
* @see RandomAccessFile
|
* @see RandomAccessFile
|
||||||
*
|
*
|
||||||
* @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/FileSeekableStream.java#4 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java#4 $
|
||||||
*/
|
*/
|
||||||
public final class FileSeekableStream extends SeekableInputStream {
|
public final class FileSeekableStream extends SeekableInputStream {
|
||||||
|
|
||||||
// TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in
|
// TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in
|
||||||
// my tests..?
|
// my tests..?
|
||||||
|
|
||||||
final RandomAccessFile mRandomAccess;
|
final RandomAccessFile mRandomAccess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code FileSeekableStream} that reads from the given
|
* Creates a {@code FileSeekableStream} that reads from the given
|
||||||
* {@code File}.
|
* {@code File}.
|
||||||
*
|
*
|
||||||
* @param pInput file to read from
|
* @param pInput file to read from
|
||||||
* @throws FileNotFoundException if {@code pInput} does not exist
|
* @throws FileNotFoundException if {@code pInput} does not exist
|
||||||
*/
|
*/
|
||||||
public FileSeekableStream(final File pInput) throws FileNotFoundException {
|
public FileSeekableStream(final File pInput) throws FileNotFoundException {
|
||||||
this(new RandomAccessFile(pInput, "r"));
|
this(new RandomAccessFile(pInput, "r"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code FileSeekableStream} that reads from the given file.
|
* Creates a {@code FileSeekableStream} that reads from the given file.
|
||||||
* The {@code RandomAccessFile} needs only to be open in read
|
* The {@code RandomAccessFile} needs only to be open in read
|
||||||
* ({@code "r"}) mode.
|
* ({@code "r"}) mode.
|
||||||
*
|
*
|
||||||
* @param pInput file to read from
|
* @param pInput file to read from
|
||||||
*/
|
*/
|
||||||
public FileSeekableStream(final RandomAccessFile pInput) {
|
public FileSeekableStream(final RandomAccessFile pInput) {
|
||||||
mRandomAccess = pInput;
|
mRandomAccess = pInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Seekable
|
/// Seekable
|
||||||
|
|
||||||
public boolean isCached() {
|
public boolean isCached() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCachedFile() {
|
public boolean isCachedFile() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCachedMemory() {
|
public boolean isCachedMemory() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// InputStream
|
/// InputStream
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
long length = mRandomAccess.length() - position;
|
long length = mRandomAccess.length() - position;
|
||||||
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
|
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeImpl() throws IOException {
|
public void closeImpl() throws IOException {
|
||||||
mRandomAccess.close();
|
mRandomAccess.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
int read = mRandomAccess.read();
|
int read = mRandomAccess.read();
|
||||||
if (read >= 0) {
|
if (read >= 0) {
|
||||||
position++;
|
position++;
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
|
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
int read = mRandomAccess.read(pBytes, pOffset, pLength);
|
int read = mRandomAccess.read(pBytes, pOffset, pLength);
|
||||||
if (read > 0) {
|
if (read > 0) {
|
||||||
position += read;
|
position += read;
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does nothing, as we don't really do any caching here.
|
* Does nothing, as we don't really do any caching here.
|
||||||
*
|
*
|
||||||
* @param pPosition the position to flush to
|
* @param pPosition the position to flush to
|
||||||
*/
|
*/
|
||||||
protected void flushBeforeImpl(long pPosition) {
|
protected void flushBeforeImpl(long pPosition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void seekImpl(long pPosition) throws IOException {
|
protected void seekImpl(long pPosition) throws IOException {
|
||||||
mRandomAccess.seek(pPosition);
|
mRandomAccess.seek(pPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +1,102 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FileSystem
|
* FileSystem
|
||||||
* <p/>
|
*
|
||||||
*
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @version $Id: FileSystem.java#1 $
|
||||||
* @version $Id: FileSystem.java#1 $
|
*/
|
||||||
*/
|
abstract class FileSystem {
|
||||||
abstract class FileSystem {
|
abstract long getFreeSpace(File pPath);
|
||||||
abstract long getFreeSpace(File pPath);
|
|
||||||
|
abstract long getTotalSpace(File pPath);
|
||||||
abstract long getTotalSpace(File pPath);
|
|
||||||
|
abstract String getName();
|
||||||
abstract String getName();
|
|
||||||
|
static BufferedReader exec(String[] pArgs) throws IOException {
|
||||||
static BufferedReader exec(String[] pArgs) throws IOException {
|
Process cmd = Runtime.getRuntime().exec(pArgs);
|
||||||
Process cmd = Runtime.getRuntime().exec(pArgs);
|
return new BufferedReader(new InputStreamReader(cmd.getInputStream()));
|
||||||
return new BufferedReader(new InputStreamReader(cmd.getInputStream()));
|
}
|
||||||
}
|
|
||||||
|
static FileSystem get() {
|
||||||
static FileSystem get() {
|
String os = System.getProperty("os.name");
|
||||||
String os = System.getProperty("os.name");
|
//System.out.println("os = " + os);
|
||||||
//System.out.println("os = " + os);
|
|
||||||
|
os = os.toLowerCase();
|
||||||
os = os.toLowerCase();
|
if (os.contains("windows")) {
|
||||||
if (os.contains("windows")) {
|
return new Win32FileSystem();
|
||||||
return new Win32FileSystem();
|
}
|
||||||
}
|
else if (os.contains("linux") ||
|
||||||
else if (os.contains("linux") ||
|
os.contains("sun os") ||
|
||||||
os.contains("sun os") ||
|
os.contains("sunos") ||
|
||||||
os.contains("sunos") ||
|
os.contains("solaris") ||
|
||||||
os.contains("solaris") ||
|
os.contains("mpe/ix") ||
|
||||||
os.contains("mpe/ix") ||
|
os.contains("hp-ux") ||
|
||||||
os.contains("hp-ux") ||
|
os.contains("aix") ||
|
||||||
os.contains("aix") ||
|
os.contains("freebsd") ||
|
||||||
os.contains("freebsd") ||
|
os.contains("irix") ||
|
||||||
os.contains("irix") ||
|
os.contains("digital unix") ||
|
||||||
os.contains("digital unix") ||
|
os.contains("unix") ||
|
||||||
os.contains("unix") ||
|
os.contains("mac os x")) {
|
||||||
os.contains("mac os x")) {
|
return new UnixFileSystem();
|
||||||
return new UnixFileSystem();
|
}
|
||||||
}
|
else {
|
||||||
else {
|
return new UnknownFileSystem(os);
|
||||||
return new UnknownFileSystem(os);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private static class UnknownFileSystem extends FileSystem {
|
||||||
private static class UnknownFileSystem extends FileSystem {
|
private final String osName;
|
||||||
private final String osName;
|
|
||||||
|
UnknownFileSystem(String pOSName) {
|
||||||
UnknownFileSystem(String pOSName) {
|
osName = pOSName;
|
||||||
osName = pOSName;
|
}
|
||||||
}
|
|
||||||
|
long getFreeSpace(File pPath) {
|
||||||
long getFreeSpace(File pPath) {
|
return 0l;
|
||||||
return 0l;
|
}
|
||||||
}
|
|
||||||
|
long getTotalSpace(File pPath) {
|
||||||
long getTotalSpace(File pPath) {
|
return 0l;
|
||||||
return 0l;
|
}
|
||||||
}
|
|
||||||
|
String getName() {
|
||||||
String getName() {
|
return "Unknown (" + osName + ")";
|
||||||
return "Unknown (" + osName + ")";
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,244 +1,250 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.util.regex.WildcardStringParser;
|
import com.twelvemonkeys.util.regex.WildcardStringParser;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Java Bean used for approving file names which are to be included in a
|
* A Java Bean used for approving file names which are to be included in a
|
||||||
* {@code java.io.File} listing.
|
* {@code java.io.File} listing.
|
||||||
* The mask is given as a well-known DOS filename format, with '*' and '?' as
|
* The mask is given as a well-known DOS filename format, with '*' and '?' as
|
||||||
* wildcards.
|
* wildcards.
|
||||||
* All other characters counts as ordinary characters.
|
* All other characters counts as ordinary characters.
|
||||||
* <p/>
|
* <p>
|
||||||
* The file name masks are used as a filter input and is given to the class via
|
* The file name masks are used as a filter input and is given to the class via
|
||||||
* the string array property:<br>
|
* the string array property:
|
||||||
* <dd>{@code filenameMasksForInclusion} - Filename mask for exclusion of
|
* </p>
|
||||||
* files (default if both properties are defined)
|
* <dl>
|
||||||
* <dd>{@code filenameMasksForExclusion} - Filename mask for exclusion of
|
* <dt>{@code filenameMasksForInclusion}</dt>
|
||||||
* files.
|
* <dd>Filename mask for exclusion of
|
||||||
* <p/>
|
* files (default if both properties are defined).</dd>
|
||||||
* A recommended way of doing this is by referencing to the component which uses
|
* <dt>{@code filenameMasksForExclusion}</dt>
|
||||||
* this class for file listing. In this way all properties are set in the same
|
* <dd>Filename mask for exclusion of files.</dd>
|
||||||
* component and this utility component is kept in the background with only
|
* </dl>
|
||||||
* initial configuration necessary.
|
* <p>
|
||||||
*
|
* A recommended way of doing this is by referencing to the component which uses
|
||||||
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
* this class for file listing. In this way all properties are set in the same
|
||||||
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
* component and this utility component is kept in the background with only
|
||||||
* @see FilenameFilter java.io.FilenameFilter
|
* initial configuration necessary.
|
||||||
* @see WildcardStringParser
|
* </p>
|
||||||
* @deprecated
|
*
|
||||||
*/
|
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
||||||
public class FilenameMaskFilter implements FilenameFilter {
|
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
||||||
|
* @see FilenameFilter java.io.FilenameFilter
|
||||||
// TODO: Rewrite to use regexp, or create new class
|
* @see WildcardStringParser
|
||||||
|
* @deprecated
|
||||||
// Members
|
*/
|
||||||
private String[] filenameMasksForInclusion;
|
@Deprecated
|
||||||
private String[] filenameMasksForExclusion;
|
public class FilenameMaskFilter implements FilenameFilter {
|
||||||
private boolean inclusion = true;
|
|
||||||
|
// TODO: Rewrite to use regexp, or create new class
|
||||||
|
|
||||||
/**
|
// Members
|
||||||
* Creates a {@code FilenameMaskFilter}
|
private String[] filenameMasksForInclusion;
|
||||||
*/
|
private String[] filenameMasksForExclusion;
|
||||||
public FilenameMaskFilter() {
|
private boolean inclusion = true;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code FilenameMaskFilter}
|
* Creates a {@code FilenameMaskFilter}
|
||||||
*
|
*/
|
||||||
* @param pFilenameMask the filename mask
|
public FilenameMaskFilter() {
|
||||||
*/
|
}
|
||||||
public FilenameMaskFilter(final String pFilenameMask) {
|
|
||||||
String[] filenameMask = {pFilenameMask};
|
/**
|
||||||
setFilenameMasksForInclusion(filenameMask);
|
* Creates a {@code FilenameMaskFilter}
|
||||||
}
|
*
|
||||||
|
* @param pFilenameMask the filename mask
|
||||||
/**
|
*/
|
||||||
* Creates a {@code FilenameMaskFilter}
|
public FilenameMaskFilter(final String pFilenameMask) {
|
||||||
*
|
String[] filenameMask = {pFilenameMask};
|
||||||
* @param pFilenameMasks the filename masks
|
setFilenameMasksForInclusion(filenameMask);
|
||||||
*/
|
}
|
||||||
public FilenameMaskFilter(final String[] pFilenameMasks) {
|
|
||||||
this(pFilenameMasks, false);
|
/**
|
||||||
}
|
* Creates a {@code FilenameMaskFilter}
|
||||||
|
*
|
||||||
/**
|
* @param pFilenameMasks the filename masks
|
||||||
* Creates a {@code FilenameMaskFilter}
|
*/
|
||||||
*
|
public FilenameMaskFilter(final String[] pFilenameMasks) {
|
||||||
* @param pFilenameMask the filename masks
|
this(pFilenameMasks, false);
|
||||||
* @param pExclusion if {@code true}, the masks will be excluded
|
}
|
||||||
*/
|
|
||||||
public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) {
|
/**
|
||||||
String[] filenameMask = {pFilenameMask};
|
* Creates a {@code FilenameMaskFilter}
|
||||||
|
*
|
||||||
if (pExclusion) {
|
* @param pFilenameMask the filename masks
|
||||||
setFilenameMasksForExclusion(filenameMask);
|
* @param pExclusion if {@code true}, the masks will be excluded
|
||||||
}
|
*/
|
||||||
else {
|
public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) {
|
||||||
setFilenameMasksForInclusion(filenameMask);
|
String[] filenameMask = {pFilenameMask};
|
||||||
}
|
|
||||||
}
|
if (pExclusion) {
|
||||||
|
setFilenameMasksForExclusion(filenameMask);
|
||||||
/**
|
}
|
||||||
* Creates a {@code FilenameMaskFilter}
|
else {
|
||||||
*
|
setFilenameMasksForInclusion(filenameMask);
|
||||||
* @param pFilenameMasks the filename masks
|
}
|
||||||
* @param pExclusion if {@code true}, the masks will be excluded
|
}
|
||||||
*/
|
|
||||||
public FilenameMaskFilter(final String[] pFilenameMasks, final boolean pExclusion) {
|
/**
|
||||||
if (pExclusion) {
|
* Creates a {@code FilenameMaskFilter}
|
||||||
setFilenameMasksForExclusion(pFilenameMasks);
|
*
|
||||||
}
|
* @param pFilenameMasks the filename masks
|
||||||
else {
|
* @param pExclusion if {@code true}, the masks will be excluded
|
||||||
setFilenameMasksForInclusion(pFilenameMasks);
|
*/
|
||||||
}
|
public FilenameMaskFilter(final String[] pFilenameMasks, final boolean pExclusion) {
|
||||||
}
|
if (pExclusion) {
|
||||||
|
setFilenameMasksForExclusion(pFilenameMasks);
|
||||||
/**
|
}
|
||||||
*
|
else {
|
||||||
* @param pFilenameMasksForInclusion the filename masks to include
|
setFilenameMasksForInclusion(pFilenameMasks);
|
||||||
*/
|
}
|
||||||
public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
|
}
|
||||||
filenameMasksForInclusion = pFilenameMasksForInclusion;
|
|
||||||
}
|
/**
|
||||||
|
*
|
||||||
/**
|
* @param pFilenameMasksForInclusion the filename masks to include
|
||||||
* @return the current inclusion masks
|
*/
|
||||||
*/
|
public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
|
||||||
public String[] getFilenameMasksForInclusion() {
|
filenameMasksForInclusion = pFilenameMasksForInclusion;
|
||||||
return filenameMasksForInclusion.clone();
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* @return the current inclusion masks
|
||||||
* @param pFilenameMasksForExclusion the filename masks to exclude
|
*/
|
||||||
*/
|
public String[] getFilenameMasksForInclusion() {
|
||||||
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
|
return filenameMasksForInclusion.clone();
|
||||||
filenameMasksForExclusion = pFilenameMasksForExclusion;
|
}
|
||||||
inclusion = false;
|
|
||||||
}
|
/**
|
||||||
|
* @param pFilenameMasksForExclusion the filename masks to exclude
|
||||||
/**
|
*/
|
||||||
* @return the current exclusion masks
|
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
|
||||||
*/
|
filenameMasksForExclusion = pFilenameMasksForExclusion;
|
||||||
public String[] getFilenameMasksForExclusion() {
|
inclusion = false;
|
||||||
return filenameMasksForExclusion.clone();
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* @return the current exclusion masks
|
||||||
* This method implements the {@code java.io.FilenameFilter} interface.
|
*/
|
||||||
*
|
public String[] getFilenameMasksForExclusion() {
|
||||||
* @param pDir the directory in which the file was found.
|
return filenameMasksForExclusion.clone();
|
||||||
* @param pName the name of the file.
|
}
|
||||||
* @return {@code true} if the file {@code pName} should be included in the file
|
|
||||||
* list; {@code false} otherwise.
|
/**
|
||||||
*/
|
* This method implements the {@code java.io.FilenameFilter} interface.
|
||||||
public boolean accept(File pDir, String pName) {
|
*
|
||||||
WildcardStringParser parser;
|
* @param pDir the directory in which the file was found.
|
||||||
|
* @param pName the name of the file.
|
||||||
// Check each filename string mask whether the file is to be accepted
|
* @return {@code true} if the file {@code pName} should be included in the file
|
||||||
if (inclusion) { // Inclusion
|
* list; {@code false} otherwise.
|
||||||
for (String mask : filenameMasksForInclusion) {
|
*/
|
||||||
parser = new WildcardStringParser(mask);
|
public boolean accept(File pDir, String pName) {
|
||||||
if (parser.parseString(pName)) {
|
WildcardStringParser parser;
|
||||||
|
|
||||||
// The filename was accepted by the filename masks provided
|
// Check each filename string mask whether the file is to be accepted
|
||||||
// - include it in filename list
|
if (inclusion) { // Inclusion
|
||||||
return true;
|
for (String mask : filenameMasksForInclusion) {
|
||||||
}
|
parser = new WildcardStringParser(mask);
|
||||||
}
|
if (parser.parseString(pName)) {
|
||||||
|
|
||||||
// The filename not was accepted by any of the filename masks
|
// The filename was accepted by the filename masks provided
|
||||||
// provided - NOT to be included in the filename list
|
// - include it in filename list
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
// Exclusion
|
|
||||||
for (String mask : filenameMasksForExclusion) {
|
// The filename not was accepted by any of the filename masks
|
||||||
parser = new WildcardStringParser(mask);
|
// provided - NOT to be included in the filename list
|
||||||
if (parser.parseString(pName)) {
|
return false;
|
||||||
|
}
|
||||||
// The filename was accepted by the filename masks provided
|
else {
|
||||||
// - NOT to be included in the filename list
|
// Exclusion
|
||||||
return false;
|
for (String mask : filenameMasksForExclusion) {
|
||||||
}
|
parser = new WildcardStringParser(mask);
|
||||||
}
|
if (parser.parseString(pName)) {
|
||||||
|
|
||||||
// The filename was not accepted by any of the filename masks
|
// The filename was accepted by the filename masks provided
|
||||||
// provided - include it in filename list
|
// - NOT to be included in the filename list
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// The filename was not accepted by any of the filename masks
|
||||||
* @return a string representation for debug purposes
|
// provided - include it in filename list
|
||||||
*/
|
return true;
|
||||||
public String toString() {
|
}
|
||||||
StringBuilder retVal = new StringBuilder();
|
}
|
||||||
int i;
|
|
||||||
|
/**
|
||||||
if (inclusion) {
|
* @return a string representation for debug purposes
|
||||||
// Inclusion
|
*/
|
||||||
if (filenameMasksForInclusion == null) {
|
public String toString() {
|
||||||
retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
|
StringBuilder retVal = new StringBuilder();
|
||||||
}
|
int i;
|
||||||
else {
|
|
||||||
retVal.append(filenameMasksForInclusion.length);
|
if (inclusion) {
|
||||||
retVal.append(" filename mask(s) - ");
|
// Inclusion
|
||||||
for (i = 0; i < filenameMasksForInclusion.length; i++) {
|
if (filenameMasksForInclusion == null) {
|
||||||
retVal.append("\"");
|
retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
|
||||||
retVal.append(filenameMasksForInclusion[i]);
|
}
|
||||||
retVal.append("\", \"");
|
else {
|
||||||
}
|
retVal.append(filenameMasksForInclusion.length);
|
||||||
}
|
retVal.append(" filename mask(s) - ");
|
||||||
}
|
for (i = 0; i < filenameMasksForInclusion.length; i++) {
|
||||||
else {
|
retVal.append("\"");
|
||||||
// Exclusion
|
retVal.append(filenameMasksForInclusion[i]);
|
||||||
if (filenameMasksForExclusion == null) {
|
retVal.append("\", \"");
|
||||||
retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
|
}
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
retVal.append(filenameMasksForExclusion.length);
|
else {
|
||||||
retVal.append(" exclusion filename mask(s) - ");
|
// Exclusion
|
||||||
for (i = 0; i < filenameMasksForExclusion.length; i++) {
|
if (filenameMasksForExclusion == null) {
|
||||||
retVal.append("\"");
|
retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
|
||||||
retVal.append(filenameMasksForExclusion[i]);
|
}
|
||||||
retVal.append("\", \"");
|
else {
|
||||||
}
|
retVal.append(filenameMasksForExclusion.length);
|
||||||
}
|
retVal.append(" exclusion filename mask(s) - ");
|
||||||
}
|
for (i = 0; i < filenameMasksForExclusion.length; i++) {
|
||||||
return retVal.toString();
|
retVal.append("\"");
|
||||||
}
|
retVal.append(filenameMasksForExclusion[i]);
|
||||||
}
|
retVal.append("\", \"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+450
-453
@@ -1,453 +1,450 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
|
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
|
||||||
*
|
*
|
||||||
* Please feel free to use any fragment of this code you need in your own work.
|
* Please feel free to use any fragment of this code you need in your own work.
|
||||||
* As far as I am concerned, it's in the public domain. No permission is necessary
|
* As far as I am concerned, it's in the public domain. No permission is necessary
|
||||||
* or required. Credit is always appreciated if you use a large chunk or base a
|
* or required. Credit is always appreciated if you use a large chunk or base a
|
||||||
* significant product on one of my examples, but that's not required either.
|
* significant product on one of my examples, but that's not required either.
|
||||||
*
|
*
|
||||||
* Elliotte Rusty Harold
|
* Elliotte Rusty Harold
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A little endian input stream reads two's complement,
|
* A little endian input stream reads two's complement,
|
||||||
* little endian integers, floating point numbers, and characters
|
* little endian integers, floating point numbers, and characters
|
||||||
* and returns them as Java primitive types.
|
* and returns them as Java primitive types.
|
||||||
* <p/>
|
* <p>
|
||||||
* The standard {@code java.io.DataInputStream} class
|
* The standard {@code java.io.DataInputStream} class
|
||||||
* which this class imitates reads big endian quantities.
|
* which this class imitates reads big endian quantities.
|
||||||
* <p/>
|
* </p>
|
||||||
* <em>Warning:
|
* <p>
|
||||||
* <!-- Beware of little indians! -->
|
* <em>Warning:
|
||||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||||
* specifies big endian byte order in their documentation.
|
* specifies big endian byte order in their documentation.
|
||||||
* This means that this class is, strictly speaking, not a proper
|
* This means that this class is, strictly speaking, not a proper
|
||||||
* implementation. However, I don't see a reason for the these interfaces to
|
* implementation. However, I don't see a reason for the these interfaces to
|
||||||
* specify the byte order of their underlying representations.
|
* specify the byte order of their underlying representations.
|
||||||
* </em>
|
* </em>
|
||||||
*
|
* </p>
|
||||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
*
|
||||||
* @see java.io.DataInputStream
|
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||||
* @see java.io.DataInput
|
* @see java.io.DataInputStream
|
||||||
* @see java.io.DataOutput
|
* @see java.io.DataInput
|
||||||
*
|
* @see java.io.DataOutput
|
||||||
* @author Elliotte Rusty Harold
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author Elliotte Rusty Harold
|
||||||
* @version 2
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
*/
|
* @version 2
|
||||||
public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
|
*/
|
||||||
// TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations?
|
public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
|
||||||
/**
|
// TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations?
|
||||||
* Creates a new little endian input stream and chains it to the
|
/**
|
||||||
* input stream specified by the {@code pStream} argument.
|
* Creates a new little endian input stream and chains it to the
|
||||||
*
|
* input stream specified by the {@code pStream} argument.
|
||||||
* @param pStream the underlying input stream.
|
*
|
||||||
* @see java.io.FilterInputStream#in
|
* @param pStream the underlying input stream.
|
||||||
*/
|
* @see java.io.FilterInputStream#in
|
||||||
public LittleEndianDataInputStream(final InputStream pStream) {
|
*/
|
||||||
super(Validate.notNull(pStream, "stream"));
|
public LittleEndianDataInputStream(final InputStream pStream) {
|
||||||
}
|
super(Validate.notNull(pStream, "stream"));
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads a {@code boolean} from the underlying input stream by
|
/**
|
||||||
* reading a single byte. If the byte is zero, false is returned.
|
* Reads a {@code boolean} from the underlying input stream by
|
||||||
* If the byte is positive, true is returned.
|
* reading a single byte. If the byte is zero, false is returned.
|
||||||
*
|
* If the byte is positive, true is returned.
|
||||||
* @return the {@code boolean} value read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input stream
|
* @return the {@code boolean} value read.
|
||||||
* has been reached
|
* @throws EOFException if the end of the underlying input stream
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public boolean readBoolean() throws IOException {
|
*/
|
||||||
int b = in.read();
|
public boolean readBoolean() throws IOException {
|
||||||
|
int b = in.read();
|
||||||
if (b < 0) {
|
|
||||||
throw new EOFException();
|
if (b < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return b != 0;
|
|
||||||
}
|
return b != 0;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads a signed {@code byte} from the underlying input stream
|
/**
|
||||||
* with value between -128 and 127
|
* Reads a signed {@code byte} from the underlying input stream
|
||||||
*
|
* with value between -128 and 127
|
||||||
* @return the {@code byte} value read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input stream
|
* @return the {@code byte} value read.
|
||||||
* has been reached
|
* @throws EOFException if the end of the underlying input stream
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public byte readByte() throws IOException {
|
*/
|
||||||
int b = in.read();
|
public byte readByte() throws IOException {
|
||||||
|
int b = in.read();
|
||||||
if (b < 0) {
|
|
||||||
throw new EOFException();
|
if (b < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return (byte) b;
|
|
||||||
|
return (byte) b;
|
||||||
}
|
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads an unsigned {@code byte} from the underlying
|
/**
|
||||||
* input stream with value between 0 and 255
|
* Reads an unsigned {@code byte} from the underlying
|
||||||
*
|
* input stream with value between 0 and 255
|
||||||
* @return the {@code byte} value read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input
|
* @return the {@code byte} value read.
|
||||||
* stream has been reached
|
* @throws EOFException if the end of the underlying input
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* stream has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public int readUnsignedByte() throws IOException {
|
*/
|
||||||
int b = in.read();
|
public int readUnsignedByte() throws IOException {
|
||||||
|
int b = in.read();
|
||||||
if (b < 0) {
|
|
||||||
throw new EOFException();
|
if (b < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return b;
|
|
||||||
}
|
return b;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads a two byte signed {@code short} from the underlying
|
/**
|
||||||
* input stream in little endian order, low byte first.
|
* Reads a two byte signed {@code short} from the underlying
|
||||||
*
|
* input stream in little endian order, low byte first.
|
||||||
* @return the {@code short} read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input stream
|
* @return the {@code short} read.
|
||||||
* has been reached
|
* @throws EOFException if the end of the underlying input stream
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public short readShort() throws IOException {
|
*/
|
||||||
int byte1 = in.read();
|
public short readShort() throws IOException {
|
||||||
int byte2 = in.read();
|
int byte1 = in.read();
|
||||||
|
int byte2 = in.read();
|
||||||
// only need to test last byte read
|
|
||||||
// if byte1 is -1 so is byte2
|
// only need to test last byte read
|
||||||
if (byte2 < 0) {
|
// if byte1 is -1 so is byte2
|
||||||
throw new EOFException();
|
if (byte2 < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return (short) (((byte2 << 24) >>> 16) | (byte1 << 24) >>> 24);
|
|
||||||
}
|
return (short) (((byte2 << 24) >>> 16) | (byte1 << 24) >>> 24);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads a two byte unsigned {@code short} from the underlying
|
/**
|
||||||
* input stream in little endian order, low byte first.
|
* Reads a two byte unsigned {@code short} from the underlying
|
||||||
*
|
* input stream in little endian order, low byte first.
|
||||||
* @return the int value of the unsigned short read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input stream
|
* @return the int value of the unsigned short read.
|
||||||
* has been reached
|
* @throws EOFException if the end of the underlying input stream
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public int readUnsignedShort() throws IOException {
|
*/
|
||||||
int byte1 = in.read();
|
public int readUnsignedShort() throws IOException {
|
||||||
int byte2 = in.read();
|
int byte1 = in.read();
|
||||||
|
int byte2 = in.read();
|
||||||
if (byte2 < 0) {
|
|
||||||
throw new EOFException();
|
if (byte2 < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return (byte2 << 8) + byte1;
|
|
||||||
}
|
return (byte2 << 8) + byte1;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads a two byte Unicode {@code char} from the underlying
|
/**
|
||||||
* input stream in little endian order, low byte first.
|
* Reads a two byte Unicode {@code char} from the underlying
|
||||||
*
|
* input stream in little endian order, low byte first.
|
||||||
* @return the int value of the unsigned short read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input stream
|
* @return the int value of the unsigned short read.
|
||||||
* has been reached
|
* @throws EOFException if the end of the underlying input stream
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public char readChar() throws IOException {
|
*/
|
||||||
int byte1 = in.read();
|
public char readChar() throws IOException {
|
||||||
int byte2 = in.read();
|
int byte1 = in.read();
|
||||||
|
int byte2 = in.read();
|
||||||
if (byte2 < 0) {
|
|
||||||
throw new EOFException();
|
if (byte2 < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return (char) (((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24));
|
|
||||||
}
|
return (char) (((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a four byte signed {@code int} from the underlying
|
/**
|
||||||
* input stream in little endian order, low byte first.
|
* Reads a four byte signed {@code int} from the underlying
|
||||||
*
|
* input stream in little endian order, low byte first.
|
||||||
* @return the {@code int} read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input stream
|
* @return the {@code int} read.
|
||||||
* has been reached
|
* @throws EOFException if the end of the underlying input stream
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public int readInt() throws IOException {
|
*/
|
||||||
int byte1 = in.read();
|
public int readInt() throws IOException {
|
||||||
int byte2 = in.read();
|
int byte1 = in.read();
|
||||||
int byte3 = in.read();
|
int byte2 = in.read();
|
||||||
int byte4 = in.read();
|
int byte3 = in.read();
|
||||||
|
int byte4 = in.read();
|
||||||
if (byte4 < 0) {
|
|
||||||
throw new EOFException();
|
if (byte4 < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return (byte4 << 24) | ((byte3 << 24) >>> 8)
|
|
||||||
| ((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24);
|
return (byte4 << 24) | ((byte3 << 24) >>> 8)
|
||||||
}
|
| ((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads an eight byte signed {@code int} from the underlying
|
/**
|
||||||
* input stream in little endian order, low byte first.
|
* Reads an eight byte signed {@code int} from the underlying
|
||||||
*
|
* input stream in little endian order, low byte first.
|
||||||
* @return the {@code int} read.
|
*
|
||||||
* @throws EOFException if the end of the underlying input stream
|
* @return the {@code int} read.
|
||||||
* has been reached
|
* @throws EOFException if the end of the underlying input stream
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* has been reached
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public long readLong() throws IOException {
|
*/
|
||||||
long byte1 = in.read();
|
public long readLong() throws IOException {
|
||||||
long byte2 = in.read();
|
long byte1 = in.read();
|
||||||
long byte3 = in.read();
|
long byte2 = in.read();
|
||||||
long byte4 = in.read();
|
long byte3 = in.read();
|
||||||
long byte5 = in.read();
|
long byte4 = in.read();
|
||||||
long byte6 = in.read();
|
long byte5 = in.read();
|
||||||
long byte7 = in.read();
|
long byte6 = in.read();
|
||||||
long byte8 = in.read();
|
long byte7 = in.read();
|
||||||
|
long byte8 = in.read();
|
||||||
if (byte8 < 0) {
|
|
||||||
throw new EOFException();
|
if (byte8 < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
return (byte8 << 56) | ((byte7 << 56) >>> 8)
|
|
||||||
| ((byte6 << 56) >>> 16) | ((byte5 << 56) >>> 24)
|
return (byte8 << 56) | ((byte7 << 56) >>> 8)
|
||||||
| ((byte4 << 56) >>> 32) | ((byte3 << 56) >>> 40)
|
| ((byte6 << 56) >>> 16) | ((byte5 << 56) >>> 24)
|
||||||
| ((byte2 << 56) >>> 48) | ((byte1 << 56) >>> 56);
|
| ((byte4 << 56) >>> 32) | ((byte3 << 56) >>> 40)
|
||||||
}
|
| ((byte2 << 56) >>> 48) | ((byte1 << 56) >>> 56);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Reads a string of no more than 65,535 characters
|
/**
|
||||||
* from the underlying input stream using UTF-8
|
* Reads a string of no more than 65,535 characters
|
||||||
* encoding. This method first reads a two byte short
|
* from the underlying input stream using UTF-8
|
||||||
* in <b>big</b> endian order as required by the
|
* encoding. This method first reads a two byte short
|
||||||
* UTF-8 specification. This gives the number of bytes in
|
* in <b>big</b> endian order as required by the
|
||||||
* the UTF-8 encoded version of the string.
|
* UTF-8 specification. This gives the number of bytes in
|
||||||
* Next this many bytes are read and decoded as UTF-8
|
* the UTF-8 encoded version of the string.
|
||||||
* encoded characters.
|
* Next this many bytes are read and decoded as UTF-8
|
||||||
*
|
* encoded characters.
|
||||||
* @return the decoded string
|
*
|
||||||
* @throws UTFDataFormatException if the string cannot be decoded
|
* @return the decoded string
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws UTFDataFormatException if the string cannot be decoded
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public String readUTF() throws IOException {
|
*/
|
||||||
int byte1 = in.read();
|
public String readUTF() throws IOException {
|
||||||
int byte2 = in.read();
|
int byte1 = in.read();
|
||||||
|
int byte2 = in.read();
|
||||||
if (byte2 < 0) {
|
|
||||||
throw new EOFException();
|
if (byte2 < 0) {
|
||||||
}
|
throw new EOFException();
|
||||||
|
}
|
||||||
int numbytes = (byte1 << 8) + byte2;
|
|
||||||
char result[] = new char[numbytes];
|
int numbytes = (byte1 << 8) + byte2;
|
||||||
int numread = 0;
|
char result[] = new char[numbytes];
|
||||||
int numchars = 0;
|
int numread = 0;
|
||||||
|
int numchars = 0;
|
||||||
while (numread < numbytes) {
|
|
||||||
int c1 = readUnsignedByte();
|
while (numread < numbytes) {
|
||||||
int c2, c3;
|
int c1 = readUnsignedByte();
|
||||||
|
int c2, c3;
|
||||||
// The first four bits of c1 determine how many bytes are in this char
|
|
||||||
int test = c1 >> 4;
|
// The first four bits of c1 determine how many bytes are in this char
|
||||||
if (test < 8) { // one byte
|
int test = c1 >> 4;
|
||||||
numread++;
|
if (test < 8) { // one byte
|
||||||
result[numchars++] = (char) c1;
|
numread++;
|
||||||
}
|
result[numchars++] = (char) c1;
|
||||||
else if (test == 12 || test == 13) { // two bytes
|
}
|
||||||
numread += 2;
|
else if (test == 12 || test == 13) { // two bytes
|
||||||
|
numread += 2;
|
||||||
if (numread > numbytes) {
|
|
||||||
throw new UTFDataFormatException();
|
if (numread > numbytes) {
|
||||||
}
|
throw new UTFDataFormatException();
|
||||||
|
}
|
||||||
c2 = readUnsignedByte();
|
|
||||||
|
c2 = readUnsignedByte();
|
||||||
if ((c2 & 0xC0) != 0x80) {
|
|
||||||
throw new UTFDataFormatException();
|
if ((c2 & 0xC0) != 0x80) {
|
||||||
}
|
throw new UTFDataFormatException();
|
||||||
|
}
|
||||||
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
|
||||||
}
|
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
||||||
else if (test == 14) { // three bytes
|
}
|
||||||
numread += 3;
|
else if (test == 14) { // three bytes
|
||||||
|
numread += 3;
|
||||||
if (numread > numbytes) {
|
|
||||||
throw new UTFDataFormatException();
|
if (numread > numbytes) {
|
||||||
}
|
throw new UTFDataFormatException();
|
||||||
|
}
|
||||||
c2 = readUnsignedByte();
|
|
||||||
c3 = readUnsignedByte();
|
c2 = readUnsignedByte();
|
||||||
|
c3 = readUnsignedByte();
|
||||||
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
|
||||||
throw new UTFDataFormatException();
|
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
||||||
}
|
throw new UTFDataFormatException();
|
||||||
|
}
|
||||||
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
|
||||||
}
|
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
||||||
else { // malformed
|
}
|
||||||
throw new UTFDataFormatException();
|
else { // malformed
|
||||||
}
|
throw new UTFDataFormatException();
|
||||||
|
}
|
||||||
} // end while
|
|
||||||
|
} // end while
|
||||||
return new String(result, 0, numchars);
|
|
||||||
|
return new String(result, 0, numchars);
|
||||||
}
|
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @return the next eight bytes of this input stream, interpreted as a
|
/**
|
||||||
* little endian {@code double}.
|
* @return the next eight bytes of this input stream, interpreted as a
|
||||||
* @throws EOFException if end of stream occurs before eight bytes
|
* little endian {@code double}.
|
||||||
* have been read.
|
* @throws EOFException if end of stream occurs before eight bytes
|
||||||
* @throws IOException if an I/O error occurs.
|
* have been read.
|
||||||
*/
|
* @throws IOException if an I/O error occurs.
|
||||||
public final double readDouble() throws IOException {
|
*/
|
||||||
return Double.longBitsToDouble(readLong());
|
public final double readDouble() throws IOException {
|
||||||
}
|
return Double.longBitsToDouble(readLong());
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @return the next four bytes of this input stream, interpreted as a
|
/**
|
||||||
* little endian {@code int}.
|
* @return the next four bytes of this input stream, interpreted as a
|
||||||
* @throws EOFException if end of stream occurs before four bytes
|
* little endian {@code int}.
|
||||||
* have been read.
|
* @throws EOFException if end of stream occurs before four bytes
|
||||||
* @throws IOException if an I/O error occurs.
|
* have been read.
|
||||||
*/
|
* @throws IOException if an I/O error occurs.
|
||||||
public final float readFloat() throws IOException {
|
*/
|
||||||
return Float.intBitsToFloat(readInt());
|
public final float readFloat() throws IOException {
|
||||||
}
|
return Float.intBitsToFloat(readInt());
|
||||||
|
}
|
||||||
/**
|
|
||||||
* See the general contract of the {@code skipBytes}
|
/**
|
||||||
* method of {@code DataInput}.
|
* See the general contract of the {@code skipBytes}
|
||||||
* <p>
|
* method of {@code DataInput}.
|
||||||
* Bytes for this operation are read from the contained input stream.
|
* <p>
|
||||||
*
|
* Bytes for this operation are read from the contained input stream.
|
||||||
* @param pLength the number of bytes to be skipped.
|
*
|
||||||
* @return the actual number of bytes skipped.
|
* @param pLength the number of bytes to be skipped.
|
||||||
* @exception IOException if an I/O error occurs.
|
* @return the actual number of bytes skipped.
|
||||||
*/
|
* @exception IOException if an I/O error occurs.
|
||||||
public final int skipBytes(int pLength) throws IOException {
|
*/
|
||||||
// NOTE: There was probably a bug in ERH's original code here, as skip
|
public final int skipBytes(int pLength) throws IOException {
|
||||||
// never returns -1, but returns 0 if no more bytes can be skipped...
|
// NOTE: There was probably a bug in ERH's original code here, as skip
|
||||||
int total = 0;
|
// never returns -1, but returns 0 if no more bytes can be skipped...
|
||||||
int skipped;
|
int total = 0;
|
||||||
|
int skipped;
|
||||||
while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) {
|
|
||||||
total += skipped;
|
while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) {
|
||||||
}
|
total += skipped;
|
||||||
|
}
|
||||||
return total;
|
|
||||||
}
|
return total;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readFully}
|
/**
|
||||||
* method of {@code DataInput}.
|
* See the general contract of the {@code readFully} method of {@code DataInput}.
|
||||||
* <p/>
|
* <p>
|
||||||
* Bytes
|
* Bytes for this operation are read from the contained input stream.
|
||||||
* for this operation are read from the contained
|
* </p>
|
||||||
* input stream.
|
*
|
||||||
*
|
* @param pBytes the buffer into which the data is read.
|
||||||
* @param pBytes the buffer into which the data is read.
|
* @throws EOFException if this input stream reaches the end before
|
||||||
* @throws EOFException if this input stream reaches the end before
|
* reading all the bytes.
|
||||||
* reading all the bytes.
|
* @throws IOException if an I/O error occurs.
|
||||||
* @throws IOException if an I/O error occurs.
|
* @see java.io.FilterInputStream#in
|
||||||
* @see java.io.FilterInputStream#in
|
*/
|
||||||
*/
|
public final void readFully(byte pBytes[]) throws IOException {
|
||||||
public final void readFully(byte pBytes[]) throws IOException {
|
readFully(pBytes, 0, pBytes.length);
|
||||||
readFully(pBytes, 0, pBytes.length);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* See the general contract of the {@code readFully} method of {@code DataInput}.
|
||||||
* See the general contract of the {@code readFully}
|
* <p>
|
||||||
* method of {@code DataInput}.
|
* Bytes for this operation are read from the contained input stream.
|
||||||
* <p/>
|
* </p>
|
||||||
* Bytes
|
*
|
||||||
* for this operation are read from the contained
|
* @param pBytes the buffer into which the data is read.
|
||||||
* input stream.
|
* @param pOffset the start offset of the data.
|
||||||
*
|
* @param pLength the number of bytes to read.
|
||||||
* @param pBytes the buffer into which the data is read.
|
* @throws EOFException if this input stream reaches the end before
|
||||||
* @param pOffset the start offset of the data.
|
* reading all the bytes.
|
||||||
* @param pLength the number of bytes to read.
|
* @throws IOException if an I/O error occurs.
|
||||||
* @throws EOFException if this input stream reaches the end before
|
* @see java.io.FilterInputStream#in
|
||||||
* reading all the bytes.
|
*/
|
||||||
* @throws IOException if an I/O error occurs.
|
public final void readFully(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||||
* @see java.io.FilterInputStream#in
|
if (pLength < 0) {
|
||||||
*/
|
throw new IndexOutOfBoundsException();
|
||||||
public final void readFully(byte pBytes[], int pOffset, int pLength) throws IOException {
|
}
|
||||||
if (pLength < 0) {
|
|
||||||
throw new IndexOutOfBoundsException();
|
int count = 0;
|
||||||
}
|
|
||||||
|
while (count < pLength) {
|
||||||
int count = 0;
|
int read = in.read(pBytes, pOffset + count, pLength - count);
|
||||||
|
|
||||||
while (count < pLength) {
|
if (read < 0) {
|
||||||
int read = in.read(pBytes, pOffset + count, pLength - count);
|
throw new EOFException();
|
||||||
|
}
|
||||||
if (read < 0) {
|
|
||||||
throw new EOFException();
|
count += read;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
count += read;
|
|
||||||
}
|
/**
|
||||||
}
|
* See the general contract of the {@code readLine}
|
||||||
|
* method of {@code DataInput}.
|
||||||
/**
|
* <p>
|
||||||
* See the general contract of the {@code readLine}
|
* Bytes for this operation are read from the contained input stream.
|
||||||
* method of {@code DataInput}.
|
*
|
||||||
* <p>
|
* @deprecated This method does not properly convert bytes to characters.
|
||||||
* Bytes for this operation are read from the contained input stream.
|
*
|
||||||
*
|
* @return the next line of text from this input stream.
|
||||||
* @deprecated This method does not properly convert bytes to characters.
|
* @exception IOException if an I/O error occurs.
|
||||||
*
|
* @see java.io.BufferedReader#readLine()
|
||||||
* @return the next line of text from this input stream.
|
* @see java.io.DataInputStream#readLine()
|
||||||
* @exception IOException if an I/O error occurs.
|
*/
|
||||||
* @see java.io.BufferedReader#readLine()
|
@Deprecated
|
||||||
* @see java.io.DataInputStream#readLine()
|
public String readLine() throws IOException {
|
||||||
* @noinspection deprecation
|
DataInputStream ds = new DataInputStream(in);
|
||||||
*/
|
return ds.readLine();
|
||||||
public String readLine() throws IOException {
|
}
|
||||||
DataInputStream ds = new DataInputStream(in);
|
}
|
||||||
return ds.readLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+341
-340
@@ -1,341 +1,342 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
|
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
|
||||||
*
|
*
|
||||||
* Please feel free to use any fragment of this code you need in your own work.
|
* Please feel free to use any fragment of this code you need in your own work.
|
||||||
* As far as I am concerned, it's in the public domain. No permission is necessary
|
* As far as I am concerned, it's in the public domain. No permission is necessary
|
||||||
* or required. Credit is always appreciated if you use a large chunk or base a
|
* or required. Credit is always appreciated if you use a large chunk or base a
|
||||||
* significant product on one of my examples, but that's not required either.
|
* significant product on one of my examples, but that's not required either.
|
||||||
*
|
*
|
||||||
* Elliotte Rusty Harold
|
* Elliotte Rusty Harold
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A little endian output stream writes primitive Java numbers
|
* A little endian output stream writes primitive Java numbers
|
||||||
* and characters to an output stream in a little endian format.
|
* and characters to an output stream in a little endian format.
|
||||||
* <p/>
|
* <p>
|
||||||
* The standard {@code java.io.DataOutputStream} class which this class
|
* The standard {@code java.io.DataOutputStream} class which this class
|
||||||
* imitates uses big endian integers.
|
* imitates uses big endian integers.
|
||||||
* <p/>
|
* </p>
|
||||||
* <em>Warning:
|
* <p>
|
||||||
* <!-- Beware of little indians! -->
|
* <em>Warning:
|
||||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||||
* specifies big endian byte order in their documentation.
|
* specifies big endian byte order in their documentation.
|
||||||
* This means that this class is, strictly speaking, not a proper
|
* This means that this class is, strictly speaking, not a proper
|
||||||
* implementation. However, I don't see a reason for the these interfaces to
|
* implementation. However, I don't see a reason for the these interfaces to
|
||||||
* specify the byte order of their underlying representations.
|
* specify the byte order of their underlying representations.
|
||||||
* </em>
|
* </em>
|
||||||
*
|
* <p>
|
||||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
*
|
||||||
* @see java.io.DataOutputStream
|
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||||
* @see java.io.DataInput
|
* @see java.io.DataOutputStream
|
||||||
* @see java.io.DataOutput
|
* @see java.io.DataInput
|
||||||
*
|
* @see java.io.DataOutput
|
||||||
* @author Elliotte Rusty Harold
|
*
|
||||||
* @version 1.0.1, 19 May 1999
|
* @author Elliotte Rusty Harold
|
||||||
*/
|
* @version 1.0.1, 19 May 1999
|
||||||
public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
|
*/
|
||||||
|
public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
|
||||||
/**
|
|
||||||
* The number of bytes written so far to the little endian output stream.
|
/**
|
||||||
*/
|
* The number of bytes written so far to the little endian output stream.
|
||||||
protected int bytesWritten;
|
*/
|
||||||
|
protected int bytesWritten;
|
||||||
/**
|
|
||||||
* Creates a new little endian output stream and chains it to the
|
/**
|
||||||
* output stream specified by the {@code pStream} argument.
|
* Creates a new little endian output stream and chains it to the
|
||||||
*
|
* output stream specified by the {@code pStream} argument.
|
||||||
* @param pStream the underlying output stream.
|
*
|
||||||
* @see java.io.FilterOutputStream#out
|
* @param pStream the underlying output stream.
|
||||||
*/
|
* @see java.io.FilterOutputStream#out
|
||||||
public LittleEndianDataOutputStream(OutputStream pStream) {
|
*/
|
||||||
super(Validate.notNull(pStream, "stream"));
|
public LittleEndianDataOutputStream(OutputStream pStream) {
|
||||||
}
|
super(Validate.notNull(pStream, "stream"));
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes the specified byte value to the underlying output stream.
|
/**
|
||||||
*
|
* Writes the specified byte value to the underlying output stream.
|
||||||
* @param pByte the {@code byte} value to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pByte the {@code byte} value to be written.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public synchronized void write(int pByte) throws IOException {
|
*/
|
||||||
out.write(pByte);
|
public synchronized void write(int pByte) throws IOException {
|
||||||
bytesWritten++;
|
out.write(pByte);
|
||||||
}
|
bytesWritten++;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes {@code pLength} bytes from the specified byte array
|
/**
|
||||||
* starting at {@code pOffset} to the underlying output stream.
|
* Writes {@code pLength} bytes from the specified byte array
|
||||||
*
|
* starting at {@code pOffset} to the underlying output stream.
|
||||||
* @param pBytes the data.
|
*
|
||||||
* @param pOffset the start offset in the data.
|
* @param pBytes the data.
|
||||||
* @param pLength the number of bytes to write.
|
* @param pOffset the start offset in the data.
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pLength the number of bytes to write.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public synchronized void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
*/
|
||||||
out.write(pBytes, pOffset, pLength);
|
public synchronized void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||||
bytesWritten += pLength;
|
out.write(pBytes, pOffset, pLength);
|
||||||
}
|
bytesWritten += pLength;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code boolean} to the underlying output stream as
|
/**
|
||||||
* a single byte. If the argument is true, the byte value 1 is written.
|
* Writes a {@code boolean} to the underlying output stream as
|
||||||
* If the argument is false, the byte value {@code 0} in written.
|
* a single byte. If the argument is true, the byte value 1 is written.
|
||||||
*
|
* If the argument is false, the byte value {@code 0} in written.
|
||||||
* @param pBoolean the {@code boolean} value to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pBoolean the {@code boolean} value to be written.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public void writeBoolean(boolean pBoolean) throws IOException {
|
*/
|
||||||
if (pBoolean) {
|
public void writeBoolean(boolean pBoolean) throws IOException {
|
||||||
write(1);
|
if (pBoolean) {
|
||||||
}
|
write(1);
|
||||||
else {
|
}
|
||||||
write(0);
|
else {
|
||||||
}
|
write(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes out a {@code byte} to the underlying output stream
|
/**
|
||||||
*
|
* Writes out a {@code byte} to the underlying output stream
|
||||||
* @param pByte the {@code byte} value to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pByte the {@code byte} value to be written.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public void writeByte(int pByte) throws IOException {
|
*/
|
||||||
out.write(pByte);
|
public void writeByte(int pByte) throws IOException {
|
||||||
bytesWritten++;
|
out.write(pByte);
|
||||||
}
|
bytesWritten++;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes a two byte {@code short} to the underlying output stream in
|
/**
|
||||||
* little endian order, low byte first.
|
* Writes a two byte {@code short} to the underlying output stream in
|
||||||
*
|
* little endian order, low byte first.
|
||||||
* @param pShort the {@code short} to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pShort the {@code short} to be written.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public void writeShort(int pShort) throws IOException {
|
*/
|
||||||
out.write(pShort & 0xFF);
|
public void writeShort(int pShort) throws IOException {
|
||||||
out.write((pShort >>> 8) & 0xFF);
|
out.write(pShort & 0xFF);
|
||||||
bytesWritten += 2;
|
out.write((pShort >>> 8) & 0xFF);
|
||||||
}
|
bytesWritten += 2;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes a two byte {@code char} to the underlying output stream
|
/**
|
||||||
* in little endian order, low byte first.
|
* Writes a two byte {@code char} to the underlying output stream
|
||||||
*
|
* in little endian order, low byte first.
|
||||||
* @param pChar the {@code char} value to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pChar the {@code char} value to be written.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public void writeChar(int pChar) throws IOException {
|
*/
|
||||||
out.write(pChar & 0xFF);
|
public void writeChar(int pChar) throws IOException {
|
||||||
out.write((pChar >>> 8) & 0xFF);
|
out.write(pChar & 0xFF);
|
||||||
bytesWritten += 2;
|
out.write((pChar >>> 8) & 0xFF);
|
||||||
}
|
bytesWritten += 2;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes a four-byte {@code int} to the underlying output stream
|
/**
|
||||||
* in little endian order, low byte first, high byte last
|
* Writes a four-byte {@code int} to the underlying output stream
|
||||||
*
|
* in little endian order, low byte first, high byte last
|
||||||
* @param pInt the {@code int} to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pInt the {@code int} to be written.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public void writeInt(int pInt) throws IOException {
|
*/
|
||||||
out.write(pInt & 0xFF);
|
public void writeInt(int pInt) throws IOException {
|
||||||
out.write((pInt >>> 8) & 0xFF);
|
out.write(pInt & 0xFF);
|
||||||
out.write((pInt >>> 16) & 0xFF);
|
out.write((pInt >>> 8) & 0xFF);
|
||||||
out.write((pInt >>> 24) & 0xFF);
|
out.write((pInt >>> 16) & 0xFF);
|
||||||
bytesWritten += 4;
|
out.write((pInt >>> 24) & 0xFF);
|
||||||
|
bytesWritten += 4;
|
||||||
}
|
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes an eight-byte {@code long} to the underlying output stream
|
/**
|
||||||
* in little endian order, low byte first, high byte last
|
* Writes an eight-byte {@code long} to the underlying output stream
|
||||||
*
|
* in little endian order, low byte first, high byte last
|
||||||
* @param pLong the {@code long} to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pLong the {@code long} to be written.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public void writeLong(long pLong) throws IOException {
|
*/
|
||||||
out.write((int) pLong & 0xFF);
|
public void writeLong(long pLong) throws IOException {
|
||||||
out.write((int) (pLong >>> 8) & 0xFF);
|
out.write((int) pLong & 0xFF);
|
||||||
out.write((int) (pLong >>> 16) & 0xFF);
|
out.write((int) (pLong >>> 8) & 0xFF);
|
||||||
out.write((int) (pLong >>> 24) & 0xFF);
|
out.write((int) (pLong >>> 16) & 0xFF);
|
||||||
out.write((int) (pLong >>> 32) & 0xFF);
|
out.write((int) (pLong >>> 24) & 0xFF);
|
||||||
out.write((int) (pLong >>> 40) & 0xFF);
|
out.write((int) (pLong >>> 32) & 0xFF);
|
||||||
out.write((int) (pLong >>> 48) & 0xFF);
|
out.write((int) (pLong >>> 40) & 0xFF);
|
||||||
out.write((int) (pLong >>> 56) & 0xFF);
|
out.write((int) (pLong >>> 48) & 0xFF);
|
||||||
bytesWritten += 8;
|
out.write((int) (pLong >>> 56) & 0xFF);
|
||||||
}
|
bytesWritten += 8;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes a 4 byte Java float to the underlying output stream in
|
/**
|
||||||
* little endian order.
|
* Writes a 4 byte Java float to the underlying output stream in
|
||||||
*
|
* little endian order.
|
||||||
* @param f the {@code float} value to be written.
|
*
|
||||||
* @throws IOException if an I/O error occurs.
|
* @param f the {@code float} value to be written.
|
||||||
*/
|
* @throws IOException if an I/O error occurs.
|
||||||
public final void writeFloat(float f) throws IOException {
|
*/
|
||||||
writeInt(Float.floatToIntBits(f));
|
public final void writeFloat(float f) throws IOException {
|
||||||
}
|
writeInt(Float.floatToIntBits(f));
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes an 8 byte Java double to the underlying output stream in
|
/**
|
||||||
* little endian order.
|
* Writes an 8 byte Java double to the underlying output stream in
|
||||||
*
|
* little endian order.
|
||||||
* @param d the {@code double} value to be written.
|
*
|
||||||
* @throws IOException if an I/O error occurs.
|
* @param d the {@code double} value to be written.
|
||||||
*/
|
* @throws IOException if an I/O error occurs.
|
||||||
public final void writeDouble(double d) throws IOException {
|
*/
|
||||||
writeLong(Double.doubleToLongBits(d));
|
public final void writeDouble(double d) throws IOException {
|
||||||
}
|
writeLong(Double.doubleToLongBits(d));
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes a string to the underlying output stream as a sequence of
|
/**
|
||||||
* bytes. Each character is written to the data output stream as
|
* Writes a string to the underlying output stream as a sequence of
|
||||||
* if by the {@link #writeByte(int)} method.
|
* bytes. Each character is written to the data output stream as
|
||||||
*
|
* if by the {@link #writeByte(int)} method.
|
||||||
* @param pString the {@code String} value to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pString the {@code String} value to be written.
|
||||||
* @see #writeByte(int)
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
* @see #out
|
* @see #writeByte(int)
|
||||||
*/
|
* @see #out
|
||||||
public void writeBytes(String pString) throws IOException {
|
*/
|
||||||
int length = pString.length();
|
public void writeBytes(String pString) throws IOException {
|
||||||
|
int length = pString.length();
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
out.write((byte) pString.charAt(i));
|
for (int i = 0; i < length; i++) {
|
||||||
}
|
out.write((byte) pString.charAt(i));
|
||||||
|
}
|
||||||
bytesWritten += length;
|
|
||||||
}
|
bytesWritten += length;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes a string to the underlying output stream as a sequence of
|
/**
|
||||||
* characters. Each character is written to the data output stream as
|
* Writes a string to the underlying output stream as a sequence of
|
||||||
* if by the {@code writeChar} method.
|
* characters. Each character is written to the data output stream as
|
||||||
*
|
* if by the {@code writeChar} method.
|
||||||
* @param pString a {@code String} value to be written.
|
*
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @param pString a {@code String} value to be written.
|
||||||
* @see #writeChar(int)
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
* @see #out
|
* @see #writeChar(int)
|
||||||
*/
|
* @see #out
|
||||||
public void writeChars(String pString) throws IOException {
|
*/
|
||||||
int length = pString.length();
|
public void writeChars(String pString) throws IOException {
|
||||||
|
int length = pString.length();
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
int c = pString.charAt(i);
|
for (int i = 0; i < length; i++) {
|
||||||
out.write(c & 0xFF);
|
int c = pString.charAt(i);
|
||||||
out.write((c >>> 8) & 0xFF);
|
out.write(c & 0xFF);
|
||||||
}
|
out.write((c >>> 8) & 0xFF);
|
||||||
|
}
|
||||||
bytesWritten += length * 2;
|
|
||||||
}
|
bytesWritten += length * 2;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Writes a string of no more than 65,535 characters
|
/**
|
||||||
* to the underlying output stream using UTF-8
|
* Writes a string of no more than 65,535 characters
|
||||||
* encoding. This method first writes a two byte short
|
* to the underlying output stream using UTF-8
|
||||||
* in <b>big</b> endian order as required by the
|
* encoding. This method first writes a two byte short
|
||||||
* UTF-8 specification. This gives the number of bytes in the
|
* in <b>big</b> endian order as required by the
|
||||||
* UTF-8 encoded version of the string, not the number of characters
|
* UTF-8 specification. This gives the number of bytes in the
|
||||||
* in the string. Next each character of the string is written
|
* UTF-8 encoded version of the string, not the number of characters
|
||||||
* using the UTF-8 encoding for the character.
|
* in the string. Next each character of the string is written
|
||||||
*
|
* using the UTF-8 encoding for the character.
|
||||||
* @param pString the string to be written.
|
*
|
||||||
* @throws UTFDataFormatException if the string is longer than
|
* @param pString the string to be written.
|
||||||
* 65,535 characters.
|
* @throws UTFDataFormatException if the string is longer than
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* 65,535 characters.
|
||||||
*/
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
public void writeUTF(String pString) throws IOException {
|
*/
|
||||||
int numchars = pString.length();
|
public void writeUTF(String pString) throws IOException {
|
||||||
int numbytes = 0;
|
int numchars = pString.length();
|
||||||
|
int numbytes = 0;
|
||||||
for (int i = 0; i < numchars; i++) {
|
|
||||||
int c = pString.charAt(i);
|
for (int i = 0; i < numchars; i++) {
|
||||||
|
int c = pString.charAt(i);
|
||||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
|
||||||
numbytes++;
|
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||||
}
|
numbytes++;
|
||||||
else if (c > 0x07FF) {
|
}
|
||||||
numbytes += 3;
|
else if (c > 0x07FF) {
|
||||||
}
|
numbytes += 3;
|
||||||
else {
|
}
|
||||||
numbytes += 2;
|
else {
|
||||||
}
|
numbytes += 2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (numbytes > 65535) {
|
|
||||||
throw new UTFDataFormatException();
|
if (numbytes > 65535) {
|
||||||
}
|
throw new UTFDataFormatException();
|
||||||
|
}
|
||||||
out.write((numbytes >>> 8) & 0xFF);
|
|
||||||
out.write(numbytes & 0xFF);
|
out.write((numbytes >>> 8) & 0xFF);
|
||||||
|
out.write(numbytes & 0xFF);
|
||||||
for (int i = 0; i < numchars; i++) {
|
|
||||||
int c = pString.charAt(i);
|
for (int i = 0; i < numchars; i++) {
|
||||||
|
int c = pString.charAt(i);
|
||||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
|
||||||
out.write(c);
|
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||||
}
|
out.write(c);
|
||||||
else if (c > 0x07FF) {
|
}
|
||||||
out.write(0xE0 | ((c >> 12) & 0x0F));
|
else if (c > 0x07FF) {
|
||||||
out.write(0x80 | ((c >> 6) & 0x3F));
|
out.write(0xE0 | ((c >> 12) & 0x0F));
|
||||||
out.write(0x80 | (c & 0x3F));
|
out.write(0x80 | ((c >> 6) & 0x3F));
|
||||||
bytesWritten += 2;
|
out.write(0x80 | (c & 0x3F));
|
||||||
}
|
bytesWritten += 2;
|
||||||
else {
|
}
|
||||||
out.write(0xC0 | ((c >> 6) & 0x1F));
|
else {
|
||||||
out.write(0x80 | (c & 0x3F));
|
out.write(0xC0 | ((c >> 6) & 0x1F));
|
||||||
bytesWritten += 1;
|
out.write(0x80 | (c & 0x3F));
|
||||||
}
|
bytesWritten += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
bytesWritten += numchars + 2;
|
|
||||||
}
|
bytesWritten += numchars + 2;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns the number of bytes written to this little endian output stream.
|
/**
|
||||||
* (This class is not thread-safe with respect to this method. It is
|
* Returns the number of bytes written to this little endian output stream.
|
||||||
* possible that this number is temporarily less than the actual
|
* (This class is not thread-safe with respect to this method. It is
|
||||||
* number of bytes written.)
|
* possible that this number is temporarily less than the actual
|
||||||
* @return the value of the {@code written} field.
|
* number of bytes written.)
|
||||||
* @see #bytesWritten
|
* @return the value of the {@code written} field.
|
||||||
*/
|
* @see #bytesWritten
|
||||||
public int size() {
|
*/
|
||||||
return bytesWritten;
|
public int size() {
|
||||||
}
|
return bytesWritten;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+628
-628
File diff suppressed because it is too large
Load Diff
+197
-193
@@ -1,193 +1,197 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code SeekableInputStream} implementation that caches data in memory.
|
* A {@code SeekableInputStream} implementation that caches data in memory.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @see FileCacheSeekableStream
|
||||||
* @see FileCacheSeekableStream
|
*
|
||||||
*
|
* @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/MemoryCacheSeekableStream.java#3 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java#3 $
|
*/
|
||||||
*/
|
public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
|
||||||
public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
|
|
||||||
|
/**
|
||||||
/**
|
* Creates a {@code MemoryCacheSeekableStream}, reading from the given
|
||||||
* Creates a {@code MemoryCacheSeekableStream}, reading from the given
|
* {@code InputStream}. Data will be cached in memory.
|
||||||
* {@code InputStream}. Data will be cached in memory.
|
*
|
||||||
*
|
* @param pStream the {@code InputStream} to read from.
|
||||||
* @param pStream the {@code InputStream} to read from.
|
*/
|
||||||
*/
|
public MemoryCacheSeekableStream(final InputStream pStream) {
|
||||||
public MemoryCacheSeekableStream(final InputStream pStream) {
|
super(pStream, new MemoryCache());
|
||||||
super(pStream, new MemoryCache());
|
}
|
||||||
}
|
|
||||||
|
public final boolean isCachedMemory() {
|
||||||
public final boolean isCachedMemory() {
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
public final boolean isCachedFile() {
|
||||||
public final boolean isCachedFile() {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
final static class MemoryCache extends StreamCache {
|
||||||
final static class MemoryCache extends StreamCache {
|
final static int BLOCK_SIZE = 1 << 13;
|
||||||
final static int BLOCK_SIZE = 1 << 13;
|
|
||||||
|
private final List<byte[]> cache = new ArrayList<>();
|
||||||
private final List<byte[]> cache = new ArrayList<byte[]>();
|
private long length;
|
||||||
private long length;
|
private long position;
|
||||||
private long position;
|
private long start;
|
||||||
private long start;
|
|
||||||
|
private byte[] getBlock() throws IOException {
|
||||||
private byte[] getBlock() throws IOException {
|
final long currPos = position - start;
|
||||||
final long currPos = position - start;
|
if (currPos < 0) {
|
||||||
if (currPos < 0) {
|
throw new IOException("StreamCache flushed before read position");
|
||||||
throw new IOException("StreamCache flushed before read position");
|
}
|
||||||
}
|
|
||||||
|
long index = currPos / BLOCK_SIZE;
|
||||||
long index = currPos / BLOCK_SIZE;
|
|
||||||
|
if (index >= Integer.MAX_VALUE) {
|
||||||
if (index >= Integer.MAX_VALUE) {
|
throw new IOException("Memory cache max size exceeded");
|
||||||
throw new IOException("Memory cache max size exceeded");
|
}
|
||||||
}
|
|
||||||
|
if (index >= cache.size()) {
|
||||||
if (index >= cache.size()) {
|
try {
|
||||||
try {
|
cache.add(new byte[BLOCK_SIZE]);
|
||||||
cache.add(new byte[BLOCK_SIZE]);
|
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
|
||||||
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
|
// System.out.println("New total size: " + cache.size() * BLOCK_SIZE + " (" + cache.size() + " blocks)");
|
||||||
// System.out.println("New total size: " + cache.size() * BLOCK_SIZE + " (" + cache.size() + " blocks)");
|
}
|
||||||
}
|
catch (OutOfMemoryError e) {
|
||||||
catch (OutOfMemoryError e) {
|
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
|
||||||
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
//System.out.println("index: " + index);
|
||||||
//System.out.println("index: " + index);
|
|
||||||
|
return cache.get((int) index);
|
||||||
return cache.get((int) index);
|
}
|
||||||
}
|
|
||||||
|
public void write(final int pByte) throws IOException {
|
||||||
public void write(final int pByte) throws IOException {
|
byte[] buffer = getBlock();
|
||||||
byte[] buffer = getBlock();
|
|
||||||
|
int idx = (int) (position % BLOCK_SIZE);
|
||||||
int idx = (int) (position % BLOCK_SIZE);
|
buffer[idx] = (byte) pByte;
|
||||||
buffer[idx] = (byte) pByte;
|
position++;
|
||||||
position++;
|
|
||||||
|
if (position > length) {
|
||||||
if (position > length) {
|
length = position;
|
||||||
length = position;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// TODO: OptimizeMe!!!
|
||||||
// TODO: OptimizeMe!!!
|
@Override
|
||||||
@Override
|
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||||
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
byte[] buffer = getBlock();
|
||||||
byte[] buffer = getBlock();
|
for (int i = 0; i < pLength; i++) {
|
||||||
for (int i = 0; i < pLength; i++) {
|
int index = (int) position % BLOCK_SIZE;
|
||||||
int index = (int) position % BLOCK_SIZE;
|
if (index == 0) {
|
||||||
if (index == 0) {
|
buffer = getBlock();
|
||||||
buffer = getBlock();
|
}
|
||||||
}
|
buffer[index] = pBuffer[pOffset + i];
|
||||||
buffer[index] = pBuffer[pOffset + i];
|
|
||||||
|
position++;
|
||||||
position++;
|
}
|
||||||
}
|
if (position > length) {
|
||||||
if (position > length) {
|
length = position;
|
||||||
length = position;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public int read() throws IOException {
|
||||||
public int read() throws IOException {
|
if (position >= length) {
|
||||||
if (position >= length) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
|
byte[] buffer = getBlock();
|
||||||
byte[] buffer = getBlock();
|
|
||||||
|
int idx = (int) (position % BLOCK_SIZE);
|
||||||
int idx = (int) (position % BLOCK_SIZE);
|
position++;
|
||||||
position++;
|
|
||||||
|
return buffer[idx] & 0xff;
|
||||||
return buffer[idx] & 0xff;
|
}
|
||||||
}
|
|
||||||
|
// TODO: OptimizeMe!!!
|
||||||
// TODO: OptimizeMe!!!
|
@Override
|
||||||
@Override
|
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
if (position >= length) {
|
||||||
if (position >= length) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
|
byte[] buffer = getBlock();
|
||||||
byte[] buffer = getBlock();
|
|
||||||
|
int bufferPos = (int) (position % BLOCK_SIZE);
|
||||||
int bufferPos = (int) (position % BLOCK_SIZE);
|
|
||||||
|
// Find maxIdx and simplify test in for-loop
|
||||||
// Find maxIdx and simplify test in for-loop
|
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), length - position);
|
||||||
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), length - position);
|
|
||||||
|
int i;
|
||||||
int i;
|
//for (i = 0; i < pLength && i < buffer.length - idx && i < length - position; i++) {
|
||||||
//for (i = 0; i < pLength && i < buffer.length - idx && i < length - position; i++) {
|
for (i = 0; i < maxLen; i++) {
|
||||||
for (i = 0; i < maxLen; i++) {
|
pBytes[pOffset + i] = buffer[bufferPos + i];
|
||||||
pBytes[pOffset + i] = buffer[bufferPos + i];
|
}
|
||||||
}
|
|
||||||
|
position += i;
|
||||||
position += i;
|
|
||||||
|
return i;
|
||||||
return i;
|
}
|
||||||
}
|
|
||||||
|
public void seek(final long pPosition) throws IOException {
|
||||||
public void seek(final long pPosition) throws IOException {
|
if (pPosition < start) {
|
||||||
if (pPosition < start) {
|
throw new IOException("Seek before flush position");
|
||||||
throw new IOException("Seek before flush position");
|
}
|
||||||
}
|
position = pPosition;
|
||||||
position = pPosition;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public void flush(final long pPosition) {
|
||||||
public void flush(final long pPosition) {
|
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
|
||||||
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
|
|
||||||
|
for (int i = 0; i < firstPos; i++) {
|
||||||
for (int i = 0; i < firstPos; i++) {
|
cache.remove(0);
|
||||||
cache.remove(0);
|
}
|
||||||
}
|
|
||||||
|
start = pPosition;
|
||||||
start = pPosition;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
public long getPosition() {
|
void close() throws IOException {
|
||||||
return position;
|
cache.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
public long getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,82 +1,81 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code InputStream} that contains no bytes.
|
* An {@code InputStream} that contains no bytes.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/NullInputStream.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullInputStream.java#2 $
|
*/
|
||||||
*/
|
public class NullInputStream extends InputStream {
|
||||||
public class NullInputStream extends InputStream {
|
|
||||||
|
/**
|
||||||
/**
|
* Creates a {@code NullInputStream}.
|
||||||
* Creates a {@code NullInputStream}.
|
*/
|
||||||
*/
|
public NullInputStream() {
|
||||||
public NullInputStream() {
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This implementation returns {@code -1} (EOF), always.
|
||||||
* This implementation returns {@code -1} (EOF), always.
|
*
|
||||||
*
|
* @return {@code -1}
|
||||||
* @return {@code -1}
|
* @throws IOException
|
||||||
* @throws IOException
|
*/
|
||||||
*/
|
public int read() throws IOException {
|
||||||
public int read() throws IOException {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This implementation returns {@code 0}, always.
|
||||||
* This implementation returns {@code 0}, always.
|
*
|
||||||
*
|
* @return {@code 0}
|
||||||
* @return {@code 0}
|
* @throws IOException
|
||||||
* @throws IOException
|
*/
|
||||||
*/
|
@Override
|
||||||
@Override
|
public int available() throws IOException {
|
||||||
public int available() throws IOException {
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This implementation returns {@code 0}, always.
|
||||||
* This implementation returns {@code 0}, always.
|
*
|
||||||
*
|
* @return {@code 0}
|
||||||
* @return {@code 0}
|
* @throws IOException
|
||||||
* @throws IOException
|
*/
|
||||||
*/
|
@Override
|
||||||
@Override
|
public long skip(long pOffset) throws IOException {
|
||||||
public long skip(long pOffset) throws IOException {
|
return 0l;
|
||||||
return 0l;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,70 +1,69 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code OutputStream} implementation that works as a sink.
|
* An {@code OutputStream} implementation that works as a sink.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/NullOutputStream.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullOutputStream.java#2 $
|
*/
|
||||||
*/
|
public class NullOutputStream extends OutputStream {
|
||||||
public class NullOutputStream extends OutputStream {
|
|
||||||
|
/**
|
||||||
/**
|
* Creates a {@code NullOutputStream}.
|
||||||
* Creates a {@code NullOutputStream}.
|
*/
|
||||||
*/
|
public NullOutputStream() {
|
||||||
public NullOutputStream() {
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This implementation does nothing.
|
||||||
* This implementation does nothing.
|
*/
|
||||||
*/
|
public void write(int pByte) throws IOException {
|
||||||
public void write(int pByte) throws IOException {
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This implementation does nothing.
|
||||||
* This implementation does nothing.
|
*/
|
||||||
*/
|
@Override
|
||||||
@Override
|
public void write(byte pBytes[]) throws IOException {
|
||||||
public void write(byte pBytes[]) throws IOException {
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This implementation does nothing.
|
||||||
* This implementation does nothing.
|
*/
|
||||||
*/
|
@Override
|
||||||
@Override
|
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||||
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,241 +1,242 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data stream that is both readable and writable, much like a
|
* A data stream that is both readable and writable, much like a
|
||||||
* {@code RandomAccessFile}, except it may be backed by something other than a file.
|
* {@code RandomAccessFile}, except it may be backed by something other than a file.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @see java.io.RandomAccessFile
|
||||||
* @see java.io.RandomAccessFile
|
*
|
||||||
*
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author last modified by $Author: haku $
|
||||||
* @author last modified by $Author: haku $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java#3 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java#3 $
|
*/
|
||||||
*/
|
public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput {
|
||||||
public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput {
|
// TODO: Use a RandomAcceessFile as backing in impl, probably
|
||||||
// TODO: Use a RandomAcceessFile as backing in impl, probably
|
// TODO: Create an in-memory implementation too?
|
||||||
// TODO: Create an in-memory implementation too?
|
// TODO: Package private SeekableDelegate?
|
||||||
// TODO: Package private SeekableDelegate?
|
|
||||||
|
// TODO: Both read and write must update stream position
|
||||||
// TODO: Both read and write must update stream position
|
//private int position = -1;
|
||||||
//private int position = -1;
|
|
||||||
|
/** This random access stream, wrapped in an {@code InputStream} */
|
||||||
/** This random access stream, wrapped in an {@code InputStream} */
|
SeekableInputStream inputView = null;
|
||||||
SeekableInputStream inputView = null;
|
/** This random access stream, wrapped in an {@code OutputStream} */
|
||||||
/** This random access stream, wrapped in an {@code OutputStream} */
|
SeekableOutputStream outputView = null;
|
||||||
SeekableOutputStream outputView = null;
|
|
||||||
|
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
|
||||||
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
|
public int read() throws IOException {
|
||||||
public int read() throws IOException {
|
try {
|
||||||
try {
|
return readByte() & 0xff;
|
||||||
return readByte() & 0xff;
|
}
|
||||||
}
|
catch (EOFException e) {
|
||||||
catch (EOFException e) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
if (pBytes == null) {
|
||||||
if (pBytes == null) {
|
throw new NullPointerException("bytes == null");
|
||||||
throw new NullPointerException("bytes == null");
|
}
|
||||||
}
|
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||||
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
throw new IndexOutOfBoundsException();
|
||||||
throw new IndexOutOfBoundsException();
|
}
|
||||||
}
|
else if (pLength == 0) {
|
||||||
else if (pLength == 0) {
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
// Special case, allready at EOF
|
||||||
// Special case, allready at EOF
|
int c = read();
|
||||||
int c = read();
|
if (c == -1) {
|
||||||
if (c == -1) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
|
// Otherwise, read as many as bytes as possible
|
||||||
// Otherwise, read as many as bytes as possible
|
pBytes[pOffset] = (byte) c;
|
||||||
pBytes[pOffset] = (byte) c;
|
|
||||||
|
int i = 1;
|
||||||
int i = 1;
|
try {
|
||||||
try {
|
for (; i < pLength; i++) {
|
||||||
for (; i < pLength; i++) {
|
c = read();
|
||||||
c = read();
|
if (c == -1) {
|
||||||
if (c == -1) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
pBytes[pOffset + i] = (byte) c;
|
||||||
pBytes[pOffset + i] = (byte) c;
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (IOException ignore) {
|
||||||
catch (IOException ignore) {
|
// Ignore exception, just return length
|
||||||
// Ignore exception, just return length
|
}
|
||||||
}
|
|
||||||
|
return i;
|
||||||
return i;
|
}
|
||||||
}
|
|
||||||
|
public final int read(byte[] pBytes) throws IOException {
|
||||||
public final int read(byte[] pBytes) throws IOException {
|
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||||
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns an input view of this {@code RandomAccessStream}.
|
||||||
* Returns an input view of this {@code RandomAccessStream}.
|
* Invoking this method several times, will return the same object.
|
||||||
* Invoking this method several times, will return the same object.
|
* <p>
|
||||||
* <p/>
|
* <em>Note that read access is NOT synchronized.</em>
|
||||||
* <em>Note that read access is NOT synchronized.</em>
|
* </p>
|
||||||
*
|
*
|
||||||
* @return a {@code SeekableInputStream} reading from this stream
|
* @return a {@code SeekableInputStream} reading from this stream
|
||||||
*/
|
*/
|
||||||
public final SeekableInputStream asInputStream() {
|
public final SeekableInputStream asInputStream() {
|
||||||
if (inputView == null) {
|
if (inputView == null) {
|
||||||
inputView = new InputStreamView(this);
|
inputView = new InputStreamView(this);
|
||||||
}
|
}
|
||||||
return inputView;
|
return inputView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an output view of this {@code RandomAccessStream}.
|
* Returns an output view of this {@code RandomAccessStream}.
|
||||||
* Invoking this method several times, will return the same object.
|
* Invoking this method several times, will return the same object.
|
||||||
* <p/>
|
* <p>
|
||||||
* <em>Note that write access is NOT synchronized.</em>
|
* <em>Note that write access is NOT synchronized.</em>
|
||||||
*
|
* </p>
|
||||||
* @return a {@code SeekableOutputStream} writing to this stream
|
*
|
||||||
*/
|
* @return a {@code SeekableOutputStream} writing to this stream
|
||||||
public final SeekableOutputStream asOutputStream() {
|
*/
|
||||||
if (outputView == null) {
|
public final SeekableOutputStream asOutputStream() {
|
||||||
outputView = new OutputStreamView(this);
|
if (outputView == null) {
|
||||||
}
|
outputView = new OutputStreamView(this);
|
||||||
return outputView;
|
}
|
||||||
}
|
return outputView;
|
||||||
|
}
|
||||||
static final class InputStreamView extends SeekableInputStream {
|
|
||||||
// TODO: Consider adding synchonization (on stream) for all operations
|
static final class InputStreamView extends SeekableInputStream {
|
||||||
// TODO: Is is a good thing that close/flush etc works on stream?
|
// TODO: Consider adding synchonization (on stream) for all operations
|
||||||
// - Or should it rather just work on the views?
|
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||||
// - Allow multiple views?
|
// - Or should it rather just work on the views?
|
||||||
|
// - Allow multiple views?
|
||||||
final private RandomAccessStream mStream;
|
|
||||||
|
final private RandomAccessStream mStream;
|
||||||
public InputStreamView(RandomAccessStream pStream) {
|
|
||||||
if (pStream == null) {
|
public InputStreamView(RandomAccessStream pStream) {
|
||||||
throw new IllegalArgumentException("stream == null");
|
if (pStream == null) {
|
||||||
}
|
throw new IllegalArgumentException("stream == null");
|
||||||
mStream = pStream;
|
}
|
||||||
}
|
mStream = pStream;
|
||||||
|
}
|
||||||
public boolean isCached() {
|
|
||||||
return mStream.isCached();
|
public boolean isCached() {
|
||||||
}
|
return mStream.isCached();
|
||||||
|
}
|
||||||
public boolean isCachedFile() {
|
|
||||||
return mStream.isCachedFile();
|
public boolean isCachedFile() {
|
||||||
}
|
return mStream.isCachedFile();
|
||||||
|
}
|
||||||
public boolean isCachedMemory() {
|
|
||||||
return mStream.isCachedMemory();
|
public boolean isCachedMemory() {
|
||||||
}
|
return mStream.isCachedMemory();
|
||||||
|
}
|
||||||
protected void closeImpl() throws IOException {
|
|
||||||
mStream.close();
|
protected void closeImpl() throws IOException {
|
||||||
}
|
mStream.close();
|
||||||
|
}
|
||||||
protected void flushBeforeImpl(long pPosition) throws IOException {
|
|
||||||
mStream.flushBefore(pPosition);
|
protected void flushBeforeImpl(long pPosition) throws IOException {
|
||||||
}
|
mStream.flushBefore(pPosition);
|
||||||
|
}
|
||||||
protected void seekImpl(long pPosition) throws IOException {
|
|
||||||
mStream.seek(pPosition);
|
protected void seekImpl(long pPosition) throws IOException {
|
||||||
}
|
mStream.seek(pPosition);
|
||||||
|
}
|
||||||
public int read() throws IOException {
|
|
||||||
return mStream.read();
|
public int read() throws IOException {
|
||||||
}
|
return mStream.read();
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
|
@Override
|
||||||
return mStream.read(pBytes, pOffset, pLength);
|
public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||||
}
|
return mStream.read(pBytes, pOffset, pLength);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
static final class OutputStreamView extends SeekableOutputStream {
|
|
||||||
// TODO: Consider adding synchonization (on stream) for all operations
|
static final class OutputStreamView extends SeekableOutputStream {
|
||||||
// TODO: Is is a good thing that close/flush etc works on stream?
|
// TODO: Consider adding synchonization (on stream) for all operations
|
||||||
// - Or should it rather just work on the views?
|
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||||
// - Allow multiple views?
|
// - Or should it rather just work on the views?
|
||||||
|
// - Allow multiple views?
|
||||||
final private RandomAccessStream mStream;
|
|
||||||
|
final private RandomAccessStream mStream;
|
||||||
public OutputStreamView(RandomAccessStream pStream) {
|
|
||||||
if (pStream == null) {
|
public OutputStreamView(RandomAccessStream pStream) {
|
||||||
throw new IllegalArgumentException("stream == null");
|
if (pStream == null) {
|
||||||
}
|
throw new IllegalArgumentException("stream == null");
|
||||||
mStream = pStream;
|
}
|
||||||
}
|
mStream = pStream;
|
||||||
|
}
|
||||||
public boolean isCached() {
|
|
||||||
return mStream.isCached();
|
public boolean isCached() {
|
||||||
}
|
return mStream.isCached();
|
||||||
|
}
|
||||||
public boolean isCachedFile() {
|
|
||||||
return mStream.isCachedFile();
|
public boolean isCachedFile() {
|
||||||
}
|
return mStream.isCachedFile();
|
||||||
|
}
|
||||||
public boolean isCachedMemory() {
|
|
||||||
return mStream.isCachedMemory();
|
public boolean isCachedMemory() {
|
||||||
}
|
return mStream.isCachedMemory();
|
||||||
|
}
|
||||||
protected void closeImpl() throws IOException {
|
|
||||||
mStream.close();
|
protected void closeImpl() throws IOException {
|
||||||
}
|
mStream.close();
|
||||||
|
}
|
||||||
protected void flushBeforeImpl(long pPosition) throws IOException {
|
|
||||||
mStream.flushBefore(pPosition);
|
protected void flushBeforeImpl(long pPosition) throws IOException {
|
||||||
}
|
mStream.flushBefore(pPosition);
|
||||||
|
}
|
||||||
protected void seekImpl(long pPosition) throws IOException {
|
|
||||||
mStream.seek(pPosition);
|
protected void seekImpl(long pPosition) throws IOException {
|
||||||
}
|
mStream.seek(pPosition);
|
||||||
|
}
|
||||||
public void write(int pByte) throws IOException {
|
|
||||||
mStream.write(pByte);
|
public void write(int pByte) throws IOException {
|
||||||
}
|
mStream.write(pByte);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
@Override
|
||||||
mStream.write(pBytes, pOffset, pLength);
|
public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
|
||||||
}
|
mStream.write(pBytes, pOffset, pLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,186 +1,193 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for seekable streams.
|
* Interface for seekable streams.
|
||||||
* <p/>
|
*
|
||||||
* @see SeekableInputStream
|
* @see SeekableInputStream
|
||||||
* @see SeekableOutputStream
|
* @see SeekableOutputStream
|
||||||
*
|
*
|
||||||
* @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/Seekable.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Seekable.java#1 $
|
||||||
*/
|
*/
|
||||||
public interface Seekable {
|
public interface Seekable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current byte position of the stream. The next read will take
|
* Returns the current byte position of the stream. The next read will take
|
||||||
* place starting at this offset.
|
* place starting at this offset.
|
||||||
*
|
*
|
||||||
* @return a {@code long} containing the position of the stream.
|
* @return a {@code long} containing the position of the stream.
|
||||||
* @throws IOException if an I/O error occurs.
|
* @throws IOException if an I/O error occurs.
|
||||||
*/
|
*/
|
||||||
long getStreamPosition() throws IOException;
|
long getStreamPosition() throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current stream position to the desired location.
|
* Sets the current stream position to the desired location.
|
||||||
* The next read will occur at this location.
|
* The next read will occur at this location.
|
||||||
* <p/>
|
* <p>
|
||||||
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
|
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
|
||||||
* than the flushed position (as returned by {@link #getFlushedPosition()}).
|
* than the flushed position (as returned by {@link #getFlushedPosition()}).
|
||||||
* <p/>
|
* </p>
|
||||||
* It is legal to seek past the end of the file; an {@code EOFException}
|
* <p>
|
||||||
* will be thrown only if a read is performed.
|
* It is legal to seek past the end of the file; an {@code EOFException}
|
||||||
*
|
* will be thrown only if a read is performed.
|
||||||
* @param pPosition a long containing the desired file pointer position.
|
* </p>
|
||||||
*
|
*
|
||||||
* @throws IndexOutOfBoundsException if {@code pPosition} is smaller than
|
* @param pPosition a long containing the desired file pointer position.
|
||||||
* the flushed position.
|
*
|
||||||
* @throws IOException if any other I/O error occurs.
|
* @throws IndexOutOfBoundsException if {@code pPosition} is smaller than
|
||||||
*/
|
* the flushed position.
|
||||||
void seek(long pPosition) throws IOException;
|
* @throws IOException if any other I/O error occurs.
|
||||||
|
*/
|
||||||
/**
|
void seek(long pPosition) throws IOException;
|
||||||
* Marks a position in the stream to be returned to by a subsequent call to
|
|
||||||
* reset.
|
/**
|
||||||
* Unlike a standard {@code InputStream}, all {@code Seekable}
|
* Marks a position in the stream to be returned to by a subsequent call to
|
||||||
* streams upport marking. Additionally, calls to {@code mark} and
|
* reset.
|
||||||
* {@code reset} may be nested arbitrarily.
|
* Unlike a standard {@code InputStream}, all {@code Seekable}
|
||||||
* <p/>
|
* streams upport marking. Additionally, calls to {@code mark} and
|
||||||
* Unlike the {@code mark} methods declared by the {@code Reader} or
|
* {@code reset} may be nested arbitrarily.
|
||||||
* {@code InputStream}
|
* <p>
|
||||||
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
|
* Unlike the {@code mark} methods declared by the {@code Reader} or
|
||||||
* of data may be read following the call to {@code mark}.
|
* {@code InputStream}
|
||||||
*/
|
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
|
||||||
void mark();
|
* of data may be read following the call to {@code mark}.
|
||||||
|
* </p>
|
||||||
/**
|
*/
|
||||||
* Returns the file pointer to its previous position,
|
void mark();
|
||||||
* at the time of the most recent unmatched call to mark.
|
|
||||||
* <p/>
|
/**
|
||||||
* Calls to reset without a corresponding call to mark will either:
|
* Returns the file pointer to its previous position,
|
||||||
* <ul>
|
* at the time of the most recent unmatched call to mark.
|
||||||
* <li>throw an {@code IOException}</li>
|
* <p>
|
||||||
* <li>or, reset to the beginning of the stream.</li>
|
* Calls to reset without a corresponding call to mark will either:
|
||||||
* </ul>
|
* </p>
|
||||||
* An {@code IOException} will be thrown if the previous marked position
|
* <ul>
|
||||||
* lies in the discarded portion of the stream.
|
* <li>throw an {@code IOException}</li>
|
||||||
*
|
* <li>or, reset to the beginning of the stream.</li>
|
||||||
* @throws IOException if an I/O error occurs.
|
* </ul>
|
||||||
* @see java.io.InputStream#reset()
|
* <p>
|
||||||
*/
|
* An {@code IOException} will be thrown if the previous marked position
|
||||||
void reset() throws IOException;
|
* lies in the discarded portion of the stream.
|
||||||
|
* </p>
|
||||||
/**
|
*
|
||||||
* Discards the initial portion of the stream prior to the indicated
|
* @throws IOException if an I/O error occurs.
|
||||||
* postion. Attempting to seek to an offset within the flushed portion of
|
* @see java.io.InputStream#reset()
|
||||||
* the stream will result in an {@code IndexOutOfBoundsException}.
|
*/
|
||||||
* <p/>
|
void reset() throws IOException;
|
||||||
* Calling {@code flushBefore} may allow classes implementing this
|
|
||||||
* interface to free up resources such as memory or disk space that are
|
/**
|
||||||
* being used to store data from the stream.
|
* Discards the initial portion of the stream prior to the indicated
|
||||||
*
|
* postion. Attempting to seek to an offset within the flushed portion of
|
||||||
* @param pPosition a long containing the length of the file prefix that
|
* the stream will result in an {@code IndexOutOfBoundsException}.
|
||||||
* may be flushed.
|
* <p>
|
||||||
*
|
* Calling {@code flushBefore} may allow classes implementing this
|
||||||
* @throws IndexOutOfBoundsException if {@code pPosition} lies in the
|
* interface to free up resources such as memory or disk space that are
|
||||||
* flushed portion of the stream or past the current stream position.
|
* being used to store data from the stream.
|
||||||
* @throws IOException if an I/O error occurs.
|
* </p>
|
||||||
*/
|
*
|
||||||
void flushBefore(long pPosition) throws IOException;
|
* @param pPosition a long containing the length of the file prefix that
|
||||||
|
* may be flushed.
|
||||||
/**
|
*
|
||||||
* Discards the initial position of the stream prior to the current stream
|
* @throws IndexOutOfBoundsException if {@code pPosition} lies in the
|
||||||
* position. Equivalent to {@code flushBefore(getStreamPosition())}.
|
* flushed portion of the stream or past the current stream position.
|
||||||
*
|
* @throws IOException if an I/O error occurs.
|
||||||
* @throws IOException if an I/O error occurs.
|
*/
|
||||||
*/
|
void flushBefore(long pPosition) throws IOException;
|
||||||
void flush() throws IOException;
|
|
||||||
|
/**
|
||||||
/**
|
* Discards the initial position of the stream prior to the current stream
|
||||||
* Returns the earliest position in the stream to which seeking may be
|
* position. Equivalent to {@code flushBefore(getStreamPosition())}.
|
||||||
* performed. The returned value will be the maximum of all values passed
|
*
|
||||||
* into previous calls to {@code flushBefore}.
|
* @throws IOException if an I/O error occurs.
|
||||||
*
|
*/
|
||||||
* @return the earliest legal position for seeking, as a {@code long}.
|
void flush() throws IOException;
|
||||||
*
|
|
||||||
* @throws IOException if an I/O error occurs.
|
/**
|
||||||
*/
|
* Returns the earliest position in the stream to which seeking may be
|
||||||
long getFlushedPosition() throws IOException;
|
* performed. The returned value will be the maximum of all values passed
|
||||||
|
* into previous calls to {@code flushBefore}.
|
||||||
/**
|
*
|
||||||
* Returns true if this {@code Seekable} stream caches data itself in order
|
* @return the earliest legal position for seeking, as a {@code long}.
|
||||||
* to allow seeking backwards. Applications may consult this in order to
|
*
|
||||||
* decide how frequently, or whether, to flush in order to conserve cache
|
* @throws IOException if an I/O error occurs.
|
||||||
* resources.
|
*/
|
||||||
*
|
long getFlushedPosition() throws IOException;
|
||||||
* @return {@code true} if this {@code Seekable} caches data.
|
|
||||||
* @see #isCachedMemory()
|
/**
|
||||||
* @see #isCachedFile()
|
* Returns true if this {@code Seekable} stream caches data itself in order
|
||||||
*/
|
* to allow seeking backwards. Applications may consult this in order to
|
||||||
boolean isCached();
|
* decide how frequently, or whether, to flush in order to conserve cache
|
||||||
|
* resources.
|
||||||
/**
|
*
|
||||||
* Returns true if this {@code Seekable} stream caches data itself in order
|
* @return {@code true} if this {@code Seekable} caches data.
|
||||||
* to allow seeking backwards, and the cache is kept in main memory.
|
* @see #isCachedMemory()
|
||||||
* Applications may consult this in order to decide how frequently, or
|
* @see #isCachedFile()
|
||||||
* whether, to flush in order to conserve cache resources.
|
*/
|
||||||
*
|
boolean isCached();
|
||||||
* @return {@code true} if this {@code Seekable} caches data in main
|
|
||||||
* memory.
|
/**
|
||||||
* @see #isCached()
|
* Returns true if this {@code Seekable} stream caches data itself in order
|
||||||
* @see #isCachedFile()
|
* to allow seeking backwards, and the cache is kept in main memory.
|
||||||
*/
|
* Applications may consult this in order to decide how frequently, or
|
||||||
boolean isCachedMemory();
|
* whether, to flush in order to conserve cache resources.
|
||||||
|
*
|
||||||
/**
|
* @return {@code true} if this {@code Seekable} caches data in main
|
||||||
* Returns true if this {@code Seekable} stream caches data itself in
|
* memory.
|
||||||
* order to allow seeking backwards, and the cache is kept in a
|
* @see #isCached()
|
||||||
* temporary file.
|
* @see #isCachedFile()
|
||||||
* Applications may consult this in order to decide how frequently,
|
*/
|
||||||
* or whether, to flush in order to conserve cache resources.
|
boolean isCachedMemory();
|
||||||
*
|
|
||||||
* @return {@code true} if this {@code Seekable} caches data in a
|
/**
|
||||||
* temporary file.
|
* Returns true if this {@code Seekable} stream caches data itself in
|
||||||
* @see #isCached
|
* order to allow seeking backwards, and the cache is kept in a
|
||||||
* @see #isCachedMemory
|
* temporary file.
|
||||||
*/
|
* Applications may consult this in order to decide how frequently,
|
||||||
boolean isCachedFile();
|
* or whether, to flush in order to conserve cache resources.
|
||||||
|
*
|
||||||
/**
|
* @return {@code true} if this {@code Seekable} caches data in a
|
||||||
* Closes the stream.
|
* temporary file.
|
||||||
*
|
* @see #isCached
|
||||||
* @throws java.io.IOException if the stream can't be closed.
|
* @see #isCachedMemory
|
||||||
*/
|
*/
|
||||||
void close() throws IOException;
|
boolean isCachedFile();
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Closes the stream.
|
||||||
|
*
|
||||||
|
* @throws java.io.IOException if the stream can't be closed.
|
||||||
|
*/
|
||||||
|
void close() throws IOException;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,238 +1,238 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
|
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
|
||||||
* <p/>
|
*
|
||||||
* @see SeekableOutputStream
|
* @see SeekableOutputStream
|
||||||
*
|
*
|
||||||
* @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/SeekableInputStream.java#4 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java#4 $
|
||||||
*/
|
*/
|
||||||
public abstract class SeekableInputStream extends InputStream implements Seekable {
|
public abstract class SeekableInputStream extends InputStream implements Seekable {
|
||||||
|
|
||||||
// TODO: It's at the moment not possible to create subclasses outside this
|
// TODO: It's at the moment not possible to create subclasses outside this
|
||||||
// package, as there's no access to position. position needs to be
|
// package, as there's no access to position. position needs to be
|
||||||
// updated from the read/read/read methods...
|
// updated from the read/read/read methods...
|
||||||
|
|
||||||
/** The stream position in this stream */
|
/** The stream position in this stream */
|
||||||
long position;
|
long position;
|
||||||
long flushedPosition;
|
long flushedPosition;
|
||||||
boolean closed;
|
boolean closed;
|
||||||
|
|
||||||
protected Stack<Long> markedPositions = new Stack<Long>();
|
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||||
|
|
||||||
/// InputStream overrides
|
/// InputStream overrides
|
||||||
@Override
|
@Override
|
||||||
public final int read(byte[] pBytes) throws IOException {
|
public final int read(byte[] pBytes) throws IOException {
|
||||||
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented using {@code seek(currentPos + pLength)}.
|
* Implemented using {@code seek(currentPos + pLength)}.
|
||||||
*
|
*
|
||||||
* @param pLength the number of bytes to skip
|
* @param pLength the number of bytes to skip
|
||||||
* @return the actual number of bytes skipped (may be equal to or less
|
* @return the actual number of bytes skipped (may be equal to or less
|
||||||
* than {@code pLength})
|
* than {@code pLength})
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O exception occurs during skip
|
* @throws IOException if an I/O exception occurs during skip
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final long skip(final long pLength) throws IOException {
|
public final long skip(final long pLength) throws IOException {
|
||||||
long pos = position;
|
long pos = position;
|
||||||
long wantedPosition = pos + pLength;
|
long wantedPosition = pos + pLength;
|
||||||
if (wantedPosition < flushedPosition) {
|
if (wantedPosition < flushedPosition) {
|
||||||
throw new IOException("position < flushedPosition");
|
throw new IOException("position < flushedPosition");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop at stream length for compatibility, even though it might be allowed
|
// Stop at stream length for compatibility, even though it might be allowed
|
||||||
// to seek past end of stream
|
// to seek past end of stream
|
||||||
int available = available();
|
int available = available();
|
||||||
if (available > 0) {
|
if (available > 0) {
|
||||||
seek(Math.min(wantedPosition, pos + available));
|
seek(Math.min(wantedPosition, pos + available));
|
||||||
}
|
}
|
||||||
// TODO: Add optimization for streams with known length!
|
// TODO: Add optimization for streams with known length!
|
||||||
else {
|
else {
|
||||||
// Slow mode...
|
// Slow mode...
|
||||||
int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
|
int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
|
||||||
while (toSkip > 0 && read() >= 0) {
|
while (toSkip > 0 && read() >= 0) {
|
||||||
toSkip--;
|
toSkip--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return position - pos;
|
return position - pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void mark(int pLimit) {
|
public final void mark(int pLimit) {
|
||||||
mark();
|
mark();
|
||||||
|
|
||||||
// TODO: We don't really need to do this.. Is it a good idea?
|
// TODO: We don't really need to do this.. Is it a good idea?
|
||||||
try {
|
try {
|
||||||
flushBefore(Math.max(position - pLimit, flushedPosition));
|
flushBefore(Math.max(position - pLimit, flushedPosition));
|
||||||
}
|
}
|
||||||
catch (IOException ignore) {
|
catch (IOException ignore) {
|
||||||
// Ignore, as it's not really critical
|
// Ignore, as it's not really critical
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true}, as marking is always supported.
|
* Returns {@code true}, as marking is always supported.
|
||||||
*
|
*
|
||||||
* @return {@code true}.
|
* @return {@code true}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final boolean markSupported() {
|
public final boolean markSupported() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Seekable implementation
|
/// Seekable implementation
|
||||||
public final void seek(long pPosition) throws IOException {
|
public final void seek(long pPosition) throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||||
// but it's kind of inconsistent with reset that throws IOException...
|
// but it's kind of inconsistent with reset that throws IOException...
|
||||||
if (pPosition < flushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||||
}
|
}
|
||||||
|
|
||||||
seekImpl(pPosition);
|
seekImpl(pPosition);
|
||||||
position = pPosition;
|
position = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
public final void mark() {
|
public final void mark() {
|
||||||
markedPositions.push(position);
|
markedPositions.push(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void reset() throws IOException {
|
public final void reset() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
if (!markedPositions.isEmpty()) {
|
if (!markedPositions.isEmpty()) {
|
||||||
long newPos = markedPositions.pop();
|
long newPos = markedPositions.pop();
|
||||||
|
|
||||||
// NOTE: This is correct according to javax.imageio (IOException),
|
// NOTE: This is correct according to javax.imageio (IOException),
|
||||||
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
|
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
|
||||||
if (newPos < flushedPosition) {
|
if (newPos < flushedPosition) {
|
||||||
throw new IOException("Previous marked position has been discarded");
|
throw new IOException("Previous marked position has been discarded");
|
||||||
}
|
}
|
||||||
|
|
||||||
seek(newPos);
|
seek(newPos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO: To iron out some wrinkles due to conflicting contracts
|
// TODO: To iron out some wrinkles due to conflicting contracts
|
||||||
// (InputStream and Seekable both declare reset),
|
// (InputStream and Seekable both declare reset),
|
||||||
// we might need to reset to the last marked position instead..
|
// we might need to reset to the last marked position instead..
|
||||||
// However, that becomes REALLY confusing if that position is after
|
// However, that becomes REALLY confusing if that position is after
|
||||||
// the current position...
|
// the current position...
|
||||||
seek(0);
|
seek(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void flushBefore(long pPosition) throws IOException {
|
public final void flushBefore(long pPosition) throws IOException {
|
||||||
if (pPosition < flushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||||
}
|
}
|
||||||
if (pPosition > getStreamPosition()) {
|
if (pPosition > getStreamPosition()) {
|
||||||
throw new IndexOutOfBoundsException("position > stream position");
|
throw new IndexOutOfBoundsException("position > stream position");
|
||||||
}
|
}
|
||||||
checkOpen();
|
checkOpen();
|
||||||
flushBeforeImpl(pPosition);
|
flushBeforeImpl(pPosition);
|
||||||
flushedPosition = pPosition;
|
flushedPosition = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discards the initial portion of the stream prior to the indicated postion.
|
* Discards the initial portion of the stream prior to the indicated postion.
|
||||||
*
|
*
|
||||||
* @param pPosition the position to flush to
|
* @param pPosition the position to flush to
|
||||||
* @throws IOException if an I/O exception occurs during the flush operation
|
* @throws IOException if an I/O exception occurs during the flush operation
|
||||||
*
|
*
|
||||||
* @see #flushBefore(long)
|
* @see #flushBefore(long)
|
||||||
*/
|
*/
|
||||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
public final void flush() throws IOException {
|
public final void flush() throws IOException {
|
||||||
flushBefore(flushedPosition);
|
flushBefore(flushedPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getFlushedPosition() throws IOException {
|
public final long getFlushedPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return flushedPosition;
|
return flushedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getStreamPosition() throws IOException {
|
public final long getStreamPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void checkOpen() throws IOException {
|
protected final void checkOpen() throws IOException {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
throw new IOException("closed");
|
throw new IOException("closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void close() throws IOException {
|
public final void close() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
closed = true;
|
closed = true;
|
||||||
closeImpl();
|
closeImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void closeImpl() throws IOException;
|
protected abstract void closeImpl() throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finalizes this object prior to garbage collection. The
|
* Finalizes this object prior to garbage collection. The
|
||||||
* {@code close} method is called to close any open input
|
* {@code close} method is called to close any open input
|
||||||
* source. This method should not be called from application
|
* source. This method should not be called from application
|
||||||
* code.
|
* code.
|
||||||
*
|
*
|
||||||
* @exception Throwable if an error occurs during superclass
|
* @exception Throwable if an error occurs during superclass
|
||||||
* finalization.
|
* finalization.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
catch (IOException ignore) {
|
catch (IOException ignore) {
|
||||||
// Ignroe
|
// Ignroe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.finalize();
|
super.finalize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +1,140 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for {@code OutputStream}s implementing the
|
* Abstract base class for {@code OutputStream}s implementing the
|
||||||
* {@code Seekable} interface.
|
* {@code Seekable} interface.
|
||||||
* <p/>
|
*
|
||||||
* @see SeekableInputStream
|
* @see SeekableInputStream
|
||||||
*
|
*
|
||||||
* @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/SeekableOutputStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
|
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
long position;
|
long position;
|
||||||
long flushedPosition;
|
long flushedPosition;
|
||||||
boolean closed;
|
boolean closed;
|
||||||
|
|
||||||
protected Stack<Long> markedPositions = new Stack<Long>();
|
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||||
|
|
||||||
/// Outputstream overrides
|
/// Outputstream overrides
|
||||||
@Override
|
@Override
|
||||||
public final void write(byte pBytes[]) throws IOException {
|
public final void write(byte pBytes[]) throws IOException {
|
||||||
write(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
write(pBytes, 0, pBytes != null ? pBytes.length : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Seekable implementation
|
/// Seekable implementation
|
||||||
// TODO: This is common behaviour/implementation with SeekableInputStream,
|
// TODO: This is common behaviour/implementation with SeekableInputStream,
|
||||||
// probably a good idea to extract a delegate..?
|
// probably a good idea to extract a delegate..?
|
||||||
public final void seek(long pPosition) throws IOException {
|
public final void seek(long pPosition) throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||||
// but it's inconsistent with reset that throws IOException...
|
// but it's inconsistent with reset that throws IOException...
|
||||||
if (pPosition < flushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||||
}
|
}
|
||||||
|
|
||||||
seekImpl(pPosition);
|
seekImpl(pPosition);
|
||||||
position = pPosition;
|
position = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
public final void mark() {
|
public final void mark() {
|
||||||
markedPositions.push(position);
|
markedPositions.push(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void reset() throws IOException {
|
public final void reset() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
if (!markedPositions.isEmpty()) {
|
if (!markedPositions.isEmpty()) {
|
||||||
long newPos = markedPositions.pop();
|
long newPos = markedPositions.pop();
|
||||||
|
|
||||||
// TODO: This is correct according to javax.imageio (IOException),
|
// TODO: This is correct according to javax.imageio (IOException),
|
||||||
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
|
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
|
||||||
if (newPos < flushedPosition) {
|
if (newPos < flushedPosition) {
|
||||||
throw new IOException("Previous marked position has been discarded!");
|
throw new IOException("Previous marked position has been discarded!");
|
||||||
}
|
}
|
||||||
|
|
||||||
seek(newPos);
|
seek(newPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void flushBefore(long pPosition) throws IOException {
|
public final void flushBefore(long pPosition) throws IOException {
|
||||||
if (pPosition < flushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||||
}
|
}
|
||||||
if (pPosition > getStreamPosition()) {
|
if (pPosition > getStreamPosition()) {
|
||||||
throw new IndexOutOfBoundsException("position > getStreamPosition()!");
|
throw new IndexOutOfBoundsException("position > getStreamPosition()!");
|
||||||
}
|
}
|
||||||
checkOpen();
|
checkOpen();
|
||||||
flushBeforeImpl(pPosition);
|
flushBeforeImpl(pPosition);
|
||||||
flushedPosition = pPosition;
|
flushedPosition = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void flush() throws IOException {
|
public final void flush() throws IOException {
|
||||||
flushBefore(flushedPosition);
|
flushBefore(flushedPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getFlushedPosition() throws IOException {
|
public final long getFlushedPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return flushedPosition;
|
return flushedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getStreamPosition() throws IOException {
|
public final long getStreamPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void checkOpen() throws IOException {
|
protected final void checkOpen() throws IOException {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
throw new IOException("closed");
|
throw new IOException("closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void close() throws IOException {
|
public final void close() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
closed = true;
|
closed = true;
|
||||||
closeImpl();
|
closeImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void closeImpl() throws IOException;
|
protected abstract void closeImpl() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,189 +1,188 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StringArrayReader
|
* StringArrayReader
|
||||||
* <p/>
|
*
|
||||||
*
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author last modified by $Author: haku $
|
||||||
* @author last modified by $Author: haku $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/StringArrayReader.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/StringArrayReader.java#2 $
|
*/
|
||||||
*/
|
public class StringArrayReader extends StringReader {
|
||||||
public class StringArrayReader extends StringReader {
|
|
||||||
|
private StringReader current;
|
||||||
private StringReader current;
|
private String[] strings;
|
||||||
private String[] strings;
|
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.
|
*
|
||||||
*
|
* @param pStrings {@code String}s providing the character stream.
|
||||||
* @param pStrings {@code String}s providing the character stream.
|
*/
|
||||||
*/
|
public StringArrayReader(final String[] pStrings) {
|
||||||
public StringArrayReader(final String[] pStrings) {
|
super("");
|
||||||
super("");
|
|
||||||
|
Validate.notNull(pStrings, "strings");
|
||||||
Validate.notNull(pStrings, "strings");
|
|
||||||
|
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
|
||||||
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
|
// reference can't change, only it's elements
|
||||||
// reference can't change, only it's elements
|
|
||||||
|
strings = pStrings.clone(); // Defensive copy for content
|
||||||
strings = pStrings.clone(); // Defensive copy for content
|
nextReader();
|
||||||
nextReader();
|
}
|
||||||
}
|
|
||||||
|
protected final Reader nextReader() {
|
||||||
protected final Reader nextReader() {
|
if (currentSting >= strings.length) {
|
||||||
if (currentSting >= strings.length) {
|
current = new EmptyReader();
|
||||||
current = new EmptyReader();
|
}
|
||||||
}
|
else {
|
||||||
else {
|
current = new StringReader(strings[currentSting++]);
|
||||||
current = new StringReader(strings[currentSting++]);
|
}
|
||||||
}
|
|
||||||
|
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
|
||||||
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
|
next = 0;
|
||||||
next = 0;
|
|
||||||
|
return current;
|
||||||
return current;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Check to make sure that the stream has not been closed
|
||||||
* Check to make sure that the stream has not been closed
|
*
|
||||||
*
|
* @throws IOException if the stream is closed
|
||||||
* @throws IOException if the stream is closed
|
*/
|
||||||
*/
|
protected final void ensureOpen() throws IOException {
|
||||||
protected final void ensureOpen() throws IOException {
|
if (strings == null) {
|
||||||
if (strings == null) {
|
throw new IOException("Stream closed");
|
||||||
throw new IOException("Stream closed");
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void close() {
|
||||||
public void close() {
|
super.close();
|
||||||
super.close();
|
strings = null;
|
||||||
strings = null;
|
current.close();
|
||||||
current.close();
|
}
|
||||||
}
|
|
||||||
|
public void mark(int pReadLimit) throws IOException {
|
||||||
public void mark(int pReadLimit) throws IOException {
|
if (pReadLimit < 0){
|
||||||
if (pReadLimit < 0){
|
throw new IllegalArgumentException("Read limit < 0");
|
||||||
throw new IllegalArgumentException("Read limit < 0");
|
}
|
||||||
}
|
|
||||||
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
ensureOpen();
|
||||||
ensureOpen();
|
mark = next;
|
||||||
mark = next;
|
markedString = currentSting;
|
||||||
markedString = currentSting;
|
|
||||||
|
current.mark(pReadLimit);
|
||||||
current.mark(pReadLimit);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void reset() throws IOException {
|
||||||
public void reset() throws IOException {
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
ensureOpen();
|
||||||
ensureOpen();
|
|
||||||
|
if (currentSting != markedString) {
|
||||||
if (currentSting != markedString) {
|
currentSting = markedString - 1;
|
||||||
currentSting = markedString - 1;
|
nextReader();
|
||||||
nextReader();
|
current.skip(mark);
|
||||||
current.skip(mark);
|
}
|
||||||
}
|
else {
|
||||||
else {
|
current.reset();
|
||||||
current.reset();
|
}
|
||||||
}
|
|
||||||
|
next = mark;
|
||||||
next = mark;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public boolean markSupported() {
|
||||||
public boolean markSupported() {
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
public int read() throws IOException {
|
||||||
public int read() throws IOException {
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
int read = current.read();
|
||||||
int read = current.read();
|
|
||||||
|
if (read < 0 && currentSting < strings.length) {
|
||||||
if (read < 0 && currentSting < strings.length) {
|
nextReader();
|
||||||
nextReader();
|
return read(); // In case of empty strings
|
||||||
return read(); // In case of empty strings
|
}
|
||||||
}
|
|
||||||
|
next++;
|
||||||
next++;
|
|
||||||
|
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);
|
|
||||||
|
if (read < 0 && currentSting < strings.length) {
|
||||||
if (read < 0 && currentSting < strings.length) {
|
nextReader();
|
||||||
nextReader();
|
return read(pBuffer, pOffset, pLength); // In case of empty strings
|
||||||
return read(pBuffer, pOffset, pLength); // In case of empty strings
|
}
|
||||||
}
|
|
||||||
|
next += read;
|
||||||
next += read;
|
|
||||||
|
return read;
|
||||||
return read;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public boolean ready() throws IOException {
|
||||||
public boolean ready() throws IOException {
|
return current.ready();
|
||||||
return current.ready();
|
}
|
||||||
}
|
|
||||||
|
public long skip(long pChars) throws IOException {
|
||||||
public long skip(long pChars) throws IOException {
|
synchronized (finalLock) {
|
||||||
synchronized (finalLock) {
|
long skipped = current.skip(pChars);
|
||||||
long skipped = current.skip(pChars);
|
|
||||||
|
if (skipped == 0 && currentSting < strings.length) {
|
||||||
if (skipped == 0 && currentSting < strings.length) {
|
nextReader();
|
||||||
nextReader();
|
return skip(pChars);
|
||||||
return skip(pChars);
|
}
|
||||||
}
|
|
||||||
|
next += skipped;
|
||||||
next += skipped;
|
|
||||||
|
return skipped;
|
||||||
return skipped;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,137 +1,134 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code InputStream} reading up to a specified number of bytes from an
|
* An {@code InputStream} reading up to a specified number of bytes from an
|
||||||
* underlying stream.
|
* underlying stream.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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 {
|
||||||
*/
|
private long bytesLeft;
|
||||||
public final class SubStream extends FilterInputStream {
|
private int markLimit;
|
||||||
private long bytesLeft;
|
|
||||||
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 length maximum number of bytes to read from this stream
|
||||||
* @param pStream the underlying input stream
|
*/
|
||||||
* @param pLength maximum number of bytes to read drom this stream
|
public SubStream(final InputStream stream, final long length) {
|
||||||
*/
|
super(Validate.notNull(stream, "stream"));
|
||||||
public SubStream(final InputStream pStream, final long pLength) {
|
bytesLeft = Validate.isTrue(length >= 0, length, "length < 0: %s");
|
||||||
super(Validate.notNull(pStream, "stream"));
|
}
|
||||||
bytesLeft = pLength;
|
|
||||||
}
|
/**
|
||||||
|
* Marks this stream as closed.
|
||||||
/**
|
* This implementation does <em>not</em> close the underlying stream.
|
||||||
* Marks this stream as closed.
|
*/
|
||||||
* This implementation does <em>not</em> close the underlying stream.
|
@Override
|
||||||
*/
|
public void close() throws IOException {
|
||||||
@Override
|
// NOTE: Do not close the underlying stream, but consume it
|
||||||
public void close() throws IOException {
|
while (bytesLeft > 0) {
|
||||||
// NOTE: Do not close the underlying stream
|
if (skip(bytesLeft) <= 0 && read() < 0) {
|
||||||
while (bytesLeft > 0) {
|
break;
|
||||||
//noinspection ResultOfMethodCallIgnored
|
}
|
||||||
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
|
public void reset() throws IOException {
|
||||||
public void reset() throws IOException {
|
super.reset();// This either succeeds or throws IOException
|
||||||
super.reset();// This either succeeds or throws IOException
|
bytesLeft += markLimit;
|
||||||
bytesLeft += markLimit;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public int read() throws IOException {
|
||||||
public int read() throws IOException {
|
if (bytesLeft-- <= 0) {
|
||||||
if (bytesLeft-- <= 0) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
return super.read();
|
return super.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int read(byte[] pBytes) throws IOException {
|
public int read(byte[] bytes) throws IOException {
|
||||||
return read(pBytes, 0, pBytes.length);
|
return read(bytes, 0, bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
public int read(final byte[] bytes, final int off, final int len) throws IOException {
|
||||||
if (bytesLeft <= 0) {
|
if (bytesLeft <= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
int read = super.read(bytes, off, (int) findMaxLen(len));
|
||||||
bytesLeft = read < 0 ? 0 : bytesLeft - read;
|
bytesLeft = read < 0 ? 0 : bytesLeft - read;
|
||||||
return read;
|
|
||||||
}
|
return read;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Finds the maximum number of bytes we can read or skip, from this stream.
|
@Override
|
||||||
*
|
public long skip(long length) throws IOException {
|
||||||
* @param pLength the requested length
|
long skipped = super.skip(findMaxLen(length)); // Skips 0 or more, never -1
|
||||||
* @return the maximum number of bytes to read
|
bytesLeft -= skipped;
|
||||||
*/
|
|
||||||
private long findMaxLen(long pLength) {
|
return skipped;
|
||||||
if (bytesLeft < pLength) {
|
}
|
||||||
return (int) Math.max(bytesLeft, 0);
|
|
||||||
}
|
/**
|
||||||
else {
|
* Finds the maximum number of bytes we can read or skip, from this stream.
|
||||||
return pLength;
|
*
|
||||||
}
|
* @param length the requested length
|
||||||
}
|
* @return the maximum number of bytes to read
|
||||||
|
*/
|
||||||
@Override
|
private long findMaxLen(long length) {
|
||||||
public long skip(long pLength) throws IOException {
|
return bytesLeft < length ? Math.max(bytesLeft, 0) : length;
|
||||||
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
|
}
|
||||||
bytesLeft -= skipped;
|
}
|
||||||
return skipped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,107 +1,106 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.util.StringTokenIterator;
|
import com.twelvemonkeys.util.StringTokenIterator;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UnixFileSystem
|
* UnixFileSystem
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/UnixFileSystem.java#1 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java#1 $
|
*/
|
||||||
*/
|
final class UnixFileSystem extends FileSystem {
|
||||||
final class UnixFileSystem extends FileSystem {
|
long getFreeSpace(File pPath) {
|
||||||
long getFreeSpace(File pPath) {
|
try {
|
||||||
try {
|
return getNumber(pPath, 3);
|
||||||
return getNumber(pPath, 3);
|
}
|
||||||
}
|
catch (IOException e) {
|
||||||
catch (IOException e) {
|
return 0l;
|
||||||
return 0l;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
long getTotalSpace(File pPath) {
|
||||||
long getTotalSpace(File pPath) {
|
try {
|
||||||
try {
|
return getNumber(pPath, 5);
|
||||||
return getNumber(pPath, 5);
|
}
|
||||||
}
|
catch (IOException e) {
|
||||||
catch (IOException e) {
|
return 0l;
|
||||||
return 0l;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private long getNumber(File pPath, int pIndex) throws IOException {
|
||||||
private long getNumber(File pPath, int pIndex) throws IOException {
|
// TODO: Test on other platforms
|
||||||
// TODO: Test on other platforms
|
// Tested on Mac OS X, CygWin
|
||||||
// Tested on Mac OS X, CygWin
|
BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
|
||||||
BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
|
|
||||||
|
String last = null;
|
||||||
String last = null;
|
String line;
|
||||||
String line;
|
try {
|
||||||
try {
|
while ((line = reader.readLine()) != null) {
|
||||||
while ((line = reader.readLine()) != null) {
|
last = line;
|
||||||
last = line;
|
}
|
||||||
}
|
}
|
||||||
}
|
finally {
|
||||||
finally {
|
FileUtil.close(reader);
|
||||||
FileUtil.close(reader);
|
}
|
||||||
}
|
|
||||||
|
if (last != null) {
|
||||||
if (last != null) {
|
String blocks = null;
|
||||||
String blocks = null;
|
StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE);
|
||||||
StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE);
|
int count = 0;
|
||||||
int count = 0;
|
// We want the 3rd last token
|
||||||
// We want the 3rd last token
|
while (count < pIndex && tokens.hasNext()) {
|
||||||
while (count < pIndex && tokens.hasNext()) {
|
blocks = tokens.nextToken();
|
||||||
blocks = tokens.nextToken();
|
count++;
|
||||||
count++;
|
}
|
||||||
}
|
|
||||||
|
if (blocks != null) {
|
||||||
if (blocks != null) {
|
try {
|
||||||
try {
|
return Long.parseLong(blocks) * 1024L;
|
||||||
return Long.parseLong(blocks) * 1024L;
|
}
|
||||||
}
|
catch (NumberFormatException ignore) {
|
||||||
catch (NumberFormatException ignore) {
|
// Ignore
|
||||||
// Ignore
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return 0l;
|
||||||
return 0l;
|
}
|
||||||
}
|
|
||||||
|
String getName() {
|
||||||
String getName() {
|
return "Unix";
|
||||||
return "Unix";
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,195 +1,194 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Win32File
|
* Win32File
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/Win32File.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32File.java#2 $
|
*/
|
||||||
*/
|
final class Win32File extends File {
|
||||||
final class Win32File extends File {
|
private final static boolean IS_WINDOWS = isWindows();
|
||||||
private final static boolean IS_WINDOWS = isWindows();
|
|
||||||
|
private static boolean isWindows() {
|
||||||
private static boolean isWindows() {
|
try {
|
||||||
try {
|
String os = System.getProperty("os.name");
|
||||||
String os = System.getProperty("os.name");
|
return os.toLowerCase().indexOf("windows") >= 0;
|
||||||
return os.toLowerCase().indexOf("windows") >= 0;
|
}
|
||||||
}
|
catch (Throwable t) {
|
||||||
catch (Throwable t) {
|
// Ignore
|
||||||
// Ignore
|
}
|
||||||
}
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
private Win32File(File pPath) {
|
||||||
private Win32File(File pPath) {
|
super(pPath.getPath());
|
||||||
super(pPath.getPath());
|
}
|
||||||
}
|
|
||||||
|
public static void main(String[] pArgs) {
|
||||||
public static void main(String[] pArgs) {
|
int argIdx = 0;
|
||||||
int argIdx = 0;
|
boolean recursive = false;
|
||||||
boolean recursive = false;
|
while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) {
|
||||||
while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) {
|
if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) {
|
||||||
if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) {
|
recursive = true;
|
||||||
recursive = true;
|
}
|
||||||
}
|
else {
|
||||||
else {
|
System.err.println("Unknown option: " + pArgs[argIdx]);
|
||||||
System.err.println("Unknown option: " + pArgs[argIdx]);
|
}
|
||||||
}
|
argIdx++;
|
||||||
argIdx++;
|
}
|
||||||
}
|
|
||||||
|
File file = wrap(new File(pArgs[argIdx]));
|
||||||
File file = wrap(new File(pArgs[argIdx]));
|
System.out.println("file: " + file);
|
||||||
System.out.println("file: " + file);
|
System.out.println("file.getClass(): " + file.getClass());
|
||||||
System.out.println("file.getClass(): " + file.getClass());
|
|
||||||
|
listFiles(file, 0, recursive);
|
||||||
listFiles(file, 0, recursive);
|
}
|
||||||
}
|
|
||||||
|
private static void listFiles(File pFile, int pLevel, boolean pRecursive) {
|
||||||
private static void listFiles(File pFile, int pLevel, boolean pRecursive) {
|
if (pFile.isDirectory()) {
|
||||||
if (pFile.isDirectory()) {
|
File[] files = pFile.listFiles();
|
||||||
File[] files = pFile.listFiles();
|
for (int l = 0; l < pLevel; l++) {
|
||||||
for (int l = 0; l < pLevel; l++) {
|
System.out.print(" ");
|
||||||
System.out.print(" ");
|
}
|
||||||
}
|
System.out.println("Contents of " + pFile + ": ");
|
||||||
System.out.println("Contents of " + pFile + ": ");
|
for (File file : files) {
|
||||||
for (File file : files) {
|
for (int l = 0; l < pLevel; l++) {
|
||||||
for (int l = 0; l < pLevel; l++) {
|
System.out.print(" ");
|
||||||
System.out.print(" ");
|
}
|
||||||
}
|
System.out.println(" " + file);
|
||||||
System.out.println(" " + file);
|
if (pRecursive) {
|
||||||
if (pRecursive) {
|
listFiles(file, pLevel + 1, pLevel < 4);
|
||||||
listFiles(file, pLevel + 1, pLevel < 4);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Wraps a {@code File} object pointing to a Windows symbolic link
|
||||||
* Wraps a {@code File} object pointing to a Windows symbolic link
|
* ({@code .lnk} file) in a {@code Win32Lnk}.
|
||||||
* ({@code .lnk} file) in a {@code Win32Lnk}.
|
* If the operating system is not Windows, the
|
||||||
* If the operating system is not Windows, the
|
* {@code pPath} parameter is returned unwrapped.
|
||||||
* {@code pPath} parameter is returned unwrapped.
|
*
|
||||||
*
|
* @param pPath any path, possibly pointing to a Windows symbolic link file.
|
||||||
* @param pPath any path, possibly pointing to a Windows symbolic link file.
|
* May be {@code null}, in which case {@code null} is returned.
|
||||||
* May be {@code null}, in which case {@code null} is returned.
|
*
|
||||||
*
|
* @return a new {@code Win32Lnk} object if the current os is Windows, and
|
||||||
* @return a new {@code Win32Lnk} object if the current os is Windows, and
|
* the file is a Windows symbolic link ({@code .lnk} file), otherwise
|
||||||
* the file is a Windows symbolic link ({@code .lnk} file), otherwise
|
* {@code pPath}
|
||||||
* {@code pPath}
|
*/
|
||||||
*/
|
public static File wrap(final File pPath) {
|
||||||
public static File wrap(final File pPath) {
|
if (pPath == null) {
|
||||||
if (pPath == null) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
if (IS_WINDOWS) {
|
||||||
if (IS_WINDOWS) {
|
// Don't wrap if allready wrapped
|
||||||
// Don't wrap if allready wrapped
|
if (pPath instanceof Win32File || pPath instanceof Win32Lnk) {
|
||||||
if (pPath instanceof Win32File || pPath instanceof Win32Lnk) {
|
return pPath;
|
||||||
return pPath;
|
}
|
||||||
}
|
|
||||||
|
if (pPath.exists() && pPath.getName().endsWith(".lnk")) {
|
||||||
if (pPath.exists() && pPath.getName().endsWith(".lnk")) {
|
// If Win32 .lnk, let's wrap
|
||||||
// If Win32 .lnk, let's wrap
|
try {
|
||||||
try {
|
return new Win32Lnk(pPath);
|
||||||
return new Win32Lnk(pPath);
|
}
|
||||||
}
|
catch (IOException e) {
|
||||||
catch (IOException e) {
|
// TODO: FixMe!
|
||||||
// TODO: FixMe!
|
e.printStackTrace();
|
||||||
e.printStackTrace();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Wwrap even if not a .lnk, as the listFiles() methods etc,
|
||||||
// Wwrap even if not a .lnk, as the listFiles() methods etc,
|
// could potentially return .lnk's, that we want to wrap later...
|
||||||
// could potentially return .lnk's, that we want to wrap later...
|
return new Win32File(pPath);
|
||||||
return new Win32File(pPath);
|
}
|
||||||
}
|
|
||||||
|
return pPath;
|
||||||
return pPath;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Wraps a {@code File} array, possibly pointing to Windows symbolic links
|
||||||
* Wraps a {@code File} array, possibly pointing to Windows symbolic links
|
* ({@code .lnk} files) in {@code Win32Lnk}s.
|
||||||
* ({@code .lnk} files) in {@code Win32Lnk}s.
|
*
|
||||||
*
|
* @param pPaths an array of {@code File}s, possibly pointing to Windows
|
||||||
* @param pPaths an array of {@code File}s, possibly pointing to Windows
|
* symbolic link files.
|
||||||
* symbolic link files.
|
* May be {@code null}, in which case {@code null} is returned.
|
||||||
* May be {@code null}, in which case {@code null} is returned.
|
*
|
||||||
*
|
* @return {@code pPaths}, with any {@code File} representing a Windows
|
||||||
* @return {@code pPaths}, with any {@code File} representing a Windows
|
* symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}.
|
||||||
* symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}.
|
*/
|
||||||
*/
|
public static File[] wrap(File[] pPaths) {
|
||||||
public static File[] wrap(File[] pPaths) {
|
if (IS_WINDOWS) {
|
||||||
if (IS_WINDOWS) {
|
for (int i = 0; pPaths != null && i < pPaths.length; i++) {
|
||||||
for (int i = 0; pPaths != null && i < pPaths.length; i++) {
|
pPaths[i] = wrap(pPaths[i]);
|
||||||
pPaths[i] = wrap(pPaths[i]);
|
}
|
||||||
}
|
}
|
||||||
}
|
return pPaths;
|
||||||
return pPaths;
|
}
|
||||||
}
|
|
||||||
|
// File overrides
|
||||||
// File overrides
|
@Override
|
||||||
@Override
|
public File getAbsoluteFile() {
|
||||||
public File getAbsoluteFile() {
|
return wrap(super.getAbsoluteFile());
|
||||||
return wrap(super.getAbsoluteFile());
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public File getCanonicalFile() throws IOException {
|
||||||
public File getCanonicalFile() throws IOException {
|
return wrap(super.getCanonicalFile());
|
||||||
return wrap(super.getCanonicalFile());
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public File getParentFile() {
|
||||||
public File getParentFile() {
|
return wrap(super.getParentFile());
|
||||||
return wrap(super.getParentFile());
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public File[] listFiles() {
|
||||||
public File[] listFiles() {
|
return wrap(super.listFiles());
|
||||||
return wrap(super.listFiles());
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public File[] listFiles(FileFilter filter) {
|
||||||
public File[] listFiles(FileFilter filter) {
|
return wrap(super.listFiles(filter));
|
||||||
return wrap(super.listFiles(filter));
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public File[] listFiles(FilenameFilter filter) {
|
||||||
public File[] listFiles(FilenameFilter filter) {
|
return wrap(super.listFiles(filter));
|
||||||
return wrap(super.listFiles(filter));
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,92 +1,91 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WindowsFileSystem
|
* WindowsFileSystem
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/Win32FileSystem.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java#2 $
|
*/
|
||||||
*/
|
final class Win32FileSystem extends FileSystem {
|
||||||
final class Win32FileSystem extends FileSystem {
|
public long getFreeSpace(File pPath) {
|
||||||
public long getFreeSpace(File pPath) {
|
try {
|
||||||
try {
|
// Windows version
|
||||||
// Windows version
|
// TODO: Test on W2K/95/98/etc... (tested on XP)
|
||||||
// TODO: Test on W2K/95/98/etc... (tested on XP)
|
BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
|
||||||
BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
|
|
||||||
|
String last = null;
|
||||||
String last = null;
|
String line;
|
||||||
String line;
|
try {
|
||||||
try {
|
while ((line = reader.readLine()) != null) {
|
||||||
while ((line = reader.readLine()) != null) {
|
last = line;
|
||||||
last = line;
|
}
|
||||||
}
|
}
|
||||||
}
|
finally {
|
||||||
finally {
|
FileUtil.close(reader);
|
||||||
FileUtil.close(reader);
|
}
|
||||||
}
|
|
||||||
|
if (last != null) {
|
||||||
if (last != null) {
|
int end = last.lastIndexOf(" bytes free");
|
||||||
int end = last.lastIndexOf(" bytes free");
|
int start = last.lastIndexOf(' ', end - 1);
|
||||||
int start = last.lastIndexOf(' ', end - 1);
|
|
||||||
|
if (start >= 0 && end >= 0) {
|
||||||
if (start >= 0 && end >= 0) {
|
try {
|
||||||
try {
|
return Long.parseLong(last.substring(start + 1, end));
|
||||||
return Long.parseLong(last.substring(start + 1, end));
|
}
|
||||||
}
|
catch (NumberFormatException ignore) {
|
||||||
catch (NumberFormatException ignore) {
|
// Ignore
|
||||||
// Ignore
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (IOException ignore) {
|
||||||
catch (IOException ignore) {
|
// Ignore
|
||||||
// Ignore
|
}
|
||||||
}
|
|
||||||
|
return 0l;
|
||||||
return 0l;
|
}
|
||||||
}
|
|
||||||
|
long getTotalSpace(File pPath) {
|
||||||
long getTotalSpace(File pPath) {
|
// TODO: Implement, probably need some JNI stuff...
|
||||||
// TODO: Implement, probably need some JNI stuff...
|
// Distribute df.exe and execute from temp!? ;-)
|
||||||
// Distribute df.exe and execute from temp!? ;-)
|
return getFreeSpace(pPath);
|
||||||
return getFreeSpace(pPath);
|
}
|
||||||
}
|
|
||||||
|
String getName() {
|
||||||
String getName() {
|
return "Win32";
|
||||||
return "Win32";
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,475 +1,477 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
|
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
|
||||||
* <p/>
|
* <p>
|
||||||
* This class is based on example code from
|
* This class is based on example code from
|
||||||
* <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>,
|
* <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>,
|
||||||
* By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
|
* By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
|
||||||
*
|
* </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/io/Win32Lnk.java#2 $
|
* @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/Win32Lnk.java#2 $
|
||||||
final class Win32Lnk extends File {
|
*/
|
||||||
private final static byte[] LNK_MAGIC = {
|
final class Win32Lnk extends File {
|
||||||
'L', 0x00, 0x00, 0x00, // Magic
|
private final static byte[] LNK_MAGIC = {
|
||||||
};
|
'L', 0x00, 0x00, 0x00, // Magic
|
||||||
private final static byte[] LNK_GUID = {
|
};
|
||||||
0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Shell Link GUID
|
private final static byte[] LNK_GUID = {
|
||||||
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
|
0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Shell Link GUID
|
||||||
};
|
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
|
||||||
|
};
|
||||||
private final File target;
|
|
||||||
|
private final File target;
|
||||||
private static final int FLAG_ITEM_ID_LIST = 0x01;
|
|
||||||
private static final int FLAG_FILE_LOC_INFO = 0x02;
|
private static final int FLAG_ITEM_ID_LIST = 0x01;
|
||||||
private static final int FLAG_DESC_STRING = 0x04;
|
private static final int FLAG_FILE_LOC_INFO = 0x02;
|
||||||
private static final int FLAG_REL_PATH_STRING = 0x08;
|
private static final int FLAG_DESC_STRING = 0x04;
|
||||||
private static final int FLAG_WORKING_DIRECTORY = 0x10;
|
private static final int FLAG_REL_PATH_STRING = 0x08;
|
||||||
private static final int FLAG_COMMAND_LINE_ARGS = 0x20;
|
private static final int FLAG_WORKING_DIRECTORY = 0x10;
|
||||||
private static final int FLAG_ICON_FILENAME = 0x40;
|
private static final int FLAG_COMMAND_LINE_ARGS = 0x20;
|
||||||
private static final int FLAG_ADDITIONAL_INFO = 0x80;
|
private static final int FLAG_ICON_FILENAME = 0x40;
|
||||||
|
private static final int FLAG_ADDITIONAL_INFO = 0x80;
|
||||||
private Win32Lnk(final String pPath) throws IOException {
|
|
||||||
super(pPath);
|
private Win32Lnk(final String pPath) throws IOException {
|
||||||
File target = parse(this);
|
super(pPath);
|
||||||
if (target == this) {
|
File target = parse(this);
|
||||||
// NOTE: This is a workaround
|
if (target == this) {
|
||||||
// target = this causes infinite loops in some methods
|
// NOTE: This is a workaround
|
||||||
target = new File(pPath);
|
// target = this causes infinite loops in some methods
|
||||||
}
|
target = new File(pPath);
|
||||||
this.target = target;
|
}
|
||||||
}
|
this.target = target;
|
||||||
|
}
|
||||||
Win32Lnk(final File pPath) throws IOException {
|
|
||||||
this(pPath.getPath());
|
Win32Lnk(final File pPath) throws IOException {
|
||||||
}
|
this(pPath.getPath());
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Parses a {@code .lnk} file to find the real file.
|
/**
|
||||||
*
|
* Parses a {@code .lnk} file to find the real file.
|
||||||
* @param pPath the path to the {@code .lnk} file
|
*
|
||||||
* @return a new file object that
|
* @param pPath the path to the {@code .lnk} file
|
||||||
* @throws java.io.IOException if the {@code .lnk} cannot be parsed
|
* @return a new file object that
|
||||||
*/
|
* @throws java.io.IOException if the {@code .lnk} cannot be parsed
|
||||||
static File parse(final File pPath) throws IOException {
|
*/
|
||||||
if (!pPath.getName().endsWith(".lnk")) {
|
static File parse(final File pPath) throws IOException {
|
||||||
return pPath;
|
if (!pPath.getName().endsWith(".lnk")) {
|
||||||
}
|
return pPath;
|
||||||
|
}
|
||||||
File result = pPath;
|
|
||||||
|
File result = pPath;
|
||||||
LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
|
|
||||||
try {
|
LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
|
||||||
byte[] magic = new byte[4];
|
try {
|
||||||
in.readFully(magic);
|
byte[] magic = new byte[4];
|
||||||
|
in.readFully(magic);
|
||||||
byte[] guid = new byte[16];
|
|
||||||
in.readFully(guid);
|
byte[] guid = new byte[16];
|
||||||
|
in.readFully(guid);
|
||||||
if (!(Arrays.equals(LNK_MAGIC, magic) && Arrays.equals(LNK_GUID, guid))) {
|
|
||||||
//System.out.println("Not a symlink");
|
if (!(Arrays.equals(LNK_MAGIC, magic) && Arrays.equals(LNK_GUID, guid))) {
|
||||||
// Not a symlink
|
//System.out.println("Not a symlink");
|
||||||
return pPath;
|
// Not a symlink
|
||||||
}
|
return pPath;
|
||||||
|
}
|
||||||
// Get the flags
|
|
||||||
int flags = in.readInt();
|
// Get the flags
|
||||||
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff));
|
int flags = in.readInt();
|
||||||
|
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff));
|
||||||
// Get to the file settings
|
|
||||||
/*int attributes = */in.readInt();
|
// Get to the file settings
|
||||||
|
/*int attributes = */in.readInt();
|
||||||
// File attributes
|
|
||||||
// 0 Target is read only.
|
// File attributes
|
||||||
// 1 Target is hidden.
|
// 0 Target is read only.
|
||||||
// 2 Target is a system file.
|
// 1 Target is hidden.
|
||||||
// 3 Target is a volume label. (Not possible)
|
// 2 Target is a system file.
|
||||||
// 4 Target is a directory.
|
// 3 Target is a volume label. (Not possible)
|
||||||
// 5 Target has been modified since last backup. (archive)
|
// 4 Target is a directory.
|
||||||
// 6 Target is encrypted (NTFS EFS)
|
// 5 Target has been modified since last backup. (archive)
|
||||||
// 7 Target is Normal??
|
// 6 Target is encrypted (NTFS EFS)
|
||||||
// 8 Target is temporary.
|
// 7 Target is Normal??
|
||||||
// 9 Target is a sparse file.
|
// 8 Target is temporary.
|
||||||
// 10 Target has reparse point data.
|
// 9 Target is a sparse file.
|
||||||
// 11 Target is compressed.
|
// 10 Target has reparse point data.
|
||||||
// 12 Target is offline.
|
// 11 Target is compressed.
|
||||||
//System.out.println("attributes: " + Integer.toBinaryString(attributes));
|
// 12 Target is offline.
|
||||||
// NOTE: Cygwin .lnks are not directory links, can't rely on this.. :-/
|
//System.out.println("attributes: " + Integer.toBinaryString(attributes));
|
||||||
|
// NOTE: Cygwin .lnks are not directory links, can't rely on this.. :-/
|
||||||
in.skipBytes(48); // TODO: Make sense of this data...
|
|
||||||
|
in.skipBytes(48); // TODO: Make sense of this data...
|
||||||
// Skipped data:
|
|
||||||
// long time 1 (creation)
|
// Skipped data:
|
||||||
// long time 2 (modification)
|
// long time 1 (creation)
|
||||||
// long time 3 (last access)
|
// long time 2 (modification)
|
||||||
// int file length
|
// long time 3 (last access)
|
||||||
// int icon number
|
// int file length
|
||||||
// int ShowVnd value
|
// int icon number
|
||||||
// int hotkey
|
// int ShowVnd value
|
||||||
// int, int - unknown: 0,0
|
// int hotkey
|
||||||
|
// int, int - unknown: 0,0
|
||||||
// If the shell settings are present, skip them
|
|
||||||
if ((flags & FLAG_ITEM_ID_LIST) != 0) {
|
// If the shell settings are present, skip them
|
||||||
// Shell Item Id List present
|
if ((flags & FLAG_ITEM_ID_LIST) != 0) {
|
||||||
//System.out.println("Shell Item Id List present");
|
// Shell Item Id List present
|
||||||
int shellLen = in.readShort(); // Short
|
//System.out.println("Shell Item Id List present");
|
||||||
//System.out.println("shellLen: " + shellLen);
|
int shellLen = in.readShort(); // Short
|
||||||
|
//System.out.println("shellLen: " + shellLen);
|
||||||
// TODO: Probably need to parse this data, to determine
|
|
||||||
// Cygwin folders...
|
// TODO: Probably need to parse this data, to determine
|
||||||
|
// Cygwin folders...
|
||||||
/*
|
|
||||||
int read = 2;
|
/*
|
||||||
int itemLen = in.readShort();
|
int read = 2;
|
||||||
while (itemLen > 0) {
|
int itemLen = in.readShort();
|
||||||
System.out.println("--> ITEM: " + itemLen);
|
while (itemLen > 0) {
|
||||||
|
System.out.println("--> ITEM: " + itemLen);
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new SubStream(in, itemLen - 2)));
|
|
||||||
//byte[] itemBytes = new byte[itemLen - 2]; // NOTE: Lenght included
|
BufferedReader reader = new BufferedReader(new InputStreamReader(new SubStream(in, itemLen - 2)));
|
||||||
//in.readFully(itemBytes);
|
//byte[] itemBytes = new byte[itemLen - 2]; // NOTE: Lenght included
|
||||||
|
//in.readFully(itemBytes);
|
||||||
String item = reader.readLine();
|
|
||||||
System.out.println("item: \"" + item + "\"");
|
String item = reader.readLine();
|
||||||
|
System.out.println("item: \"" + item + "\"");
|
||||||
itemLen = in.readShort();
|
|
||||||
read += itemLen;
|
itemLen = in.readShort();
|
||||||
}
|
read += itemLen;
|
||||||
|
}
|
||||||
System.out.println("read: " + read);
|
|
||||||
*/
|
System.out.println("read: " + read);
|
||||||
|
*/
|
||||||
in.skipBytes(shellLen);
|
|
||||||
}
|
in.skipBytes(shellLen);
|
||||||
|
}
|
||||||
if ((flags & FLAG_FILE_LOC_INFO) != 0) {
|
|
||||||
// File Location Info Table present
|
if ((flags & FLAG_FILE_LOC_INFO) != 0) {
|
||||||
//System.out.println("File Location Info Table present");
|
// File Location Info Table present
|
||||||
|
//System.out.println("File Location Info Table present");
|
||||||
// 0h 1 dword This is the total length of this structure and all following data
|
|
||||||
// 4h 1 dword This is a pointer to first offset after this structure. 1Ch
|
// 0h 1 dword This is the total length of this structure and all following data
|
||||||
// 8h 1 dword Flags
|
// 4h 1 dword This is a pointer to first offset after this structure. 1Ch
|
||||||
// Ch 1 dword Offset of local volume info
|
// 8h 1 dword Flags
|
||||||
// 10h 1 dword Offset of base pathname on local system
|
// Ch 1 dword Offset of local volume info
|
||||||
// 14h 1 dword Offset of network volume info
|
// 10h 1 dword Offset of base pathname on local system
|
||||||
// 18h 1 dword Offset of remaining pathname
|
// 14h 1 dword Offset of network volume info
|
||||||
|
// 18h 1 dword Offset of remaining pathname
|
||||||
// Flags:
|
|
||||||
// Bit Meaning
|
// Flags:
|
||||||
// 0 Available on a local volume
|
// Bit Meaning
|
||||||
// 1 Available on a network share
|
// 0 Available on a local volume
|
||||||
// TODO: Make sure the path is on a local disk, etc..
|
// 1 Available on a network share
|
||||||
|
// TODO: Make sure the path is on a local disk, etc..
|
||||||
int tableLen = in.readInt(); // Int
|
|
||||||
//System.out.println("tableLen: " + tableLen);
|
int tableLen = in.readInt(); // Int
|
||||||
|
//System.out.println("tableLen: " + tableLen);
|
||||||
in.readInt(); // Skip
|
|
||||||
|
in.readInt(); // Skip
|
||||||
int locFlags = in.readInt();
|
|
||||||
//System.out.println("locFlags: " + Integer.toBinaryString(locFlags));
|
int locFlags = in.readInt();
|
||||||
if ((locFlags & 0x01) != 0) {
|
//System.out.println("locFlags: " + Integer.toBinaryString(locFlags));
|
||||||
//System.out.println("Available local");
|
if ((locFlags & 0x01) != 0) {
|
||||||
}
|
//System.out.println("Available local");
|
||||||
if ((locFlags & 0x02) != 0) {
|
}
|
||||||
//System.err.println("Available on network path");
|
if ((locFlags & 0x02) != 0) {
|
||||||
}
|
//System.err.println("Available on network path");
|
||||||
|
}
|
||||||
// Get the local volume and local system values
|
|
||||||
in.skipBytes(4); // TODO: see above for structure
|
// Get the local volume and local system values
|
||||||
|
in.skipBytes(4); // TODO: see above for structure
|
||||||
int localSysOff = in.readInt();
|
|
||||||
//System.out.println("localSysOff: " + localSysOff);
|
int localSysOff = in.readInt();
|
||||||
in.skipBytes(localSysOff - 20); // Relative to start of chunk
|
//System.out.println("localSysOff: " + localSysOff);
|
||||||
|
in.skipBytes(localSysOff - 20); // Relative to start of chunk
|
||||||
byte[] pathBytes = new byte[tableLen - localSysOff - 1];
|
|
||||||
in.readFully(pathBytes, 0, pathBytes.length);
|
byte[] pathBytes = new byte[tableLen - localSysOff - 1];
|
||||||
String path = new String(pathBytes, 0, pathBytes.length - 1);
|
in.readFully(pathBytes, 0, pathBytes.length);
|
||||||
/*
|
String path = new String(pathBytes, 0, pathBytes.length - 1);
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
/*
|
||||||
byte read;
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
// Read bytes until the null (0) character
|
byte read;
|
||||||
while (true) {
|
// Read bytes until the null (0) character
|
||||||
read = in.readByte();
|
while (true) {
|
||||||
if (read == 0) {
|
read = in.readByte();
|
||||||
break;
|
if (read == 0) {
|
||||||
}
|
break;
|
||||||
bytes.write(read & 0xff);
|
}
|
||||||
}
|
bytes.write(read & 0xff);
|
||||||
|
}
|
||||||
String path = new String(bytes.toByteArray(), 0, bytes.size());
|
|
||||||
//*/
|
String path = new String(bytes.toByteArray(), 0, bytes.size());
|
||||||
|
//*/
|
||||||
// Recurse to end of link chain
|
|
||||||
// TODO: This may cause endless loop if cyclic chain...
|
// Recurse to end of link chain
|
||||||
//System.out.println("path: \"" + path + "\"");
|
// TODO: This may cause endless loop if cyclic chain...
|
||||||
try {
|
//System.out.println("path: \"" + path + "\"");
|
||||||
result = parse(new File(path));
|
try {
|
||||||
}
|
result = parse(new File(path));
|
||||||
catch (StackOverflowError e) {
|
}
|
||||||
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
|
catch (StackOverflowError e) {
|
||||||
}
|
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ((flags & FLAG_DESC_STRING) != 0) {
|
|
||||||
// Description String present, skip it.
|
if ((flags & FLAG_DESC_STRING) != 0) {
|
||||||
//System.out.println("Description String present");
|
// Description String present, skip it.
|
||||||
|
//System.out.println("Description String present");
|
||||||
// The string length is the first word which must also be skipped.
|
|
||||||
int descLen = in.readShort();
|
// The string length is the first word which must also be skipped.
|
||||||
//System.out.println("descLen: " + descLen);
|
int descLen = in.readShort();
|
||||||
|
//System.out.println("descLen: " + descLen);
|
||||||
byte[] descBytes = new byte[descLen];
|
|
||||||
in.readFully(descBytes, 0, descLen);
|
byte[] descBytes = new byte[descLen];
|
||||||
|
in.readFully(descBytes, 0, descLen);
|
||||||
//String desc = new String(descBytes, 0, descLen);
|
|
||||||
//System.out.println("desc: " + desc);
|
//String desc = new String(descBytes, 0, descLen);
|
||||||
}
|
//System.out.println("desc: " + desc);
|
||||||
|
}
|
||||||
if ((flags & FLAG_REL_PATH_STRING) != 0) {
|
|
||||||
// Relative Path String present
|
if ((flags & FLAG_REL_PATH_STRING) != 0) {
|
||||||
//System.out.println("Relative Path String present");
|
// Relative Path String present
|
||||||
|
//System.out.println("Relative Path String present");
|
||||||
// The string length is the first word which must also be skipped.
|
|
||||||
int pathLen = in.readShort();
|
// The string length is the first word which must also be skipped.
|
||||||
//System.out.println("pathLen: " + pathLen);
|
int pathLen = in.readShort();
|
||||||
|
//System.out.println("pathLen: " + pathLen);
|
||||||
byte[] pathBytes = new byte[pathLen];
|
|
||||||
in.readFully(pathBytes, 0, pathLen);
|
byte[] pathBytes = new byte[pathLen];
|
||||||
|
in.readFully(pathBytes, 0, pathLen);
|
||||||
String path = new String(pathBytes, 0, pathLen);
|
|
||||||
|
String path = new String(pathBytes, 0, pathLen);
|
||||||
// TODO: This may cause endless loop if cyclic chain...
|
|
||||||
//System.out.println("path: \"" + path + "\"");
|
// TODO: This may cause endless loop if cyclic chain...
|
||||||
if (result == pPath) {
|
//System.out.println("path: \"" + path + "\"");
|
||||||
try {
|
if (result == pPath) {
|
||||||
result = parse(new File(pPath.getParentFile(), path));
|
try {
|
||||||
}
|
result = parse(new File(pPath.getParentFile(), path));
|
||||||
catch (StackOverflowError e) {
|
}
|
||||||
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
|
catch (StackOverflowError e) {
|
||||||
}
|
throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ((flags & FLAG_WORKING_DIRECTORY) != 0) {
|
|
||||||
//System.out.println("Working Directory present");
|
if ((flags & FLAG_WORKING_DIRECTORY) != 0) {
|
||||||
}
|
//System.out.println("Working Directory present");
|
||||||
if ((flags & FLAG_COMMAND_LINE_ARGS) != 0) {
|
}
|
||||||
//System.out.println("Command Line Arguments present");
|
if ((flags & FLAG_COMMAND_LINE_ARGS) != 0) {
|
||||||
// NOTE: This means this .lnk is not a folder, don't follow
|
//System.out.println("Command Line Arguments present");
|
||||||
result = pPath;
|
// NOTE: This means this .lnk is not a folder, don't follow
|
||||||
}
|
result = pPath;
|
||||||
if ((flags & FLAG_ICON_FILENAME) != 0) {
|
}
|
||||||
//System.out.println("Icon Filename present");
|
if ((flags & FLAG_ICON_FILENAME) != 0) {
|
||||||
}
|
//System.out.println("Icon Filename present");
|
||||||
if ((flags & FLAG_ADDITIONAL_INFO) != 0) {
|
}
|
||||||
//System.out.println("Additional Info present");
|
if ((flags & FLAG_ADDITIONAL_INFO) != 0) {
|
||||||
}
|
//System.out.println("Additional Info present");
|
||||||
}
|
}
|
||||||
finally {
|
}
|
||||||
in.close();
|
finally {
|
||||||
}
|
in.close();
|
||||||
|
}
|
||||||
return result;
|
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
/*
|
|
||||||
private static String getNullDelimitedString(byte[] bytes, int off) {
|
/*
|
||||||
int len = 0;
|
private static String getNullDelimitedString(byte[] bytes, int off) {
|
||||||
// Count bytes until the null (0) character
|
int len = 0;
|
||||||
while (true) {
|
// Count bytes until the null (0) character
|
||||||
if (bytes[off + len] == 0) {
|
while (true) {
|
||||||
break;
|
if (bytes[off + len] == 0) {
|
||||||
}
|
break;
|
||||||
len++;
|
}
|
||||||
}
|
len++;
|
||||||
|
}
|
||||||
System.err.println("--> " + len);
|
|
||||||
|
System.err.println("--> " + len);
|
||||||
return new String(bytes, off, len);
|
|
||||||
}
|
return new String(bytes, off, len);
|
||||||
*/
|
}
|
||||||
|
*/
|
||||||
/**
|
|
||||||
* Converts two bytes into a short.
|
/**
|
||||||
* <p/>
|
* Converts two bytes into a short.
|
||||||
* NOTE: this is little endian because it's for an
|
* <p>
|
||||||
* Intel only OS
|
* NOTE: this is little endian because it's for an
|
||||||
*
|
* Intel only OS
|
||||||
* @ param bytes
|
* </p>
|
||||||
* @ param off
|
*
|
||||||
* @return the bytes as a short.
|
* @ param bytes
|
||||||
*/
|
* @ param off
|
||||||
/*
|
* @return the bytes as a short.
|
||||||
private static int bytes2short(byte[] bytes, int off) {
|
*/
|
||||||
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
|
/*
|
||||||
}
|
private static int bytes2short(byte[] bytes, int off) {
|
||||||
*/
|
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
|
||||||
|
}
|
||||||
public File getTarget() {
|
*/
|
||||||
return target;
|
|
||||||
}
|
public File getTarget() {
|
||||||
|
return target;
|
||||||
// java.io.File overrides below
|
}
|
||||||
|
|
||||||
@Override
|
// java.io.File overrides below
|
||||||
public boolean isDirectory() {
|
|
||||||
return target.isDirectory();
|
@Override
|
||||||
}
|
public boolean isDirectory() {
|
||||||
|
return target.isDirectory();
|
||||||
@Override
|
}
|
||||||
public boolean canRead() {
|
|
||||||
return target.canRead();
|
@Override
|
||||||
}
|
public boolean canRead() {
|
||||||
|
return target.canRead();
|
||||||
@Override
|
}
|
||||||
public boolean canWrite() {
|
|
||||||
return target.canWrite();
|
@Override
|
||||||
}
|
public boolean canWrite() {
|
||||||
|
return target.canWrite();
|
||||||
// NOTE: equals is implemented using compareto == 0
|
}
|
||||||
/*
|
|
||||||
public int compareTo(File pathname) {
|
// NOTE: equals is implemented using compareto == 0
|
||||||
// TODO: Verify this
|
/*
|
||||||
// Probably not a good idea, as it IS NOT THE SAME file
|
public int compareTo(File pathname) {
|
||||||
// It's probably better to not override
|
// TODO: Verify this
|
||||||
return target.compareTo(pathname);
|
// Probably not a good idea, as it IS NOT THE SAME file
|
||||||
}
|
// It's probably better to not override
|
||||||
*/
|
return target.compareTo(pathname);
|
||||||
|
}
|
||||||
// Should probably never allow creating a new .lnk
|
*/
|
||||||
// public boolean createNewFile() throws IOException
|
|
||||||
|
// Should probably never allow creating a new .lnk
|
||||||
// Deletes only the .lnk
|
// public boolean createNewFile() throws IOException
|
||||||
// public boolean delete() {
|
|
||||||
//public void deleteOnExit() {
|
// Deletes only the .lnk
|
||||||
|
// public boolean delete() {
|
||||||
@Override
|
//public void deleteOnExit() {
|
||||||
public boolean exists() {
|
|
||||||
return target.exists();
|
@Override
|
||||||
}
|
public boolean exists() {
|
||||||
|
return target.exists();
|
||||||
// A .lnk may be absolute
|
}
|
||||||
//public File getAbsoluteFile() {
|
|
||||||
//public String getAbsolutePath() {
|
// A .lnk may be absolute
|
||||||
|
//public File getAbsoluteFile() {
|
||||||
// Theses should be resolved according to the API (for Unix).
|
//public String getAbsolutePath() {
|
||||||
@Override
|
|
||||||
public File getCanonicalFile() throws IOException {
|
// Theses should be resolved according to the API (for Unix).
|
||||||
return target.getCanonicalFile();
|
@Override
|
||||||
}
|
public File getCanonicalFile() throws IOException {
|
||||||
|
return target.getCanonicalFile();
|
||||||
@Override
|
}
|
||||||
public String getCanonicalPath() throws IOException {
|
|
||||||
return target.getCanonicalPath();
|
@Override
|
||||||
}
|
public String getCanonicalPath() throws IOException {
|
||||||
|
return target.getCanonicalPath();
|
||||||
//public String getName() {
|
}
|
||||||
|
|
||||||
// I guess the parent should be the parent of the .lnk, not the target
|
//public String getName() {
|
||||||
//public String getParent() {
|
|
||||||
//public File getParentFile() {
|
// I guess the parent should be the parent of the .lnk, not the target
|
||||||
|
//public String getParent() {
|
||||||
// public boolean isAbsolute() {
|
//public File getParentFile() {
|
||||||
@Override
|
|
||||||
public boolean isFile() {
|
// public boolean isAbsolute() {
|
||||||
return target.isFile();
|
@Override
|
||||||
}
|
public boolean isFile() {
|
||||||
|
return target.isFile();
|
||||||
@Override
|
}
|
||||||
public boolean isHidden() {
|
|
||||||
return target.isHidden();
|
@Override
|
||||||
}
|
public boolean isHidden() {
|
||||||
|
return target.isHidden();
|
||||||
@Override
|
}
|
||||||
public long lastModified() {
|
|
||||||
return target.lastModified();
|
@Override
|
||||||
}
|
public long lastModified() {
|
||||||
|
return target.lastModified();
|
||||||
@Override
|
}
|
||||||
public long length() {
|
|
||||||
return target.length();
|
@Override
|
||||||
}
|
public long length() {
|
||||||
|
return target.length();
|
||||||
@Override
|
}
|
||||||
public String[] list() {
|
|
||||||
return target.list();
|
@Override
|
||||||
}
|
public String[] list() {
|
||||||
|
return target.list();
|
||||||
@Override
|
}
|
||||||
public String[] list(final FilenameFilter filter) {
|
|
||||||
return target.list(filter);
|
@Override
|
||||||
}
|
public String[] list(final FilenameFilter filter) {
|
||||||
|
return target.list(filter);
|
||||||
@Override
|
}
|
||||||
public File[] listFiles() {
|
|
||||||
return Win32File.wrap(target.listFiles());
|
@Override
|
||||||
}
|
public File[] listFiles() {
|
||||||
|
return Win32File.wrap(target.listFiles());
|
||||||
@Override
|
}
|
||||||
public File[] listFiles(final FileFilter filter) {
|
|
||||||
return Win32File.wrap(target.listFiles(filter));
|
@Override
|
||||||
}
|
public File[] listFiles(final FileFilter filter) {
|
||||||
|
return Win32File.wrap(target.listFiles(filter));
|
||||||
@Override
|
}
|
||||||
public File[] listFiles(final FilenameFilter filter) {
|
|
||||||
return Win32File.wrap(target.listFiles(filter));
|
@Override
|
||||||
}
|
public File[] listFiles(final FilenameFilter filter) {
|
||||||
|
return Win32File.wrap(target.listFiles(filter));
|
||||||
// Makes no sense, does it?
|
}
|
||||||
//public boolean mkdir() {
|
|
||||||
//public boolean mkdirs() {
|
// Makes no sense, does it?
|
||||||
|
//public boolean mkdir() {
|
||||||
// Only rename the lnk
|
//public boolean mkdirs() {
|
||||||
//public boolean renameTo(File dest) {
|
|
||||||
|
// Only rename the lnk
|
||||||
@Override
|
//public boolean renameTo(File dest) {
|
||||||
public boolean setLastModified(long time) {
|
|
||||||
return target.setLastModified(time);
|
@Override
|
||||||
}
|
public boolean setLastModified(long time) {
|
||||||
|
return target.setLastModified(time);
|
||||||
@Override
|
}
|
||||||
public boolean setReadOnly() {
|
|
||||||
return target.setReadOnly();
|
@Override
|
||||||
}
|
public boolean setReadOnly() {
|
||||||
|
return target.setReadOnly();
|
||||||
@Override
|
}
|
||||||
public String toString() {
|
|
||||||
if (target.equals(this)) {
|
@Override
|
||||||
return super.toString();
|
public String toString() {
|
||||||
}
|
if (target.equals(this)) {
|
||||||
return super.toString() + " -> " + target.toString();
|
return super.toString();
|
||||||
}
|
}
|
||||||
}
|
return super.toString() + " -> " + target.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,239 +1,240 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.DateUtil;
|
import com.twelvemonkeys.lang.DateUtil;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a {@code Writer} in an {@code OutputStream}.
|
* Wraps a {@code Writer} in an {@code OutputStream}.
|
||||||
* <p/>
|
* <p>
|
||||||
* <em>Instances of this class are not thread-safe.</em>
|
* <em>Instances of this class are not thread-safe.</em>
|
||||||
* <p/>
|
* </p>
|
||||||
* <em>NOTE: This class is probably not the right way of solving your problem,
|
* <p>
|
||||||
* however it might prove useful in JSPs etc.
|
* <em>NOTE: This class is probably not the right way of solving your problem,
|
||||||
* If possible, it's always better to use the {@code Writer}'s underlying
|
* however it might prove useful in JSPs etc.
|
||||||
* {@code OutputStream}, or wrap it's native backing.
|
* If possible, it's always better to use the {@code Writer}'s underlying
|
||||||
* </em>
|
* {@code OutputStream}, or wrap it's native backing.
|
||||||
* <p/>
|
* </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/io/WriterOutputStream.java#2 $
|
* @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/WriterOutputStream.java#2 $
|
||||||
public class WriterOutputStream extends OutputStream {
|
*/
|
||||||
protected Writer writer;
|
public class WriterOutputStream extends OutputStream {
|
||||||
final protected Decoder decoder;
|
protected Writer writer;
|
||||||
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
|
final protected Decoder decoder;
|
||||||
|
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
|
||||||
private volatile boolean isFlushing = false; // Ugly but critical...
|
|
||||||
|
private volatile boolean isFlushing = false; // Ugly but critical...
|
||||||
private static final boolean NIO_AVAILABLE = isNIOAvailable();
|
|
||||||
|
private static final boolean NIO_AVAILABLE = isNIOAvailable();
|
||||||
private static boolean isNIOAvailable() {
|
|
||||||
try {
|
private static boolean isNIOAvailable() {
|
||||||
Class.forName("java.nio.charset.Charset");
|
try {
|
||||||
return true;
|
Class.forName("java.nio.charset.Charset");
|
||||||
}
|
return true;
|
||||||
catch (Throwable t) {
|
}
|
||||||
// Ignore
|
catch (Throwable t) {
|
||||||
}
|
// Ignore
|
||||||
|
}
|
||||||
return false;
|
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
public WriterOutputStream(final Writer pWriter, final String pCharset) {
|
|
||||||
writer = pWriter;
|
public WriterOutputStream(final Writer pWriter, final String pCharset) {
|
||||||
decoder = getDecoder(pCharset);
|
writer = pWriter;
|
||||||
}
|
decoder = getDecoder(pCharset);
|
||||||
|
}
|
||||||
public WriterOutputStream(final Writer pWriter) {
|
|
||||||
this(pWriter, null);
|
public WriterOutputStream(final Writer pWriter) {
|
||||||
}
|
this(pWriter, null);
|
||||||
|
}
|
||||||
private static Decoder getDecoder(final String pCharset) {
|
|
||||||
// NOTE: The CharsetDecoder is typically 10-20% faster than
|
private static Decoder getDecoder(final String pCharset) {
|
||||||
// StringDecoder according to my tests
|
// NOTE: The CharsetDecoder is typically 10-20% faster than
|
||||||
// StringEncoder is horribly slow on 1.2 systems, but there's no
|
// StringDecoder according to my tests
|
||||||
// alternative...
|
// StringEncoder is horribly slow on 1.2 systems, but there's no
|
||||||
if (NIO_AVAILABLE) {
|
// alternative...
|
||||||
return new CharsetDecoder(pCharset);
|
if (NIO_AVAILABLE) {
|
||||||
}
|
return new CharsetDecoder(pCharset);
|
||||||
|
}
|
||||||
return new StringDecoder(pCharset);
|
|
||||||
}
|
return new StringDecoder(pCharset);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
@Override
|
||||||
flush();
|
public void close() throws IOException {
|
||||||
writer.close();
|
flush();
|
||||||
writer = null;
|
writer.close();
|
||||||
}
|
writer = null;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void flush() throws IOException {
|
@Override
|
||||||
flushBuffer();
|
public void flush() throws IOException {
|
||||||
writer.flush();
|
flushBuffer();
|
||||||
}
|
writer.flush();
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public final void write(byte[] pBytes) throws IOException {
|
@Override
|
||||||
if (pBytes == null) {
|
public final void write(byte[] pBytes) throws IOException {
|
||||||
throw new NullPointerException("bytes == null");
|
if (pBytes == null) {
|
||||||
}
|
throw new NullPointerException("bytes == null");
|
||||||
write(pBytes, 0, pBytes.length);
|
}
|
||||||
}
|
write(pBytes, 0, pBytes.length);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
@Override
|
||||||
flushBuffer();
|
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||||
decoder.decodeTo(writer, pBytes, pOffset, pLength);
|
flushBuffer();
|
||||||
}
|
decoder.decodeTo(writer, pBytes, pOffset, pLength);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public final void write(int pByte) {
|
@Override
|
||||||
// TODO: Is it possible to know if this is a good place in the stream to
|
public final void write(int pByte) {
|
||||||
// flush? It might be in the middle of a multi-byte encoded character..
|
// TODO: Is it possible to know if this is a good place in the stream to
|
||||||
bufferStream.write(pByte);
|
// flush? It might be in the middle of a multi-byte encoded character..
|
||||||
}
|
bufferStream.write(pByte);
|
||||||
|
}
|
||||||
private void flushBuffer() throws IOException {
|
|
||||||
if (!isFlushing && bufferStream.size() > 0) {
|
private void flushBuffer() throws IOException {
|
||||||
isFlushing = true;
|
if (!isFlushing && bufferStream.size() > 0) {
|
||||||
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
|
isFlushing = true;
|
||||||
bufferStream.reset();
|
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
|
||||||
isFlushing = false;
|
bufferStream.reset();
|
||||||
}
|
isFlushing = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
public static void main(String[] pArgs) throws IOException {
|
///////////////////////////////////////////////////////////////////////////
|
||||||
int iterations = 1000000;
|
public static void main(String[] pArgs) throws IOException {
|
||||||
|
int iterations = 1000000;
|
||||||
byte[] bytes = "������ klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
|
|
||||||
|
byte[] bytes = "������ klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
|
||||||
Decoder d;
|
|
||||||
long start;
|
Decoder d;
|
||||||
long time;
|
long start;
|
||||||
Writer sink = new PrintWriter(new NullOutputStream());
|
long time;
|
||||||
StringWriter writer;
|
Writer sink = new PrintWriter(new NullOutputStream());
|
||||||
String str;
|
StringWriter writer;
|
||||||
|
String str;
|
||||||
d = new StringDecoder("UTF-8");
|
|
||||||
for (int i = 0; i < 10000; i++) {
|
d = new StringDecoder("UTF-8");
|
||||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
for (int i = 0; i < 10000; i++) {
|
||||||
}
|
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||||
start = System.currentTimeMillis();
|
}
|
||||||
for (int i = 0; i < iterations; i++) {
|
start = System.currentTimeMillis();
|
||||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
for (int i = 0; i < iterations; i++) {
|
||||||
}
|
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||||
time = DateUtil.delta(start);
|
}
|
||||||
System.out.println("StringDecoder");
|
time = DateUtil.delta(start);
|
||||||
System.out.println("time: " + time);
|
System.out.println("StringDecoder");
|
||||||
|
System.out.println("time: " + time);
|
||||||
writer = new StringWriter();
|
|
||||||
d.decodeTo(writer, bytes, 0, bytes.length);
|
writer = new StringWriter();
|
||||||
str = writer.toString();
|
d.decodeTo(writer, bytes, 0, bytes.length);
|
||||||
System.out.println("str: \"" + str + "\"");
|
str = writer.toString();
|
||||||
System.out.println("chars.length: " + str.length());
|
System.out.println("str: \"" + str + "\"");
|
||||||
System.out.println();
|
System.out.println("chars.length: " + str.length());
|
||||||
|
System.out.println();
|
||||||
if (NIO_AVAILABLE) {
|
|
||||||
d = new CharsetDecoder("UTF-8");
|
if (NIO_AVAILABLE) {
|
||||||
for (int i = 0; i < 10000; i++) {
|
d = new CharsetDecoder("UTF-8");
|
||||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
for (int i = 0; i < 10000; i++) {
|
||||||
}
|
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||||
start = System.currentTimeMillis();
|
}
|
||||||
for (int i = 0; i < iterations; i++) {
|
start = System.currentTimeMillis();
|
||||||
d.decodeTo(sink, bytes, 0, bytes.length);
|
for (int i = 0; i < iterations; i++) {
|
||||||
}
|
d.decodeTo(sink, bytes, 0, bytes.length);
|
||||||
time = DateUtil.delta(start);
|
}
|
||||||
System.out.println("CharsetDecoder");
|
time = DateUtil.delta(start);
|
||||||
System.out.println("time: " + time);
|
System.out.println("CharsetDecoder");
|
||||||
writer = new StringWriter();
|
System.out.println("time: " + time);
|
||||||
d.decodeTo(writer, bytes, 0, bytes.length);
|
writer = new StringWriter();
|
||||||
str = writer.toString();
|
d.decodeTo(writer, bytes, 0, bytes.length);
|
||||||
System.out.println("str: \"" + str + "\"");
|
str = writer.toString();
|
||||||
System.out.println("chars.length: " + str.length());
|
System.out.println("str: \"" + str + "\"");
|
||||||
System.out.println();
|
System.out.println("chars.length: " + str.length());
|
||||||
}
|
System.out.println();
|
||||||
|
}
|
||||||
OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
|
|
||||||
os.write(bytes);
|
OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
|
||||||
os.flush();
|
os.write(bytes);
|
||||||
System.out.println();
|
os.flush();
|
||||||
|
System.out.println();
|
||||||
for (byte b : bytes) {
|
|
||||||
os.write(b & 0xff);
|
for (byte b : bytes) {
|
||||||
}
|
os.write(b & 0xff);
|
||||||
os.flush();
|
}
|
||||||
}
|
os.flush();
|
||||||
|
}
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
private static interface Decoder {
|
///////////////////////////////////////////////////////////////////////////
|
||||||
void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException;
|
private static interface Decoder {
|
||||||
}
|
void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException;
|
||||||
|
}
|
||||||
private static final class CharsetDecoder implements Decoder {
|
|
||||||
final Charset mCharset;
|
private static final class CharsetDecoder implements Decoder {
|
||||||
|
final Charset mCharset;
|
||||||
CharsetDecoder(String pCharset) {
|
|
||||||
// Handle null-case, to get default charset
|
CharsetDecoder(String pCharset) {
|
||||||
String charset = pCharset != null ? pCharset :
|
// Handle null-case, to get default charset
|
||||||
System.getProperty("file.encoding", "ISO-8859-1");
|
String charset = pCharset != null ? pCharset :
|
||||||
mCharset = Charset.forName(charset);
|
System.getProperty("file.encoding", "ISO-8859-1");
|
||||||
}
|
mCharset = Charset.forName(charset);
|
||||||
|
}
|
||||||
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
|
|
||||||
CharBuffer cb = mCharset.decode(ByteBuffer.wrap(pBytes, pOffset, pLength));
|
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||||
pWriter.write(cb.array(), 0, cb.length());
|
CharBuffer cb = mCharset.decode(ByteBuffer.wrap(pBytes, pOffset, pLength));
|
||||||
}
|
pWriter.write(cb.array(), 0, cb.length());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static final class StringDecoder implements Decoder {
|
|
||||||
final String mCharset;
|
private static final class StringDecoder implements Decoder {
|
||||||
|
final String mCharset;
|
||||||
StringDecoder(String pCharset) {
|
|
||||||
mCharset = pCharset;
|
StringDecoder(String pCharset) {
|
||||||
}
|
mCharset = pCharset;
|
||||||
|
}
|
||||||
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
|
|
||||||
String str = mCharset == null ?
|
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||||
new String(pBytes, pOffset, pLength) :
|
String str = mCharset == null ?
|
||||||
new String(pBytes, pOffset, pLength, mCharset);
|
new String(pBytes, pOffset, pLength) :
|
||||||
|
new String(pBytes, pOffset, pLength, mCharset);
|
||||||
pWriter.write(str);
|
|
||||||
}
|
pWriter.write(str);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,188 +1,188 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code Decoder} implementation for standard base64 encoding.
|
* {@code Decoder} implementation for standard base64 encoding.
|
||||||
* <p/>
|
*
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a>
|
* @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
|
||||||
*
|
*
|
||||||
* @see Base64Encoder
|
* @see Base64Encoder
|
||||||
*
|
*
|
||||||
* @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/enc/Base64Decoder.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java#2 $
|
||||||
*/
|
*/
|
||||||
public final class Base64Decoder implements Decoder {
|
public final class Base64Decoder implements Decoder {
|
||||||
/**
|
/**
|
||||||
* This array maps the characters to their 6 bit values
|
* This array maps the characters to their 6 bit values
|
||||||
*/
|
*/
|
||||||
final static byte[] PEM_ARRAY = {
|
final static byte[] PEM_ARRAY = {
|
||||||
//0 1 2 3 4 5 6 7
|
//0 1 2 3 4 5 6 7
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
|
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
|
||||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
|
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
|
||||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
|
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
|
||||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
|
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
|
||||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
|
||||||
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
|
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
|
||||||
'4', '5', '6', '7', '8', '9', '+', '/' // 7
|
'4', '5', '6', '7', '8', '9', '+', '/' // 7
|
||||||
};
|
};
|
||||||
|
|
||||||
final static byte[] PEM_CONVERT_ARRAY;
|
final static byte[] PEM_CONVERT_ARRAY;
|
||||||
|
|
||||||
private byte[] decodeBuffer = new byte[4];
|
private byte[] decodeBuffer = new byte[4];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PEM_CONVERT_ARRAY = new byte[256];
|
PEM_CONVERT_ARRAY = new byte[256];
|
||||||
|
|
||||||
for (int i = 0; i < 255; i++) {
|
for (int i = 0; i < 255; i++) {
|
||||||
PEM_CONVERT_ARRAY[i] = -1;
|
PEM_CONVERT_ARRAY[i] = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < PEM_ARRAY.length; i++) {
|
for (int i = 0; i < PEM_ARRAY.length; i++) {
|
||||||
PEM_CONVERT_ARRAY[PEM_ARRAY[i]] = (byte) i;
|
PEM_CONVERT_ARRAY[PEM_ARRAY[i]] = (byte) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static int readFully(final InputStream pStream, final byte pBytes[], final int pOffset, final int pLength)
|
protected static int readFully(final InputStream pStream, final byte pBytes[], final int pOffset, final int pLength)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
for (int i = 0; i < pLength; i++) {
|
for (int i = 0; i < pLength; i++) {
|
||||||
int read = pStream.read();
|
int read = pStream.read();
|
||||||
|
|
||||||
if (read == -1) {
|
if (read == -1) {
|
||||||
return i != 0 ? i : -1;
|
return i != 0 ? i : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pBytes[i + pOffset] = (byte) read;
|
pBytes[i + pOffset] = (byte) read;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pLength;
|
return pLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
|
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
byte byte0 = -1;
|
byte byte0 = -1;
|
||||||
byte byte1 = -1;
|
byte byte1 = -1;
|
||||||
byte byte2 = -1;
|
byte byte2 = -1;
|
||||||
byte byte3 = -1;
|
byte byte3 = -1;
|
||||||
|
|
||||||
if (pLength < 2) {
|
if (pLength < 2) {
|
||||||
throw new IOException("BASE64Decoder: Not enough bytes for an atom.");
|
throw new IOException("BASE64Decoder: Not enough bytes for an atom.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
// Skip line feeds
|
// Skip line feeds
|
||||||
do {
|
do {
|
||||||
read = pInput.read();
|
read = pInput.read();
|
||||||
|
|
||||||
if (read == -1) {
|
if (read == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} while (read == 10 || read == 13);
|
} while (read == 10 || read == 13);
|
||||||
|
|
||||||
decodeBuffer[0] = (byte) read;
|
decodeBuffer[0] = (byte) read;
|
||||||
read = readFully(pInput, decodeBuffer, 1, pLength - 1);
|
read = readFully(pInput, decodeBuffer, 1, pLength - 1);
|
||||||
|
|
||||||
if (read == -1) {
|
if (read == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = pLength;
|
int length = pLength;
|
||||||
|
|
||||||
if (length > 3 && decodeBuffer[3] == 61) {
|
if (length > 3 && decodeBuffer[3] == 61) {
|
||||||
length = 3;
|
length = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > 2 && decodeBuffer[2] == 61) {
|
if (length > 2 && decodeBuffer[2] == 61) {
|
||||||
length = 2;
|
length = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 4:
|
case 4:
|
||||||
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
|
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
|
||||||
// fall through
|
// fall through
|
||||||
case 3:
|
case 3:
|
||||||
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
|
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
|
||||||
// fall through
|
// fall through
|
||||||
case 2:
|
case 2:
|
||||||
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
|
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
|
||||||
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
|
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
|
||||||
// fall through
|
// fall through
|
||||||
default:
|
default:
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 2:
|
case 2:
|
||||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||||
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||||
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||||
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
|
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||||
do {
|
do {
|
||||||
int k = 72;
|
int k = 72;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i + 4 < k; i += 4) {
|
for (i = 0; i + 4 < k; i += 4) {
|
||||||
if(!decodeAtom(stream, buffer, 4)) {
|
if(!decodeAtom(stream, buffer, 4)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decodeAtom(stream, buffer, k - i)) {
|
if (!decodeAtom(stream, buffer, k - i)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
|
while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
|
||||||
|
|
||||||
return buffer.position();
|
return buffer.position();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +1,106 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code Encoder} implementation for standard base64 encoding.
|
* {@code Encoder} implementation for standard base64 encoding.
|
||||||
* <p/>
|
*
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a>
|
* @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
|
||||||
*
|
*
|
||||||
* @see Base64Decoder
|
* @see Base64Decoder
|
||||||
*
|
*
|
||||||
* @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/enc/Base64Encoder.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java#2 $
|
||||||
*/
|
*/
|
||||||
public class Base64Encoder implements Encoder {
|
public class Base64Encoder implements Encoder {
|
||||||
|
|
||||||
public void encode(final OutputStream stream, final ByteBuffer buffer)
|
public void encode(final OutputStream stream, final ByteBuffer buffer)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
// NOTE: This is impossible, given the current spec, as we need to either:
|
// NOTE: This is impossible, given the current spec, as we need to either:
|
||||||
// - buffer all data in the EncoderStream
|
// - buffer all data in the EncoderStream
|
||||||
// - or have flush/end method(s) in the Encoder
|
// - or have flush/end method(s) in the Encoder
|
||||||
// to ensure proper end of stream handling
|
// to ensure proper end of stream handling
|
||||||
|
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
// TODO: Temp impl, will only work for single writes
|
// TODO: Temp impl, will only work for single writes
|
||||||
while (buffer.hasRemaining()) {
|
while (buffer.hasRemaining()) {
|
||||||
byte a, b, c;
|
byte a, b, c;
|
||||||
|
|
||||||
// if ((buffer.remaining()) > 2) {
|
// if ((buffer.remaining()) > 2) {
|
||||||
// length = 3;
|
// length = 3;
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// length = buffer.remaining();
|
// length = buffer.remaining();
|
||||||
// }
|
// }
|
||||||
length = Math.min(3, buffer.remaining());
|
length = Math.min(3, buffer.remaining());
|
||||||
|
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 1:
|
case 1:
|
||||||
a = buffer.get();
|
a = buffer.get();
|
||||||
b = 0;
|
b = 0;
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
||||||
stream.write('=');
|
stream.write('=');
|
||||||
stream.write('=');
|
stream.write('=');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
a = buffer.get();
|
a = buffer.get();
|
||||||
b = buffer.get();
|
b = buffer.get();
|
||||||
c = 0;
|
c = 0;
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
|
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
|
||||||
stream.write('=');
|
stream.write('=');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
a = buffer.get();
|
a = buffer.get();
|
||||||
b = buffer.get();
|
b = buffer.get();
|
||||||
c = buffer.get();
|
c = buffer.get();
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
|
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
|
||||||
stream.write(Base64Decoder.PEM_ARRAY[c & 0x3F]);
|
stream.write(Base64Decoder.PEM_ARRAY[c & 0x3F]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,55 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown by {@code Decoder}s when encoded data can not be decoded.
|
* Thrown by {@code Decoder}s when encoded data can not be decoded.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/enc/DecodeException.java#2 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java#2 $
|
*/
|
||||||
*/
|
public class DecodeException extends IOException {
|
||||||
public class DecodeException extends IOException {
|
|
||||||
|
public DecodeException(final String pMessage) {
|
||||||
public DecodeException(final String pMessage) {
|
super(pMessage);
|
||||||
super(pMessage);
|
}
|
||||||
}
|
|
||||||
|
public DecodeException(final String pMessage, final Throwable pCause) {
|
||||||
public DecodeException(final String pMessage, final Throwable pCause) {
|
super(pMessage);
|
||||||
super(pMessage);
|
initCause(pCause);
|
||||||
initCause(pCause);
|
}
|
||||||
}
|
|
||||||
|
public DecodeException(final Throwable pCause) {
|
||||||
public DecodeException(final Throwable pCause) {
|
this(pCause.getMessage(), pCause);
|
||||||
this(pCause.getMessage(), pCause);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,68 +1,69 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for decoders.
|
* Interface for decoders.
|
||||||
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
|
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
|
||||||
* on-the-fly decoding from an {@code InputStream}.
|
* on-the-fly decoding from an {@code InputStream}.
|
||||||
* <p/>
|
* <p>
|
||||||
* Important note: Decoder implementations are typically not synchronized.
|
* Important note: Decoder implementations are typically not synchronized.
|
||||||
* <p/>
|
* </p>
|
||||||
* @see Encoder
|
*
|
||||||
* @see DecoderStream
|
* @see Encoder
|
||||||
*
|
* @see DecoderStream
|
||||||
* @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/enc/Decoder.java#2 $
|
* @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/enc/Decoder.java#2 $
|
||||||
public interface Decoder {
|
*/
|
||||||
|
public interface Decoder {
|
||||||
/**
|
|
||||||
* Decodes up to {@code buffer.length} bytes from the given input stream,
|
/**
|
||||||
* into the given buffer.
|
* Decodes up to {@code buffer.length} bytes from the given input stream,
|
||||||
*
|
* into the given buffer.
|
||||||
* @param stream the input stream to decode data from
|
*
|
||||||
* @param buffer buffer to store the read data
|
* @param stream the input stream to decode data from
|
||||||
*
|
* @param buffer buffer to store the read data
|
||||||
* @return the total number of bytes read into the buffer, or {@code 0}
|
*
|
||||||
* if there is no more data because the end of the stream has been reached.
|
* @return the total number of bytes read into the buffer, or {@code 0}
|
||||||
*
|
* if there is no more data because the end of the stream has been reached.
|
||||||
* @throws DecodeException if encoded data is corrupt.
|
*
|
||||||
* @throws IOException if an I/O error occurs.
|
* @throws DecodeException if encoded data is corrupt.
|
||||||
* @throws java.io.EOFException if a premature end-of-file is encountered.
|
* @throws IOException if an I/O error occurs.
|
||||||
* @throws java.lang.NullPointerException if either argument is {@code null}.
|
* @throws java.io.EOFException if a premature end-of-file is encountered.
|
||||||
*/
|
* @throws java.lang.NullPointerException if either argument is {@code null}.
|
||||||
int decode(InputStream stream, ByteBuffer buffer) throws IOException;
|
*/
|
||||||
}
|
int decode(InputStream stream, ByteBuffer buffer) throws IOException;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,200 +1,199 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code InputStream} that provides on-the-fly decoding from an underlying
|
* An {@code InputStream} that provides on-the-fly decoding from an underlying stream.
|
||||||
* stream.
|
*
|
||||||
* <p/>
|
* @see EncoderStream
|
||||||
* @see EncoderStream
|
* @see Decoder
|
||||||
* @see Decoder
|
*
|
||||||
*
|
* @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/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();
|
}
|
||||||
}
|
|
||||||
|
public int available() throws IOException {
|
||||||
public int available() throws IOException {
|
return buffer.remaining();
|
||||||
return buffer.remaining();
|
}
|
||||||
}
|
|
||||||
|
public int read() throws IOException {
|
||||||
public int read() throws IOException {
|
if (!buffer.hasRemaining()) {
|
||||||
if (!buffer.hasRemaining()) {
|
if (fill() < 0) {
|
||||||
if (fill() < 0) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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;
|
}
|
||||||
}
|
|
||||||
|
// End of file?
|
||||||
// End of file?
|
if (!buffer.hasRemaining()) {
|
||||||
if (!buffer.hasRemaining()) {
|
if (fill() < 0) {
|
||||||
if (fill() < 0) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 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;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 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;
|
|
||||||
|
// Increase count
|
||||||
// Increase count
|
count += dstLen;
|
||||||
count += dstLen;
|
}
|
||||||
}
|
|
||||||
|
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) {
|
return 0; // Yes, 0, not -1
|
||||||
return 0; // Yes, 0, not -1
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 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;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 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;
|
}
|
||||||
}
|
|
||||||
|
return total;
|
||||||
return total;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Fills the buffer, by decoding data from the underlying input stream.
|
||||||
* Fills the buffer, by decoding data from the underlying input stream.
|
*
|
||||||
*
|
* @return the number of bytes decoded, or {@code -1} if the end of the
|
||||||
* @return the number of bytes decoded, or {@code -1} if the end of the
|
* file is reached
|
||||||
* file is reached
|
*
|
||||||
*
|
* @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);
|
|
||||||
|
// TODO: Enforce this in test case, leave here to aid debugging
|
||||||
// TODO: Enforce this in test case, leave here to aid debugging
|
if (read > buffer.capacity()) {
|
||||||
if (read > buffer.capacity()) {
|
throw new AssertionError(
|
||||||
throw new AssertionError(
|
String.format(
|
||||||
String.format(
|
"Decode beyond buffer (%d): %d (using %s decoder)",
|
||||||
"Decode beyond buffer (%d): %d (using %s decoder)",
|
buffer.capacity(), read, decoder.getClass().getName()
|
||||||
buffer.capacity(), read, decoder.getClass().getName()
|
)
|
||||||
)
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
buffer.flip();
|
||||||
buffer.flip();
|
|
||||||
|
if (read == 0) {
|
||||||
if (read == 0) {
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
|
return read;
|
||||||
return read;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,65 +1,66 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for encoders.
|
* Interface for encoders.
|
||||||
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
|
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
|
||||||
* on-the-fly encoding to an {@code OutputStream}.
|
* on-the-fly encoding to an {@code OutputStream}.
|
||||||
* <p/>
|
* <p>
|
||||||
* Important note: Encoder implementations are typically not synchronized.
|
* Important note: Encoder implementations are typically not synchronized.
|
||||||
*
|
* </p>
|
||||||
* @see Decoder
|
*
|
||||||
* @see EncoderStream
|
* @see Decoder
|
||||||
*
|
* @see EncoderStream
|
||||||
* @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/enc/Encoder.java#2 $
|
* @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/enc/Encoder.java#2 $
|
||||||
public interface Encoder {
|
*/
|
||||||
|
public interface Encoder {
|
||||||
/**
|
|
||||||
* Encodes up to {@code buffer.remaining()} bytes into the given input stream,
|
/**
|
||||||
* from the given buffer.
|
* Encodes up to {@code buffer.remaining()} bytes into the given input stream,
|
||||||
*
|
* from the given buffer.
|
||||||
* @param stream the output stream to encode data to
|
*
|
||||||
* @param buffer buffer to read data from
|
* @param stream the output stream to encode data to
|
||||||
*
|
* @param buffer buffer to read data from
|
||||||
* @throws java.io.IOException if an I/O error occurs
|
*
|
||||||
*/
|
* @throws java.io.IOException if an I/O error occurs
|
||||||
void encode(OutputStream stream, ByteBuffer buffer) throws IOException;
|
*/
|
||||||
|
void encode(OutputStream stream, ByteBuffer buffer) throws IOException;
|
||||||
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
|
|
||||||
// void flush()?
|
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
|
||||||
}
|
// void flush()?
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,137 +1,134 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.FilterOutputStream;
|
import java.io.FilterOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code OutputStream} that provides on-the-fly encoding to an underlying
|
* An {@code OutputStream} that provides on-the-fly encoding to an underlying stream.
|
||||||
* stream.
|
*
|
||||||
* <p/>
|
* @see DecoderStream
|
||||||
* @see DecoderStream
|
* @see Encoder
|
||||||
* @see Encoder
|
*
|
||||||
*
|
* @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/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;
|
||||||
|
private final boolean flushOnWrite;
|
||||||
protected final Encoder encoder;
|
|
||||||
private final boolean flushOnWrite;
|
private final ByteBuffer buffer;
|
||||||
|
|
||||||
protected final ByteBuffer buffer;
|
/**
|
||||||
|
* Creates an output stream filter built on top of the specified
|
||||||
/**
|
* underlying output stream.
|
||||||
* Creates an output stream filter built on top of the specified
|
*
|
||||||
* underlying output stream.
|
* @param stream the underlying output stream
|
||||||
*
|
* @param encoder the encoder to use
|
||||||
* @param pStream the underlying output stream
|
*/
|
||||||
* @param pEncoder the encoder to use
|
public EncoderStream(final OutputStream stream, final Encoder encoder) {
|
||||||
*/
|
this(stream, encoder, false);
|
||||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
|
}
|
||||||
this(pStream, pEncoder, false);
|
|
||||||
}
|
/**
|
||||||
|
* Creates an output stream filter built on top of the specified
|
||||||
/**
|
* underlying output stream.
|
||||||
* Creates an output stream filter built on top of the specified
|
*
|
||||||
* underlying output stream.
|
* @param stream the underlying output stream
|
||||||
*
|
* @param encoder the encoder to use
|
||||||
* @param pStream the underlying output stream
|
* @param flushOnWrite if {@code true}, calls to the byte-array
|
||||||
* @param pEncoder the encoder to use
|
* {@code write} methods will automatically flush the buffer.
|
||||||
* @param pFlushOnWrite if {@code true}, calls to the byte-array
|
*/
|
||||||
* {@code write} methods will automatically flush the buffer.
|
public EncoderStream(final OutputStream stream, final Encoder encoder, final boolean flushOnWrite) {
|
||||||
*/
|
super(stream);
|
||||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
|
||||||
super(pStream);
|
this.encoder = encoder;
|
||||||
|
this.flushOnWrite = flushOnWrite;
|
||||||
encoder = pEncoder;
|
|
||||||
flushOnWrite = pFlushOnWrite;
|
buffer = ByteBuffer.allocate(1024);
|
||||||
|
}
|
||||||
buffer = ByteBuffer.allocate(1024);
|
|
||||||
buffer.flip();
|
public void close() throws IOException {
|
||||||
}
|
flush();
|
||||||
|
super.close();
|
||||||
public void close() throws IOException {
|
}
|
||||||
flush();
|
|
||||||
super.close();
|
public void flush() throws IOException {
|
||||||
}
|
encodeBuffer();
|
||||||
|
super.flush();
|
||||||
public void flush() throws IOException {
|
}
|
||||||
encodeBuffer();
|
|
||||||
super.flush();
|
private void encodeBuffer() throws IOException {
|
||||||
}
|
if (buffer.position() != 0) {
|
||||||
|
buffer.flip();
|
||||||
private void encodeBuffer() throws IOException {
|
|
||||||
if (buffer.position() != 0) {
|
// Make sure all remaining data in buffer is written to the stream
|
||||||
buffer.flip();
|
encoder.encode(out, buffer);
|
||||||
|
|
||||||
// Make sure all remaining data in buffer is written to the stream
|
// Reset buffer
|
||||||
encoder.encode(out, buffer);
|
buffer.clear();
|
||||||
|
}
|
||||||
// Reset buffer
|
}
|
||||||
buffer.clear();
|
|
||||||
}
|
public void write(final byte[] bytes) throws IOException {
|
||||||
}
|
write(bytes, 0, bytes.length);
|
||||||
|
}
|
||||||
public final void write(final byte[] pBytes) throws IOException {
|
|
||||||
write(pBytes, 0, pBytes.length);
|
// 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
|
||||||
|
// that the encoder can't buffer. In that case, the encoder should probably
|
||||||
// TODO: Verify that this works for the general case (it probably won't)...
|
// tell the EncoderStream how large buffer it prefers...
|
||||||
// TODO: We might need a way to explicitly flush the encoder, or specify
|
public void write(final byte[] values, final int offset, final int length) throws IOException {
|
||||||
// that the encoder can't buffer. In that case, the encoder should probably
|
if (!flushOnWrite && length < buffer.remaining()) {
|
||||||
// tell the EncoderStream how large buffer it prefers...
|
// Buffer data
|
||||||
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
buffer.put(values, offset, length);
|
||||||
if (!flushOnWrite && pLength < buffer.remaining()) {
|
}
|
||||||
// Buffer data
|
else {
|
||||||
buffer.put(pBytes, pOffset, pLength);
|
// Encode data already in the buffer
|
||||||
}
|
encodeBuffer();
|
||||||
else {
|
|
||||||
// Encode data already in the buffer
|
// Encode rest without buffering
|
||||||
encodeBuffer();
|
encoder.encode(out, ByteBuffer.wrap(values, offset, length));
|
||||||
|
}
|
||||||
// Encode rest without buffering
|
}
|
||||||
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
|
|
||||||
}
|
public void write(final int value) throws IOException {
|
||||||
}
|
if (!buffer.hasRemaining()) {
|
||||||
|
encodeBuffer(); // Resets bufferPos to 0
|
||||||
public void write(final int pByte) throws IOException {
|
}
|
||||||
if (!buffer.hasRemaining()) {
|
|
||||||
encodeBuffer(); // Resets bufferPos to 0
|
buffer.put((byte) value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
buffer.put((byte) pByte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,210 +1,194 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
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 java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decoder implementation for Apple PackBits run-length encoding.
|
* Decoder implementation for Apple PackBits run-length encoding.
|
||||||
* <p/>
|
* <p>
|
||||||
* <small>From Wikipedia, the free encyclopedia</small><br/>
|
* <small>From Wikipedia, the free encyclopedia</small>
|
||||||
* PackBits is a fast, simple compression scheme for run-length encoding of
|
* <br>
|
||||||
* data.
|
* PackBits is a fast, simple compression scheme for run-length encoding of
|
||||||
* <p/>
|
* data.
|
||||||
* Apple introduced the PackBits format with the release of MacPaint on the
|
* </p>
|
||||||
* Macintosh computer. This compression scheme is one of the types of
|
* <p>
|
||||||
* compression that can be used in TIFF-files.
|
* Apple introduced the PackBits format with the release of MacPaint on the
|
||||||
* <p/>
|
* Macintosh computer. This compression scheme is one of the types of
|
||||||
* A PackBits data stream consists of packets of one byte of header followed by
|
* compression that can be used in TIFF-files.
|
||||||
* data. The header is a signed byte; the data can be signed, unsigned, or
|
* </p>
|
||||||
* packed (such as MacPaint pixels).
|
* <p>
|
||||||
* <p/>
|
* A PackBits data stream consists of packets of one byte of header followed by
|
||||||
* <table><tr><th>Header byte</th><th>Data</th></tr>
|
* data. The header is a signed byte; the data can be signed, unsigned, or
|
||||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
* packed (such as MacPaint pixels).
|
||||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
|
* </p>
|
||||||
* the decompressed output</td></tr>
|
* <table>
|
||||||
* <tr><td>-128</td> <td>No operation</td></tr></table>
|
* <caption>PackBits</caption>
|
||||||
* <p/>
|
* <tr><th>Header byte</th><th>Data</th></tr>
|
||||||
* Note that interpreting 0 as positive or negative makes no difference in the
|
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||||
* output. Runs of two bytes adjacent to non-runs are typically written as
|
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
|
||||||
* literal data.
|
* <tr><td>-128</td> <td>No operation</td></tr>
|
||||||
* <p/>
|
* </table>
|
||||||
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
* <p>
|
||||||
*
|
* Note that interpreting 0 as positive or negative makes no difference in the
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* output. Runs of two bytes adjacent to non-runs are typically written as
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
|
* literal data.
|
||||||
*/
|
* </p>
|
||||||
public final class PackBitsDecoder implements Decoder {
|
*
|
||||||
// TODO: Look at ICNSImageReader#unpackbits... What is this weirdness?
|
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
||||||
|
*
|
||||||
private final boolean disableNoOp;
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
private final byte[] sample;
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
|
||||||
|
*/
|
||||||
private int leftOfRun;
|
public final class PackBitsDecoder implements Decoder {
|
||||||
private boolean splitRun;
|
// TODO: Look at ICNSImageReader#unpackbits... What is this weirdness?
|
||||||
private boolean reachedEOF;
|
|
||||||
|
private final boolean disableNoOp;
|
||||||
/** Creates a {@code PackBitsDecoder}. */
|
private final byte[] sample;
|
||||||
public PackBitsDecoder() {
|
|
||||||
this(1, false);
|
private boolean reachedEOF;
|
||||||
}
|
|
||||||
|
/** Creates a {@code PackBitsDecoder}. */
|
||||||
/**
|
public PackBitsDecoder() {
|
||||||
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
this(1, false);
|
||||||
* <p/>
|
}
|
||||||
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
|
||||||
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
/**
|
||||||
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
||||||
*
|
* <p>
|
||||||
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
||||||
*/
|
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
||||||
public PackBitsDecoder(final boolean disableNoOp) {
|
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
||||||
this(1, disableNoOp);
|
* </p>
|
||||||
}
|
*
|
||||||
|
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
||||||
/**
|
*/
|
||||||
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
public PackBitsDecoder(final boolean disableNoOp) {
|
||||||
* <p/>
|
this(1, disableNoOp);
|
||||||
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
}
|
||||||
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
|
||||||
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
/**
|
||||||
*
|
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
||||||
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
* <p>
|
||||||
*/
|
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
||||||
public PackBitsDecoder(int sampleSize, final boolean disableNoOp) {
|
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
||||||
this.sample = new byte[sampleSize];
|
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
||||||
this.disableNoOp = disableNoOp;
|
* </p>
|
||||||
}
|
*
|
||||||
|
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
||||||
/**
|
*/
|
||||||
* Decodes bytes from the given input stream, to the given buffer.
|
public PackBitsDecoder(int sampleSize, final boolean disableNoOp) {
|
||||||
*
|
this.sample = new byte[sampleSize];
|
||||||
* @param stream the stream to decode from
|
this.disableNoOp = disableNoOp;
|
||||||
* @param buffer a byte array, minimum 128 (or 129 if no-op is disabled) bytes long
|
}
|
||||||
* @return The number of bytes decoded
|
|
||||||
*
|
/**
|
||||||
* @throws java.io.IOException
|
* Decodes bytes from the given input stream, to the given buffer.
|
||||||
*/
|
*
|
||||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
* @param stream the stream to decode from
|
||||||
if (reachedEOF) {
|
* @param buffer a byte array, minimum 128 (or 129 if no-op is disabled) bytes long
|
||||||
return -1;
|
* @return The number of bytes decoded
|
||||||
}
|
*
|
||||||
|
* @throws java.io.IOException if a problem occurs during decoding.
|
||||||
// TODO: Don't decode more than single runs, because some writers add pad bytes inside the stream...
|
*/
|
||||||
while (buffer.hasRemaining()) {
|
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||||
int n;
|
if (reachedEOF) {
|
||||||
|
return -1;
|
||||||
if (splitRun) {
|
}
|
||||||
// Continue run
|
|
||||||
n = leftOfRun;
|
// NOTE: We don't decode more than single runs, because some writers add pad bytes inside the stream...
|
||||||
splitRun = false;
|
// Start new run
|
||||||
}
|
int b = stream.read();
|
||||||
else {
|
if (b < 0) {
|
||||||
// Start new run
|
reachedEOF = true;
|
||||||
int b = stream.read();
|
return 0;
|
||||||
if (b < 0) {
|
}
|
||||||
reachedEOF = true;
|
|
||||||
break;
|
int n = (byte) b;
|
||||||
}
|
|
||||||
n = (byte) b;
|
try {
|
||||||
}
|
if (n >= 0) {
|
||||||
|
// Copy next n + 1 bytes literally
|
||||||
// Split run at or before max
|
readFully(stream, buffer, sample.length * (n + 1));
|
||||||
if (n >= 0 && n + 1 > buffer.remaining()) {
|
}
|
||||||
leftOfRun = n;
|
// Allow -128 for compatibility, see above
|
||||||
splitRun = true;
|
else if (disableNoOp || n != -128) {
|
||||||
break;
|
// Replicate the next byte -n + 1 times
|
||||||
}
|
for (int s = 0; s < sample.length; s++) {
|
||||||
else if (n < 0 && -n + 1 > buffer.remaining()) {
|
sample[s] = readByte(stream);
|
||||||
leftOfRun = n;
|
}
|
||||||
splitRun = true;
|
|
||||||
break;
|
for (int i = -n + 1; i > 0; i--) {
|
||||||
}
|
buffer.put(sample);
|
||||||
|
}
|
||||||
try {
|
}
|
||||||
if (n >= 0) {
|
// else NOOP (-128)
|
||||||
// Copy next n + 1 bytes literally
|
}
|
||||||
readFully(stream, buffer, sample.length * (n + 1));
|
catch (IndexOutOfBoundsException e) {
|
||||||
}
|
throw new DecodeException("Error in PackBits decompression, data seems corrupt", e);
|
||||||
// Allow -128 for compatibility, see above
|
}
|
||||||
else if (disableNoOp || n != -128) {
|
|
||||||
// Replicate the next byte -n + 1 times
|
return buffer.position();
|
||||||
for (int s = 0; s < sample.length; s++) {
|
}
|
||||||
sample[s] = readByte(stream);
|
|
||||||
}
|
static byte readByte(final InputStream pStream) throws IOException {
|
||||||
|
int read = pStream.read();
|
||||||
for (int i = -n + 1; i > 0; i--) {
|
|
||||||
buffer.put(sample);
|
if (read < 0) {
|
||||||
}
|
throw new EOFException("Unexpected end of PackBits stream");
|
||||||
}
|
}
|
||||||
// else NOOP (-128)
|
|
||||||
}
|
return (byte) read;
|
||||||
catch (IndexOutOfBoundsException e) {
|
}
|
||||||
throw new DecodeException("Error in PackBits decompression, data seems corrupt", e);
|
|
||||||
}
|
static void readFully(final InputStream pStream, final ByteBuffer pBuffer, final int pLength) throws IOException {
|
||||||
}
|
if (pLength < 0) {
|
||||||
|
throw new IndexOutOfBoundsException(String.format("Negative length: %d", pLength));
|
||||||
return buffer.position();
|
}
|
||||||
}
|
|
||||||
|
int total = 0;
|
||||||
static byte readByte(final InputStream pStream) throws IOException {
|
|
||||||
int read = pStream.read();
|
while (total < pLength) {
|
||||||
|
int count = pStream.read(pBuffer.array(), pBuffer.arrayOffset() + pBuffer.position() + total, pLength - total);
|
||||||
if (read < 0) {
|
|
||||||
throw new EOFException("Unexpected end of PackBits stream");
|
if (count < 0) {
|
||||||
}
|
throw new EOFException("Unexpected end of PackBits stream");
|
||||||
|
}
|
||||||
return (byte) read;
|
|
||||||
}
|
total += count;
|
||||||
|
}
|
||||||
static void readFully(final InputStream pStream, final ByteBuffer pBuffer, final int pLength) throws IOException {
|
|
||||||
if (pLength < 0) {
|
pBuffer.position(pBuffer.position() + total);
|
||||||
throw new IndexOutOfBoundsException(String.format("Negative length: %d", pLength));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int total = 0;
|
|
||||||
|
|
||||||
while (total < pLength) {
|
|
||||||
int count = pStream.read(pBuffer.array(), pBuffer.arrayOffset() + pBuffer.position() + total, pLength - total);
|
|
||||||
|
|
||||||
if (count < 0) {
|
|
||||||
throw new EOFException("Unexpected end of PackBits stream");
|
|
||||||
}
|
|
||||||
|
|
||||||
total += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
pBuffer.position(pBuffer.position() + total);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,132 +1,138 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encoder implementation for Apple PackBits run-length encoding.
|
* Encoder implementation for Apple PackBits run-length encoding.
|
||||||
* <p/>
|
* <p>
|
||||||
* From Wikipedia, the free encyclopedia<br/>
|
* From Wikipedia, the free encyclopedia
|
||||||
* PackBits is a fast, simple compression scheme for run-length encoding of
|
* <br>
|
||||||
* data.
|
* PackBits is a fast, simple compression scheme for run-length encoding of
|
||||||
* <p/>
|
* data.
|
||||||
* Apple introduced the PackBits format with the release of MacPaint on the
|
* </p>
|
||||||
* Macintosh computer. This compression scheme is one of the types of
|
* <p>
|
||||||
* compression that can be used in TIFF-files.
|
* Apple introduced the PackBits format with the release of MacPaint on the
|
||||||
* <p/>
|
* Macintosh computer. This compression scheme is one of the types of
|
||||||
* A PackBits data stream consists of packets of one byte of header followed by
|
* compression that can be used in TIFF-files.
|
||||||
* data. The header is a signed byte; the data can be signed, unsigned, or
|
* </p>
|
||||||
* packed (such as MacPaint pixels).
|
* <p>
|
||||||
* <p/>
|
* A PackBits data stream consists of packets of one byte of header followed by
|
||||||
* <table><tr><th>Header byte</th><th>Data</th></tr>
|
* data. The header is a signed byte; the data can be signed, unsigned, or
|
||||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
* packed (such as MacPaint pixels).
|
||||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
|
* </p>
|
||||||
* the decompressed output</td></tr>
|
* <table>
|
||||||
* <tr><td>-128</td> <td>No operation</td></tr></table>
|
* <caption>PackBits</caption>
|
||||||
* <p/>
|
* <tr><th>Header byte</th><th>Data</th></tr>
|
||||||
* Note that interpreting 0 as positive or negative makes no difference in the
|
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||||
* output. Runs of two bytes adjacent to non-runs are typically written as
|
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
|
||||||
* literal data.
|
* <tr><td>-128</td> <td>No operation</td></tr>
|
||||||
* <p/>
|
* </table>
|
||||||
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
* <p>
|
||||||
*
|
* Note that interpreting 0 as positive or negative makes no difference in the
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* output. Runs of two bytes adjacent to non-runs are typically written as
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $
|
* literal data.
|
||||||
*/
|
* </p>
|
||||||
public final class PackBitsEncoder implements Encoder {
|
*
|
||||||
|
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
||||||
final private byte[] buffer = new byte[128];
|
*
|
||||||
|
* @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/enc/PackBitsEncoder.java#1 $
|
||||||
* Creates a {@code PackBitsEncoder}.
|
*/
|
||||||
*/
|
public final class PackBitsEncoder implements Encoder {
|
||||||
public PackBitsEncoder() {
|
|
||||||
}
|
final private byte[] buffer = new byte[128];
|
||||||
|
|
||||||
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
|
/**
|
||||||
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
* Creates a {@code PackBitsEncoder}.
|
||||||
buffer.position(buffer.remaining());
|
*/
|
||||||
}
|
public PackBitsEncoder() {
|
||||||
|
}
|
||||||
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
|
|
||||||
// NOTE: It's best to encode a 2 byte repeat
|
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
|
||||||
// run as a replicate run except when preceded and followed by a
|
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||||
// literal run, in which case it's best to merge the three into one
|
buffer.position(buffer.remaining());
|
||||||
// literal run. Always encode 3 byte repeats as replicate runs.
|
}
|
||||||
// NOTE: Worst case: output = input + (input + 127) / 128
|
|
||||||
|
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
|
||||||
int offset = pOffset;
|
// NOTE: It's best to encode a 2 byte repeat
|
||||||
final int max = pOffset + pLength - 1;
|
// run as a replicate run except when preceded and followed by a
|
||||||
final int maxMinus1 = max - 1;
|
// literal run, in which case it's best to merge the three into one
|
||||||
|
// literal run. Always encode 3 byte repeats as replicate runs.
|
||||||
while (offset <= max) {
|
// NOTE: Worst case: output = input + (input + 127) / 128
|
||||||
// Compressed run
|
|
||||||
int run = 1;
|
int offset = pOffset;
|
||||||
byte replicate = pBuffer[offset];
|
final int max = pOffset + pLength - 1;
|
||||||
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
|
final int maxMinus1 = max - 1;
|
||||||
offset++;
|
|
||||||
run++;
|
while (offset <= max) {
|
||||||
}
|
// Compressed run
|
||||||
|
int run = 1;
|
||||||
if (run > 1) {
|
byte replicate = pBuffer[offset];
|
||||||
offset++;
|
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
|
||||||
pStream.write(-(run - 1));
|
offset++;
|
||||||
pStream.write(replicate);
|
run++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Literal run
|
if (run > 1) {
|
||||||
run = 0;
|
offset++;
|
||||||
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|
pStream.write(-(run - 1));
|
||||||
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
|
pStream.write(replicate);
|
||||||
buffer[run++] = pBuffer[offset++];
|
}
|
||||||
}
|
|
||||||
|
// Literal run
|
||||||
// If last byte, include it in literal run, if space
|
run = 0;
|
||||||
if (offset == max && run > 0 && run < 128) {
|
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|
||||||
buffer[run++] = pBuffer[offset++];
|
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
|
||||||
}
|
buffer[run++] = pBuffer[offset++];
|
||||||
|
}
|
||||||
if (run > 0) {
|
|
||||||
pStream.write(run - 1);
|
// If last byte, include it in literal run, if space
|
||||||
pStream.write(buffer, 0, run);
|
if (offset == max && run > 0 && run < 128) {
|
||||||
}
|
buffer[run++] = pBuffer[offset++];
|
||||||
|
}
|
||||||
// If last byte, and not space, start new literal run
|
|
||||||
if (offset == max && (run <= 0 || run >= 128)) {
|
if (run > 0) {
|
||||||
pStream.write(0);
|
pStream.write(run - 1);
|
||||||
pStream.write(pBuffer[offset++]);
|
pStream.write(buffer, 0, run);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
// If last byte, and not space, start new literal run
|
||||||
}
|
if (offset == max && (run <= 0 || run >= 128)) {
|
||||||
|
pStream.write(0);
|
||||||
|
pStream.write(pBuffer[offset++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,342 +1,344 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.io.ole2;
|
package com.twelvemonkeys.io.ole2;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.SeekableInputStream;
|
import com.twelvemonkeys.io.SeekableInputStream;
|
||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an OLE 2 compound document entry.
|
* Represents an OLE 2 compound document entry.
|
||||||
* This is similar to a file in a file system, or an entry in a ZIP or JAR file.
|
* This is similar to a file in a file system, or an entry in a ZIP or JAR file.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/Entry.java#4 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/Entry.java#4 $
|
||||||
* @see com.twelvemonkeys.io.ole2.CompoundDocument
|
* @see com.twelvemonkeys.io.ole2.CompoundDocument
|
||||||
*/
|
*/
|
||||||
// TODO: Consider extending java.io.File...
|
// TODO: Consider extending java.io.File...
|
||||||
public final class Entry implements Comparable<Entry> {
|
public final class Entry implements Comparable<Entry> {
|
||||||
String name;
|
String name;
|
||||||
byte type;
|
byte type;
|
||||||
byte nodeColor;
|
byte nodeColor;
|
||||||
|
|
||||||
int prevDId;
|
int prevDId;
|
||||||
int nextDId;
|
int nextDId;
|
||||||
int rootNodeDId;
|
int rootNodeDId;
|
||||||
|
|
||||||
long createdTimestamp;
|
long createdTimestamp;
|
||||||
long modifiedTimestamp;
|
long modifiedTimestamp;
|
||||||
|
|
||||||
int startSId;
|
int startSId;
|
||||||
int streamSize;
|
int streamSize;
|
||||||
|
|
||||||
CompoundDocument document;
|
CompoundDocument document;
|
||||||
Entry parent;
|
Entry parent;
|
||||||
SortedSet<Entry> children;
|
SortedSet<Entry> children;
|
||||||
|
|
||||||
public final static int LENGTH = 128;
|
public final static int LENGTH = 128;
|
||||||
|
|
||||||
static final int EMPTY = 0;
|
static final int EMPTY = 0;
|
||||||
static final int USER_STORAGE = 1;
|
static final int USER_STORAGE = 1;
|
||||||
static final int USER_STREAM = 2;
|
static final int USER_STREAM = 2;
|
||||||
static final int LOCK_BYTES = 3;
|
static final int LOCK_BYTES = 3;
|
||||||
static final int PROPERTY = 4;
|
static final int PROPERTY = 4;
|
||||||
static final int ROOT_STORAGE = 5;
|
static final int ROOT_STORAGE = 5;
|
||||||
|
|
||||||
private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>());
|
private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>());
|
||||||
|
|
||||||
private Entry() {
|
private Entry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an entry from the input.
|
* Reads an entry from the input.
|
||||||
*
|
*
|
||||||
* @param pInput the input data
|
* @param pInput the input data
|
||||||
* @return the {@code Entry} read from the input data
|
* @return the {@code Entry} read from the input data
|
||||||
* @throws IOException if an i/o exception occurs during reading
|
* @throws IOException if an i/o exception occurs during reading
|
||||||
*/
|
*/
|
||||||
static Entry readEntry(final DataInput pInput) throws IOException {
|
static Entry readEntry(final DataInput pInput) throws IOException {
|
||||||
Entry p = new Entry();
|
Entry p = new Entry();
|
||||||
p.read(pInput);
|
p.read(pInput);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads this entry
|
* Reads this entry
|
||||||
*
|
*
|
||||||
* @param pInput the input data
|
* @param pInput the input data
|
||||||
* @throws IOException if an i/o exception occurs during reading
|
* @throws IOException if an i/o exception occurs during reading
|
||||||
*/
|
*/
|
||||||
private void read(final DataInput pInput) throws IOException {
|
private void read(final DataInput pInput) throws IOException {
|
||||||
byte[] bytes = new byte[64];
|
byte[] bytes = new byte[64];
|
||||||
pInput.readFully(bytes);
|
pInput.readFully(bytes);
|
||||||
|
|
||||||
// NOTE: Length is in bytes, including the null-terminator...
|
// NOTE: Length is in bytes, including the null-terminator...
|
||||||
int nameLength = pInput.readShort();
|
int nameLength = pInput.readShort();
|
||||||
name = new String(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE"));
|
name = new String(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE"));
|
||||||
// System.out.println("name: " + name);
|
// System.out.println("name: " + name);
|
||||||
|
|
||||||
type = pInput.readByte();
|
type = pInput.readByte();
|
||||||
// System.out.println("type: " + type);
|
// System.out.println("type: " + type);
|
||||||
|
|
||||||
nodeColor = pInput.readByte();
|
nodeColor = pInput.readByte();
|
||||||
// System.out.println("nodeColor: " + nodeColor);
|
// System.out.println("nodeColor: " + nodeColor);
|
||||||
|
|
||||||
prevDId = pInput.readInt();
|
prevDId = pInput.readInt();
|
||||||
// System.out.println("prevDId: " + prevDId);
|
// System.out.println("prevDId: " + prevDId);
|
||||||
nextDId = pInput.readInt();
|
nextDId = pInput.readInt();
|
||||||
// System.out.println("nextDId: " + nextDId);
|
// System.out.println("nextDId: " + nextDId);
|
||||||
rootNodeDId = pInput.readInt();
|
rootNodeDId = pInput.readInt();
|
||||||
// System.out.println("rootNodeDId: " + rootNodeDId);
|
// System.out.println("rootNodeDId: " + rootNodeDId);
|
||||||
|
|
||||||
// UID (16) + user flags (4), ignored
|
// UID (16) + user flags (4), ignored
|
||||||
if (pInput.skipBytes(20) != 20) {
|
if (pInput.skipBytes(20) != 20) {
|
||||||
throw new CorruptDocumentException();
|
throw new CorruptDocumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
||||||
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
||||||
|
|
||||||
startSId = pInput.readInt();
|
startSId = pInput.readInt();
|
||||||
// System.out.println("startSId: " + startSId);
|
// System.out.println("startSId: " + startSId);
|
||||||
streamSize = pInput.readInt();
|
streamSize = pInput.readInt();
|
||||||
// System.out.println("streamSize: " + streamSize);
|
// System.out.println("streamSize: " + streamSize);
|
||||||
|
|
||||||
// Reserved
|
// Reserved
|
||||||
pInput.readInt();
|
pInput.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If {@code true} this {@code Entry} is the root {@code Entry}.
|
* If {@code true} this {@code Entry} is the root {@code Entry}.
|
||||||
*
|
*
|
||||||
* @return {@code true} if this is the root {@code Entry}
|
* @return {@code true} if this is the root {@code Entry}
|
||||||
*/
|
*/
|
||||||
public boolean isRoot() {
|
public boolean isRoot() {
|
||||||
return type == ROOT_STORAGE;
|
return type == ROOT_STORAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If {@code true} this {@code Entry} is a directory
|
* If {@code true} this {@code Entry} is a directory
|
||||||
* {@code Entry}.
|
* {@code Entry}.
|
||||||
*
|
*
|
||||||
* @return {@code true} if this is a directory {@code Entry}
|
* @return {@code true} if this is a directory {@code Entry}
|
||||||
*/
|
*/
|
||||||
public boolean isDirectory() {
|
public boolean isDirectory() {
|
||||||
return type == USER_STORAGE;
|
return type == USER_STORAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If {@code true} this {@code Entry} is a file (document)
|
* If {@code true} this {@code Entry} is a file (document)
|
||||||
* {@code Entry}.
|
* {@code Entry}.
|
||||||
*
|
*
|
||||||
* @return {@code true} if this is a document {@code Entry}
|
* @return {@code true} if this is a document {@code Entry}
|
||||||
*/
|
*/
|
||||||
public boolean isFile() {
|
public boolean isFile() {
|
||||||
return type == USER_STREAM;
|
return type == USER_STREAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of this {@code Entry}
|
* Returns the name of this {@code Entry}
|
||||||
*
|
*
|
||||||
* @return the name of this {@code Entry}
|
* @return the name of this {@code Entry}
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@code InputStream} for this {@code Entry}
|
* Returns the {@code InputStream} for this {@code Entry}
|
||||||
*
|
*
|
||||||
* @return an {@code InputStream} containing the data for this
|
* @return an {@code InputStream} containing the data for this
|
||||||
* {@code Entry} or {@code null} if this is a directory {@code Entry}
|
* {@code Entry} or {@code null} if this is a directory {@code Entry}
|
||||||
* @throws java.io.IOException if an I/O exception occurs
|
* @throws java.io.IOException if an I/O exception occurs
|
||||||
* @see #length()
|
* @see #length()
|
||||||
*/
|
*/
|
||||||
public SeekableInputStream getInputStream() throws IOException {
|
public SeekableInputStream getInputStream() throws IOException {
|
||||||
if (!isFile()) {
|
if (!isFile()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return document.getInputStreamForSId(startSId, streamSize);
|
return document.getInputStreamForSId(startSId, streamSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the length of this entry
|
* Returns the length of this entry
|
||||||
*
|
*
|
||||||
* @return the length of the stream for this entry, or {@code 0} if this is
|
* @return the length of the stream for this entry, or {@code 0} if this is
|
||||||
* a directory {@code Entry}
|
* a directory {@code Entry}
|
||||||
* @see #getInputStream()
|
* @see #getInputStream()
|
||||||
*/
|
*/
|
||||||
public long length() {
|
public long length() {
|
||||||
if (!isFile()) {
|
if (!isFile()) {
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
return streamSize;
|
return streamSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the time that this entry was created.
|
* Returns the time that this entry was created.
|
||||||
* The time is converted from its internal representation to standard Java
|
* The time is converted from its internal representation to standard Java
|
||||||
* representation, milliseconds since the epoch
|
* representation, milliseconds since the epoch
|
||||||
* (00:00:00 GMT, January 1, 1970).
|
* (00:00:00 GMT, January 1, 1970).
|
||||||
* <p/>
|
* <p>
|
||||||
* Note that most applications leaves this value empty ({@code 0L}).
|
* Note that most applications leaves this value empty ({@code 0L}).
|
||||||
*
|
* </p>
|
||||||
* @return A {@code long} value representing the time this entry was
|
*
|
||||||
* created, measured in milliseconds since the epoch
|
* @return A {@code long} value representing the time this entry was
|
||||||
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
* created, measured in milliseconds since the epoch
|
||||||
* creation time stamp exists for this entry.
|
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
||||||
*/
|
* creation time stamp exists for this entry.
|
||||||
public long created() {
|
*/
|
||||||
return createdTimestamp;
|
public long created() {
|
||||||
}
|
return createdTimestamp;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns the time that this entry was last modified.
|
/**
|
||||||
* The time is converted from its internal representation to standard Java
|
* Returns the time that this entry was last modified.
|
||||||
* representation, milliseconds since the epoch
|
* The time is converted from its internal representation to standard Java
|
||||||
* (00:00:00 GMT, January 1, 1970).
|
* representation, milliseconds since the epoch
|
||||||
* <p/>
|
* (00:00:00 GMT, January 1, 1970).
|
||||||
* Note that many applications leaves this value empty ({@code 0L}).
|
* <p>
|
||||||
*
|
* Note that many applications leaves this value empty ({@code 0L}).
|
||||||
* @return A {@code long} value representing the time this entry was
|
* </p>
|
||||||
* last modified, measured in milliseconds since the epoch
|
*
|
||||||
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
* @return A {@code long} value representing the time this entry was
|
||||||
* modification time stamp exists for this entry.
|
* last modified, measured in milliseconds since the epoch
|
||||||
*/
|
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
|
||||||
public long lastModified() {
|
* modification time stamp exists for this entry.
|
||||||
return modifiedTimestamp;
|
*/
|
||||||
}
|
public long lastModified() {
|
||||||
|
return modifiedTimestamp;
|
||||||
/**
|
}
|
||||||
* Return the parent of this {@code Entry}
|
|
||||||
*
|
/**
|
||||||
* @return the parent of this {@code Entry}, or {@code null} if this is
|
* Return the parent of this {@code Entry}
|
||||||
* the root {@code Entry}
|
*
|
||||||
*/
|
* @return the parent of this {@code Entry}, or {@code null} if this is
|
||||||
public Entry getParentEntry() {
|
* the root {@code Entry}
|
||||||
return parent;
|
*/
|
||||||
}
|
public Entry getParentEntry() {
|
||||||
|
return parent;
|
||||||
/**
|
}
|
||||||
* Returns the child of this {@code Entry} with the given name.
|
|
||||||
*
|
/**
|
||||||
* @param pName the name of the child {@code Entry}
|
* Returns the child of this {@code Entry} with the given name.
|
||||||
* @return the child {@code Entry} or {@code null} if thee is no such
|
*
|
||||||
* child
|
* @param pName the name of the child {@code Entry}
|
||||||
* @throws java.io.IOException if an I/O exception occurs
|
* @return the child {@code Entry} or {@code null} if thee is no such
|
||||||
*/
|
* child
|
||||||
public Entry getChildEntry(final String pName) throws IOException {
|
* @throws java.io.IOException if an I/O exception occurs
|
||||||
if (isFile() || rootNodeDId == -1) {
|
*/
|
||||||
return null;
|
public Entry getChildEntry(final String pName) throws IOException {
|
||||||
}
|
if (isFile() || rootNodeDId == -1) {
|
||||||
|
return null;
|
||||||
Entry dummy = new Entry();
|
}
|
||||||
dummy.name = pName;
|
|
||||||
dummy.parent = this;
|
Entry dummy = new Entry();
|
||||||
|
dummy.name = pName;
|
||||||
SortedSet child = getChildEntries().tailSet(dummy);
|
dummy.parent = this;
|
||||||
return (Entry) child.first();
|
|
||||||
}
|
SortedSet child = getChildEntries().tailSet(dummy);
|
||||||
|
return (Entry) child.first();
|
||||||
/**
|
}
|
||||||
* Returns the children of this {@code Entry}.
|
|
||||||
*
|
/**
|
||||||
* @return a {@code SortedSet} of {@code Entry} objects
|
* Returns the children of this {@code Entry}.
|
||||||
* @throws java.io.IOException if an I/O exception occurs
|
*
|
||||||
*/
|
* @return a {@code SortedSet} of {@code Entry} objects
|
||||||
public SortedSet<Entry> getChildEntries() throws IOException {
|
* @throws java.io.IOException if an I/O exception occurs
|
||||||
if (children == null) {
|
*/
|
||||||
if (isFile() || rootNodeDId == -1) {
|
public SortedSet<Entry> getChildEntries() throws IOException {
|
||||||
children = NO_CHILDREN;
|
if (children == null) {
|
||||||
}
|
if (isFile() || rootNodeDId == -1) {
|
||||||
else {
|
children = NO_CHILDREN;
|
||||||
// Start at root node in R/B tree, and read to the left and right,
|
}
|
||||||
// re-build tree, according to the docs
|
else {
|
||||||
children = Collections.unmodifiableSortedSet(document.getEntries(rootNodeDId, this));
|
// Start at root node in R/B tree, and read to the left and right,
|
||||||
}
|
// re-build tree, according to the docs
|
||||||
}
|
children = Collections.unmodifiableSortedSet(document.getEntries(rootNodeDId, this));
|
||||||
|
}
|
||||||
return children;
|
}
|
||||||
}
|
|
||||||
|
return children;
|
||||||
@Override
|
}
|
||||||
public String toString() {
|
|
||||||
return "\"" + name + "\""
|
@Override
|
||||||
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
|
public String toString() {
|
||||||
+ (parent != null ? ", parent: \"" + parent.getName() + "\"" : "")
|
return "\"" + name + "\""
|
||||||
+ (isFile() ? "" : ", children: " + (children != null ? String.valueOf(children.size()) : "(unknown)"))
|
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
|
||||||
+ ", SId=" + startSId + ", length=" + streamSize + ")";
|
+ (parent != null ? ", parent: \"" + parent.getName() + "\"" : "")
|
||||||
}
|
+ (isFile() ? "" : ", children: " + (children != null ? String.valueOf(children.size()) : "(unknown)"))
|
||||||
|
+ ", SId=" + startSId + ", length=" + streamSize + ")";
|
||||||
@Override
|
}
|
||||||
public boolean equals(final Object pOther) {
|
|
||||||
if (pOther == this) {
|
@Override
|
||||||
return true;
|
public boolean equals(final Object pOther) {
|
||||||
}
|
if (pOther == this) {
|
||||||
if (!(pOther instanceof Entry)) {
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
if (!(pOther instanceof Entry)) {
|
||||||
|
return false;
|
||||||
Entry other = (Entry) pOther;
|
}
|
||||||
return name.equals(other.name) && (parent == other.parent
|
|
||||||
|| (parent != null && parent.equals(other.parent)));
|
Entry other = (Entry) pOther;
|
||||||
}
|
return name.equals(other.name) && (parent == other.parent
|
||||||
|
|| (parent != null && parent.equals(other.parent)));
|
||||||
@Override
|
}
|
||||||
public int hashCode() {
|
|
||||||
return name.hashCode() ^ startSId;
|
@Override
|
||||||
}
|
public int hashCode() {
|
||||||
|
return name.hashCode() ^ startSId;
|
||||||
public int compareTo(final Entry pOther) {
|
}
|
||||||
if (this == pOther) {
|
|
||||||
return 0;
|
public int compareTo(final Entry pOther) {
|
||||||
}
|
if (this == pOther) {
|
||||||
|
return 0;
|
||||||
// NOTE: This is the sorting algorthm defined by the Compound Document:
|
}
|
||||||
// - first sort by name length
|
|
||||||
// - if lengths are equal, sort by comparing strings, case sensitive
|
// NOTE: This is the sorting algorthm defined by the Compound Document:
|
||||||
|
// - first sort by name length
|
||||||
int diff = name.length() - pOther.name.length();
|
// - if lengths are equal, sort by comparing strings, case sensitive
|
||||||
if (diff != 0) {
|
|
||||||
return diff;
|
int diff = name.length() - pOther.name.length();
|
||||||
}
|
if (diff != 0) {
|
||||||
|
return diff;
|
||||||
return name.compareTo(pOther.name);
|
}
|
||||||
}
|
|
||||||
}
|
return name.compareTo(pOther.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,314 +1,313 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, 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
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.net;
|
package com.twelvemonkeys.net;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import com.twelvemonkeys.lang.SystemUtil;
|
import com.twelvemonkeys.lang.SystemUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains mappings from file extension to mime-types and from mime-type to file-types.
|
* Contains mappings from file extension to mime-types and from mime-type to file-types.
|
||||||
* <p/>
|
*
|
||||||
*
|
* @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/net/MIMEUtil.java#5 $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $
|
*
|
||||||
*
|
* @see <A href="http://www.iana.org/assignments/media-types/">MIME Media Types</A>
|
||||||
* @see <A href="http://www.iana.org/assignments/media-types/">MIME Media Types</A>
|
*/
|
||||||
*/
|
public final class MIMEUtil {
|
||||||
public final class MIMEUtil {
|
// TODO: Piggy-back on the mappings form the JRE? (1.6 comes with javax.activation)
|
||||||
// TODO: Piggy-back on the mappings form the JRE? (1.6 comes with javax.activation)
|
// TODO: Piggy-back on mappings from javax.activation?
|
||||||
// TODO: Piggy-back on mappings from javax.activation?
|
// See: http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/activation/MimetypesFileTypeMap.html
|
||||||
// See: http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/activation/MimetypesFileTypeMap.html
|
// See: http://java.sun.com/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html
|
||||||
// See: http://java.sun.com/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html
|
// TODO: Use the format (and lookup) specified by the above URLs
|
||||||
// TODO: Use the format (and lookup) specified by the above URLs
|
// TODO: Allow 3rd party to add mappings? Will need application context support to do it safe.. :-P
|
||||||
// TODO: Allow 3rd party to add mappings? Will need application context support to do it safe.. :-P
|
|
||||||
|
private static Map<String, List<String>> sExtToMIME = new HashMap<String, List<String>>();
|
||||||
private static Map<String, List<String>> sExtToMIME = new HashMap<String, List<String>>();
|
private static Map<String, List<String>> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
|
||||||
private static Map<String, List<String>> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
|
|
||||||
|
private static Map<String, List<String>> sMIMEToExt = new HashMap<String, List<String>>();
|
||||||
private static Map<String, List<String>> sMIMEToExt = new HashMap<String, List<String>>();
|
private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
|
||||||
private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
|
|
||||||
|
static {
|
||||||
static {
|
// Load mapping for MIMEUtil
|
||||||
// Load mapping for MIMEUtil
|
try {
|
||||||
try {
|
Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
|
||||||
Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
|
|
||||||
|
for (Map.Entry entry : mappings.entrySet()) {
|
||||||
for (Map.Entry entry : mappings.entrySet()) {
|
// Convert and break up extensions and mimeTypes
|
||||||
// Convert and break up extensions and mimeTypes
|
String extStr = StringUtil.toLowerCase((String) entry.getKey());
|
||||||
String extStr = StringUtil.toLowerCase((String) entry.getKey());
|
List<String> extensions =
|
||||||
List<String> extensions =
|
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
|
||||||
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
|
|
||||||
|
String typeStr = StringUtil.toLowerCase((String) entry.getValue());
|
||||||
String typeStr = StringUtil.toLowerCase((String) entry.getValue());
|
List<String> mimeTypes =
|
||||||
List<String> mimeTypes =
|
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
|
||||||
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
|
|
||||||
|
// TODO: Handle duplicates in MIME to extension mapping, like
|
||||||
// TODO: Handle duplicates in MIME to extension mapping, like
|
// xhtml=application/xhtml+xml;application/xml
|
||||||
// xhtml=application/xhtml+xml;application/xml
|
// xml=text/xml;application/xml
|
||||||
// xml=text/xml;application/xml
|
|
||||||
|
// Populate normal and reverse MIME-mappings
|
||||||
// Populate normal and reverse MIME-mappings
|
for (String extension : extensions) {
|
||||||
for (String extension : extensions) {
|
sExtToMIME.put(extension, mimeTypes);
|
||||||
sExtToMIME.put(extension, mimeTypes);
|
}
|
||||||
}
|
|
||||||
|
for (String mimeType : mimeTypes) {
|
||||||
for (String mimeType : mimeTypes) {
|
sMIMEToExt.put(mimeType, extensions);
|
||||||
sMIMEToExt.put(mimeType, extensions);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (IOException e) {
|
||||||
catch (IOException e) {
|
System.err.println("Could not read properties for MIMEUtil: " + e.getMessage());
|
||||||
System.err.println("Could not read properties for MIMEUtil: " + e.getMessage());
|
e.printStackTrace();
|
||||||
e.printStackTrace();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Disallow construction
|
||||||
// Disallow construction
|
private MIMEUtil() {
|
||||||
private MIMEUtil() {
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns the default MIME type for the given file extension.
|
||||||
* Returns the default MIME type for the given file extension.
|
*
|
||||||
*
|
* @param pFileExt the file extension
|
||||||
* @param pFileExt the file extension
|
*
|
||||||
*
|
* @return a {@code String} containing the MIME type, or {@code null} if
|
||||||
* @return a {@code String} containing the MIME type, or {@code null} if
|
* there are no known MIME types for the given file extension.
|
||||||
* there are no known MIME types for the given file extension.
|
*/
|
||||||
*/
|
public static String getMIMEType(final String pFileExt) {
|
||||||
public static String getMIMEType(final String pFileExt) {
|
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
||||||
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
return (types == null || types.isEmpty()) ? null : types.get(0);
|
||||||
return (types == null || types.isEmpty()) ? null : types.get(0);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns all MIME types for the given file extension.
|
||||||
* Returns all MIME types for the given file extension.
|
*
|
||||||
*
|
* @param pFileExt the file extension
|
||||||
* @param pFileExt the file extension
|
*
|
||||||
*
|
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
|
||||||
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
|
* list, if there are no known MIME types for the given file extension.
|
||||||
* list, if there are no known MIME types for the given file extension.
|
*/
|
||||||
*/
|
public static List<String> getMIMETypes(final String pFileExt) {
|
||||||
public static List<String> getMIMETypes(final String pFileExt) {
|
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
||||||
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
|
return maskNull(types);
|
||||||
return maskNull(types);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns an unmodifiabale {@link Map} view of the extension to
|
||||||
* Returns an unmodifiabale {@link Map} view of the extension to
|
* MIME mapping, to use as the default mapping in client applications.
|
||||||
* MIME mapping, to use as the default mapping in client applications.
|
*
|
||||||
*
|
* @return an unmodifiabale {@code Map} view of the extension to
|
||||||
* @return an unmodifiabale {@code Map} view of the extension to
|
* MIME mapping.
|
||||||
* MIME mapping.
|
*/
|
||||||
*/
|
public static Map<String, List<String>> getMIMETypeMappings() {
|
||||||
public static Map<String, List<String>> getMIMETypeMappings() {
|
return sUnmodifiableExtToMIME;
|
||||||
return sUnmodifiableExtToMIME;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns the default file extension for the given MIME type.
|
||||||
* Returns the default file extension for the given MIME type.
|
* Specifying a wildcard type will return {@code null}.
|
||||||
* Specifying a wildcard type will return {@code null}.
|
*
|
||||||
*
|
* @param pMIME the MIME type
|
||||||
* @param pMIME the MIME type
|
*
|
||||||
*
|
* @return a {@code String} containing the file extension, or {@code null}
|
||||||
* @return a {@code String} containing the file extension, or {@code null}
|
* if there are no known file extensions for the given MIME type.
|
||||||
* if there are no known file extensions for the given MIME type.
|
*/
|
||||||
*/
|
public static String getExtension(final String pMIME) {
|
||||||
public static String getExtension(final String pMIME) {
|
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
||||||
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
List<String> extensions = sMIMEToExt.get(mime);
|
||||||
List<String> extensions = sMIMEToExt.get(mime);
|
return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0);
|
||||||
return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns all file extension for the given MIME type.
|
||||||
* Returns all file extension for the given MIME type.
|
* The default extension will be the first in the list.
|
||||||
* The default extension will be the first in the list.
|
* Note that no specific order is given for wildcard types (image/*, */* etc).
|
||||||
* Note that no specific order is given for wildcard types (image/*, */* etc).
|
*
|
||||||
*
|
* @param pMIME the MIME type
|
||||||
* @param pMIME the MIME type
|
*
|
||||||
*
|
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
|
||||||
* @return a {@link List} of {@code String}s containing the MIME types, or an empty
|
* list, if there are no known file extensions for the given MIME type.
|
||||||
* list, if there are no known file extensions for the given MIME type.
|
*/
|
||||||
*/
|
public static List<String> getExtensions(final String pMIME) {
|
||||||
public static List<String> getExtensions(final String pMIME) {
|
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
||||||
String mime = bareMIME(StringUtil.toLowerCase(pMIME));
|
if (mime.endsWith("/*")) {
|
||||||
if (mime.endsWith("/*")) {
|
return getExtensionForWildcard(mime);
|
||||||
return getExtensionForWildcard(mime);
|
}
|
||||||
}
|
List<String> extensions = sMIMEToExt.get(mime);
|
||||||
List<String> extensions = sMIMEToExt.get(mime);
|
return maskNull(extensions);
|
||||||
return maskNull(extensions);
|
}
|
||||||
}
|
|
||||||
|
// Gets all extensions for a wildcard MIME type
|
||||||
// Gets all extensions for a wildcard MIME type
|
private static List<String> getExtensionForWildcard(final String pMIME) {
|
||||||
private static List<String> getExtensionForWildcard(final String pMIME) {
|
final String family = pMIME.substring(0, pMIME.length() - 1);
|
||||||
final String family = pMIME.substring(0, pMIME.length() - 1);
|
Set<String> extensions = new LinkedHashSet<String>();
|
||||||
Set<String> extensions = new LinkedHashSet<String>();
|
for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) {
|
||||||
for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) {
|
if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
|
||||||
if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
|
extensions.addAll(mimeToExt.getValue());
|
||||||
extensions.addAll(mimeToExt.getValue());
|
}
|
||||||
}
|
}
|
||||||
}
|
return Collections.unmodifiableList(new ArrayList<String>(extensions));
|
||||||
return Collections.unmodifiableList(new ArrayList<String>(extensions));
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns an unmodifiabale {@link Map} view of the MIME to
|
||||||
* Returns an unmodifiabale {@link Map} view of the MIME to
|
* extension mapping, to use as the default mapping in client applications.
|
||||||
* extension mapping, to use as the default mapping in client applications.
|
*
|
||||||
*
|
* @return an unmodifiabale {@code Map} view of the MIME to
|
||||||
* @return an unmodifiabale {@code Map} view of the MIME to
|
* extension mapping.
|
||||||
* extension mapping.
|
*/
|
||||||
*/
|
public static Map<String, List<String>> getExtensionMappings() {
|
||||||
public static Map<String, List<String>> getExtensionMappings() {
|
return sUnmodifiableMIMEToExt;
|
||||||
return sUnmodifiableMIMEToExt;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Tests wehter the type is a subtype of the type family.
|
||||||
* Tests wehter the type is a subtype of the type family.
|
*
|
||||||
*
|
* @param pTypeFamily the MIME type family ({@code image/*, */*}, etc)
|
||||||
* @param pTypeFamily the MIME type family ({@code image/*, */*}, etc)
|
* @param pType the MIME type
|
||||||
* @param pType the MIME type
|
* @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false}
|
||||||
* @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false}
|
*/
|
||||||
*/
|
// TODO: Rename? isSubtype?
|
||||||
// TODO: Rename? isSubtype?
|
// TODO: Make public
|
||||||
// TODO: Make public
|
static boolean includes(final String pTypeFamily, final String pType) {
|
||||||
static boolean includes(final String pTypeFamily, final String pType) {
|
// TODO: Handle null in a well-defined way
|
||||||
// TODO: Handle null in a well-defined way
|
// - Is null family same as */*?
|
||||||
// - Is null family same as */*?
|
// - Is null subtype of any family? Subtype of no family?
|
||||||
// - Is null subtype of any family? Subtype of no family?
|
|
||||||
|
String type = bareMIME(pType);
|
||||||
String type = bareMIME(pType);
|
return type.equals(pTypeFamily)
|
||||||
return type.equals(pTypeFamily)
|
|| "*/*".equals(pTypeFamily)
|
||||||
|| "*/*".equals(pTypeFamily)
|
|| pTypeFamily.endsWith("/*") && pTypeFamily.startsWith(type.substring(0, type.indexOf('/')));
|
||||||
|| pTypeFamily.endsWith("/*") && pTypeFamily.startsWith(type.substring(0, type.indexOf('/')));
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Removes any charset or extra info from the mime-type string (anything after a semicolon, {@code ;}, inclusive).
|
||||||
* Removes any charset or extra info from the mime-type string (anything after a semicolon, {@code ;}, inclusive).
|
*
|
||||||
*
|
* @param pMIME the mime-type string
|
||||||
* @param pMIME the mime-type string
|
* @return the bare mime-type
|
||||||
* @return the bare mime-type
|
*/
|
||||||
*/
|
public static String bareMIME(final String pMIME) {
|
||||||
public static String bareMIME(final String pMIME) {
|
int idx;
|
||||||
int idx;
|
if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) {
|
||||||
if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) {
|
return pMIME.substring(0, idx);
|
||||||
return pMIME.substring(0, idx);
|
}
|
||||||
}
|
return pMIME;
|
||||||
return pMIME;
|
}
|
||||||
}
|
|
||||||
|
// Returns the list or empty list if list is null
|
||||||
// Returns the list or empty list if list is null
|
private static List<String> maskNull(List<String> pTypes) {
|
||||||
private static List<String> maskNull(List<String> pTypes) {
|
return (pTypes == null) ? Collections.<String>emptyList() : pTypes;
|
||||||
return (pTypes == null) ? Collections.<String>emptyList() : pTypes;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* For debugging. Prints all known MIME types and file extensions.
|
||||||
* For debugging. Prints all known MIME types and file extensions.
|
*
|
||||||
*
|
* @param pArgs command line arguments
|
||||||
* @param pArgs command line arguments
|
*/
|
||||||
*/
|
public static void main(String[] pArgs) {
|
||||||
public static void main(String[] pArgs) {
|
if (pArgs.length > 1) {
|
||||||
if (pArgs.length > 1) {
|
String type = pArgs[0];
|
||||||
String type = pArgs[0];
|
String family = pArgs[1];
|
||||||
String family = pArgs[1];
|
boolean incuded = includes(family, type);
|
||||||
boolean incuded = includes(family, type);
|
System.out.println(
|
||||||
System.out.println(
|
"Mime type family " + family
|
||||||
"Mime type family " + family
|
+ (incuded ? " includes " : " does not include ")
|
||||||
+ (incuded ? " includes " : " does not include ")
|
+ "type " + type
|
||||||
+ "type " + type
|
);
|
||||||
);
|
}
|
||||||
}
|
if (pArgs.length > 0) {
|
||||||
if (pArgs.length > 0) {
|
String str = pArgs[0];
|
||||||
String str = pArgs[0];
|
|
||||||
|
if (str.indexOf('/') >= 0) {
|
||||||
if (str.indexOf('/') >= 0) {
|
// MIME
|
||||||
// MIME
|
String extension = getExtension(str);
|
||||||
String extension = getExtension(str);
|
System.out.println("Default extension for MIME type '" + str + "' is "
|
||||||
System.out.println("Default extension for MIME type '" + str + "' is "
|
+ (extension != null ? ": '" + extension + "'" : "unknown") + ".");
|
||||||
+ (extension != null ? ": '" + extension + "'" : "unknown") + ".");
|
System.out.println("All possible: " + getExtensions(str));
|
||||||
System.out.println("All possible: " + getExtensions(str));
|
}
|
||||||
}
|
else {
|
||||||
else {
|
// EXT
|
||||||
// EXT
|
String mimeType = getMIMEType(str);
|
||||||
String mimeType = getMIMEType(str);
|
System.out.println("Default MIME type for extension '" + str + "' is "
|
||||||
System.out.println("Default MIME type for extension '" + str + "' is "
|
+ (mimeType != null ? ": '" + mimeType + "'" : "unknown") + ".");
|
||||||
+ (mimeType != null ? ": '" + mimeType + "'" : "unknown") + ".");
|
System.out.println("All possible: " + getMIMETypes(str));
|
||||||
System.out.println("All possible: " + getMIMETypes(str));
|
}
|
||||||
}
|
|
||||||
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
Set set = sMIMEToExt.keySet();
|
||||||
Set set = sMIMEToExt.keySet();
|
String[] mimeTypes = new String[set.size()];
|
||||||
String[] mimeTypes = new String[set.size()];
|
int i = 0;
|
||||||
int i = 0;
|
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
||||||
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
String mime = (String) iterator.next();
|
||||||
String mime = (String) iterator.next();
|
mimeTypes[i] = mime;
|
||||||
mimeTypes[i] = mime;
|
}
|
||||||
}
|
Arrays.sort(mimeTypes);
|
||||||
Arrays.sort(mimeTypes);
|
|
||||||
|
System.out.println("Known MIME types (" + mimeTypes.length + "):");
|
||||||
System.out.println("Known MIME types (" + mimeTypes.length + "):");
|
for (int j = 0; j < mimeTypes.length; j++) {
|
||||||
for (int j = 0; j < mimeTypes.length; j++) {
|
String mimeType = mimeTypes[j];
|
||||||
String mimeType = mimeTypes[j];
|
|
||||||
|
if (j != 0) {
|
||||||
if (j != 0) {
|
System.out.print(", ");
|
||||||
System.out.print(", ");
|
}
|
||||||
}
|
|
||||||
|
System.out.print(mimeType);
|
||||||
System.out.print(mimeType);
|
}
|
||||||
}
|
|
||||||
|
System.out.println("\n");
|
||||||
System.out.println("\n");
|
|
||||||
|
set = sExtToMIME.keySet();
|
||||||
set = sExtToMIME.keySet();
|
String[] extensions = new String[set.size()];
|
||||||
String[] extensions = new String[set.size()];
|
i = 0;
|
||||||
i = 0;
|
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
||||||
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
|
String ext = (String) iterator.next();
|
||||||
String ext = (String) iterator.next();
|
extensions[i] = ext;
|
||||||
extensions[i] = ext;
|
}
|
||||||
}
|
Arrays.sort(extensions);
|
||||||
Arrays.sort(extensions);
|
|
||||||
|
System.out.println("Known file types (" + extensions.length + "):");
|
||||||
System.out.println("Known file types (" + extensions.length + "):");
|
for (int j = 0; j < extensions.length; j++) {
|
||||||
for (int j = 0; j < extensions.length; j++) {
|
String extension = extensions[j];
|
||||||
String extension = extensions[j];
|
|
||||||
|
if (j != 0) {
|
||||||
if (j != 0) {
|
System.out.print(", ");
|
||||||
System.out.print(", ");
|
}
|
||||||
}
|
|
||||||
|
System.out.print(extension);
|
||||||
System.out.print(extension);
|
}
|
||||||
}
|
System.out.println();
|
||||||
System.out.println();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,9 @@
|
|||||||
|
|
||||||
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;
|
||||||
@@ -38,9 +41,6 @@ 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,22 +88,12 @@ 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.
|
||||||
* <p/>
|
* <p>
|
||||||
* Note: This is a hint, and may be ignored by DOM implemenations.
|
* Note: This is a hint, and may be ignored by DOM implementations.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param pPrettyPrint {@code true} to enable pretty printing
|
* @param pPrettyPrint {@code true} to enable pretty printing
|
||||||
*/
|
*/
|
||||||
@@ -168,13 +158,7 @@ public final class DOMSerializer {
|
|||||||
try {
|
try {
|
||||||
return DOMImplementationRegistry.newInstance();
|
return DOMImplementationRegistry.newInstance();
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e) {
|
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
catch (InstantiationException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException e) {
|
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+23
-15
@@ -30,16 +30,23 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.xml;
|
package com.twelvemonkeys.xml;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import java.io.ByteArrayInputStream;
|
||||||
import org.w3c.dom.*;
|
import java.io.ByteArrayOutputStream;
|
||||||
import org.xml.sax.SAXException;
|
import java.io.IOException;
|
||||||
|
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 java.nio.charset.Charset;
|
import org.w3c.dom.*;
|
||||||
import java.util.Date;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XMLSerializer
|
* XMLSerializer
|
||||||
@@ -290,7 +297,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.substring(pStart, pEnd));
|
pBuilder.append(pString, pStart, pEnd);
|
||||||
pBuilder.append(pEntity);
|
pBuilder.append(pEntity);
|
||||||
return pEnd + 1;
|
return pEnd + 1;
|
||||||
}
|
}
|
||||||
@@ -389,9 +396,10 @@ public class XMLSerializer {
|
|||||||
private void writeDocument(final PrintWriter pOut, final Node pNode, final SerializationContext pContext) {
|
private void writeDocument(final PrintWriter pOut, final Node pNode, final SerializationContext pContext) {
|
||||||
// Document fragments might not have child nodes...
|
// Document fragments might not have child nodes...
|
||||||
if (pNode.hasChildNodes()) {
|
if (pNode.hasChildNodes()) {
|
||||||
NodeList nodes = pNode.getChildNodes();
|
Node child = pNode.getFirstChild();
|
||||||
for (int i = 0; i < nodes.getLength(); i++) {
|
while (child != null) {
|
||||||
writeNodeRecursive(pOut, nodes.item(i), pContext);
|
writeNodeRecursive(pOut, child, pContext);
|
||||||
|
child = child.getNextSibling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,9 +456,10 @@ public class XMLSerializer {
|
|||||||
pOut.println();
|
pOut.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeList children = pNode.getChildNodes();
|
Node child = pNode.getFirstChild();
|
||||||
for (int i = 0; i < children.getLength(); i++) {
|
while (child != null) {
|
||||||
writeNodeRecursive(pOut, children.item(i), pContext.push());
|
writeNodeRecursive(pOut, child, pContext.push());
|
||||||
|
child = child.getNextSibling();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pContext.preserveSpace) {
|
if (!pContext.preserveSpace) {
|
||||||
@@ -525,8 +534,7 @@ public class XMLSerializer {
|
|||||||
builder = factory.newDocumentBuilder();
|
builder = factory.newDocumentBuilder();
|
||||||
}
|
}
|
||||||
catch (ParserConfigurationException e) {
|
catch (ParserConfigurationException e) {
|
||||||
//noinspection ThrowableInstanceNeverThrown BOGUS
|
throw new IOException(e);
|
||||||
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DOMImplementation dom = builder.getDOMImplementation();
|
DOMImplementation dom = builder.getDOMImplementation();
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ 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;
|
||||||
@@ -40,7 +39,8 @@ import java.io.StringReader;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompoundReaderTestCase
|
* CompoundReaderTestCase
|
||||||
|
|||||||
+2
-3
@@ -30,12 +30,11 @@
|
|||||||
|
|
||||||
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 static org.junit.Assert.assertEquals;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FastByteArrayOutputStreamTestCase
|
* FastByteArrayOutputStreamTestCase
|
||||||
|
|||||||
@@ -30,11 +30,10 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MemoryCacheSeekableStreamTestCase
|
* MemoryCacheSeekableStreamTestCase
|
||||||
@@ -92,13 +91,13 @@ public class FileSeekableStreamTest extends SeekableInputStreamAbstractTest {
|
|||||||
try {
|
try {
|
||||||
FileUtil.read(stream); // Read until EOF
|
FileUtil.read(stream); // Read until EOF
|
||||||
|
|
||||||
assertEquals("EOF not reached (test case broken)", -1, stream.read());
|
assertEquals(-1, stream.read(), "EOF not reached (test case broken)");
|
||||||
assertFalse("Underlying stream closed before close", closed[0]);
|
assertFalse(closed[0], "Underlying stream closed before close");
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue("Underlying stream not closed", closed[0]);
|
assertTrue(closed[0], "Underlying stream not closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
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("Check Size [" + i + "]", (size - i) >= input.available());
|
assertTrue((size - i) >= input.available(), "Check Size [" + i + "]");
|
||||||
assertEquals("Check Value [" + i + "]", i, input.read());
|
assertEquals(i, input.read(), "Check Value [" + i + "]");
|
||||||
}
|
}
|
||||||
assertEquals("Available after contents all read", 0, input.available());
|
assertEquals(0, input.available(), "Available after contents all read");
|
||||||
|
|
||||||
// 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("Wrong value read after end of file", -1, result);
|
assertEquals( -1, result, "Wrong value read after end of file");
|
||||||
}
|
}
|
||||||
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("Unexpected EOF", input.read() < 0);
|
assertFalse(input.read() < 0, "Unexpected EOF");
|
||||||
assertEquals("Available after contents all read", 0, input.available());
|
assertEquals(0, input.available(), "Available after contents all read");
|
||||||
|
|
||||||
// Check availbale is zero after End of file
|
// Check availbale is zero after End of file
|
||||||
assertEquals("End of File", -1, input.read());
|
assertEquals(-1, input.read(), "End of File");
|
||||||
assertEquals("Available after End of File", 0, input.available());
|
assertEquals( 0, input.available(), "Available after End of File");
|
||||||
}
|
}
|
||||||
|
|
||||||
@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("Read 1", bytes.length, count1);
|
assertEquals(bytes.length, count1, "Read 1");
|
||||||
for (int i = 0; i < count1; i++) {
|
for (int i = 0; i < count1; i++) {
|
||||||
assertEquals("Check Bytes 1", i, bytes[i]);
|
assertEquals(i, bytes[i], "Check Bytes 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read into array
|
// Read into array
|
||||||
int count2 = input.read(bytes);
|
int count2 = input.read(bytes);
|
||||||
assertEquals("Read 2", 5, count2);
|
assertEquals(5, count2, "Read 2");
|
||||||
for (int i = 0; i < count2; i++) {
|
for (int i = 0; i < count2; i++) {
|
||||||
assertEquals("Check Bytes 2", count1 + i, bytes[i]);
|
assertEquals(count1 + i, bytes[i], "Check Bytes 2");
|
||||||
}
|
}
|
||||||
|
|
||||||
// End of File
|
// End of File
|
||||||
int count3 = input.read(bytes);
|
int count3 = input.read(bytes);
|
||||||
assertEquals("Read 3 (EOF)", -1, count3);
|
assertEquals(-1, count3, "Read 3 (EOF)");
|
||||||
|
|
||||||
// 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("Wrong value read after end of file", -1, result);
|
assertEquals(-1, result, "Wrong value read after end of file");
|
||||||
}
|
}
|
||||||
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("Read 5", lth, count5);
|
assertEquals(lth, count5, "Read 5");
|
||||||
for (int i = offset; i < lth; i++) {
|
for (int i = offset; i < lth; i++) {
|
||||||
assertEquals("Check Bytes 2", i - offset, bytes[i]);
|
assertEquals(i - offset, bytes[i], "Check Bytes 2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEOF() throws Exception {
|
public void testEOF() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(2));
|
InputStream input = makeInputStream(makeOrderedArray(2));
|
||||||
assertEquals("Read 1", 0, input.read());
|
assertEquals(0, input.read(), "Read 1");
|
||||||
assertEquals("Read 2", 1, input.read());
|
assertEquals(1, input.read(), "Read 2");
|
||||||
assertEquals("Read 3", -1, input.read());
|
assertEquals(-1, input.read(), "Read 3");
|
||||||
assertEquals("Read 4", -1, input.read());
|
assertEquals(-1, input.read(), "Read 4");
|
||||||
assertEquals("Read 5", -1, input.read());
|
assertEquals(-1, input.read(), "Read 5");
|
||||||
}
|
}
|
||||||
|
|
||||||
@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("Wrong messge: " + e.getMessage(), e.getMessage().contains("reset"));
|
assertTrue(e.getMessage().contains("reset"), "Wrong messge: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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("Re-read of reset data should be same", 0, input.read());
|
assertEquals(0, input.read(), "Re-read of reset data should be same");
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue("Wrong no mark IOException message", e.getMessage().contains("mark"));
|
assertTrue(e.getMessage().contains("mark"), "Wrong no mark IOException message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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("Read After Mark [" + i + "]", (position + i), input.read());
|
assertEquals((position + i), input.read(), "Read After Mark [" + i + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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("Read After Reset [" + i + "]", (position + i), input.read());
|
assertEquals((position + i), input.read(), "Read After Reset [" + i + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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("Read After Reset [" + i + "]", (position + i), input.read());
|
assertEquals((position + i), input.read(), "Read After Reset [" + i + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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("Re-read of reset data should be same", 1, input.read());
|
assertEquals(1, input.read(), "Re-read of reset data should be same");
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,29 +302,29 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int first = input.read();
|
int first = input.read();
|
||||||
assertTrue("Expected to read positive value", first >= 0);
|
assertTrue(first >= 0, "Expected to read positive value");
|
||||||
|
|
||||||
int readlimit = 5;
|
int readlimit = 5;
|
||||||
|
|
||||||
// Mark
|
// Mark
|
||||||
input.mark(readlimit);
|
input.mark(readlimit);
|
||||||
int read = input.read();
|
int read = input.read();
|
||||||
assertTrue("Expected to read positive value", read >= 0);
|
assertTrue(read >= 0, "Expected to read positive value");
|
||||||
|
|
||||||
assertTrue(input.read() >= 0);
|
assertTrue(input.read() >= 0);
|
||||||
assertTrue(input.read() >= 0);
|
assertTrue(input.read() >= 0);
|
||||||
|
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals("Expected value read differs from actual", read, input.read());
|
assertEquals(read, input.read(), "Expected value read differs from actual");
|
||||||
|
|
||||||
// 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("Re-read of reset data should be same as initially marked or first", reRead == read || reRead == first);
|
assertTrue(reRead == read || reRead == first, "Re-read of reset data should be same as initially marked or first");
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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("Unexpected value read", 0, input.read());
|
assertEquals(0, input.read(), "Unexpected value read");
|
||||||
assertEquals("Unexpected value read", 1, input.read());
|
assertEquals(1, input.read(), "Unexpected value read");
|
||||||
assertEquals("Unexpected number of bytes skipped", 5, input.skip(5));
|
assertEquals(5, input.skip(5), "Unexpected number of bytes skipped");
|
||||||
assertEquals("Unexpected value read", 7, input.read());
|
assertEquals(7, input.read(), "Unexpected value read");
|
||||||
|
|
||||||
assertEquals("Unexpected number of bytes skipped", 2, input.skip(5)); // only 2 left to skip
|
assertEquals(2, input.skip(5), "Unexpected number of bytes skipped"); // only 2 left to skip
|
||||||
assertEquals("Unexpected value read after EOF", -1, input.read());
|
assertEquals(-1, input.read(), "Unexpected value read after EOF");
|
||||||
|
|
||||||
// Spec says skip might return 0 or negative after EOF...
|
// Spec says skip might return 0 or negative after EOF...
|
||||||
assertTrue("Positive value skipped after EOF", input.skip(5) <= 0); // End of file
|
assertTrue(input.skip(5) <= 0, "Positive value skipped after EOF"); // End of file
|
||||||
assertEquals("Unexpected value read after EOF", -1, input.read());
|
assertEquals(-1, input.read(), "Unexpected value read after EOF");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+2
-3
@@ -30,12 +30,11 @@
|
|||||||
|
|
||||||
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 static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LittleEndianDataInputStreamTest
|
* LittleEndianDataInputStreamTest
|
||||||
|
|||||||
@@ -31,13 +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.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
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,10 +30,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SeekableAbstractTestCase
|
* SeekableAbstractTestCase
|
||||||
|
|||||||
+22
-23
@@ -30,14 +30,13 @@
|
|||||||
|
|
||||||
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 static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SeekableInputStreamAbstractTest
|
* SeekableInputStreamAbstractTest
|
||||||
@@ -79,25 +78,25 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
return; // Not supported, skip test
|
return; // Not supported, skip test
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue("Expected to read positive value", input.read() >= 0);
|
assertTrue(input.read() >= 0, "Expected to read positive value");
|
||||||
|
|
||||||
int readlimit = 5;
|
int readlimit = 5;
|
||||||
|
|
||||||
// Mark
|
// Mark
|
||||||
input.mark(readlimit);
|
input.mark(readlimit);
|
||||||
int read = input.read();
|
int read = input.read();
|
||||||
assertTrue("Expected to read positive value", read >= 0);
|
assertTrue(read >= 0, "Expected to read positive value");
|
||||||
|
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals("Expected value read differs from actual", read, input.read());
|
assertEquals(read, input.read(), "Expected value read differs from actual");
|
||||||
|
|
||||||
// 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("Re-read of reset data should be first", 0, input.read());
|
assertEquals(0, input.read(), "Re-read of reset data should be first");
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +126,7 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
|
|
||||||
seekable.seek(pos);
|
seekable.seek(pos);
|
||||||
long streamPos = seekable.getStreamPosition();
|
long streamPos = seekable.getStreamPosition();
|
||||||
assertEquals("Stream positon should match seeked position", pos, streamPos);
|
assertEquals(pos, streamPos, "Stream positon should match seeked position");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -137,7 +136,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("Flushed positon should match position", pos, flushedPos);
|
assertEquals(pos, flushedPos, "Flushed positon should match position");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
seekable.seek(pos - 1);
|
seekable.seek(pos - 1);
|
||||||
@@ -382,13 +381,13 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
int val;
|
int val;
|
||||||
|
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse("Unexepected EOF", val == -1);
|
assertFalse(val == -1, "Unexepected EOF");
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse("Unexepected EOF", val == -1);
|
assertFalse(val == -1, "Unexepected EOF");
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse("Unexepected EOF", val == -1);
|
assertFalse(val == -1, "Unexepected EOF");
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse("Unexepected EOF", val == -1);
|
assertFalse(val == -1, "Unexepected EOF");
|
||||||
|
|
||||||
stream.seek(0);
|
stream.seek(0);
|
||||||
|
|
||||||
@@ -422,19 +421,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("Wrong stream position", i, stream.getStreamPosition());
|
assertEquals(i, stream.getStreamPosition(), "Wrong stream position");
|
||||||
int count = stream.read(buffer, 0, 2);
|
int count = stream.read(buffer, 0, 2);
|
||||||
assertEquals(2, count);
|
assertEquals(2, count);
|
||||||
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], buffer[0]);
|
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 + 1], buffer[1]);
|
assertEquals(bytes[i + 1], buffer[1], String.format("Wrong value read at pos %d", stream.getStreamPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.seek(0);
|
stream.seek(0);
|
||||||
for (int i = 0; i < bytes.length; i++) {
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
assertEquals("Wrong stream position", i, stream.getStreamPosition());
|
assertEquals(i, stream.getStreamPosition(), "Wrong stream position");
|
||||||
int actual = stream.read();
|
int actual = stream.read();
|
||||||
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i] & 0xff, actual);
|
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], (byte) actual);
|
assertEquals(bytes[i], (byte) actual, String.format("Wrong value read at pos %d", stream.getStreamPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -456,14 +455,14 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
try {
|
try {
|
||||||
FileUtil.read(stream); // Read until EOF
|
FileUtil.read(stream); // Read until EOF
|
||||||
|
|
||||||
assertEquals("EOF not reached (test case broken)", -1, stream.read());
|
assertEquals(-1, stream.read(), "EOF not reached (test case broken)");
|
||||||
assertFalse("Underlying stream closed before close", closed[0]);
|
assertFalse(closed[0], "Underlying stream closed before close");
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue("Underlying stream not closed", closed[0]);
|
assertTrue(closed[0], "Underlying stream not closed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StringArrayReaderTestCase
|
* StringArrayReaderTestCase
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
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,15 +31,13 @@
|
|||||||
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.Assert.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64DecoderTest
|
* Base64DecoderTest
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -66,7 +64,7 @@ public class Base64DecoderTest extends DecoderAbstractTest {
|
|||||||
|
|
||||||
FileUtil.copy(in, bytes);
|
FileUtil.copy(in, bytes);
|
||||||
|
|
||||||
assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
|
assertEquals("", new String(bytes.toByteArray()), "Strings does not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -78,7 +76,7 @@ public class Base64DecoderTest extends DecoderAbstractTest {
|
|||||||
|
|
||||||
FileUtil.copy(in, bytes);
|
FileUtil.copy(in, bytes);
|
||||||
|
|
||||||
assertEquals("Strings does not match", "test", new String(bytes.toByteArray()));
|
assertEquals("test", new String(bytes.toByteArray()), "Strings does not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -93,11 +91,12 @@ public class Base64DecoderTest extends DecoderAbstractTest {
|
|||||||
|
|
||||||
FileUtil.copy(in, bytes);
|
FileUtil.copy(in, bytes);
|
||||||
|
|
||||||
assertEquals("Strings does not match",
|
assertEquals(
|
||||||
"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,13 +30,12 @@
|
|||||||
|
|
||||||
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.Assert.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64EncoderTest
|
* Base64EncoderTest
|
||||||
@@ -63,7 +62,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("Strings does not match", "", new String(bytes.toByteArray()));
|
assertEquals("", new String(bytes.toByteArray()), "Strings does not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -74,7 +73,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("Strings does not match", "dGVzdA==", new String(bytes.toByteArray()));
|
assertEquals("dGVzdA==", new String(bytes.toByteArray()), "Strings does not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -88,11 +87,12 @@ 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("Strings does not match",
|
assertEquals(
|
||||||
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
|
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
|
||||||
"c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
|
"c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
|
||||||
"b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
|
"b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
|
||||||
"bmlzaSBpbiBkaWN0dW0gYW1ldC4=",
|
"bmlzaSBpbiBkaWN0dW0gYW1ldC4=",
|
||||||
new String(bytes.toByteArray()));
|
new String(bytes.toByteArray()),
|
||||||
|
"Strings does not match");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,13 @@ 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 static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractDecoderTest
|
* AbstractDecoderTest
|
||||||
@@ -55,13 +56,13 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
|
|||||||
return createDecoder();
|
return createDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
@Test
|
||||||
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
|
||||||
@@ -71,7 +72,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("Should not be able to read any bytes", 0, count);
|
assertEquals( 0, count, "Should not be able to read any bytes");
|
||||||
}
|
}
|
||||||
catch (EOFException allowed) {
|
catch (EOFException allowed) {
|
||||||
// Okay
|
// Okay
|
||||||
@@ -94,7 +95,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(String.format("Data %d", pLength), data, decoded);
|
assertArrayEquals(data, decoded, String.format("Data %d", pLength));
|
||||||
|
|
||||||
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
|
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
|
||||||
outBytes = new ByteArrayOutputStream();
|
outBytes = new ByteArrayOutputStream();
|
||||||
@@ -103,7 +104,7 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
|
|||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
decoded = outBytes.toByteArray();
|
decoded = outBytes.toByteArray();
|
||||||
assertArrayEquals(String.format("Data %d", pLength), data, decoded);
|
assertArrayEquals(data, decoded, String.format("Data %d", pLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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,14 +32,13 @@ 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 static org.junit.Assert.assertTrue;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractEncoderTest
|
* AbstractEncoderTest
|
||||||
@@ -73,7 +72,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createData(final int pLength) throws Exception {
|
private byte[] createData(final int pLength) {
|
||||||
byte[] bytes = new byte[pLength];
|
byte[] bytes = new byte[pLength];
|
||||||
RANDOM.nextBytes(bytes);
|
RANDOM.nextBytes(bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
@@ -82,9 +81,8 @@ 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 {
|
try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
|
||||||
// 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);
|
||||||
@@ -92,9 +90,6 @@ 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();
|
||||||
|
|
||||||
@@ -102,7 +97,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()));
|
||||||
assertTrue(Arrays.equals(data, decoded));
|
assertArrayEquals(data, decoded);
|
||||||
|
|
||||||
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
|
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
|
||||||
outBytes = new ByteArrayOutputStream();
|
outBytes = new ByteArrayOutputStream();
|
||||||
@@ -116,7 +111,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decoded = outBytes.toByteArray();
|
decoded = outBytes.toByteArray();
|
||||||
assertTrue(Arrays.equals(data, decoded));
|
assertArrayEquals(data, decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -129,10 +124,6 @@ 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) {
|
||||||
@@ -143,10 +134,6 @@ 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) {
|
||||||
@@ -157,14 +144,8 @@ 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.
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
-48
@@ -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 static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
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("Missing test resource!", input);
|
assertNotNull(input, "Missing test resource!");
|
||||||
assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
|
assertEquals( "file", input.getProtocol(), "Test resource not a file:// resource");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new CompoundDocument(new File(input.toURI()));
|
return new CompoundDocument(new File(input.toURI()));
|
||||||
@@ -72,71 +72,72 @@ public class CompoundDocumentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRoot() throws IOException {
|
public void testRoot() throws IOException {
|
||||||
CompoundDocument document = createTestDocument();
|
try (CompoundDocument document = createTestDocument()) {
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
Entry root = document.getRootEntry();
|
assertNotNull(root);
|
||||||
|
assertEquals("Root Entry", root.getName());
|
||||||
assertNotNull(root);
|
assertTrue(root.isRoot());
|
||||||
assertEquals("Root Entry", root.getName());
|
assertFalse(root.isFile());
|
||||||
assertTrue(root.isRoot());
|
assertFalse(root.isDirectory());
|
||||||
assertFalse(root.isFile());
|
assertEquals(0, root.length());
|
||||||
assertFalse(root.isDirectory());
|
assertNull(root.getInputStream());
|
||||||
assertEquals(0, root.length());
|
}
|
||||||
assertNull(root.getInputStream());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContents() throws IOException {
|
public void testContents() throws IOException {
|
||||||
CompoundDocument document = createTestDocument();
|
try (CompoundDocument document = createTestDocument()) {
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
Entry root = document.getRootEntry();
|
assertNotNull(root);
|
||||||
|
|
||||||
assertNotNull(root);
|
SortedSet<Entry> children = new TreeSet<Entry>(root.getChildEntries());
|
||||||
|
assertEquals(25, children.size());
|
||||||
|
|
||||||
SortedSet<Entry> children = new TreeSet<Entry>(root.getChildEntries());
|
// Weirdness in the file format, name is *written backwards* 1-24 + Catalog
|
||||||
assertEquals(25, children.size());
|
for (String name : "1,2,3,4,5,6,7,8,9,01,02,11,12,21,22,31,32,41,42,51,61,71,81,91,Catalog".split(",")) {
|
||||||
|
assertEquals(name, children.first().getName());
|
||||||
// Weirdness in the file format, name is *written backwards* 1-24 + Catalog
|
children.remove(children.first());
|
||||||
for (String name : "1,2,3,4,5,6,7,8,9,01,02,11,12,21,22,31,32,41,42,51,61,71,81,91,Catalog".split(",")) {
|
}
|
||||||
assertEquals(name, children.first().getName());
|
|
||||||
children.remove(children.first());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = UnsupportedOperationException.class)
|
@Test
|
||||||
public void testChildEntriesUnmodifiable() throws IOException {
|
public void testChildEntriesUnmodifiable() throws IOException {
|
||||||
CompoundDocument document = createTestDocument();
|
try (CompoundDocument document = createTestDocument()) {
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
Entry root = document.getRootEntry();
|
assertNotNull(root);
|
||||||
|
|
||||||
assertNotNull(root);
|
SortedSet<Entry> children = root.getChildEntries();
|
||||||
|
assertThrows(UnsupportedOperationException.class, () -> {
|
||||||
SortedSet<Entry> children = root.getChildEntries();
|
// Should not be allowed, as it modifies the internal structure
|
||||||
|
children.remove(children.first());
|
||||||
// Should not be allowed, as it modifies the internal structure
|
});
|
||||||
children.remove(children.first());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadThumbsCatalogFile() throws IOException {
|
public void testReadThumbsCatalogFile() throws IOException {
|
||||||
CompoundDocument document = createTestDocument();
|
try (CompoundDocument document = createTestDocument()) {
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
Entry root = document.getRootEntry();
|
assertNotNull(root);
|
||||||
|
assertEquals(25, root.getChildEntries().size());
|
||||||
|
|
||||||
assertNotNull(root);
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
assertEquals(25, root.getChildEntries().size());
|
|
||||||
|
|
||||||
Entry catalog = root.getChildEntry("Catalog");
|
assertNotNull(catalog);
|
||||||
|
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
||||||
assertNotNull(catalog);
|
}
|
||||||
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadCatalogInputStream() throws IOException {
|
public void testReadCatalogInputStream() throws IOException {
|
||||||
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
||||||
|
|
||||||
assertNotNull("Missing test resource!", input);
|
assertNotNull(input, "Missing test resource!");
|
||||||
|
|
||||||
CompoundDocument document = new CompoundDocument(input);
|
CompoundDocument document = new CompoundDocument(input);
|
||||||
Entry root = document.getRootEntry();
|
Entry root = document.getRootEntry();
|
||||||
@@ -145,14 +146,14 @@ public class CompoundDocumentTest {
|
|||||||
|
|
||||||
Entry catalog = root.getChildEntry("Catalog");
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
assertNotNull(catalog);
|
assertNotNull(catalog);
|
||||||
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@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("Missing test resource!", input);
|
assertNotNull(input, "Missing test resource!");
|
||||||
|
|
||||||
CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input));
|
CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input));
|
||||||
Entry root = document.getRootEntry();
|
Entry root = document.getRootEntry();
|
||||||
@@ -161,14 +162,14 @@ public class CompoundDocumentTest {
|
|||||||
|
|
||||||
Entry catalog = root.getChildEntry("Catalog");
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
assertNotNull(catalog);
|
assertNotNull(catalog);
|
||||||
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@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("Missing test resource!", input);
|
assertNotNull(input, "Missing test resource!");
|
||||||
|
|
||||||
MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input);
|
MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input);
|
||||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||||
@@ -183,6 +184,6 @@ public class CompoundDocumentTest {
|
|||||||
Entry catalog = root.getChildEntry("Catalog");
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
|
|
||||||
assertNotNull(catalog);
|
assertNotNull(catalog);
|
||||||
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
|||||||
+9
-39
@@ -33,16 +33,16 @@ 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 com.twelvemonkeys.io.SeekableInputStream;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.net.URISyntaxException;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.net.URL;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompoundDocument_StreamTestCase
|
* CompoundDocument_StreamTestCase
|
||||||
@@ -51,35 +51,7 @@ import static org.junit.Assert.*;
|
|||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: CompoundDocument_StreamTestCase.java,v 1.0 13.10.11 12:01 haraldk Exp$
|
* @version $Id: CompoundDocument_StreamTestCase.java,v 1.0 13.10.11 12:01 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
//@Ignore("Need proper in-memory creation of CompoundDocuments")
|
|
||||||
public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
|
public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
|
||||||
private static final String SAMPLE_DATA = "/Thumbs-camera.db";
|
|
||||||
|
|
||||||
protected final CompoundDocument createTestDocument() throws IOException {
|
|
||||||
URL input = getClass().getResource(SAMPLE_DATA);
|
|
||||||
|
|
||||||
assertNotNull("Missing test resource!", input);
|
|
||||||
assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
|
|
||||||
|
|
||||||
try {
|
|
||||||
return new CompoundDocument(new File(input.toURI()));
|
|
||||||
}
|
|
||||||
catch (URISyntaxException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SeekableInputStream createRealInputStream() {
|
|
||||||
try {
|
|
||||||
Entry first = createTestDocument().getRootEntry().getChildEntries().first();
|
|
||||||
assertNotNull(first);
|
|
||||||
return first.getInputStream();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream makeInputStream(byte[] data) {
|
protected InputStream makeInputStream(byte[] data) {
|
||||||
try {
|
try {
|
||||||
@@ -182,21 +154,19 @@ public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
|
|||||||
return pad;
|
return pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testDev() throws IOException {
|
public void testStreamRead() throws IOException {
|
||||||
InputStream stream = makeInputStream(makeOrderedArray(32));
|
InputStream stream = makeInputStream(makeOrderedArray(32));
|
||||||
|
|
||||||
int read;
|
int read;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while ((read = stream.read()) >= 0) {
|
while ((read = stream.read()) >= 0) {
|
||||||
// System.out.printf("read %02d: 0x%02x%n", count, read & 0xFF);
|
|
||||||
assertEquals(count, read);
|
assertEquals(count, read);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFalse("Short stream", count < 32);
|
assertFalse(count < 32, "Short stream");
|
||||||
assertFalse("Stream overrun", count > 32);
|
assertFalse(count > 32, "Stream overrun");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -30,9 +30,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.net;
|
package com.twelvemonkeys.net;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTPUtilTest
|
* HTTPUtilTest
|
||||||
|
|||||||
@@ -4,13 +4,25 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.4</version>
|
<version>3.13.2-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>
|
||||||
The TwelveMonkeys Common Language support
|
TwelveMonkeys Common language support classes.
|
||||||
</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>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user