/*
* #%L
* BSD implementations of Bio-Formats readers and writers
* %%
* Copyright (C) 2005 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package loci.formats;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.formats.in.MetadataLevel;
import loci.formats.in.MetadataOptions;
import loci.formats.meta.MetadataStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ImageReader is the master file format reader for all supported formats.
* It uses one instance of each reader subclass (specified in readers.txt,
* or other class list source) to identify file formats and read data.
*
* @author Curtis Rueden ctrueden at wisc.edu
*/
public class ImageReader implements IFormatReader {
// -- Constants --
private static final Logger LOGGER =
LoggerFactory.getLogger(ImageReader.class);
// -- Static fields --
/** Default list of reader classes, for use with noargs constructor. */
private static ClassList<IFormatReader> defaultClasses;
// -- Static utility methods --
public static ClassList<IFormatReader> getDefaultReaderClasses() {
if (defaultClasses == null) {
// load built-in reader classes from readers.txt file
try {
defaultClasses =
new ClassList<IFormatReader>("readers.txt", IFormatReader.class);
}
catch (IOException exc) {
defaultClasses = new ClassList<IFormatReader>(IFormatReader.class);
LOGGER.info("Could not parse class list; using default classes", exc);
}
}
return defaultClasses;
}
// -- Fields --
/** List of supported file format readers. */
private IFormatReader[] readers;
/**
* Valid suffixes for this file format.
* Populated the first time getSuffixes() is called.
*/
private String[] suffixes;
/** Name of current file. */
private String currentId;
/** Current form index. */
private int current;
private boolean allowOpen = true;
// -- Constructors --
/**
* Constructs a new ImageReader with the default
* list of reader classes from readers.txt.
*/
public ImageReader() {
this(getDefaultReaderClasses());
}
/** Constructs a new ImageReader from the given list of reader classes. */
public ImageReader(ClassList<IFormatReader> classList) {
// add readers to the list
List<IFormatReader> list = new ArrayList<IFormatReader>();
Class<? extends IFormatReader>[] c = classList.getClasses();
for (int i=0; i<c.length; i++) {
IFormatReader reader = null;
try {
reader = c[i].newInstance();
}
catch (IllegalAccessException exc) { }
catch (InstantiationException exc) { }
if (reader == null) {
LOGGER.error("{} cannot be instantiated.", c[i].getName());
continue;
}
list.add(reader);
}
readers = new IFormatReader[list.size()];
list.toArray(readers);
}
// -- ImageReader API methods --
/**
* Toggles whether or not file system access is allowed when doing type
* detection. By default, file system access is allowed.
*/
public void setAllowOpenFiles(boolean allowOpen) {
this.allowOpen = allowOpen;
}
/** Gets a string describing the file format for the given file. */
public String getFormat(String id) throws FormatException, IOException {
return getReader(id).getFormat();
}
/** Gets the reader used to open the given file. */
public IFormatReader getReader(String id)
throws FormatException, IOException
{
// HACK: skip file existence check for fake files
if (id.endsWith(File.separator)) {
id = id.substring(0, id.length() - 1);
}
boolean fake = id != null && id.toLowerCase().endsWith(".fake");
boolean omero = id != null && id.toLowerCase().startsWith("omero:") &&
id.indexOf("\n") > 0;
// blacklist temporary files that are being copied e.g. by WinSCP
boolean invalid = id != null && id.toLowerCase().endsWith(".filepart");
// NB: Check that we can generate a valid handle for the ID;
// e.g., for files, this will throw an exception if the file is missing.
if (!fake && !omero) {
Location.checkValidId(id);
}
if (!id.equals(currentId)) {
// initialize file
boolean success = false;
if (!invalid) {
for (int i=0; i<readers.length; i++) {
if (readers[i].isThisType(id, allowOpen)) {
current = i;
currentId = id;
success = true;
break;
}
}
}
if (!success) {
throw new UnknownFormatException("Unknown file format: " + id);
}
}
return getReader();
}
/** Gets the reader used to open the current file. */
public IFormatReader getReader() {
FormatTools.assertId(currentId, true, 2);
return readers[current];
}
/** Gets the file format reader instance matching the given class. */
public IFormatReader getReader(Class<? extends IFormatReader> c) {
for (int i=0; i<readers.length; i++) {
if (readers[i].getClass().equals(c)) return readers[i];
}
return null;
}
/** Gets all constituent file format readers. */
public IFormatReader[] getReaders() {
IFormatReader[] r = new IFormatReader[readers.length];
System.arraycopy(readers, 0, r, 0, readers.length);
return r;
}
// -- IMetadataConfigurable API methods --
/* @see loci.formats.IMetadataConfigurable#getSupportedMetadataLevels() */
@Override
public Set<MetadataLevel> getSupportedMetadataLevels() {
return getReaders()[0].getSupportedMetadataLevels();
}
/* @see loci.formats.IMetadataConfigurable#getMetadataOptions() */
@Override
public MetadataOptions getMetadataOptions() {
return getReaders()[0].getMetadataOptions();
}
/**
* @see loci.formats.IMetadataConfigurable#setMetadataOptions(MetadataOptions)
*/
@Override
public void setMetadataOptions(MetadataOptions options) {
for (IFormatReader reader : readers) {
reader.setMetadataOptions(options);
}
}
// -- IFormatReader API methods --
/* @see IFormatReader#isThisType(String, boolean) */
@Override
public boolean isThisType(String name, boolean open) {
for (int i=0; i<readers.length; i++) {
if (readers[i].isThisType(name, open)) return true;
}
return false;
}
/* @see IFormatReader.isThisType(byte[]) */
@Override
public boolean isThisType(byte[] block) {
for (int i=0; i<readers.length; i++) {
if (readers[i].isThisType(block)) return true;
}
return false;
}
/* @see IFormatReader.isThisType(RandomAccessInputStream) */
@Override
public boolean isThisType(RandomAccessInputStream stream) throws IOException {
for (int i=0; i<readers.length; i++) {
if (readers[i].isThisType(stream)) return true;
}
return false;
}
/* @see IFormatReader#getImageCount() */
@Override
public int getImageCount() {
return getReader().getImageCount();
}
/* @see IFormatReader#isRGB() */
@Override
public boolean isRGB() {
return getReader().isRGB();
}
/* @see IFormatReader#getSizeX() */
@Override
public int getSizeX() {
return getReader().getSizeX();
}
/* @see IFormatReader#getSizeY() */
@Override
public int getSizeY() {
return getReader().getSizeY();
}
/* @see IFormatReader#getSizeC() */
@Override
public int getSizeC() {
return getReader().getSizeC();
}
/* @see IFormatReader#getSizeZ() */
@Override
public int getSizeZ() {
return getReader().getSizeZ();
}
/* @see IFormatReader#getSizeT() */
@Override
public int getSizeT() {
return getReader().getSizeT();
}
/* @see IFormatReader#getPixelType() */
@Override
public int getPixelType() {
return getReader().getPixelType();
}
/* @see IFormatReader#getBitsPerPixel() */
@Override
public int getBitsPerPixel() {
return getReader().getBitsPerPixel();
}
/* @see IFormatReader#getEffectiveSizeC() */
@Override
public int getEffectiveSizeC() {
return getReader().getEffectiveSizeC();
}
/* @see IFormatReader#getRGBChannelCount() */
@Override
public int getRGBChannelCount() {
return getReader().getRGBChannelCount();
}
/* @see IFormatReader#isIndexed() */
@Override
public boolean isIndexed() {
return getReader().isIndexed();
}
/* @see IFormatReader#isFalseColor() */
@Override
public boolean isFalseColor() {
return getReader().isFalseColor();
}
/* @see IFormatReader#get8BitLookupTable() */
@Override
public byte[][] get8BitLookupTable() throws FormatException, IOException {
return getReader().get8BitLookupTable();
}
/* @see IFormatReader#get16BitLookupTable() */
@Override
public short[][] get16BitLookupTable() throws FormatException, IOException {
return getReader().get16BitLookupTable();
}
/* @see IFormatReader#getModuloZ() */
@Override
public Modulo getModuloZ() {
return getReader().getModuloZ();
}
/* @see IFormatReader#getModuloC() */
@Override
public Modulo getModuloC() {
return getReader().getModuloC();
}
/* @see IFormatReader#getModuloT() */
@Override
public Modulo getModuloT() {
return getReader().getModuloT();
}
/* @see IFormatReader#getThumbSizeX() */
@Override
public int getThumbSizeX() {
return getReader().getThumbSizeX();
}
/* @see IFormatReader#getThumbSizeY() */
@Override
public int getThumbSizeY() {
return getReader().getThumbSizeY();
}
/* @see IFormatReader#isLittleEndian() */
@Override
public boolean isLittleEndian() {
return getReader().isLittleEndian();
}
/* @see IFormatReader#getDimensionOrder() */
@Override
public String getDimensionOrder() {
return getReader().getDimensionOrder();
}
/* @see IFormatReader#isOrderCertain() */
@Override
public boolean isOrderCertain() {
return getReader().isOrderCertain();
}
/* @see IFormatReader#isThumbnailSeries() */
@Override
public boolean isThumbnailSeries() {
return getReader().isThumbnailSeries();
}
/* @see IFormatReader#isInterleaved() */
@Override
public boolean isInterleaved() {
return getReader().isInterleaved();
}
/* @see IFormatReader#isInterleaved(int) */
@Override
public boolean isInterleaved(int subC) {
return getReader().isInterleaved(subC);
}
/* @see IFormatReader#openBytes(int) */
@Override
public byte[] openBytes(int no) throws FormatException, IOException {
return getReader().openBytes(no);
}
/* @see IFormatReader#openBytes(int, int, int, int, int) */
@Override
public byte[] openBytes(int no, int x, int y, int w, int h)
throws FormatException, IOException
{
return getReader().openBytes(no, x, y, w, h);
}
/* @see IFormatReader#openBytes(int, byte[]) */
@Override
public byte[] openBytes(int no, byte[] buf)
throws FormatException, IOException
{
return getReader().openBytes(no, buf);
}
/* @see IFormatReader#openBytes(int, byte[], int, int, int, int) */
@Override
public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h)
throws FormatException, IOException
{
return getReader().openBytes(no, buf, x, y, w, h);
}
/* @see IFormatReader#openPlane(int, int, int, int, int) */
@Override
public Object openPlane(int no, int x, int y, int w, int h)
throws FormatException, IOException
{
return getReader().openPlane(no, x, y, w, h);
}
/* @see IFormatReader#openThumbBytes(int) */
@Override
public byte[] openThumbBytes(int no) throws FormatException, IOException {
return getReader().openThumbBytes(no);
}
/* @see IFormatReader#getSeriesCount() */
@Override
public int getSeriesCount() {
return getReader().getSeriesCount();
}
/* @see IFormatReader#setSeries(int) */
@Override
public void setSeries(int no) {
getReader().setSeries(no);
}
/* @see IFormatReader#getSeries() */
@Override
public int getSeries() {
return getReader().getSeries();
}
/* @see IFormatReader#getUsedFiles() */
@Override
public String[] getUsedFiles() {
return getReader().getUsedFiles();
}
/* @see IFormatReader#getUsedFiles(boolean) */
@Override
public String[] getUsedFiles(boolean noPixels) {
return getReader().getUsedFiles(noPixels);
}
/* @see IFormatReader#getSeriesUsedFiles() */
@Override
public String[] getSeriesUsedFiles() {
return getReader().getSeriesUsedFiles();
}
/* @see IFormatReader#getSeriesUsedFiles(boolean) */
@Override
public String[] getSeriesUsedFiles(boolean noPixels) {
return getReader().getSeriesUsedFiles(noPixels);
}
/* @see IFormatReader#getAdvancedUsedFiles(boolean) */
@Override
public FileInfo[] getAdvancedUsedFiles(boolean noPixels) {
return getReader().getAdvancedUsedFiles(noPixels);
}
/* @see IFormatReader#getAdvancedSeriesUsedFiles(boolean) */
@Override
public FileInfo[] getAdvancedSeriesUsedFiles(boolean noPixels) {
return getReader().getAdvancedSeriesUsedFiles(noPixels);
}
/* @see IFormatReader#getIndex(int, int, int) */
@Override
public int getIndex(int z, int c, int t) {
return getReader().getIndex(z, c, t);
}
/* @see IFormatReader#getIndex(int, int, int, int, int, int) */
@Override
public int getIndex(int z, int c, int t, int moduloZ, int moduloC, int moduloT) {
return getReader().getIndex(z, c, t, moduloZ, moduloC, moduloT);
}
/* @see IFormatReader#getZCTCoords(int) */
@Override
public int[] getZCTCoords(int index) {
return getReader().getZCTCoords(index);
}
/* @see IFormatReader#getZCTModuloCoords(int) */
@Override
public int[] getZCTModuloCoords(int index) {
return getReader().getZCTModuloCoords(index);
}
/* @see IFormatReader#getMetadataValue(String) */
@Override
public Object getMetadataValue(String field) {
return getReader().getMetadataValue(field);
}
/* @see IFormatReader#getSeriesMetadataValue(String) */
@Override
public Object getSeriesMetadataValue(String field) {
return getReader().getSeriesMetadataValue(field);
}
/* @see IFormatReader#getGlobalMetadata() */
@Override
public Hashtable<String, Object> getGlobalMetadata() {
return getReader().getGlobalMetadata();
}
/* @see IFormatReader#getSeriesMetadata() */
@Override
public Hashtable<String, Object> getSeriesMetadata() {
return getReader().getSeriesMetadata();
}
/* @see IFormatReader#getCoreMetadataList() */
@Override
public List<CoreMetadata> getCoreMetadataList() {
return getReader().getCoreMetadataList();
}
/* @see IFormatReader#close(boolean) */
@Override
public void close(boolean fileOnly) throws IOException {
for (int i=0; i<readers.length; i++) readers[i].close(fileOnly);
if (!fileOnly) currentId = null;
}
/* @see IFormatReader#setGroupFiles(boolean) */
@Override
public void setGroupFiles(boolean group) {
FormatTools.assertId(currentId, false, 2);
for (int i=0; i<readers.length; i++) readers[i].setGroupFiles(group);
}
/* @see IFormatReader#isGroupFiles() */
@Override
public boolean isGroupFiles() {
// all readers should have same file grouping setting
return readers[0].isGroupFiles();
}
/* @see IFormatReader#fileGroupOption(String) */
@Override
public int fileGroupOption(String id) throws FormatException, IOException {
return getReader(id).fileGroupOption(id);
}
/* @see IFormatReader#isMetadataComplete() */
@Override
public boolean isMetadataComplete() {
return getReader().isMetadataComplete();
}
/* @see IFormatReader#setNormalized(boolean) */
@Override
public void setNormalized(boolean normalize) {
FormatTools.assertId(currentId, false, 2);
for (int i=0; i<readers.length; i++) readers[i].setNormalized(normalize);
}
/* @see IFormatReader#isNormalized() */
@Override
public boolean isNormalized() {
// NB: all readers should have the same normalization setting
return readers[0].isNormalized();
}
/* @see IFormatReader#setOriginalMetadataPopulated(boolean) */
@Override
public void setOriginalMetadataPopulated(boolean populate) {
FormatTools.assertId(currentId, false, 1);
for (int i=0; i<readers.length; i++) {
readers[i].setOriginalMetadataPopulated(populate);
}
}
/* @see IFormatReader#isOriginalMetadataPopulated() */
@Override
public boolean isOriginalMetadataPopulated() {
return readers[0].isOriginalMetadataPopulated();
}
/* @see IFormatReader#getCurrentFile() */
@Override
public String getCurrentFile() {
if (currentId == null) return null;
return getReader().getCurrentFile();
}
/* @see IFormatReader#setMetadataFiltered(boolean) */
@Override
public void setMetadataFiltered(boolean filter) {
FormatTools.assertId(currentId, false, 2);
for (int i=0; i<readers.length; i++) readers[i].setMetadataFiltered(filter);
}
/* @see IFormatReader#isMetadataFiltered() */
@Override
public boolean isMetadataFiltered() {
// NB: all readers should have the same metadata filtering setting
return readers[0].isMetadataFiltered();
}
/* @see IFormatReader#setMetadataStore(MetadataStore) */
@Override
public void setMetadataStore(MetadataStore store) {
FormatTools.assertId(currentId, false, 2);
for (int i=0; i<readers.length; i++) readers[i].setMetadataStore(store);
}
/* @see IFormatReader#getMetadataStore() */
@Override
public MetadataStore getMetadataStore() {
return getReader().getMetadataStore();
}
/* @see IFormatReader#getMetadataStoreRoot() */
@Override
public Object getMetadataStoreRoot() {
return getReader().getMetadataStoreRoot();
}
/* @see IFormatReader#getUnderlyingReaders() */
@Override
public IFormatReader[] getUnderlyingReaders() {
return getReaders();
}
/* @see IFormatReader#isSingleFile(String) */
@Override
public boolean isSingleFile(String id) throws FormatException, IOException {
return getReader(id).isSingleFile(id);
}
/* @see IFormatReader#getRequiredDirectories(String[]) */
@Override
public int getRequiredDirectories(String[] files)
throws FormatException, IOException
{
return getReader(files[0]).getRequiredDirectories(files);
}
/* @see IFormatReader#getDatasetStructureDescription() */
@Override
public String getDatasetStructureDescription() {
return getReader().getDatasetStructureDescription();
}
/* @see IFormatReader#hasCompanionFiles() */
@Override
public boolean hasCompanionFiles() {
return getReader().hasCompanionFiles();
}
/* @see IFormatReader#getPossibleDomains(String) */
@Override
public String[] getPossibleDomains(String id)
throws FormatException, IOException
{
return getReader(id).getPossibleDomains(id);
}
/* @see IFormatReader#getDomains() */
@Override
public String[] getDomains() {
return getReader().getDomains();
}
/* @see IFormatReader#getOptimalTileWidth() */
@Override
public int getOptimalTileWidth() {
return getReader().getOptimalTileWidth();
}
/* @see IFormatReader#getOptimalTileHeight() */
@Override
public int getOptimalTileHeight() {
return getReader().getOptimalTileHeight();
}
/* @see IFormatReader#getCoreIndex() */
@Override
public int getCoreIndex() {
return getReader().getCoreIndex();
}
/* @see IFormatReader#setCoreIndex(int) */
@Override
public void setCoreIndex(int no) {
getReader().setCoreIndex(no);
}
@Override
public int seriesToCoreIndex(int series) {
return getReader().seriesToCoreIndex(series);
}
@Override
public int coreIndexToSeries(int index) {
return getReader().coreIndexToSeries(index);
}
/* @see IFormatReader#getResolutionCount() */
@Override
public int getResolutionCount() {
return getReader().getResolutionCount();
}
/* @see IFormatReader#setResolution(int) */
@Override
public void setResolution(int no) {
getReader().setResolution(no);
}
/* @see IFormatReader#getResolution() */
@Override
public int getResolution() {
return getReader().getResolution();
}
/* @see IFormatReader#hasFlattenedResolutions() */
@Override
public boolean hasFlattenedResolutions() {
// all readers should have the same flattened setting
return readers[0].hasFlattenedResolutions();
}
/* @see IFormatReader#setFlattenedResolutions(boolean) */
@Override
public void setFlattenedResolutions(boolean flattened) {
for (IFormatReader reader : readers) {
reader.setFlattenedResolutions(flattened);
}
}
// -- IFormatHandler API methods --
/* @see IFormatHandler#isThisType(String) */
@Override
public boolean isThisType(String name) {
// if necessary, open the file for further analysis
// but check isThisType(name, false) first, for efficiency
return isThisType(name, false) || isThisType(name, true);
}
/* @see IFormatHandler#getFormat() */
@Override
public String getFormat() { return getReader().getFormat(); }
/* @see IFormatHandler#getSuffixes() */
@Override
public String[] getSuffixes() {
if (suffixes == null) {
HashSet<String> suffixSet = new HashSet<String>();
for (int i=0; i<readers.length; i++) {
String[] suf = readers[i].getSuffixes();
for (int j=0; j<suf.length; j++) suffixSet.add(suf[j]);
}
suffixes = new String[suffixSet.size()];
suffixSet.toArray(suffixes);
Arrays.sort(suffixes);
}
return suffixes;
}
/* @see IFormatHandler#getNativeDataType() */
@Override
public Class<?> getNativeDataType() {
return getReader().getNativeDataType();
}
/* @see IFormatHandler#setId(String) */
@Override
public void setId(String id) throws FormatException, IOException {
IFormatReader currentReader = getReader(id);
LOGGER.info("{} initializing {}",
currentReader.getClass().getSimpleName(), id);
currentReader.setId(id);
}
/* @see IFormatReader#reopenFile() */
@Override
public void reopenFile() throws IOException {
getReader().reopenFile();
}
/* @see IFormatHandler#close() */
@Override
public void close() throws IOException { close(false); }
}