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
  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 + "invalid count");
  69 
  70         // check value type recognition
  71         int v = 1 << 16;
  72         f = new TIFFField(tag, v - 1);
  73         check(f.getType() == TIFFTag.TIFF_SHORT, "must be treated as short");
  74         check(f.isIntegral(), "must be integral");
  75         f = new TIFFField(tag, v);
  76         check(f.getType() == TIFFTag.TIFF_LONG, "must be treated as long");
  77 
  78         // other checks
  79         check(f.getAsLongs().length == 1, "invalid long[] size");
  80         check(f.isIntegral(), "must be integral");
  81         check((f.getDirectory() == null) && !f.hasDirectory(),
  82             "must not have directory");
  83         check(f.getValueAsString(0).equals(String.valueOf(v)),
  84             "invalid string representation of value");
  85         check(f.getTag().getNumber() == f.getTagNumber(),
  86             "invalid tag number");
  87         check(f.getCount() == 1, "invalid count");
  88         check(f.getTagNumber() == NUM, "invalid tag number");
  89 
  90         // constructor: TIFFField(tag, type, count)
  91         int type = TIFFTag.TIFF_SHORT;
  92 
  93         ok = false;
  94         try { new TIFFField(null, type, 1); }
  95         catch (NullPointerException e) { ok = true; }
  96         check(ok, CONSTRUCT + "null tag");
  97 
  98         ok = false;
  99         try { new TIFFField(tag, MAX_TYPE + 1, 1); }
 100         catch (IllegalArgumentException e) { ok = true; }
 101         check(ok, CONSTRUCT + "invalid type tag");
 102 
 103         // check that count == 1 for TIFF_IFD_POINTER
 104         ok = false;
 105         try { new TIFFField(tag, TIFFTag.TIFF_IFD_POINTER, 0); }
 106         catch (IllegalArgumentException e) { ok = true; }
 107         check(ok, "only count = 1 should be allowed for IFDPointer");
 108 
 109         ok = false;
 110         try { new TIFFField(tag, TIFFTag.TIFF_IFD_POINTER, 2); }
 111         catch (IllegalArgumentException e) { ok = true; }
 112         check(ok, "only count = 1 should be allowed for IFDPointer");
 113 
 114         // check that count == 0 is not allowed for TIFF_RATIONAL, TIFF_SRATIONAL
 115         // (see fix for JDK-8149120)
 116         ok = false;
 117         try { new TIFFField(tag, TIFFTag.TIFF_RATIONAL, 0); }
 118         catch (IllegalArgumentException e) { ok = true; }
 119         check(ok, "count = 0 should not be allowed for Rational");
 120 
 121         ok = false;
 122         try { new TIFFField(tag, TIFFTag.TIFF_SRATIONAL, 0); }
 123         catch (IllegalArgumentException e) { ok = true; }
 124         check(ok, "count = 0 should not be allowed for SRational");
 125 
 126         ok = false;
 127         try { new TIFFField(tag, type, -1); }
 128         catch (IllegalArgumentException e) { ok = true; }
 129         check(ok, CONSTRUCT + "with invalid data count");
 130 
 131         f = new TIFFField(tag, type, 0);
 132         check(f.getCount() == 0, "invalid count");
 133         check(!f.hasDirectory(), "must not have directory");
 134 
 135         // constructor: TIFFField(tag, type, count, data)
 136         double a[] = {0.1, 0.2, 0.3};
 137         ok = false;
 138         try { new TIFFField(null, TIFFTag.TIFF_DOUBLE, a.length, a); }
 139         catch (NullPointerException e) { ok = true; }
 140         check(ok, CONSTRUCT + "null tag");
 141 
 142         ok = false;
 143         try { new TIFFField(tag, type, a.length - 1, a); }
 144         catch (IllegalArgumentException e) { ok = true; }
 145         check(ok, CONSTRUCT + "invalid data count");
 146 
 147         String a2[] = {"one", "two"};
 148         ok = false;
 149         try { new TIFFField(tag, type, 2, a2); }
 150         catch (IllegalArgumentException e) { ok = true; }
 151         check(ok, CONSTRUCT + "invalid data type");
 152         check((f.getDirectory() == null) && !f.hasDirectory(),
 153             "must not have directory");
 154 
 155         // constructor: TIFFField(tag, type, offset, dir)
 156         List<TIFFTag> tags = new ArrayList<>();
 157         tags.add(tag);
 158         TIFFTagSet sets[] = {new TIFFTagSet(tags)};
 159         TIFFDirectory dir = new TIFFDirectory(sets, null);
 160 
 161         ok = false;
 162         try { new TIFFField(null, type, 4L, dir); }
 163         catch (NullPointerException e) { ok = true; }
 164         check(ok, CONSTRUCT + "null tag");
 165 
 166         ok = false;
 167         try { new TIFFField(tag, type, 0L, dir); }
 168         catch (IllegalArgumentException e) { ok = true; }
 169         check(ok, CONSTRUCT + "non-positive offset");
 170 
 171         long offset = 4;
 172 
 173         for (int t = MIN_TYPE; t <= MAX_TYPE; t++) {
 174 
 175             tag = new TIFFTag(NAME, NUM, 1 << t);
 176 
 177             // only TIFF_LONG and TIFF_IFD_POINTER types are allowed
 178             if (t == TIFFTag.TIFF_LONG || t == TIFFTag.TIFF_IFD_POINTER) {
 179 
 180                 f = new TIFFField(tag, t, offset, dir);
 181                 check(f.hasDirectory(), "must have directory");
 182 
 183                 check(f.getDirectory().getTag(NUM).getName().equals(NAME),
 184                     "invalid tag name");
 185 
 186                 check(f.getCount() == 1, "invalid count");
 187                 check(f.getAsLong(0) == offset, "invalid offset");
 188             } else {
 189                 ok = false;
 190                 try { new TIFFField(tag, t, offset, dir); }
 191                 catch (IllegalArgumentException e) { ok = true; }
 192                 check(ok, CONSTRUCT + "invalid data type");
 193             }
 194         }
 195 
 196         type = TIFFTag.TIFF_IFD_POINTER;
 197         tag = new TIFFTag(NAME, NUM, 1 << type);
 198         ok = false;
 199         try { new TIFFField(tag, type, offset, null); }
 200         catch (NullPointerException e) { ok = true; }
 201         check(ok, CONSTRUCT + "null TIFFDirectory");
 202 
 203         type = TIFFTag.TIFF_LONG;
 204         tag = new TIFFTag(NAME, NUM, 1 << type);
 205         ok = false;
 206         try { new TIFFField(tag, type, offset, null); }
 207         catch (NullPointerException e) { ok = true; }
 208         check(ok, CONSTRUCT + "null TIFFDirectory");
 209     }
 210 
 211     private void testTypes() {
 212 
 213         // test getTypeName(), getTypeByName() methods
 214 
 215         boolean ok = false;
 216         try { TIFFField.getTypeName(MIN_TYPE - 1); }
 217         catch (IllegalArgumentException e) { ok = true; }
 218         check(ok, "invalid data type number used");
 219 
 220         ok = false;
 221         try { TIFFField.getTypeName(MAX_TYPE + 1); }
 222         catch (IllegalArgumentException e) { ok = true; }
 223         check(ok, "invalid data type number used");
 224 
 225         for (int type = MIN_TYPE; type <= MAX_TYPE; type++) {
 226             String name = TIFFField.getTypeName(type);
 227             check(TIFFField.getTypeByName(name) == type, "invalid type");
 228         }
 229 
 230         for (int type = MIN_TYPE; type <= MAX_TYPE; type++) {
 231 
 232             TIFFTag tag = new TIFFTag(NAME, NUM, 1 << type);
 233             TIFFField f = new TIFFField(tag, type, 1);
 234             check(f.getType() == type, "invalid type");
 235 
 236             // check that invalid data types can not be used
 237             for (int type2 = MIN_TYPE; type2 <= MAX_TYPE; ++type2) {
 238                 if (type2 != type) {
 239                     ok = false;
 240                     try { new TIFFField(tag, type2, 1); } // invalid type
 241                     catch (IllegalArgumentException e) { ok = true; }
 242                     check(ok, "invalid type was successfully set");
 243                 }
 244             }
 245         }
 246     }
 247 
 248     private void testGetAs() {
 249 
 250         // test getAs...() methods
 251 
 252         int type = TIFFTag.TIFF_SHORT;
 253         TIFFTag tag = new TIFFTag(NAME, NUM, 1 << TIFFTag.TIFF_SHORT);
 254 
 255         short v = 123;
 256         TIFFField f = new TIFFField(tag, v);
 257 
 258         check(f.getAsInt(0)    ==   (int)  v, "invalid int value");
 259         check(f.getAsLong(0)   ==   (long) v, "invalid long value");
 260         check(f.getAsFloat(0)  ==  (float) v, "invalid float value");
 261         check(f.getAsDouble(0) == (double) v, "invalid double value");
 262         check(f.getValueAsString(0).equals(Short.toString(v)),
 263             "invalid string representation");
 264 
 265         check(f.getAsInts().length == 1, "inavlid array size");
 266         check((int) v == f.getAsInts()[0], "invalid int value");
 267 
 268         float fa[] = {0.01f, 1.01f};
 269         type = TIFFTag.TIFF_FLOAT;
 270         f = new TIFFField(
 271             new TIFFTag(NAME, NUM, 1 << type), type, fa.length, fa);
 272         check(f.getCount() == fa.length, "invalid count");
 273         float fa2[] = f.getAsFloats();
 274         check(fa2.length == fa.length, "invalid array size");
 275 
 276         for (int i = 0; i < fa.length; i++) {
 277             check(fa2[i] == fa[i], "invalid value");
 278             check(f.getAsDouble(i) == fa[i], "invalid value");
 279             check(f.getAsInt(i) == (int) fa[i], "invalid value"); // cast to int
 280             check(f.getValueAsString(i).equals(Float.toString(fa[i])),
 281             "invalid string representation");
 282         }
 283 
 284         byte ba[] = {-1, -10, -100};
 285         type = TIFFTag.TIFF_BYTE;
 286         f = new TIFFField(
 287             new TIFFTag(NAME, NUM, 1 << type), type, ba.length, ba);
 288         check(f.getCount() == ba.length, "invalid count");
 289         byte ba2[] = f.getAsBytes();
 290         check(ba2.length == ba.length, "invalid count");
 291 
 292         for (int i = 0; i < ba.length; i++) {
 293             check(ba[i] == ba2[i], "invalid value");
 294             check(ba[i] == (byte) f.getAsDouble(i), "invalid value");
 295             check(ba[i] == (byte) f.getAsLong(i),   "invalid value");
 296 
 297             int unsigned = ba[i] & 0xff;
 298             check(f.getAsInt(i) == unsigned, "must be treated as unsigned");
 299         }
 300 
 301         char ca[] = {'a', 'z', 0xffff};
 302         type = TIFFTag.TIFF_SHORT;
 303         f = new TIFFField(
 304             new TIFFTag(NAME, NUM, 1 << type), type, ca.length, ca);
 305         check(f.getCount() == ca.length, "invalid count");
 306         char ca2[] = f.getAsChars();
 307         check(ba2.length == ba.length, "invalid count");
 308 
 309         for (int i = 0; i < ca.length; i++) {
 310             check(ca[i] == ca2[i], "invalid value");
 311             check(ca[i] == (char) f.getAsDouble(i), "invalid value");
 312             check(ca[i] == (char) f.getAsLong(i), "invalid value");
 313             check(ca[i] == (char) f.getAsInt(i), "invalid value");
 314         }
 315 
 316         type = TIFFTag.TIFF_DOUBLE;
 317         double da[] = {0.1, 0.2, 0.3};
 318         f = new TIFFField(
 319             new TIFFTag(NAME, NUM, 1 << type), type, da.length, da);
 320         check(!f.isIntegral(), "isIntegral must be false");
 321 
 322         double da2[] = f.getAsDoubles();
 323         check(f.getData() instanceof double[], "invalid data type");
 324         double da3[] = (double[]) f.getData();
 325         check((da.length == da2.length) &&
 326               (da.length == da2.length) &&
 327               (da.length == f.getCount()),
 328                "invalid data count");
 329         for (int i = 0; i < da.length; ++i) {
 330             check(da[i] == da2[i], "invalid data");
 331             check(da[i] == da3[i], "invalid data");
 332         }
 333 
 334         boolean ok = false;
 335         try { f.getAsShorts(); }
 336         catch (ClassCastException e) { ok = true; }
 337         check(ok, "invalid data cast");
 338 
 339         ok = false;
 340         try { f.getAsRationals(); }
 341         catch (ClassCastException e) { ok = true; }
 342         check(ok, "invalid data cast");
 343 
 344         ok = false;
 345         try { TIFFField.createArrayForType(TIFFTag.MIN_DATATYPE - 1, 1); }
 346         catch (IllegalArgumentException e) { ok = true; }
 347         check(ok, "can create array with invalid datatype");
 348 
 349         ok = false;
 350         try { TIFFField.createArrayForType(TIFFTag.MAX_DATATYPE + 1, 1); }
 351         catch (IllegalArgumentException e) { ok = true; }
 352         check(ok, "can create array with invalid datatype");
 353 
 354         ok = false;
 355         try { TIFFField.createArrayForType(TIFFTag.TIFF_FLOAT, -1); }
 356         catch (IllegalArgumentException e) { ok = true; }
 357         check(ok, "can create array with negative count");
 358 
 359         int n = 3;
 360         Object
 361             RA  = TIFFField.createArrayForType(TIFFTag.TIFF_RATIONAL,  n),
 362             SRA = TIFFField.createArrayForType(TIFFTag.TIFF_SRATIONAL, n);
 363         check(RA  instanceof long[][], "invalid data type");
 364         check(SRA instanceof  int[][], "invalid data type");
 365 
 366         long ra[][] = (long[][]) RA;
 367         int sra[][] = (int[][]) SRA;
 368         check((ra.length == n) && (sra.length == n), "invalid data size");
 369         for (int i = 0; i < n; i++) {
 370             check((ra[i].length == 2) && (sra[i].length == 2),
 371                 "invalid data size");
 372             ra[i][0]  =  1;  ra[i][1]  = 5 + i;
 373             sra[i][0] = -1;  sra[i][1] = 5 + i;
 374         }
 375 
 376         type = TIFFTag.TIFF_RATIONAL;
 377         TIFFField f1 = new TIFFField(
 378             new TIFFTag(NAME, NUM, 1 << type), type, n, ra);
 379         type = TIFFTag.TIFF_SRATIONAL;
 380         TIFFField f2 = new TIFFField(
 381             new TIFFTag(NAME, NUM, 1 << type), type, n, sra);
 382 
 383         check((f1.getCount() == ra.length) && (f2.getCount() == sra.length),
 384             "invalid data count");
 385 
 386         check(f1.getAsRationals().length  == n, "invalid data count");
 387         check(f2.getAsSRationals().length == n, "invalid data count");
 388         for (int i = 0; i < n; i++) {
 389             long r[] = f1.getAsRational(i);
 390             check(r.length == 2, "invalid data format");
 391             check((r[0] == 1) && (r[1] == i + 5), "invalid data");
 392 
 393             int sr[] = f2.getAsSRational(i);
 394             check(sr.length == 2, "invalid data format");
 395             check((sr[0] == -1) && (sr[1] == i + 5), "invalid data");
 396 
 397             // check string representation
 398             String s = Long.toString(r[0]) + "/" + Long.toString(r[1]);
 399             check(s.equals(f1.getValueAsString(i)),
 400                 "invalid string representation");
 401 
 402             s = Integer.toString(sr[0]) + "/" + Integer.toString(sr[1]);
 403             check(s.equals(f2.getValueAsString(i)),
 404                 "invalid string representation");
 405 
 406             // see the documentation for getAsInt:
 407             // TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated
 408             // by dividing the numerator into the denominator using
 409             // double-precision arithmetic and then casting to int
 410             check(f1.getAsInt(i) == (int)(r[0] / r[1]),
 411                 "invalid result for getAsInt");
 412             check(f2.getAsInt(i) == (int)(r[0] / r[1]),
 413                 "invalid result for getAsInt");
 414         }
 415 
 416         ok = false;
 417         try { f1.getAsRational(ra.length); }
 418         catch (ArrayIndexOutOfBoundsException e) { ok = true; }
 419         check(ok, "invalid index");
 420 
 421         String sa[] = {"-1.e-25", "22", "-1.23E5"};
 422         type = TIFFTag.TIFF_ASCII;
 423         f = new TIFFField(
 424             new TIFFTag(NAME, NUM, 1 << type), type, sa.length, sa);
 425 
 426         // test clone() method
 427         TIFFField cloned = null;
 428         try { cloned = f.clone(); } catch (CloneNotSupportedException e) {
 429             throw new RuntimeException(e);
 430         }
 431 
 432         check(f.getCount() == cloned.getCount(), "invalid cloned field count");
 433 
 434         check(f.getCount() == sa.length, "invalid data count");
 435         for (int i = 0; i < sa.length; i++) {
 436             check(sa[i].equals(f.getAsString(i)), "invalid data");
 437             // see docs: "data in TIFF_ASCII format will be parsed as by
 438             // the Double.parseDouble method, with the result cast to int"
 439             check(f.getAsInt(i) ==
 440                 (int) Double.parseDouble(sa[i]), "invalid data");
 441             check(f.getAsDouble(i) == Double.parseDouble(sa[i]), "invalid data");
 442 
 443             check(sa[i].equals(cloned.getAsString(i)), "invalid cloned data");
 444         }
 445     }
 446 
 447     private void testCreateFromNode() {
 448 
 449         int type = TIFFTag.TIFF_LONG;
 450 
 451         List<TIFFTag> tags = new ArrayList<>();
 452         int v = 1234567;
 453         TIFFTag tag = new TIFFTag(NAME, NUM, 1 << type);
 454         tags.add(tag);
 455         TIFFTagSet ts = new TIFFTagSet(tags);
 456 
 457         boolean ok = false;
 458         try { TIFFField.createFromMetadataNode(ts, null); }
 459         catch (NullPointerException e) { ok = true; }
 460         check(ok, "can create TIFFField from a null node");
 461 
 462         TIFFField f = new TIFFField(tag, v);
 463         Node node = f.getAsNativeNode();
 464         check(node.getNodeName().equals(f.getClass().getSimpleName()),
 465             "invalid node name");
 466 
 467         NamedNodeMap attrs = node.getAttributes();
 468         for (int i = 0; i < attrs.getLength(); i++) {
 469             String an = attrs.item(i).getNodeName().toLowerCase();
 470             String av = attrs.item(i).getNodeValue();
 471             if (an.contains("name")) {
 472                 check(av.equals(NAME), "invalid tag name");
 473             } else if (an.contains("number")) {
 474                 check(av.equals(Integer.toString(NUM)), "invalid tag number");
 475             }
 476         }
 477 
 478         // invalid node
 479         IIOMetadataNode nok = new IIOMetadataNode("NOK");
 480 
 481         ok = false;
 482         try { TIFFField.createFromMetadataNode(ts, nok); }
 483         catch (IllegalArgumentException e) { ok = true; }
 484         check(ok, CONSTRUCT + "invalid node name");
 485 
 486         TIFFField f2 = TIFFField.createFromMetadataNode(ts, node);
 487         check(f2.getType() == type, "invalid type");
 488         check(f2.getTagNumber() == NUM, "invalid tag number");
 489         check(f2.getTag().getName().equals(NAME), "invalid tag name");
 490         check(f2.getCount()  == 1, "invalid count");
 491         check(f2.getAsInt(0) == v, "invalid value");
 492     }
 493 
 494     public static void main(String[] args) {
 495 
 496         TIFFFieldTest test = new TIFFFieldTest();
 497         test.testConstructors();
 498         test.testCreateFromNode();
 499         test.testTypes();
 500         test.testGetAs();
 501     }
 502 }