//
// FormatReader.java
//
/*
OME Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.formats;
import java.io.IOException;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;
import loci.common.DataTools;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.common.services.DependencyException;
import loci.common.services.ServiceFactory;
import loci.formats.in.DefaultMetadataOptions;
import loci.formats.in.MetadataLevel;
import loci.formats.in.MetadataOptions;
import loci.formats.meta.DummyMetadata;
import loci.formats.meta.FilterMetadata;
import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataStore;
import loci.formats.ome.OMEXMLMetadata;
import loci.formats.services.OMEXMLService;
import ome.xml.model.enums.AcquisitionMode;
import ome.xml.model.enums.ArcType;
import ome.xml.model.enums.Binning;
import ome.xml.model.enums.Compression;
import ome.xml.model.enums.ContrastMethod;
import ome.xml.model.enums.Correction;
import ome.xml.model.enums.DetectorType;
import ome.xml.model.enums.DimensionOrder;
import ome.xml.model.enums.EnumerationException;
import ome.xml.model.enums.ExperimentType;
import ome.xml.model.enums.FilamentType;
import ome.xml.model.enums.FillRule;
import ome.xml.model.enums.FilterType;
import ome.xml.model.enums.FontFamily;
import ome.xml.model.enums.FontStyle;
import ome.xml.model.enums.IlluminationType;
import ome.xml.model.enums.Immersion;
import ome.xml.model.enums.LaserMedium;
import ome.xml.model.enums.LaserType;
import ome.xml.model.enums.LineCap;
import ome.xml.model.enums.Marker;
import ome.xml.model.enums.Medium;
import ome.xml.model.enums.MicrobeamManipulationType;
import ome.xml.model.enums.MicroscopeType;
import ome.xml.model.enums.NamingConvention;
import ome.xml.model.enums.PixelType;
import ome.xml.model.enums.Pulse;
import ome.xml.model.enums.handlers.AcquisitionModeEnumHandler;
import ome.xml.model.enums.handlers.ArcTypeEnumHandler;
import ome.xml.model.enums.handlers.BinningEnumHandler;
import ome.xml.model.enums.handlers.CompressionEnumHandler;
import ome.xml.model.enums.handlers.ContrastMethodEnumHandler;
import ome.xml.model.enums.handlers.CorrectionEnumHandler;
import ome.xml.model.enums.handlers.DetectorTypeEnumHandler;
import ome.xml.model.enums.handlers.DimensionOrderEnumHandler;
import ome.xml.model.enums.handlers.ExperimentTypeEnumHandler;
import ome.xml.model.enums.handlers.FilamentTypeEnumHandler;
import ome.xml.model.enums.handlers.FillRuleEnumHandler;
import ome.xml.model.enums.handlers.FilterTypeEnumHandler;
import ome.xml.model.enums.handlers.FontFamilyEnumHandler;
import ome.xml.model.enums.handlers.FontStyleEnumHandler;
import ome.xml.model.enums.handlers.IlluminationTypeEnumHandler;
import ome.xml.model.enums.handlers.ImmersionEnumHandler;
import ome.xml.model.enums.handlers.LaserMediumEnumHandler;
import ome.xml.model.enums.handlers.LaserTypeEnumHandler;
import ome.xml.model.enums.handlers.LineCapEnumHandler;
import ome.xml.model.enums.handlers.MarkerEnumHandler;
import ome.xml.model.enums.handlers.MediumEnumHandler;
import ome.xml.model.enums.handlers.MicrobeamManipulationTypeEnumHandler;
import ome.xml.model.enums.handlers.MicroscopeTypeEnumHandler;
import ome.xml.model.enums.handlers.NamingConventionEnumHandler;
import ome.xml.model.enums.handlers.PixelTypeEnumHandler;
import ome.xml.model.enums.handlers.PulseEnumHandler;
/**
* Abstract superclass of all biological file format readers.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/FormatReader.java">Trac</a>,
* <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/FormatReader.java;hb=HEAD">Gitweb</a></dd></dl>
*/
public abstract class FormatReader extends FormatHandler
implements IFormatReader
{
// -- Constants --
/** Default thumbnail width and height. */
protected static final int THUMBNAIL_DIMENSION = 128;
// -- Fields --
/** Current file. */
protected RandomAccessInputStream in;
/** Hashtable containing metadata key/value pairs. */
protected Hashtable<String, Object> metadata;
/** The number of the current series. */
protected int series = 0;
/** Core metadata values. */
protected CoreMetadata[] core;
/**
* Whether the file extension matching one of the reader's suffixes
* is necessary to identify the file as an instance of this format.
*/
protected boolean suffixNecessary = true;
/**
* Whether the file extension matching one of the reader's suffixes
* is sufficient to identify the file as an instance of this format.
*/
protected boolean suffixSufficient = true;
/** Whether this format supports multi-file datasets. */
protected boolean hasCompanionFiles = false;
/** Short description of the structure of the dataset. */
protected String datasetDescription = "Single file";
/** Whether or not to normalize float data. */
protected boolean normalizeData;
/** Whether or not to filter out invalid metadata. */
protected boolean filterMetadata;
/** Whether or not to save proprietary metadata in the MetadataStore. */
protected boolean saveOriginalMetadata = false;
/** Whether or not MetadataStore sets C = 3 for indexed color images. */
protected boolean indexedAsRGB = false;
/** Whether or not to group multi-file formats. */
protected boolean group = true;
/** List of domains in which this format is used. */
protected String[] domains = new String[0];
/**
* Current metadata store. Should never be accessed directly as the
* semantics of {@link #getMetadataStore()} prevent "null" access.
*/
protected MetadataStore metadataStore = new DummyMetadata();
/** Metadata parsing options. */
protected MetadataOptions metadataOptions = new DefaultMetadataOptions();
private ServiceFactory factory;
private OMEXMLService service;
// -- Constructors --
/** Constructs a format reader with the given name and default suffix. */
public FormatReader(String format, String suffix) { super(format, suffix); }
/** Constructs a format reader with the given name and default suffixes. */
public FormatReader(String format, String[] suffixes) {
super(format, suffixes);
}
// -- Internal FormatReader API methods --
/**
* Initializes the given file (parsing header information, etc.).
* Most subclasses should override this method to perform
* initialization operations such as parsing metadata.
*
* @throws FormatException if a parsing error occurs processing the file.
* @throws IOException if an I/O error occurs processing the file
*/
protected void initFile(String id) throws FormatException, IOException {
LOGGER.debug("{}.initFile({})", this.getClass().getName(), id);
if (currentId != null) {
String[] s = getUsedFiles();
for (int i=0; i<s.length; i++) {
if (id.equals(s[i])) return;
}
}
series = 0;
close();
currentId = id;
metadata = new Hashtable<String, Object>();
core = new CoreMetadata[1];
core[0] = new CoreMetadata();
core[0].orderCertain = true;
// reinitialize the MetadataStore
// NB: critical for metadata conversion to work properly!
getMetadataStore().createRoot();
}
/** Returns true if the given file name is in the used files list. */
protected boolean isUsedFile(String file) {
String[] usedFiles = getUsedFiles();
for (String used : usedFiles) {
if (used.equals(file)) return true;
String path = new Location(file).getAbsolutePath();
if (used.equals(path)) return true;
}
return false;
}
/** Adds an entry to the specified Hashtable. */
protected void addMeta(String key, Object value,
Hashtable<String, Object> meta)
{
if (key == null || value == null || !isMetadataCollected()) {
return;
}
key = key.trim();
boolean string = value instanceof String || value instanceof Character;
boolean simple = string ||
value instanceof Number ||
value instanceof Boolean;
// string value, if passed in value is a string
String val = string ? String.valueOf(value) : null;
if (filterMetadata ||
(saveOriginalMetadata && (getMetadataStore() instanceof OMEXMLMetadata)))
{
// filter out complex data types
if (!simple) return;
// verify key & value are reasonable length
int maxLen = 8192;
if (key.length() > maxLen) return;
if (string && val.length() > maxLen) return;
// remove all non-printable characters
key = DataTools.sanitize(key);
if (string) val = DataTools.sanitize(val);
// verify key contains at least one alphabetic character
if (!key.matches(".*[a-zA-Z].*")) return;
// remove <, > and & to prevent XML parsing errors
String[] invalidSequences = new String[] {
"<", ">", "&", "<", ">", "&"
};
for (int i=0; i<invalidSequences.length; i++) {
key = key.replaceAll(invalidSequences[i], "");
if (string) val = val.replaceAll(invalidSequences[i], "");
}
// verify key & value are not empty
if (key.length() == 0) return;
if (string && val.trim().length() == 0) return;
if (string) value = val;
}
meta.put(key, val == null ? value : val);
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, Object value) {
addMeta(key, value, getGlobalMetadata());
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, boolean value) {
addGlobalMeta(key, new Boolean(value));
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, byte value) {
addGlobalMeta(key, new Byte(value));
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, short value) {
addGlobalMeta(key, new Short(value));
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, int value) {
addGlobalMeta(key, new Integer(value));
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, long value) {
addGlobalMeta(key, new Long(value));
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, float value) {
addGlobalMeta(key, new Float(value));
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, double value) {
addGlobalMeta(key, new Double(value));
}
/** Adds an entry to the global metadata table. */
protected void addGlobalMeta(String key, char value) {
addGlobalMeta(key, new Character(value));
}
/** Gets a value from the global metadata table. */
protected Object getGlobalMeta(String key) {
return metadata.get(key);
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, Object value) {
addMeta(key, value, core[series].seriesMetadata);
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, boolean value) {
addSeriesMeta(key, new Boolean(value));
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, byte value) {
addSeriesMeta(key, new Byte(value));
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, short value) {
addSeriesMeta(key, new Short(value));
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, int value) {
addSeriesMeta(key, new Integer(value));
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, long value) {
addSeriesMeta(key, new Long(value));
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, float value) {
addSeriesMeta(key, new Float(value));
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, double value) {
addSeriesMeta(key, new Double(value));
}
/** Adds an entry to the metadata table for the current series. */
protected void addSeriesMeta(String key, char value) {
addSeriesMeta(key, new Character(value));
}
/** Gets an entry from the metadata table for the current series. */
protected Object getSeriesMeta(String key) {
return core[series].seriesMetadata.get(key);
}
/** Reads a raw plane from disk. */
protected byte[] readPlane(RandomAccessInputStream s, int x, int y,
int w, int h, byte[] buf) throws IOException
{
return readPlane(s, x, y, w, h, 0, buf);
}
/** Reads a raw plane from disk. */
protected byte[] readPlane(RandomAccessInputStream s, int x, int y,
int w, int h, int scanlinePad, byte[] buf) throws IOException
{
int c = getRGBChannelCount();
int bpp = FormatTools.getBytesPerPixel(getPixelType());
if (x == 0 && y == 0 && w == getSizeX() && h == getSizeY() &&
scanlinePad == 0)
{
s.read(buf);
}
else if (x == 0 && w == getSizeX() && scanlinePad == 0) {
if (isInterleaved()) {
s.skipBytes(y * w * bpp * c);
s.read(buf, 0, h * w * bpp * c);
}
else {
int rowLen = w * bpp;
for (int channel=0; channel<c; channel++) {
s.skipBytes(y * rowLen);
s.read(buf, channel * h * rowLen, h * rowLen);
if (channel < c - 1) {
// no need to skip bytes after reading final channel
s.skipBytes((getSizeY() - y - h) * rowLen);
}
}
}
}
else {
int scanlineWidth = getSizeX() + scanlinePad;
if (isInterleaved()) {
s.skipBytes(y * scanlineWidth * bpp * c);
for (int row=0; row<h; row++) {
s.skipBytes(x * bpp * c);
s.read(buf, row * w * bpp * c, w * bpp * c);
if (row < h - 1) {
// no need to skip bytes after reading final row
s.skipBytes(bpp * c * (scanlineWidth - w - x));
}
}
}
else {
for (int channel=0; channel<c; channel++) {
s.skipBytes(y * scanlineWidth * bpp);
for (int row=0; row<h; row++) {
s.skipBytes(x * bpp);
s.read(buf, channel * w * h * bpp + row * w * bpp, w * bpp);
if (row < h - 1 || channel < c - 1) {
// no need to skip bytes after reading final row of final channel
s.skipBytes(bpp * (scanlineWidth - w - x));
}
}
if (channel < c - 1) {
// no need to skip bytes after reading final channel
s.skipBytes(scanlineWidth * bpp * (getSizeY() - y - h));
}
}
}
}
return buf;
}
/** Return a properly configured loci.formats.meta.FilterMetadata. */
protected MetadataStore makeFilterMetadata() {
return new FilterMetadata(getMetadataStore(), isMetadataFiltered());
}
// -- IMetadataConfigurable API methods --
/* (non-Javadoc)
* @see loci.formats.IMetadataConfigurable#getSupportedMetadataLevels()
*/
public Set<MetadataLevel> getSupportedMetadataLevels() {
Set<MetadataLevel> supportedLevels = new HashSet<MetadataLevel>();
supportedLevels.add(MetadataLevel.ALL);
supportedLevels.add(MetadataLevel.NO_OVERLAYS);
supportedLevels.add(MetadataLevel.MINIMUM);
return supportedLevels;
}
/* (non-Javadoc)
* @see loci.formats.IMetadataConfigurable#getMetadataOptions()
*/
public MetadataOptions getMetadataOptions() {
return metadataOptions;
}
/* (non-Javadoc)
* @see loci.formats.IMetadataConfigurable#setMetadataOptions(loci.formats.in.MetadataOptions)
*/
public void setMetadataOptions(MetadataOptions options) {
this.metadataOptions = options;
}
// -- IFormatReader API methods --
/**
* Checks if a file matches the type of this format reader.
* Checks filename suffixes against those known for this format.
* If the suffix check is inconclusive and the open parameter is true,
* the file is opened and tested with
* {@link #isThisType(RandomAccessInputStream)}.
*
* @param open If true, and the file extension is insufficient to determine
* the file type, the (existing) file is opened for further analysis.
*/
public boolean isThisType(String name, boolean open) {
// if file extension ID is insufficient and we can't open the file, give up
if (!suffixSufficient && !open) return false;
if (suffixNecessary || suffixSufficient) {
// it's worth checking the file extension
boolean suffixMatch = super.isThisType(name);
// if suffix match is required but it doesn't match, failure
if (suffixNecessary && !suffixMatch) return false;
// if suffix matches and that's all we need, green light it
if (suffixMatch && suffixSufficient) return true;
}
// suffix matching was inconclusive; we need to analyze the file contents
if (!open) return false; // not allowed to open any files
try {
RandomAccessInputStream stream = new RandomAccessInputStream(name);
boolean isThisType = isThisType(stream);
stream.close();
return isThisType;
}
catch (IOException exc) {
LOGGER.debug("", exc);
return false;
}
}
/* @see IFormatReader#isThisType(byte[]) */
public boolean isThisType(byte[] block) {
try {
RandomAccessInputStream stream = new RandomAccessInputStream(block);
boolean isThisType = isThisType(stream);
stream.close();
return isThisType;
}
catch (IOException e) {
LOGGER.debug("", e);
}
return false;
}
/* @see IFormatReader#isThisType(RandomAccessInputStream) */
public boolean isThisType(RandomAccessInputStream stream) throws IOException {
return false;
}
/* @see IFormatReader#getImageCount() */
public int getImageCount() {
FormatTools.assertId(currentId, true, 1);
return core[series].imageCount;
}
/* @see IFormatReader#isRGB() */
public boolean isRGB() {
FormatTools.assertId(currentId, true, 1);
return core[series].rgb;
}
/* @see IFormatReader#getSizeX() */
public int getSizeX() {
FormatTools.assertId(currentId, true, 1);
return core[series].sizeX;
}
/* @see IFormatReader#getSizeY() */
public int getSizeY() {
FormatTools.assertId(currentId, true, 1);
return core[series].sizeY;
}
/* @see IFormatReader#getSizeZ() */
public int getSizeZ() {
FormatTools.assertId(currentId, true, 1);
return core[series].sizeZ;
}
/* @see IFormatReader#getSizeC() */
public int getSizeC() {
FormatTools.assertId(currentId, true, 1);
return core[series].sizeC;
}
/* @see IFormatReader#getSizeT() */
public int getSizeT() {
FormatTools.assertId(currentId, true, 1);
return core[series].sizeT;
}
/* @see IFormatReader#getPixelType() */
public int getPixelType() {
FormatTools.assertId(currentId, true, 1);
return core[series].pixelType;
}
/* @see IFormatReader#getBitsPerPixel() */
public int getBitsPerPixel() {
FormatTools.assertId(currentId, true, 1);
if (core[series].bitsPerPixel == 0) {
core[series].bitsPerPixel =
FormatTools.getBytesPerPixel(getPixelType()) * 8;
}
return core[series].bitsPerPixel;
}
/* @see IFormatReader#getEffectiveSizeC() */
public int getEffectiveSizeC() {
// NB: by definition, imageCount == effectiveSizeC * sizeZ * sizeT
int sizeZT = getSizeZ() * getSizeT();
if (sizeZT == 0) return 0;
return getImageCount() / sizeZT;
}
/* @see IFormatReader#getRGBChannelCount() */
public int getRGBChannelCount() {
int effSizeC = getEffectiveSizeC();
if (effSizeC == 0) return 0;
return getSizeC() / effSizeC;
}
/* @see IFormatReader#isIndexed() */
public boolean isIndexed() {
FormatTools.assertId(currentId, true, 1);
return core[series].indexed;
}
/* @see IFormatReader#isFalseColor() */
public boolean isFalseColor() {
FormatTools.assertId(currentId, true, 1);
return core[series].falseColor;
}
/* @see IFormatReader#get8BitLookupTable() */
public byte[][] get8BitLookupTable() throws FormatException, IOException {
return null;
}
/* @see IFormatReader#get16BitLookupTable() */
public short[][] get16BitLookupTable() throws FormatException, IOException {
return null;
}
/* @see IFormatReader#getChannelDimLengths() */
public int[] getChannelDimLengths() {
FormatTools.assertId(currentId, true, 1);
if (core[series].cLengths == null) return new int[] {core[series].sizeC};
return core[series].cLengths;
}
/* @see IFormatReader#getChannelDimTypes() */
public String[] getChannelDimTypes() {
FormatTools.assertId(currentId, true, 1);
if (core[series].cTypes == null) return new String[] {FormatTools.CHANNEL};
return core[series].cTypes;
}
/* @see IFormatReader#getThumbSizeX() */
public int getThumbSizeX() {
FormatTools.assertId(currentId, true, 1);
if (core[series].thumbSizeX == 0) {
int sx = getSizeX();
int sy = getSizeY();
int thumbSizeX = 0;
if (sx > sy) thumbSizeX = THUMBNAIL_DIMENSION;
else if (sy > 0) thumbSizeX = sx * THUMBNAIL_DIMENSION / sy;
if (thumbSizeX == 0) thumbSizeX = 1;
return thumbSizeX;
}
return core[series].thumbSizeX;
}
/* @see IFormatReader#getThumbSizeY() */
public int getThumbSizeY() {
FormatTools.assertId(currentId, true, 1);
if (core[series].thumbSizeY == 0) {
int sx = getSizeX();
int sy = getSizeY();
int thumbSizeY = 1;
if (sy > sx) thumbSizeY = THUMBNAIL_DIMENSION;
else if (sx > 0) thumbSizeY = sy * THUMBNAIL_DIMENSION / sx;
if (thumbSizeY == 0) thumbSizeY = 1;
return thumbSizeY;
}
return core[series].thumbSizeY;
}
/* @see IFormatReader.isLittleEndian() */
public boolean isLittleEndian() {
FormatTools.assertId(currentId, true, 1);
return core[series].littleEndian;
}
/* @see IFormatReader#getDimensionOrder() */
public String getDimensionOrder() {
FormatTools.assertId(currentId, true, 1);
return core[series].dimensionOrder;
}
/* @see IFormatReader#isOrderCertain() */
public boolean isOrderCertain() {
FormatTools.assertId(currentId, true, 1);
return core[series].orderCertain;
}
/* @see IFormatReader#isThumbnailSeries() */
public boolean isThumbnailSeries() {
FormatTools.assertId(currentId, true, 1);
return core[series].thumbnail;
}
/* @see IFormatReader#isInterleaved() */
public boolean isInterleaved() {
return isInterleaved(0);
}
/* @see IFormatReader#isInterleaved(int) */
public boolean isInterleaved(int subC) {
FormatTools.assertId(currentId, true, 1);
return core[series].interleaved;
}
/* @see IFormatReader#openBytes(int) */
public byte[] openBytes(int no) throws FormatException, IOException {
return openBytes(no, 0, 0, getSizeX(), getSizeY());
}
/* @see IFormatReader#openBytes(int, byte[]) */
public byte[] openBytes(int no, byte[] buf)
throws FormatException, IOException
{
return openBytes(no, buf, 0, 0, getSizeX(), getSizeY());
}
/* @see IFormatReader#openBytes(int, int, int, int, int) */
public byte[] openBytes(int no, int x, int y, int w, int h)
throws FormatException, IOException
{
long bufSize = FormatTools.getPlaneSize(this, w, h);
if (bufSize < 0 || bufSize > Integer.MAX_VALUE) {
throw new FormatException("Image plane too large. Only 2GB of data can " +
"be extracted at one time. You can workaround the problem by opening " +
"the plane in tiles; for further details, see: " +
"http://www.openmicroscopy.org/site/support/faq/bio-formats/" +
"i-see-an-outofmemory-or-negativearraysize-error-message-when-" +
"attempting-to-open-an-svs-or-jpeg-2000-file.-what-does-this-mean");
}
byte[] newBuffer = new byte[(int) bufSize];
return openBytes(no, newBuffer, x, y, w, h);
}
/* @see IFormatReader#openBytes(int, byte[], int, int, int, int) */
public abstract byte[] openBytes(int no, byte[] buf, int x, int y,
int w, int h) throws FormatException, IOException;
/* @see IFormatReader#openPlane(int, int, int, int, int int) */
public Object openPlane(int no, int x, int y, int w, int h)
throws FormatException, IOException
{
// NB: Readers use byte arrays by default as the native type.
return openBytes(no, x, y, w, h);
}
/* @see IFormatReader#openThumbBytes(int) */
public byte[] openThumbBytes(int no) throws FormatException, IOException {
FormatTools.assertId(currentId, true, 1);
return FormatTools.openThumbBytes(this, no);
}
/* @see IFormatReader#close(boolean) */
public void close(boolean fileOnly) throws IOException {
if (in != null) in.close();
if (!fileOnly) {
in = null;
currentId = null;
}
}
/* @see IFormatReader#getSeriesCount() */
public int getSeriesCount() {
FormatTools.assertId(currentId, true, 1);
return core.length;
}
/* @see IFormatReader#setSeries(int) */
public void setSeries(int no) {
if (no < 0 || no >= getSeriesCount()) {
throw new IllegalArgumentException("Invalid series: " + no);
}
series = no;
}
/* @see IFormatReader#getSeries() */
public int getSeries() {
return series;
}
/* @see IFormatReader#setGroupFiles(boolean) */
public void setGroupFiles(boolean groupFiles) {
FormatTools.assertId(currentId, false, 1);
group = groupFiles;
}
/* @see IFormatReader#isGroupFiles() */
public boolean isGroupFiles() {
return group;
}
/* @see IFormatReader#fileGroupOption(String) */
public int fileGroupOption(String id)
throws FormatException, IOException
{
return FormatTools.CANNOT_GROUP;
}
/* @see IFormatReader#isMetadataComplete() */
public boolean isMetadataComplete() {
FormatTools.assertId(currentId, true, 1);
return core[series].metadataComplete;
}
/* @see IFormatReader#setNormalized(boolean) */
public void setNormalized(boolean normalize) {
FormatTools.assertId(currentId, false, 1);
normalizeData = normalize;
}
/* @see IFormatReader#isNormalized() */
public boolean isNormalized() {
return normalizeData;
}
/**
* @deprecated
* @see IFormatReader#setMetadataCollected(boolean)
*/
public void setMetadataCollected(boolean collect) {
FormatTools.assertId(currentId, false, 1);
MetadataLevel level = collect ? MetadataLevel.ALL : MetadataLevel.MINIMUM;
setMetadataOptions(new DefaultMetadataOptions(level));
}
/**
* @deprecated
* @see IFormatReader#isMetadataCollected()
*/
public boolean isMetadataCollected() {
return getMetadataOptions().getMetadataLevel() == MetadataLevel.ALL;
}
/* @see IFormatReader#setOriginalMetadataPopulated(boolean) */
public void setOriginalMetadataPopulated(boolean populate) {
FormatTools.assertId(currentId, false, 1);
saveOriginalMetadata = populate;
}
/* @see IFormatReader#isOriginalMetadataPopulated() */
public boolean isOriginalMetadataPopulated() {
return saveOriginalMetadata;
}
/* @see IFormatReader#getUsedFiles() */
public String[] getUsedFiles() {
return getUsedFiles(false);
}
/* @see IFormatReader#getUsedFiles() */
public String[] getUsedFiles(boolean noPixels) {
int oldSeries = getSeries();
Vector<String> files = new Vector<String>();
for (int i=0; i<getSeriesCount(); i++) {
setSeries(i);
String[] s = getSeriesUsedFiles(noPixels);
if (s != null) {
for (String file : s) {
if (!files.contains(file)) {
files.add(file);
}
}
}
}
setSeries(oldSeries);
return files.toArray(new String[files.size()]);
}
/* @see IFormatReader#getSeriesUsedFiles() */
public String[] getSeriesUsedFiles() {
return getSeriesUsedFiles(false);
}
/* @see IFormatReader#getSeriesUsedFiles(boolean) */
public String[] getSeriesUsedFiles(boolean noPixels) {
return noPixels ? null : new String[] {currentId};
}
/* @see IFormatReader#getAdvancedUsedFiles(boolean) */
public FileInfo[] getAdvancedUsedFiles(boolean noPixels) {
String[] files = getUsedFiles(noPixels);
if (files == null) return null;
FileInfo[] infos = new FileInfo[files.length];
for (int i=0; i<infos.length; i++) {
infos[i] = new FileInfo();
infos[i].filename = files[i];
infos[i].reader = this.getClass();
infos[i].usedToInitialize = files[i].endsWith(getCurrentFile());
}
return infos;
}
/* @see IFormatReader#getAdvancedSeriesUsedFiles(boolean) */
public FileInfo[] getAdvancedSeriesUsedFiles(boolean noPixels) {
String[] files = getSeriesUsedFiles(noPixels);
if (files == null) return null;
FileInfo[] infos = new FileInfo[files.length];
for (int i=0; i<infos.length; i++) {
infos[i] = new FileInfo();
infos[i].filename = files[i];
infos[i].reader = this.getClass();
infos[i].usedToInitialize = files[i].endsWith(getCurrentFile());
}
return infos;
}
/* @see IFormatReader#getCurrentFile() */
public String getCurrentFile() {
return currentId;
}
/* @see IFormatReader#getIndex(int, int, int) */
public int getIndex(int z, int c, int t) {
FormatTools.assertId(currentId, true, 1);
return FormatTools.getIndex(this, z, c, t);
}
/* @see IFormatReader#getZCTCoords(int) */
public int[] getZCTCoords(int index) {
FormatTools.assertId(currentId, true, 1);
return FormatTools.getZCTCoords(this, index);
}
/* @see IFormatReader#getMetadataValue(String) */
public Object getMetadataValue(String field) {
FormatTools.assertId(currentId, true, 1);
return getGlobalMeta(field);
}
/* @see IFormatReader#getSeriesMetadataValue(String) */
public Object getSeriesMetadataValue(String field) {
FormatTools.assertId(currentId, true, 1);
return getSeriesMeta(field);
}
/* @see IFormatReader#getGlobalMetadata() */
public Hashtable<String, Object> getGlobalMetadata() {
FormatTools.assertId(currentId, true, 1);
return metadata;
}
/* @see IFormatReader#getSeriesMetadata() */
public Hashtable<String, Object> getSeriesMetadata() {
FormatTools.assertId(currentId, true, 1);
return core[series].seriesMetadata;
}
/** @deprecated */
public Hashtable<String, Object> getMetadata() {
FormatTools.assertId(currentId, true, 1);
Hashtable<String, Object> h =
new Hashtable<String, Object>(getGlobalMetadata());
int oldSeries = getSeries();
IMetadata meta = getMetadataStore() instanceof IMetadata ?
(IMetadata) getMetadataStore() : null;
for (int series=0; series<getSeriesCount(); series++) {
String name = "Series " + series;
if (meta != null) {
String realName = meta.getImageName(series);
if (realName != null && realName.trim().length() != 0) {
name = realName;
}
}
setSeries(series);
MetadataTools.merge(getSeriesMetadata(), h, name + " ");
}
setSeries(oldSeries);
return h;
}
/* @see IFormatReader#getCoreMetadata() */
public CoreMetadata[] getCoreMetadata() {
FormatTools.assertId(currentId, true, 1);
return core;
}
/* @see IFormatReader#setMetadataFiltered(boolean) */
public void setMetadataFiltered(boolean filter) {
FormatTools.assertId(currentId, false, 1);
filterMetadata = filter;
}
/* @see IFormatReader#isMetadataFiltered() */
public boolean isMetadataFiltered() {
return filterMetadata;
}
/* @see IFormatReader#setMetadataStore(MetadataStore) */
public void setMetadataStore(MetadataStore store) {
FormatTools.assertId(currentId, false, 1);
if (store == null) {
throw new IllegalArgumentException("Metadata object cannot be null; " +
"use loci.formats.meta.DummyMetadata instead");
}
metadataStore = store;
}
/* @see IFormatReader#getMetadataStore() */
public MetadataStore getMetadataStore() {
return metadataStore;
}
/* @see IFormatReader#getMetadataStoreRoot() */
public Object getMetadataStoreRoot() {
FormatTools.assertId(currentId, true, 1);
return getMetadataStore().getRoot();
}
/* @see IFormatReader#getUnderlyingReaders() */
public IFormatReader[] getUnderlyingReaders() {
return null;
}
/* @see IFormatReader#isSingleFile(String) */
public boolean isSingleFile(String id) throws FormatException, IOException {
return true;
}
/* @see IFormatReader#getDatasetStructureDescription() */
public String getDatasetStructureDescription() {
return datasetDescription;
}
/* @see IFormatReader#hasCompanionFiles() */
public boolean hasCompanionFiles() {
return hasCompanionFiles;
}
/* @see IFormatReader#getPossibleDomains(String) */
public String[] getPossibleDomains(String id)
throws FormatException, IOException
{
return domains;
}
/* @see IFormatReader#getDomains() */
public String[] getDomains() {
FormatTools.assertId(currentId, true, 1);
return domains;
}
/* @see IFormatReader#getOptimalTileWidth() */
public int getOptimalTileWidth() {
FormatTools.assertId(currentId, true, 1);
return getSizeX();
}
/* @see IFormatReader#getOptimalTileHeight() */
public int getOptimalTileHeight() {
FormatTools.assertId(currentId, true, 1);
int bpp = FormatTools.getBytesPerPixel(getPixelType());
int maxHeight = (1024 * 1024) / (getSizeX() * getRGBChannelCount() * bpp);
return (int) Math.min(maxHeight, getSizeY());
}
// -- IFormatHandler API methods --
/* @see IFormatHandler#isThisType(String) */
@Override
public boolean isThisType(String name) {
// if necessary, open the file for further analysis
return isThisType(name, true);
}
/* @see IFormatHandler#setId(String) */
public void setId(String id) throws FormatException, IOException {
if (!id.equals(currentId)) {
initFile(id);
if (saveOriginalMetadata) {
MetadataStore store = getMetadataStore();
if (store instanceof OMEXMLMetadata) {
try {
if (factory == null) factory = new ServiceFactory();
if (service == null) {
service = factory.getInstance(OMEXMLService.class);
}
Hashtable<String, Object> allMetadata =
new Hashtable<String, Object>();
allMetadata.putAll(metadata);
for (int series=0; series<getSeriesCount(); series++) {
String name = "Series " + series;
try {
String realName = ((IMetadata) store).getImageName(series);
if (realName != null && realName.trim().length() != 0) {
name = realName;
}
}
catch (Exception e) { }
setSeries(series);
MetadataTools.merge(getSeriesMetadata(), allMetadata, name + " ");
}
setSeries(0);
service.populateOriginalMetadata(
(OMEXMLMetadata) store, allMetadata);
}
catch (DependencyException e) {
LOGGER.warn("OMEXMLService not available.", e);
}
}
}
}
}
/* @see IFormatHandler#close() */
public void close() throws IOException {
close(false);
}
// -- Metadata enumeration convenience methods --
/**
* Retrieves an {@link ome.xml.model.enums.AcquisitionMode} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected AcquisitionMode getAcquisitionMode(String value)
throws FormatException
{
AcquisitionModeEnumHandler handler = new AcquisitionModeEnumHandler();
try {
return (AcquisitionMode) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("AcquisitionMode creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.ArcType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected ArcType getArcType(String value) throws FormatException {
ArcTypeEnumHandler handler = new ArcTypeEnumHandler();
try {
return (ArcType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("ArcType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.Binning} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected Binning getBinning(String value) throws FormatException {
BinningEnumHandler handler = new BinningEnumHandler();
try {
return (Binning) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("Binning creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.Compression} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected Compression getCompression(String value) throws FormatException {
CompressionEnumHandler handler = new CompressionEnumHandler();
try {
return (Compression) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("Compression creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.ContrastMethod} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected ContrastMethod getContrastMethod(String value)
throws FormatException
{
ContrastMethodEnumHandler handler = new ContrastMethodEnumHandler();
try {
return (ContrastMethod) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("ContrastMethod creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.Correction} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected Correction getCorrection(String value) throws FormatException {
CorrectionEnumHandler handler = new CorrectionEnumHandler();
try {
return (Correction) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("Correction creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.DetectorType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected DetectorType getDetectorType(String value) throws FormatException {
DetectorTypeEnumHandler handler = new DetectorTypeEnumHandler();
try {
return (DetectorType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("DetectorType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.DimensionOrder} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected DimensionOrder getDimensionOrder(String value)
throws FormatException
{
DimensionOrderEnumHandler handler = new DimensionOrderEnumHandler();
try {
return (DimensionOrder) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("DimensionOrder creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.ExperimentType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected ExperimentType getExperimentType(String value)
throws FormatException
{
ExperimentTypeEnumHandler handler = new ExperimentTypeEnumHandler();
try {
return (ExperimentType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("ExperimentType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.FilamentType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected FilamentType getFilamentType(String value) throws FormatException {
FilamentTypeEnumHandler handler = new FilamentTypeEnumHandler();
try {
return (FilamentType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("FilamentType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.FillRule} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected FillRule getFillRule(String value) throws FormatException {
FillRuleEnumHandler handler = new FillRuleEnumHandler();
try {
return (FillRule) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("FillRule creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.FilterType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected FilterType getFilterType(String value) throws FormatException {
FilterTypeEnumHandler handler = new FilterTypeEnumHandler();
try {
return (FilterType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("FilterType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.FontFamily} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected FontFamily getFontFamily(String value) throws FormatException {
FontFamilyEnumHandler handler = new FontFamilyEnumHandler();
try {
return (FontFamily) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("FontFamily creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.FontStyle} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected FontStyle getFontStyle(String value) throws FormatException {
FontStyleEnumHandler handler = new FontStyleEnumHandler();
try {
return (FontStyle) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("FontStyle creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.IlluminationType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected IlluminationType getIlluminationType(String value)
throws FormatException
{
IlluminationTypeEnumHandler handler = new IlluminationTypeEnumHandler();
try {
return (IlluminationType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("IlluminationType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.Immersion} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected Immersion getImmersion(String value) throws FormatException {
ImmersionEnumHandler handler = new ImmersionEnumHandler();
try {
return (Immersion) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("Immersion creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.LaserMedium} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected LaserMedium getLaserMedium(String value) throws FormatException {
LaserMediumEnumHandler handler = new LaserMediumEnumHandler();
try {
return (LaserMedium) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("LaserMedium creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.LaserType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected LaserType getLaserType(String value) throws FormatException {
LaserTypeEnumHandler handler = new LaserTypeEnumHandler();
try {
return (LaserType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("LaserType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.LineCap} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected LineCap getLineCap(String value) throws FormatException {
LineCapEnumHandler handler = new LineCapEnumHandler();
try {
return (LineCap) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("LineCap creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.Marker} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected Marker getMarker(String value) throws FormatException {
MarkerEnumHandler handler = new MarkerEnumHandler();
try {
return (Marker) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("Marker creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.Medium} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected Medium getMedium(String value) throws FormatException {
MediumEnumHandler handler = new MediumEnumHandler();
try {
return (Medium) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("Medium creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.MicrobeamManipulationType}
* enumeration value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected MicrobeamManipulationType getMicrobeamManipulationType(String value)
throws FormatException
{
MicrobeamManipulationTypeEnumHandler handler =
new MicrobeamManipulationTypeEnumHandler();
try {
return (MicrobeamManipulationType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("MicrobeamManipulationType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.MicroscopeType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected MicroscopeType getMicroscopeType(String value)
throws FormatException
{
MicroscopeTypeEnumHandler handler = new MicroscopeTypeEnumHandler();
try {
return (MicroscopeType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("MicroscopeType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.NamingConvention} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected NamingConvention getNamingConvention(String value)
throws FormatException
{
NamingConventionEnumHandler handler = new NamingConventionEnumHandler();
try {
return (NamingConvention) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("NamingConvention creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.PixelType} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected PixelType getPixelType(String value) throws FormatException {
PixelTypeEnumHandler handler = new PixelTypeEnumHandler();
try {
return (PixelType) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("PixelType creation failed", e);
}
}
/**
* Retrieves an {@link ome.xml.model.enums.Pulse} enumeration
* value for the given String.
*
* @throws ome.xml.model.enums.EnumerationException if an appropriate
* enumeration value is not found.
*/
protected Pulse getPulse(String value) throws FormatException {
PulseEnumHandler handler = new PulseEnumHandler();
try {
return (Pulse) handler.getEnumeration(value);
}
catch (EnumerationException e) {
throw new FormatException("Pulse creation failed", e);
}
}
}