in endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/NativeMetadata.java [107:235]
final DefaultTreeTable read(final Reader reader) throws IOException {
input = reader.input;
isClassic = reader.intSizeExpansion == 0;
final int offsetSize = Integer.BYTES << reader.intSizeExpansion;
final DefaultTreeTable table = new DefaultTreeTable(CODE, NAME, VALUE);
final TreeTable.Node root = table.getRoot();
root.setValue(NAME, "TIFF");
input.mark();
try {
input.seek(isClassic ? 2*Short.BYTES : 4*Short.BYTES);
final Set<Long> doneIFD = new HashSet<>();
long nextIFD;
/*
* Following loop is a simplified copy of `Reader.getImageFileDirectory(int)` method,
* without the "deferred entries" mechanism. Instead, we seek immediately.
*/
int imageNumber = 0;
while ((nextIFD = readInt(false)) != 0) {
if (!doneIFD.add(nextIFD)) {
// Safety against infinite recursion.
break;
}
final TreeTable.Node image = root.newChild();
image.setValue(NAME, vocabulary.getString(Vocabulary.Keys.Image_1, imageNumber));
input.seek(nextIFD);
for (long remaining = readInt(true); --remaining >= 0;) {
final short tag = (short) input.readUnsignedShort();
final Type type = Type.valueOf(input.readShort()); // May be null.
final long count = readInt(false);
final long size = (type != null) ? Math.multiplyExact(type.size, count) : 0;
final long next = addExact(input.getStreamPosition(), offsetSize);
boolean visible;
/*
* Exclude the tags about location of tiles in the GeoTIFF files.
* Values of those tags are potentially large and rarely useful for human reading.
* This switch is only about tags to skip; special handlings of some tags are done later.
*/
switch (tag) {
case TAG_TILE_OFFSETS:
case TAG_STRIP_OFFSETS:
case TAG_TILE_BYTE_COUNTS:
case TAG_STRIP_BYTE_COUNTS: visible = false; break;
default: visible = (size != 0); break;
}
if (visible) {
if (size > offsetSize) {
final long offset = readInt(false);
input.seek(offset);
}
/*
* Some tags need to be handle in a special way. The main cases are GeoTIFF keys.
* But other cases exist (e.g. GEO_METADATA and GDAL_METADATA).
*/
Object value = null;
XMLMetadata children = null;
switch (tag) {
case (short) TAG_GEO_KEY_DIRECTORY: {
writeGeoKeys(); // Flush previous keys if any (should never happen).
keyDirectory = type.readAsVector(input, count);
value = "GeoTIFF";
break;
}
case (short) TAG_GEO_DOUBLE_PARAMS: {
numericParameters = type.readAsVector(input, count);
visible = false;
break;
}
case (short) TAG_GEO_ASCII_PARAMS: {
setAsciiParameters(type.readAsStrings(input, count, reader.store.encoding));
visible = false;
break;
}
case Tags.GDAL_METADATA:
case Tags.GEO_METADATA: {
children = reader.readXML(type, count, tag);
if (children.isEmpty()) {
// Fallback on showing array of numerical values.
value = type.readAsVector(input, count);
}
break;
}
default: {
value = type.readAsObject(input, count);
if (value instanceof Vector) {
final Vector v = (Vector) value;
switch (v.size()) {
case 0: value = null; break;
case 1: value = v.get(0); break;
}
}
/*
* Replace a few numerical values by a more readable string when available.
* We currently perform this replacement only for tags for which we defined
* an enumeration.
*/
switch (tag) {
case TAG_COMPRESSION: value = toString(value, Compression::valueOf, Compression.UNKNOWN); break;
case TAG_PREDICTOR: value = toString(value, Predictor::valueOf, Predictor.UNKNOWN); break;
}
}
}
if (visible) {
final String name = Tags.name(tag);
final TreeTable.Node node;
if (children != null) {
node = new XMLMetadata.Root(children, (DefaultTreeTable.Node) image, name);
} else {
node = image.newChild();
node.setValue(NAME, name);
node.setValue(VALUE, value);
}
node.setValue(CODE, Short.toUnsignedInt(tag));
if (tag == (short) TAG_GEO_KEY_DIRECTORY) {
geoNode = node;
}
}
}
input.seek(next);
}
imageNumber++;
}
} catch (ArithmeticException e) {
throw new IOException(e); // Cannot seek that far.
} finally {
input.reset();
}
writeGeoKeys();
return table;
}