1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug     8152183 8149562 8169725
  27  * @author  a.stepanov
  28  * @summary Some checks for TIFFField methods
  29  * @run     main TIFFFieldTest
  30  */
  31 
  32 import java.util.List;
  33 import java.util.ArrayList;
  34 import javax.imageio.metadata.IIOMetadataNode;
  35 import javax.imageio.plugins.tiff.*;
  36 import org.w3c.dom.NamedNodeMap;
  37 import org.w3c.dom.Node;
  38 
  39 public class TIFFFieldTest {
  40 
  41     private final static String NAME = "tag"; // tag name
  42     private final static int    NUM  = 12345; // tag number
  43     private final static int MIN_TYPE = TIFFTag.MIN_DATATYPE;
  44     private final static int MAX_TYPE = TIFFTag.MAX_DATATYPE;
  45     private final static String CONSTRUCT = "can construct TIFFField with ";
  46 
  47     private void check(boolean ok, String msg) {
  48         if (!ok) { throw new RuntimeException(msg); }
  49     }
  50 
  51     private void testConstructors() {
  52 
  53         // test constructors
  54 
  55         TIFFTag tag = new TIFFTag(
  56             NAME, NUM, 1 << TIFFTag.TIFF_SHORT | 1 << TIFFTag.TIFF_LONG);
  57         TIFFField f;
  58 
  59         // constructor: TIFFField(tag, value)
  60         boolean ok = false;
  61         try { new TIFFField(null, 0); }
  62         catch (NullPointerException e) { ok = true; }
  63         check(ok, CONSTRUCT + "null tag");
  64 
  65         ok = false;
  66         try { new TIFFField(tag, -1); }
  67         catch (IllegalArgumentException e) { ok = true; }
  68         check(ok, CONSTRUCT + "negative value");
  69 
  70         ok = false;
  71         try { new TIFFField(tag, 1L << 32); }
  72         catch (IllegalArgumentException e) { ok = true; }
  73         check(ok, CONSTRUCT + "value > 0xffffffff");
  74 
  75         ok = false;
  76         try {
  77             TIFFTag t = new TIFFTag(NAME, NUM, 1 << TIFFTag.TIFF_SHORT);
  78             new TIFFField(t, 0x10000);
  79         } catch (IllegalArgumentException e) { ok = true; }
  80         check(ok, CONSTRUCT + "value 0x10000 incompatible with TIFF_SHORT");
  81 
  82         ok = false;
  83         try {
  84             TIFFTag t = new TIFFTag(NAME, NUM, 1 << TIFFTag.TIFF_LONG);
  85             new TIFFField(t, 0xffff);
  86         } catch (IllegalArgumentException e) { ok = true; }
  87         check(ok, CONSTRUCT + "value 0xffff incompatible with TIFF_LONG");
  88 
  89         // check value type recognition
  90         int v = 1 << 16;
  91         f = new TIFFField(tag, v - 1);
  92         check(f.getType() == TIFFTag.TIFF_SHORT, "must be treated as short");
  93         check(f.isIntegral(), "must be integral");
  94         f = new TIFFField(tag, v);
  95         check(f.getType() == TIFFTag.TIFF_LONG, "must be treated as long");
  96 
  97         // other checks
  98         check(f.getAsLongs().length == 1, "invalid long[] size");
  99         check(f.isIntegral(), "must be integral");
 100         check((f.getDirectory() == null) && !f.hasDirectory(),
 101             "must not have directory");
 102         check(f.getValueAsString(0).equals(String.valueOf(v)),
 103             "invalid string representation of value");
 104         check(f.getTag().getNumber() == f.getTagNumber(),
 105             "invalid tag number");
 106         check(f.getCount() == 1, "invalid count");
 107         check(f.getTagNumber() == NUM, "invalid tag number");
 108 
 109         // constructor: TIFFField(tag, type, count)
 110         int type = TIFFTag.TIFF_SHORT;
 111 
 112         ok = false;
 113         try { new TIFFField(null, type, 1); }
 114         catch (NullPointerException e) { ok = true; }
 115         check(ok, CONSTRUCT + "null tag");
 116 
 117         ok = false;
 118         try { new TIFFField(tag, MAX_TYPE + 1, 1); }
 119         catch (IllegalArgumentException e) { ok = true; }
 120         check(ok, CONSTRUCT + "invalid type tag");
 121 
 122         // check that count == 1 for TIFF_IFD_POINTER
 123         ok = false;
 124         try { new TIFFField(tag, TIFFTag.TIFF_IFD_POINTER, 0); }
 125         catch (IllegalArgumentException e) { ok = true; }
 126         check(ok, "only count = 1 should be allowed for IFDPointer");
 127 
 128         ok = false;
 129         try { new TIFFField(tag, TIFFTag.TIFF_IFD_POINTER, 2); }
 130         catch (IllegalArgumentException e) { ok = true; }
 131         check(ok, "only count = 1 should be allowed for IFDPointer");
 132 
 133         // check that count == 0 is not allowed for TIFF_RATIONAL, TIFF_SRATIONAL
 134         // (see fix for JDK-8149120)
 135         ok = false;
 136         try { new TIFFField(tag, TIFFTag.TIFF_RATIONAL, 0); }
 137         catch (IllegalArgumentException e) { ok = true; }
 138         check(ok, "count = 0 should not be allowed for Rational");
 139 
 140         ok = false;
 141         try { new TIFFField(tag, TIFFTag.TIFF_SRATIONAL, 0); }
 142         catch (IllegalArgumentException e) { ok = true; }
 143         check(ok, "count = 0 should not be allowed for SRational");
 144 
 145         ok = false;
 146         try { new TIFFField(tag, type, -1); }
 147         catch (IllegalArgumentException e) { ok = true; }
 148         check(ok, CONSTRUCT + "with invalid data count");
 149 
 150         f = new TIFFField(tag, type, 0);
 151         check(f.getCount() == 0, "invalid count");
 152         check(!f.hasDirectory(), "must not have directory");
 153 
 154         // constructor: TIFFField(tag, type, count, data)
 155         double a[] = {0.1, 0.2, 0.3};
 156         ok = false;
 157         try { new TIFFField(null, TIFFTag.TIFF_DOUBLE, a.length, a); }
 158         catch (NullPointerException e) { ok = true; }
 159         check(ok, CONSTRUCT + "null tag");
 160 
 161         ok = false;
 162         try { new TIFFField(tag, type, a.length - 1, a); }
 163         catch (IllegalArgumentException e) { ok = true; }
 164         check(ok, CONSTRUCT + "invalid data count");
 165 
 166         String a2[] = {"one", "two"};
 167         ok = false;
 168         try { new TIFFField(tag, type, 2, a2); }
 169         catch (IllegalArgumentException e) { ok = true; }
 170         check(ok, CONSTRUCT + "invalid data type");
 171         check((f.getDirectory() == null) && !f.hasDirectory(),
 172             "must not have directory");
 173 
 174         // constructor: TIFFField(tag, type, offset, dir)
 175         List<TIFFTag> tags = new ArrayList<>();
 176         tags.add(tag);
 177         TIFFTagSet sets[] = {new TIFFTagSet(tags)};
 178         TIFFDirectory dir = new TIFFDirectory(sets, null);
 179 
 180         ok = false;
 181         try { new TIFFField(null, type, 4L, dir); }
 182         catch (NullPointerException e) { ok = true; }
 183         check(ok, CONSTRUCT + "null tag");
 184 
 185         ok = false;
 186         try { new TIFFField(tag, type, 0L, dir); }
 187         catch (IllegalArgumentException e) { ok = true; }
 188         check(ok, CONSTRUCT + "non-positive offset");
 189 
 190         long offset = 4;
 191 
 192         for (int t = MIN_TYPE; t <= MAX_TYPE; t++) {
 193 
 194             tag = new TIFFTag(NAME, NUM, 1 << t);
 195 
 196             // only TIFF_LONG and TIFF_IFD_POINTER types are allowed
 197             if (t == TIFFTag.TIFF_LONG || t == TIFFTag.TIFF_IFD_POINTER) {
 198 
 199                 f = new TIFFField(tag, t, offset, dir);
 200                 check(f.hasDirectory(), "must have directory");
 201 
 202                 check(f.getDirectory().getTag(NUM).getName().equals(NAME),
 203                     "invalid tag name");
 204 
 205                 check(f.getCount() == 1, "invalid count");
 206                 check(f.getAsLong(0) == offset, "invalid offset");
 207             } else {
 208                 ok = false;
 209                 try { new TIFFField(tag, t, offset, dir); }
 210                 catch (IllegalArgumentException e) { ok = true; }
 211                 check(ok, CONSTRUCT + "invalid data type");
 212             }
 213         }
 214 
 215         type = TIFFTag.TIFF_IFD_POINTER;
 216         tag = new TIFFTag(NAME, NUM, 1 << type);
 217         ok = false;
 218         try { new TIFFField(tag, type, offset, null); }
 219         catch (NullPointerException e) { ok = true; }
 220         check(ok, CONSTRUCT + "null TIFFDirectory");
 221 
 222         type = TIFFTag.TIFF_LONG;
 223         tag = new TIFFTag(NAME, NUM, 1 << type);
 224         ok = false;
 225         try { new TIFFField(tag, type, offset, null); }
 226         catch (NullPointerException e) { ok = true; }
 227         check(ok, CONSTRUCT + "null TIFFDirectory");
 228     }
 229 
 230     private void testTypes() {
 231 
 232         // test getTypeName(), getTypeByName() methods
 233 
 234         boolean ok = false;
 235         try { TIFFField.getTypeName(MIN_TYPE - 1); }
 236         catch (IllegalArgumentException e) { ok = true; }
 237         check(ok, "invalid data type number used");
 238 
 239         ok = false;
 240         try { TIFFField.getTypeName(MAX_TYPE + 1); }
 241         catch (IllegalArgumentException e) { ok = true; }
 242         check(ok, "invalid data type number used");
 243 
 244         for (int type = MIN_TYPE; type <= MAX_TYPE; type++) {
 245             String name = TIFFField.getTypeName(type);
 246             check(TIFFField.getTypeByName(name) == type, "invalid type");
 247         }
 248 
 249         for (int type = MIN_TYPE; type <= MAX_TYPE; type++) {
 250 
 251             TIFFTag tag = new TIFFTag(NAME, NUM, 1 << type);
 252             TIFFField f = new TIFFField(tag, type, 1);
 253             check(f.getType() == type, "invalid type");
 254 
 255             // check that invalid data types can not be used
 256             for (int type2 = MIN_TYPE; type2 <= MAX_TYPE; ++type2) {
 257                 if (type2 != type) {
 258                     ok = false;
 259                     try { new TIFFField(tag, type2, 1); } // invalid type
 260                     catch (IllegalArgumentException e) { ok = true; }
 261                     check(ok, "invalid type was successfully set");
 262                 }
 263             }
 264         }
 265     }
 266 
 267     private void testGetAs() {
 268 
 269         // test getAs...() methods
 270 
 271         int type = TIFFTag.TIFF_SHORT;
 272         TIFFTag tag = new TIFFTag(NAME, NUM, 1 << TIFFTag.TIFF_SHORT);
 273 
 274         short v = 123;
 275         TIFFField f = new TIFFField(tag, v);
 276 
 277         check(f.getAsInt(0)    ==    (int) v, "invalid int value");
 278         check(f.getAsLong(0)   ==   (long) v, "invalid long value");
 279         check(f.getAsFloat(0)  ==  (float) v, "invalid float value");
 280         check(f.getAsDouble(0) == (double) v, "invalid double value");
 281         check(f.getValueAsString(0).equals(Short.toString(v)),
 282             "invalid string representation");
 283 
 284         check(f.getAsInts().length == 1, "inavlid array size");
 285         check((int) v == f.getAsInts()[0], "invalid int value");
 286 
 287         float fa[] = {0.01f, 1.01f};
 288         type = TIFFTag.TIFF_FLOAT;
 289         f = new TIFFField(
 290             new TIFFTag(NAME, NUM, 1 << type), type, fa.length, fa);
 291         check(f.getCount() == fa.length, "invalid count");
 292         float fa2[] = f.getAsFloats();
 293         check(fa2.length == fa.length, "invalid array size");
 294 
 295         for (int i = 0; i < fa.length; i++) {
 296             check(fa2[i] == fa[i], "invalid value");
 297             check(f.getAsDouble(i) == fa[i], "invalid value");
 298             check(f.getAsInt(i) == (int) fa[i], "invalid value"); // cast to int
 299             check(f.getValueAsString(i).equals(Float.toString(fa[i])),
 300             "invalid string representation");
 301         }
 302 
 303         byte ba[] = {-1, -10, -100};
 304         type = TIFFTag.TIFF_BYTE;
 305         f = new TIFFField(
 306             new TIFFTag(NAME, NUM, 1 << type), type, ba.length, ba);
 307         check(f.getCount() == ba.length, "invalid count");
 308         byte ba2[] = f.getAsBytes();
 309         check(ba2.length == ba.length, "invalid count");
 310 
 311         for (int i = 0; i < ba.length; i++) {
 312             check(ba[i] == ba2[i], "invalid value");
 313             check(ba[i] == (byte) f.getAsDouble(i), "invalid value");
 314             check(ba[i] == (byte) f.getAsLong(i),   "invalid value");
 315 
 316             int unsigned = ba[i] & 0xff;
 317             check(f.getAsInt(i) == unsigned, "must be treated as unsigned");
 318         }
 319 
 320         char ca[] = {'a', 'z', 0xffff};
 321         type = TIFFTag.TIFF_SHORT;
 322         f = new TIFFField(
 323             new TIFFTag(NAME, NUM, 1 << type), type, ca.length, ca);
 324         check(f.getCount() == ca.length, "invalid count");
 325         char ca2[] = f.getAsChars();
 326         check(ba2.length == ba.length, "invalid count");
 327 
 328         for (int i = 0; i < ca.length; i++) {
 329             check(ca[i] == ca2[i], "invalid value");
 330             check(ca[i] == (char) f.getAsDouble(i), "invalid value");
 331             check(ca[i] == (char) f.getAsLong(i), "invalid value");
 332             check(ca[i] == (char) f.getAsInt(i), "invalid value");
 333         }
 334 
 335         type = TIFFTag.TIFF_DOUBLE;
 336         double da[] = {0.1, 0.2, 0.3};
 337         f = new TIFFField(
 338             new TIFFTag(NAME, NUM, 1 << type), type, da.length, da);
 339         check(!f.isIntegral(), "isIntegral must be false");
 340 
 341         double da2[] = f.getAsDoubles();
 342         check(f.getData() instanceof double[], "invalid data type");
 343         double da3[] = (double[]) f.getData();
 344         check((da.length == da2.length) &&
 345               (da.length == da2.length) &&
 346               (da.length == f.getCount()),
 347                "invalid data count");
 348         for (int i = 0; i < da.length; ++i) {
 349             check(da[i] == da2[i], "invalid data");
 350             check(da[i] == da3[i], "invalid data");
 351         }
 352 
 353         boolean ok = false;
 354         try { f.getAsShorts(); }
 355         catch (ClassCastException e) { ok = true; }
 356         check(ok, "invalid data cast");
 357 
 358         ok = false;
 359         try { f.getAsRationals(); }
 360         catch (ClassCastException e) { ok = true; }
 361         check(ok, "invalid data cast");
 362 
 363         ok = false;
 364         try { TIFFField.createArrayForType(TIFFTag.MIN_DATATYPE - 1, 1); }
 365         catch (IllegalArgumentException e) { ok = true; }
 366         check(ok, "can create array with invalid datatype");
 367 
 368         ok = false;
 369         try { TIFFField.createArrayForType(TIFFTag.MAX_DATATYPE + 1, 1); }
 370         catch (IllegalArgumentException e) { ok = true; }
 371         check(ok, "can create array with invalid datatype");
 372 
 373         ok = false;
 374         try { TIFFField.createArrayForType(TIFFTag.TIFF_FLOAT, -1); }
 375         catch (IllegalArgumentException e) { ok = true; }
 376         check(ok, "can create array with negative count");
 377 
 378         int n = 3;
 379         Object
 380             RA  = TIFFField.createArrayForType(TIFFTag.TIFF_RATIONAL,  n),
 381             SRA = TIFFField.createArrayForType(TIFFTag.TIFF_SRATIONAL, n);
 382         check(RA  instanceof long[][], "invalid data type");
 383         check(SRA instanceof  int[][], "invalid data type");
 384 
 385         long ra[][] = (long[][]) RA;
 386         int sra[][] = (int[][]) SRA;
 387         check((ra.length == n) && (sra.length == n), "invalid data size");
 388         for (int i = 0; i < n; i++) {
 389             check((ra[i].length == 2) && (sra[i].length == 2),
 390                 "invalid data size");
 391             ra[i][0]  =  1;  ra[i][1]  = 5 + i;
 392             sra[i][0] = -1;  sra[i][1] = 5 + i;
 393         }
 394 
 395         type = TIFFTag.TIFF_RATIONAL;
 396         TIFFField f1 = new TIFFField(
 397             new TIFFTag(NAME, NUM, 1 << type), type, n, ra);
 398         type = TIFFTag.TIFF_SRATIONAL;
 399         TIFFField f2 = new TIFFField(
 400             new TIFFTag(NAME, NUM, 1 << type), type, n, sra);
 401 
 402         check((f1.getCount() == ra.length) && (f2.getCount() == sra.length),
 403             "invalid data count");
 404 
 405         check(f1.getAsRationals().length  == n, "invalid data count");
 406         check(f2.getAsSRationals().length == n, "invalid data count");
 407         for (int i = 0; i < n; i++) {
 408             long r[] = f1.getAsRational(i);
 409             check(r.length == 2, "invalid data format");
 410             check((r[0] == 1) && (r[1] == i + 5), "invalid data");
 411 
 412             int sr[] = f2.getAsSRational(i);
 413             check(sr.length == 2, "invalid data format");
 414             check((sr[0] == -1) && (sr[1] == i + 5), "invalid data");
 415 
 416             // check string representation
 417             String s = Long.toString(r[0]) + "/" + Long.toString(r[1]);
 418             check(s.equals(f1.getValueAsString(i)),
 419                 "invalid string representation");
 420 
 421             s = Integer.toString(sr[0]) + "/" + Integer.toString(sr[1]);
 422             check(s.equals(f2.getValueAsString(i)),
 423                 "invalid string representation");
 424 
 425             // see the documentation for getAsInt:
 426             // TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated
 427             // by dividing the numerator into the denominator using
 428             // double-precision arithmetic and then casting to int
 429             check(f1.getAsInt(i) == (int)(r[0] / r[1]),
 430                 "invalid result for getAsInt");
 431             check(f2.getAsInt(i) == (int)(r[0] / r[1]),
 432                 "invalid result for getAsInt");
 433         }
 434 
 435         ok = false;
 436         try { f1.getAsRational(ra.length); }
 437         catch (ArrayIndexOutOfBoundsException e) { ok = true; }
 438         check(ok, "invalid index");
 439 
 440         String sa[] = {"-1.e-25", "22", "-1.23E5"};
 441         type = TIFFTag.TIFF_ASCII;
 442         f = new TIFFField(
 443             new TIFFTag(NAME, NUM, 1 << type), type, sa.length, sa);
 444 
 445         // test clone() method
 446         TIFFField cloned = null;
 447         try { cloned = f.clone(); } catch (CloneNotSupportedException e) {
 448             throw new RuntimeException(e);
 449         }
 450 
 451         check(f.getCount() == cloned.getCount(), "invalid cloned field count");
 452 
 453         check(f.getCount() == sa.length, "invalid data count");
 454         for (int i = 0; i < sa.length; i++) {
 455             check(sa[i].equals(f.getAsString(i)), "invalid data");
 456             // see docs: "data in TIFF_ASCII format will be parsed as by
 457             // the Double.parseDouble method, with the result cast to int"
 458             check(f.getAsInt(i) ==
 459                 (int) Double.parseDouble(sa[i]), "invalid data");
 460             check(f.getAsDouble(i) == Double.parseDouble(sa[i]), "invalid data");
 461 
 462             check(sa[i].equals(cloned.getAsString(i)), "invalid cloned data");
 463         }
 464     }
 465 
 466     private void testCreateFromNode() {
 467 
 468         int type = TIFFTag.TIFF_LONG;
 469 
 470         List<TIFFTag> tags = new ArrayList<>();
 471         int v = 1234567;
 472         TIFFTag tag = new TIFFTag(NAME, NUM, 1 << type);
 473         tags.add(tag);
 474         TIFFTagSet ts = new TIFFTagSet(tags);
 475 
 476         boolean ok = false;
 477         try {
 478             TIFFField.createFromMetadataNode(ts, null);
 479         } catch (IllegalArgumentException e) {
 480             // createFromMetadataNode() formerly threw a NullPointerException
 481             // if its Node parameter was null, but the specification has been
 482             // modified to allow only IllegalArgumentExceptions, perhaps with
 483             // a cause set. In the present invocation the cause would be set
 484             // to a NullPointerException but this is not explicitly specified
 485             // hence not verified here.
 486             ok = true;
 487         }
 488         check(ok, "can create TIFFField from a null node");
 489 
 490         TIFFField f = new TIFFField(tag, v);
 491         Node node = f.getAsNativeNode();
 492         check(node.getNodeName().equals(f.getClass().getSimpleName()),
 493             "invalid node name");
 494 
 495         NamedNodeMap attrs = node.getAttributes();
 496         for (int i = 0; i < attrs.getLength(); i++) {
 497             String an = attrs.item(i).getNodeName().toLowerCase();
 498             String av = attrs.item(i).getNodeValue();
 499             if (an.contains("name")) {
 500                 check(av.equals(NAME), "invalid tag name");
 501             } else if (an.contains("number")) {
 502                 check(av.equals(Integer.toString(NUM)), "invalid tag number");
 503             }
 504         }
 505 
 506         // invalid node
 507         IIOMetadataNode nok = new IIOMetadataNode("NOK");
 508 
 509         ok = false;
 510         try { TIFFField.createFromMetadataNode(ts, nok); }
 511         catch (IllegalArgumentException e) { ok = true; }
 512         check(ok, CONSTRUCT + "invalid node name");
 513 
 514         TIFFField f2 = TIFFField.createFromMetadataNode(ts, node);
 515         check(f2.getType() == type, "invalid type");
 516         check(f2.getTagNumber() == NUM, "invalid tag number");
 517         check(f2.getTag().getName().equals(NAME), "invalid tag name");
 518         check(f2.getCount()  == 1, "invalid count");
 519         check(f2.getAsInt(0) == v, "invalid value");
 520     }
 521 
 522     public static void main(String[] args) {
 523 
 524         TIFFFieldTest test = new TIFFFieldTest();
 525         test.testConstructors();
 526         test.testCreateFromNode();
 527         test.testTypes();
 528         test.testGetAs();
 529     }
 530 }