/*
* #%L
* OME Bio-Formats package for reading and converting biological file formats.
* %%
* Copyright (C) 2005 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* 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, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package loci.formats.in;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import loci.common.ByteArrayHandle;
import loci.common.DataTools;
import loci.common.DateTools;
import loci.common.IniList;
import loci.common.IniParser;
import loci.common.IniTable;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.common.services.DependencyException;
import loci.common.services.ServiceFactory;
import loci.formats.CoreMetadata;
import loci.formats.FormatException;
import loci.formats.FormatReader;
import loci.formats.FormatTools;
import loci.formats.MetadataTools;
import loci.formats.meta.MetadataStore;
import loci.formats.services.POIService;
import loci.formats.tiff.IFD;
import loci.formats.tiff.IFDList;
import loci.formats.tiff.TiffParser;
import ome.xml.model.primitives.NonNegativeInteger;
import ome.xml.model.primitives.PositiveFloat;
import ome.xml.model.primitives.PositiveInteger;
import ome.xml.model.primitives.Timestamp;
import ome.units.quantity.ElectricPotential;
import ome.units.quantity.Length;
import ome.units.quantity.Time;
import ome.units.UNITS;
/**
* FV1000Reader is the file format reader for Fluoview FV 1000 OIB and
* Fluoview FV 1000 OIF files.
*
* @author Melissa Linkert melissa at glencoesoftware.com
*/
public class FV1000Reader extends FormatReader {
// -- Constants --
public static final String FV1000_MAGIC_STRING_1 = "FileInformation";
public static final String FV1000_MAGIC_STRING_2 = "Acquisition Parameters";
public static final String[] OIB_SUFFIX = {"oib"};
public static final String[] OIF_SUFFIX = {"oif"};
public static final String[] FV1000_SUFFIXES = {"oib", "oif"};
public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final int NUM_DIMENSIONS = 9;
/** ROI types. */
private static final int POINT = 2;
private static final int LINE = 3;
private static final int POLYLINE = 4;
private static final int RECTANGLE = 5;
private static final int CIRCLE = 6;
private static final int ELLIPSE = 7;
private static final int POLYGON = 8;
private static final int FREE_SHAPE = 9;
private static final int FREE_LINE = 10;
private static final int GRID = 11;
private static final int ARROW = 12;
private static final int COLOR_BAR = 13;
private static final int SCALE = 15;
private static final String ROTATION = "rotate(%d %f %f)";
// -- Fields --
private IniParser parser = new IniParser();
/** Names of every TIFF file to open. */
private List<String> tiffs;
/** Name of thumbnail file. */
private String thumbId;
/** Helper reader for thumbnail. */
private BMPReader thumbReader;
/** Used file list. */
private List<String> usedFiles;
/** Flag indicating this is an OIB dataset. */
private boolean isOIB;
/** File mappings for OIB file. */
private Map<String, String> oibMapping;
private String[] code, size;
private Double[] pixelSize;
private int imageDepth;
private List<String> previewNames;
private String pixelSizeX, pixelSizeY;
private int validBits;
private List<String> illuminations;
private List<Double> wavelengths;
private String pinholeSize;
private String magnification, lensNA, objectiveName, workingDistance;
private String creationDate;
private List<ChannelData> channels;
private List<String> lutNames = new ArrayList<String>();
private Map<Integer, String> filenames =
new HashMap<Integer, String>();
private Map<Integer, String> roiFilenames =
new HashMap<Integer, String>();
private List<PlaneData> planes;
private transient POIService poi;
private short[][][] lut;
private int lastChannel = 0;
private double pixelSizeZ = 1, pixelSizeT = 1;
private String ptyStart = null, ptyEnd = null, ptyPattern = null, line = null;
private ArrayList<IFDList> ifds = new ArrayList<IFDList>();
// -- Constructor --
/** Constructs a new FV1000 reader. */
public FV1000Reader() {
super("Olympus FV1000", new String[] {"oib", "oif", "pty", "lut"});
domains = new String[] {FormatTools.LM_DOMAIN};
hasCompanionFiles = true;
datasetDescription = "Single .oib file or one .oif file and a " +
"similarly-named directory containing .tif/.tiff files";
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#getOptimalTileWidth() */
@Override
public int getOptimalTileWidth() {
FormatTools.assertId(currentId, true, 1);
RandomAccessInputStream plane = getPlane(getSeries(), 0);
if (plane == null) return super.getOptimalTileWidth();
try {
TiffParser tp = new TiffParser(plane);
IFD ifd = tp.getFirstIFD();
plane.close();
return (int) ifd.getTileWidth();
}
catch (FormatException e) {
LOGGER.debug("Could not retrieve tile width", e);
}
catch (IOException e) {
LOGGER.debug("Could not retrieve tile width", e);
}
return super.getOptimalTileWidth();
}
/* @see loci.formats.IFormatReader#getOptimalTileHeight() */
@Override
public int getOptimalTileHeight() {
FormatTools.assertId(currentId, true, 1);
RandomAccessInputStream plane = getPlane(getSeries(), 0);
if (plane == null) return super.getOptimalTileHeight();
try {
TiffParser tp = new TiffParser(plane);
IFD ifd = tp.getFirstIFD();
plane.close();
return (int) ifd.getTileLength();
}
catch (FormatException e) {
LOGGER.debug("Could not retrieve tile height", e);
}
catch (IOException e) {
LOGGER.debug("Could not retrieve tile height", e);
}
return super.getOptimalTileHeight();
}
/* @see loci.formats.IFormatReader#isSingleFile(String) */
@Override
public boolean isSingleFile(String id) throws FormatException, IOException {
return checkSuffix(id, OIB_SUFFIX);
}
/* @see loci.formats.IFormatReader#isThisType(String, boolean) */
@Override
public boolean isThisType(String name, boolean open) {
if (checkSuffix(name, FV1000_SUFFIXES)) return true;
if (!open) return false; // not allowed to touch the file system
try {
Location oif = new Location(findOIFFile(name));
return oif.exists() && !oif.isDirectory() &&
checkSuffix(oif.getAbsolutePath(), "oif") && !checkSuffix(name, "bmp");
}
catch (IndexOutOfBoundsException e) { }
catch (NullPointerException e) { }
catch (FormatException e) { }
return false;
}
/* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */
@Override
public boolean isThisType(RandomAccessInputStream stream) throws IOException {
final int blockLen = 1024;
if (!FormatTools.validStream(stream, blockLen, false)) return false;
String s = DataTools.stripString(stream.readString(blockLen));
return s.indexOf(FV1000_MAGIC_STRING_1) >= 0 ||
s.indexOf(FV1000_MAGIC_STRING_2) >= 0;
}
/* @see loci.formats.IFormatReader#fileGroupOption(String) */
@Override
public int fileGroupOption(String id) throws FormatException, IOException {
if (checkSuffix(id, OIB_SUFFIX)) {
return FormatTools.CANNOT_GROUP;
}
return FormatTools.MUST_GROUP;
}
/* @see loci.formats.IFormatReader#get16BitLookupTable() */
@Override
public short[][] get16BitLookupTable() {
FormatTools.assertId(currentId, true, 1);
return lut == null || !isIndexed() ? null : lut[lastChannel];
}
/**
* @see loci.formats.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
{
FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h);
int nFiles = getSeries() == 0 ? tiffs.size() : previewNames.size();
int image = no % (getImageCount() / nFiles);
int file = no / (getImageCount() / nFiles);
int[] coords = getZCTCoords(image);
lastChannel = coords[1];
RandomAccessInputStream plane = getPlane(getSeries(), no);
if (plane == null) return buf;
TiffParser tp = new TiffParser(plane);
int index = getSeries() == 0 ? file : tiffs.size() + file;
IFDList ifdList = ifds.get(index);
if (image >= ifdList.size()) return buf;
IFD ifd = ifdList.get(image);
if (getSizeY() != ifd.getImageLength()) {
tp.getSamples(ifd, buf, x,
getIndex(coords[0], 0, coords[2]), w, 1);
}
else tp.getSamples(ifd, buf, x, y, w, h);
plane.close();
plane = null;
tp = null;
return buf;
}
/* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */
@Override
public String[] getSeriesUsedFiles(boolean noPixels) {
FormatTools.assertId(currentId, true, 1);
if (isOIB) {
return noPixels ? null : new String[] {currentId};
}
final List<String> files = new ArrayList<String>();
if (usedFiles != null) {
for (String file : usedFiles) {
String f = file.toLowerCase();
if (!f.endsWith(".tif") && !f.endsWith(".tiff") && !f.endsWith(".bmp"))
{
if (!files.contains(file)) {
files.add(file);
}
}
}
}
if (!noPixels) {
if (getSeries() == 0 && tiffs != null) {
files.addAll(tiffs);
}
else if (getSeries() == 1 && previewNames != null) {
files.addAll(previewNames);
}
}
return files.toArray(new String[0]);
}
/* @see loci.formats.IFormatReader#close(boolean) */
@Override
public void close(boolean fileOnly) throws IOException {
super.close(fileOnly);
if (thumbReader != null) thumbReader.close(fileOnly);
if (!fileOnly) {
tiffs = usedFiles = null;
filenames.clear();
roiFilenames.clear();
thumbReader = null;
thumbId = null;
isOIB = false;
previewNames = null;
if (poi != null) poi.close();
poi = null;
lastChannel = 0;
wavelengths = null;
illuminations = null;
oibMapping = null;
code = size = null;
pixelSize = null;
imageDepth = 0;
pixelSizeX = pixelSizeY = null;
validBits = 0;
pinholeSize = null;
magnification = lensNA = objectiveName = workingDistance = null;
creationDate = null;
lut = null;
channels = null;
planes = null;
if (lutNames != null) {
lutNames.clear();
}
ifds.clear();
}
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
@Override
protected void initFile(String id) throws FormatException, IOException {
super.initFile(id);
parser.setCommentDelimiter(null);
isOIB = checkSuffix(id, OIB_SUFFIX);
if (isOIB) {
initPOIService();
}
// mappedOIF is used to distinguish between datasets that are being read
// directly (e.g. using ImageJ or showinf), and datasets that are being
// imported through omebf. In the latter case, the necessary directory
// structure is not preserved (only relative file names are stored in
// OMEIS), so we will need to use slightly different logic to build the
// list of associated files.
boolean mappedOIF = !isOIB && !new File(id).getAbsoluteFile().exists();
wavelengths = new ArrayList<Double>();
illuminations = new ArrayList<String>();
channels = new ArrayList<ChannelData>();
planes = new ArrayList<PlaneData>();
String oifName = null;
if (isOIB) {
oifName = mapOIBFiles();
}
else {
// make sure we have the OIF file, not a TIFF
if (!checkSuffix(id, OIF_SUFFIX)) {
currentId = findOIFFile(id);
initFile(currentId);
}
oifName = currentId;
}
String oifPath = new Location(oifName).getAbsoluteFile().getAbsolutePath();
if (mappedOIF) {
oifPath = oifName.substring(0, oifName.lastIndexOf(File.separator) + 1);
}
String path = isOIB ? "" :
oifPath.substring(0, oifPath.lastIndexOf(File.separator) + 1);
try {
RandomAccessInputStream s = getFile(oifName);
s.close();
}
catch (IOException e) {
oifName = oifName.replaceAll(".oif", ".OIF");
}
// parse key/value pairs from the OIF file
code = new String[NUM_DIMENSIONS];
size = new String[NUM_DIMENSIONS];
pixelSize = new Double[NUM_DIMENSIONS];
previewNames = new ArrayList<String>();
boolean laserEnabled = true;
IniList f = getIniFile(oifName);
IniTable saveInfo = f.getTable("ProfileSaveInfo");
String[] saveKeys = saveInfo.keySet().toArray(new String[saveInfo.size()]);
for (String key : saveKeys) {
String value = saveInfo.get(key).toString();
value = sanitizeValue(value).trim();
if (key.startsWith("IniFileName") && key.indexOf("Thumb") == -1 &&
!isPreviewName(value))
{
filenames.put(new Integer(key.substring(11)), value);
}
else if (key.startsWith("RoiFileName") && key.indexOf("Thumb") == -1 &&
!isPreviewName(value))
{
try {
roiFilenames.put(new Integer(key.substring(11)), value);
}
catch (NumberFormatException e) { }
}
else if (key.equals("PtyFileNameS")) ptyStart = value;
else if (key.equals("PtyFileNameE")) ptyEnd = value;
else if (key.equals("PtyFileNameT2")) ptyPattern = value;
else if (key.indexOf("Thumb") != -1) {
if (thumbId == null) thumbId = value.trim();
}
else if (key.startsWith("LutFileName")) {
lutNames.add(path + value);
}
else if (isPreviewName(value)) {
try {
RandomAccessInputStream s = getFile(path + value.trim());
if (s != null) {
s.close();
previewNames.add(path + value.trim());
}
}
catch (FormatException e) {
LOGGER.debug("Preview file not found", e);
}
catch (IOException e) {
LOGGER.debug("Preview file not found", e);
}
}
}
if (filenames.isEmpty()) addPtyFiles();
for (int i=0; i<NUM_DIMENSIONS; i++) {
IniTable commonParams = f.getTable("Axis " + i + " Parameters Common");
code[i] = commonParams.get("AxisCode");
size[i] = commonParams.get("MaxSize");
double end = Double.parseDouble(commonParams.get("EndPosition"));
double start = Double.parseDouble(commonParams.get("StartPosition"));
pixelSize[i] = end - start;
}
IniTable referenceParams = f.getTable("Reference Image Parameter");
imageDepth = Integer.parseInt(referenceParams.get("ImageDepth"));
pixelSizeX = referenceParams.get("WidthConvertValue");
pixelSizeY = referenceParams.get("HeightConvertValue");
String ripValidBitCounts = referenceParams.get("ValidBitCounts");
if (ripValidBitCounts != null) {
validBits = Integer.parseInt(ripValidBitCounts);
}
int index = 0;
IniTable laser = f.getTable("Laser " + index + " Parameters");
while (laser != null) {
laserEnabled = laser.get("Laser Enable").equals("1");
if (laserEnabled) {
wavelengths.add(new Double(laser.get("LaserWavelength")));
}
creationDate = laser.get("ImageCaputreDate");
if (creationDate == null) {
creationDate = laser.get("ImageCaptureDate");
}
index++;
laser = f.getTable("Laser " + index + " Parameters");
}
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
index = 1;
IniTable guiChannel = f.getTable("GUI Channel " + index + " Parameters");
while (guiChannel != null) {
ChannelData channel = new ChannelData();
String gain = guiChannel.get("AnalogPMTGain");
if (gain != null) channel.gain = new Double(gain);
String voltage = guiChannel.get("AnalogPMTVoltage");
if (voltage != null) channel.voltage = new Double(voltage);
channel.barrierFilter = guiChannel.get("BF Name");
channel.active = Integer.parseInt(guiChannel.get("CH Activate")) != 0;
channel.name = guiChannel.get("CH Name");
channel.dyeName = guiChannel.get("DyeName");
channel.emissionFilter = guiChannel.get("EmissionDM Name");
String emWave = guiChannel.get("EmissionWavelength");
if (emWave != null) channel.emWave = new Double(emWave);
channel.excitationFilter = guiChannel.get("ExcitationDM Name");
String exWave = guiChannel.get("ExcitationWavelength");
if (emWave != null) channel.exWave = new Double(exWave);
channels.add(channel);
index++;
guiChannel = f.getTable("GUI Channel " + index + " Parameters");
}
index = 1;
IniTable channel = f.getTable("Channel " + index + " Parameters");
while (channel != null) {
String illumination = channel.get("LightType");
if (illumination != null) illumination = illumination.toLowerCase();
if (illumination == null) {
// Ignored
}
else if (illumination.indexOf("fluorescence") != -1) {
illumination = "Epifluorescence";
}
else if (illumination.indexOf("transmitted") != -1) {
illumination = "Transmitted";
}
else illumination = null;
illuminations.add(illumination);
index++;
channel = f.getTable("Channel " + index + " Parameters");
}
HashMap<String, String> iniMap = f.flattenIntoHashMap();
metadata.putAll(iniMap);
}
LOGGER.info("Initializing helper readers");
// populate core metadata for preview series
if (previewNames.size() > 0) {
final List<String> v = new ArrayList<String>();
for (int i=0; i<previewNames.size(); i++) {
String ss = previewNames.get(i);
ss = replaceExtension(ss, "pty", "tif");
if (ss.endsWith(".tif")) v.add(ss);
}
previewNames = v;
if (previewNames.size() > 0) {
core.clear();
core.add(new CoreMetadata());
core.add(new CoreMetadata());
IFDList ifds = null;
CoreMetadata ms1 = core.get(1);
for (String previewName : previewNames) {
RandomAccessInputStream preview = getFile(previewName);
TiffParser tp = new TiffParser(preview);
ifds = tp.getIFDs();
preview.close();
tp = null;
ms1.imageCount += ifds.size();
}
ms1.sizeX = (int) ifds.get(0).getImageWidth();
ms1.sizeY = (int) ifds.get(0).getImageLength();
ms1.sizeZ = 1;
ms1.sizeT = 1;
ms1.sizeC = ms1.imageCount;
ms1.rgb = false;
int bits = ifds.get(0).getBitsPerSample()[0];
while ((bits % 8) != 0) bits++;
bits /= 8;
ms1.pixelType = FormatTools.pixelTypeFromBytes(bits, false, false);
ms1.dimensionOrder = "XYCZT";
ms1.indexed = false;
}
}
CoreMetadata ms0 = core.get(0);
ms0.imageCount = filenames.size();
tiffs = new ArrayList<String>(getImageCount());
thumbReader = new BMPReader();
if (thumbId != null) {
thumbId = replaceExtension(thumbId, "pty", "bmp");
thumbId = sanitizeFile(thumbId, path);
}
LOGGER.info("Reading additional metadata");
// open each INI file (.pty extension) and build list of TIFF files
String tiffPath = null;
ms0.dimensionOrder = "XY";
final Map<String, String> values = new HashMap<String, String>();
final List<String> baseKeys = new ArrayList<String>();
for (int i=0, ii=0; ii<getImageCount(); i++, ii++) {
String file = filenames.get(i);
while (file == null) file = filenames.get(++i);
file = sanitizeFile(file, path);
if (file.indexOf(File.separator) != -1) {
tiffPath = file.substring(0, file.lastIndexOf(File.separator));
}
else tiffPath = file;
Location ptyFile = new Location(file);
if (!isOIB && !ptyFile.exists()) {
LOGGER.warn("Could not find .pty file ({}); guessing at the " +
"corresponding TIFF file.", file);
String tiff = replaceExtension(file, "pty", "tif");
Location tiffFile = new Location(tiff);
if (tiffFile.exists()) {
tiffs.add(ii, tiffFile.getAbsolutePath());
continue;
}
else {
if (!tiffFile.getParentFile().exists()) {
String realOIFName = new Location(currentId).getName();
String basePath = tiffFile.getParentFile().getParent();
if (mappedOIF) {
tiffPath = basePath + File.separator + realOIFName + ".files";
ptyFile = new Location(tiffPath, ptyFile.getName());
file = ptyFile.getAbsolutePath();
}
else {
Location newFile = new Location(basePath, realOIFName + ".files");
ptyFile = new Location(newFile, ptyFile.getName());
file = ptyFile.getAbsolutePath();
tiffPath = newFile.getAbsolutePath();
}
}
}
}
else if (!isOIB) {
file = ptyFile.getAbsolutePath();
}
IniList pty = getIniFile(file);
IniTable fileInfo = pty.getTable("File Info");
file = sanitizeValue(fileInfo.get("DataName"));
if (!isPreviewName(file)) {
while (file.indexOf("GST") != -1) {
file = removeGST(file);
}
if (isOIB) {
file = tiffPath + File.separator + file;
}
else file = new Location(tiffPath, file).getAbsolutePath();
file = replaceExtension(file, "pty", "tif");
tiffs.add(ii, file);
}
PlaneData plane = new PlaneData();
for (int dim=0; dim<NUM_DIMENSIONS; dim++) {
IniTable axis = pty.getTable("Axis " + dim + " Parameters");
if (axis == null) break;
boolean addAxis = Integer.parseInt(axis.get("Number")) > 1;
if (dim == 2) {
if (addAxis && getDimensionOrder().indexOf("C") == -1) {
ms0.dimensionOrder += "C";
}
}
else if (dim == 3) {
if (addAxis && getDimensionOrder().indexOf("Z") == -1) {
ms0.dimensionOrder += "Z";
}
final Double number = Double.valueOf(axis.get("AbsPositionValue"));
plane.positionZ = new Length(number, UNITS.REFERENCEFRAME);
}
else if (dim == 4) {
if (addAxis && getDimensionOrder().indexOf("T") == -1) {
ms0.dimensionOrder += "T";
}
// divide by 1000, as the position is in milliseconds
// and DeltaT is in seconds
plane.deltaT =
Double.parseDouble(axis.get("AbsPositionValue")) / 1000;
}
else if (dim == 7) {
try {
String xPos = axis.get("AbsPositionValueX");
if (xPos != null) {
final Double number = Double.valueOf(xPos);
plane.positionX = new Length(number, UNITS.REFERENCEFRAME);
}
}
catch (NumberFormatException e) { }
try {
String yPos = axis.get("AbsPositionValueY");
if (yPos != null) {
final Double number = Double.valueOf(yPos);
plane.positionY = new Length(number, UNITS.REFERENCEFRAME);
}
}
catch (NumberFormatException e) { }
}
}
ms0.bitsPerPixel = validBits;
planes.add(plane);
IniTable acquisition = pty.getTable("Acquisition Parameters Common");
if (acquisition != null) {
magnification = acquisition.get("Magnification");
lensNA = acquisition.get("ObjectiveLens NAValue");
objectiveName = acquisition.get("ObjectiveLens Name");
workingDistance = acquisition.get("ObjectiveLens WDValue");
pinholeSize = acquisition.get("PinholeDiameter");
String validBitCounts = acquisition.get("ValidBitCounts");
if (validBitCounts != null) {
ms0.bitsPerPixel = Integer.parseInt(validBitCounts);
}
}
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
for (IniTable table : pty) {
String[] keys = table.keySet().toArray(new String[table.size()]);
for (String key : keys) {
values.put("Image " + ii + " : " + key, table.get(key));
if (!baseKeys.contains(key)) baseKeys.add(key);
}
}
}
}
for (String key : baseKeys) {
if (key.equals("DataName") || key.indexOf("FileName") >= 0) break;
boolean equal = true;
String first = values.get("Image 0 : " + key);
for (int i=1; i<getImageCount(); i++) {
if (!first.equals(values.get("Image " + i + " : " + key))) {
equal = false;
break;
}
}
if (equal) {
addGlobalMeta(key, first);
}
else {
for (int i=0; i<getImageCount(); i++) {
String k = "Image " + i + " : " + key;
addGlobalMeta(k, values.get(k));
}
}
}
if (tiffs.size() != getImageCount()) {
ms0.imageCount = tiffs.size();
}
usedFiles = new ArrayList<String>();
if (tiffPath != null) {
usedFiles.add(isOIB ? id : oifName);
if (!isOIB) {
Location dir = new Location(tiffPath);
if (!mappedOIF && !dir.exists()) {
throw new FormatException(
"Required directory " + tiffPath + " was not found.");
}
String[] list = mappedOIF ?
Location.getIdMap().keySet().toArray(new String[0]) : dir.list(true);
for (int i=0; i<list.length; i++) {
if (mappedOIF) usedFiles.add(list[i]);
else {
String p = new Location(tiffPath, list[i]).getAbsolutePath();
String check = p.toLowerCase();
if (!check.endsWith(".tif") && !check.endsWith(".pty") &&
!check.endsWith(".roi") && !check.endsWith(".lut") &&
!check.endsWith(".bmp"))
{
continue;
}
usedFiles.add(p);
}
}
}
}
LOGGER.info("Populating metadata");
// calculate axis sizes
int realChannels = 0;
for (int i=0; i<NUM_DIMENSIONS; i++) {
int ss = Integer.parseInt(size[i]);
if (pixelSize[i] == null) pixelSize[i] = 1.0;
if (code[i].equals("X")) ms0.sizeX = ss;
else if (code[i].equals("Y") && ss > 1) ms0.sizeY = ss;
else if (code[i].equals("Z")) {
if (getSizeY() == 0) {
ms0.sizeY = ss;
}
else {
ms0.sizeZ = ss;
// Z size stored in nm
pixelSizeZ =
Math.abs((pixelSize[i].doubleValue() / (getSizeZ() - 1)) / 1000);
}
}
else if (code[i].equals("T")) {
if (getSizeY() == 0) {
ms0.sizeY = ss;
}
else {
ms0.sizeT = ss;
pixelSizeT =
Math.abs((pixelSize[i].doubleValue() / (getSizeT() - 1)) / 1000);
}
}
else if (ss > 0) {
if (getSizeC() == 0) ms0.sizeC = ss;
else ms0.sizeC *= ss;
if (code[i].equals("C")) realChannels = ss;
}
}
if (getSizeZ() == 0) ms0.sizeZ = 1;
if (getSizeC() == 0) ms0.sizeC = 1;
if (getSizeT() == 0) ms0.sizeT = 1;
if (getImageCount() == getSizeC() && getSizeY() == 1) {
ms0.imageCount *= getSizeZ() * getSizeT();
}
else if (getImageCount() == getSizeC()) {
ms0.sizeZ = 1;
ms0.sizeT = 1;
}
if (getSizeZ() * getSizeT() * getSizeC() != getImageCount()) {
int diff = (getSizeZ() * getSizeC() * getSizeT()) - getImageCount();
if (diff == previewNames.size() || diff < 0) {
diff /= getSizeC();
if (getSizeT() > 1 && getSizeZ() == 1) ms0.sizeT -= diff;
else if (getSizeZ() > 1 && getSizeT() == 1) ms0.sizeZ -= diff;
}
else ms0.imageCount += diff;
}
if (getSizeC() > 1 && getSizeZ() == 1 && getSizeT() == 1) {
if (getDimensionOrder().indexOf("C") == -1) ms0.dimensionOrder += "C";
}
if (getDimensionOrder().indexOf("Z") == -1) ms0.dimensionOrder += "Z";
if (getDimensionOrder().indexOf("C") == -1) ms0.dimensionOrder += "C";
if (getDimensionOrder().indexOf("T") == -1) ms0.dimensionOrder += "T";
ms0.pixelType =
FormatTools.pixelTypeFromBytes(imageDepth, false, false);
// set up thumbnail file mapping
try {
RandomAccessInputStream thumb = getFile(thumbId);
byte[] b = new byte[(int) thumb.length()];
thumb.read(b);
thumb.close();
Location.mapFile("thumbnail.bmp", new ByteArrayHandle(b));
thumbReader.setId("thumbnail.bmp");
for (int i=0; i<getSeriesCount(); i++) {
core.get(i).thumbSizeX = thumbReader.getSizeX();
core.get(i).thumbSizeY = thumbReader.getSizeY();
}
thumbReader.close();
Location.mapFile("thumbnail.bmp", null);
}
catch (IOException e) {
LOGGER.debug("Could not read thumbnail", e);
}
catch (FormatException e) {
LOGGER.debug("Could not read thumbnail", e);
}
// initialize lookup table
lut = new short[getSizeC()][3][65536];
byte[] buffer = new byte[65536 * 4];
int count = (int) Math.min(getSizeC(), lutNames.size());
for (int c=0; c<count; c++) {
Exception exc = null;
try {
RandomAccessInputStream stream = getFile(lutNames.get(c));
stream.seek(stream.length() - 65536 * 4);
stream.read(buffer);
stream.close();
for (int q=0; q<buffer.length; q+=4) {
lut[c][0][q / 4] = (short) ((buffer[q + 2] & 0xff) * 257);
lut[c][1][q / 4] = (short) ((buffer[q + 1] & 0xff) * 257);
lut[c][2][q / 4] = (short) ((buffer[q] & 0xff) * 257);
}
}
catch (IOException e) { exc = e; }
catch (FormatException e) { exc = e; }
if (exc != null) {
LOGGER.debug("Could not read LUT", exc);
lut = null;
break;
}
}
for (int i=0; i<getSeriesCount(); i++) {
CoreMetadata ms = core.get(i);
ms.rgb = false;
ms.littleEndian = true;
ms.interleaved = false;
ms.metadataComplete = true;
ms.indexed = lut != null;
ms.falseColor = true;
int nFiles = i == 0 ? tiffs.size() : previewNames.size();
for (int file=0; file<nFiles; file++) {
RandomAccessInputStream plane =
getFile(i == 0 ? tiffs.get(file) : previewNames.get(file));
if (plane == null) {
ifds.add(null);
continue;
}
try {
TiffParser tp = new TiffParser(plane);
IFDList ifd = tp.getIFDs();
ifds.add(ifd);
}
finally {
plane.close();
}
}
}
// populate MetadataStore
MetadataStore store = makeFilterMetadata();
MetadataTools.populatePixels(store, this, true);
if (creationDate != null) {
creationDate = creationDate.replaceAll("'", "");
creationDate = DateTools.formatDate(creationDate, DATE_FORMAT);
}
for (int i=0; i<getSeriesCount(); i++) {
// populate Image data
store.setImageName("Series " + (i + 1), i);
if (creationDate != null) store.setImageAcquisitionDate(
new Timestamp(creationDate), i);
}
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
populateMetadataStore(store, path);
}
}
private void populateMetadataStore(MetadataStore store, String path)
throws FormatException, IOException
{
String instrumentID = MetadataTools.createLSID("Instrument", 0);
store.setInstrumentID(instrumentID, 0);
for (int i=0; i<getSeriesCount(); i++) {
// link Instrument and Image
store.setImageInstrumentRef(instrumentID, i);
// populate Dimensions data
if (pixelSizeX != null) {
Double sizeX = new Double(pixelSizeX);
Length size = FormatTools.getPhysicalSizeX(sizeX);
if (size != null) {
store.setPixelsPhysicalSizeX(size, i);
}
}
if (pixelSizeY != null) {
Double sizeY = new Double(pixelSizeY);
Length size = FormatTools.getPhysicalSizeY(sizeY);
if (size != null) {
store.setPixelsPhysicalSizeY(size, i);
}
}
if (pixelSizeZ == Double.NEGATIVE_INFINITY ||
pixelSizeZ == Double.POSITIVE_INFINITY || getSizeZ() == 1)
{
pixelSizeZ = 1d;
}
if (pixelSizeT == Double.NEGATIVE_INFINITY ||
pixelSizeT == Double.POSITIVE_INFINITY || getSizeT() == 1)
{
pixelSizeT = 1d;
}
Length sizeZ = FormatTools.getPhysicalSizeZ(pixelSizeZ);
if (sizeZ != null) {
store.setPixelsPhysicalSizeZ(sizeZ, i);
}
store.setPixelsTimeIncrement(new Time(pixelSizeT, UNITS.S), i);
for (int p=0; p<core.get(i).imageCount; p++) {
store.setPlaneDeltaT(new Time(pixelSizeT * p, UNITS.S), i, p);
}
// populate LogicalChannel data
for (int c=0; c<core.get(i).sizeC; c++) {
if (c < illuminations.size()) {
store.setChannelIlluminationType(
getIlluminationType(illuminations.get(c)), i, c);
}
}
}
int channelIndex = 0;
for (ChannelData channel : channels) {
if (!channel.active) continue;
if (channelIndex >= getEffectiveSizeC()) break;
// populate Detector data
String detectorID = MetadataTools.createLSID("Detector", 0, channelIndex);
store.setDetectorID(detectorID, 0, channelIndex);
store.setDetectorSettingsID(detectorID, 0, channelIndex);
store.setDetectorGain(channel.gain, 0, channelIndex);
ElectricPotential theVoltage = FormatTools.createElectricPotential(channel.voltage, UNITS.V);
if (theVoltage != null) {
store.setDetectorVoltage(
theVoltage, 0, channelIndex);
}
store.setDetectorType(getDetectorType("PMT"), 0, channelIndex);
// populate LogicalChannel data
store.setChannelName(channel.name, 0, channelIndex);
String lightSourceID =
MetadataTools.createLSID("LightSource", 0, channelIndex);
store.setChannelLightSourceSettingsID(lightSourceID, 0, channelIndex);
Length wavelength = FormatTools.getWavelength(channel.exWave);
Length emission = FormatTools.getEmissionWavelength(channel.emWave);
Length excitation = FormatTools.getExcitationWavelength(channel.exWave);
if (emission != null) {
store.setChannelEmissionWavelength(emission, 0, channelIndex);
}
if (excitation != null) {
store.setChannelExcitationWavelength(excitation, 0, channelIndex);
}
if (wavelength != null) {
store.setChannelLightSourceSettingsWavelength(
wavelength, 0, channelIndex);
}
// populate Filter data
if (channel.barrierFilter != null) {
String filterID = MetadataTools.createLSID("Filter", 0, channelIndex);
store.setFilterID(filterID, 0, channelIndex);
store.setFilterModel(channel.barrierFilter, 0, channelIndex);
if (channel.barrierFilter.indexOf("-") != -1) {
String[] emValues = channel.barrierFilter.split("-");
for (int i=0; i<emValues.length; i++) {
emValues[i] = emValues[i].replaceAll("\\D", "");
}
try {
Double cutIn = new Double(emValues[0]);
Double cutOut = new Double(emValues[1]);
Length in = FormatTools.getCutIn(cutIn);
Length out = FormatTools.getCutOut(cutOut);
if (in != null) {
store.setTransmittanceRangeCutIn(in, 0, channelIndex);
}
if (out != null) {
store.setTransmittanceRangeCutOut(out, 0, channelIndex);
}
}
catch (NumberFormatException e) { }
}
store.setLightPathEmissionFilterRef(filterID, 0, channelIndex, 0);
}
// populate FilterSet data
int emIndex = channelIndex * 2;
int exIndex = channelIndex * 2 + 1;
String emFilter = MetadataTools.createLSID("Dichroic", 0, emIndex);
String exFilter = MetadataTools.createLSID("Dichroic", 0, exIndex);
// populate Dichroic data
store.setDichroicID(emFilter, 0, emIndex);
store.setDichroicModel(channel.emissionFilter, 0, emIndex);
store.setDichroicID(exFilter, 0, exIndex);
store.setDichroicModel(channel.excitationFilter, 0, exIndex);
store.setLightPathDichroicRef(exFilter, 0, channelIndex);
// populate Laser data
store.setLaserID(lightSourceID, 0, channelIndex);
store.setLaserLaserMedium(getLaserMedium(channel.dyeName),
0, channelIndex);
if (channelIndex < wavelengths.size()) {
Length wave =
FormatTools.getWavelength(wavelengths.get(channelIndex));
if (wave != null) {
store.setLaserWavelength(wave, 0, channelIndex);
}
}
store.setLaserType(getLaserType("Other"), 0, channelIndex);
channelIndex++;
}
// populate Objective data
if (lensNA != null) store.setObjectiveLensNA(new Double(lensNA), 0, 0);
store.setObjectiveModel(objectiveName, 0, 0);
if (magnification != null) {
Double mag = Double.parseDouble(magnification);
store.setObjectiveNominalMagnification(mag, 0, 0);
}
if (workingDistance != null) {
store.setObjectiveWorkingDistance(new Length(new Double(workingDistance), UNITS.MICROM), 0, 0);
}
store.setObjectiveCorrection(getCorrection("Other"), 0, 0);
store.setObjectiveImmersion(getImmersion("Other"), 0, 0);
// link Objective to Image using ObjectiveSettings
String objectiveID = MetadataTools.createLSID("Objective", 0, 0);
store.setObjectiveID(objectiveID, 0, 0);
store.setObjectiveSettingsID(objectiveID, 0);
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.NO_OVERLAYS) {
int nextROI = -1;
// populate ROI data - there is one ROI file per plane
for (int i=0; i<roiFilenames.size(); i++) {
if (i >= getImageCount()) break;
String filename = roiFilenames.get(i);
filename = sanitizeFile(filename, path);
nextROI = parseROIFile(filename, store, nextROI, i);
}
}
// Populate metadata for the planes
for (int i=0; i<planes.size(); i++) {
PlaneData plane = planes.get(i);
if (plane.deltaT != null) {
store.setPlaneDeltaT(new Time(plane.deltaT, UNITS.S), 0, i);
}
store.setPlanePositionX(plane.positionX, 0, i);
store.setPlanePositionY(plane.positionY, 0, i);
store.setPlanePositionZ(plane.positionZ, 0, i);
}
}
private int parseROIFile(String filename, MetadataStore store, int nextROI,
int plane) throws FormatException, IOException
{
int[] coordinates = getZCTCoords(plane);
IniList roiFile = null;
try {
roiFile = getIniFile(filename);
}
catch (FormatException e) {
LOGGER.debug("Could not parse ROI file {}", filename, e);
return nextROI;
}
catch (IOException e) {
LOGGER.debug("Could not parse ROI file {}", filename, e);
return nextROI;
}
boolean validROI = false;
int shape = -1;
int shapeType = -1;
String[] xc = null, yc = null;
int divide = 0;
int fontSize = 0, lineWidth = 0, angle = 0;
String fontName = null, name = null;
for (IniTable table : roiFile) {
String tableName = table.get(IniTable.HEADER_KEY);
if (tableName.equals("ROIBase FileInformation")) {
try {
String roiName = table.get("Name").replaceAll("\"", "");
validROI = Integer.parseInt(roiName) > 1;
}
catch (NumberFormatException e) { validROI = false; }
if (!validROI) continue;
}
else if (tableName.equals("ROIBase Body")) {
shapeType = Integer.parseInt(table.get("SHAPE"));
divide = Integer.parseInt(table.get("DIVIDE"));
String[] fontAttributes = table.get("FONT").split(",");
fontName = fontAttributes[0];
fontSize = Integer.parseInt(fontAttributes[1]);
Length font = FormatTools.getFontSize(fontSize);
lineWidth = Integer.parseInt(table.get("LINEWIDTH"));
name = table.get("NAME");
angle = Integer.parseInt(table.get("ANGLE"));
xc = table.get("X").split(",");
yc = table.get("Y").split(",");
int x = Integer.parseInt(xc[0]);
int width = xc.length > 1 ? Integer.parseInt(xc[1]) - x : 0;
int y = Integer.parseInt(yc[0]);
int height = yc.length > 1 ? Integer.parseInt(yc[1]) - y : 0;
if (width + x <= getSizeX() && height + y <= getSizeY()) {
shape++;
final Integer zIndex = coordinates[0];
final Integer tIndex = coordinates[2];
if (shape == 0) {
nextROI++;
if (shapeType == POINT || shapeType == GRID ||
shapeType == RECTANGLE || shapeType == LINE ||
shapeType == CIRCLE || shapeType == ELLIPSE ||
shapeType == POLYGON || shapeType == FREE_SHAPE ||
shapeType == POLYLINE || shapeType == FREE_LINE)
{
String roiID = MetadataTools.createLSID("ROI", nextROI);
store.setROIID(roiID, nextROI);
store.setImageROIRef(roiID, 0, nextROI);
}
}
String shapeID = MetadataTools.createLSID("Shape", nextROI, shape);
if (shapeType == POINT) {
store.setPointID(shapeID, nextROI, shape);
store.setPointTheZ(new NonNegativeInteger(zIndex), nextROI, shape);
store.setPointTheT(new NonNegativeInteger(tIndex), nextROI, shape);
if (font != null) {
store.setPointFontSize(font, nextROI, shape);
}
Length l = new Length((double) lineWidth, UNITS.PIXEL);
store.setPointStrokeWidth(l, nextROI, shape);
store.setPointX(new Double(xc[0]), nextROI, shape);
store.setPointY(new Double(yc[0]), nextROI, shape);
}
else if (shapeType == GRID || shapeType == RECTANGLE) {
if (shapeType == RECTANGLE) divide = 1;
width /= divide;
height /= divide;
for (int row=0; row<divide; row++) {
for (int col=0; col<divide; col++) {
double realX = x + col * width;
double realY = y + row * height;
shapeID = MetadataTools.createLSID("Shape", nextROI, shape);
store.setRectangleID(shapeID, nextROI, shape);
store.setRectangleX(realX, nextROI, shape);
store.setRectangleY(realY, nextROI, shape);
store.setRectangleWidth((double) width, nextROI, shape);
store.setRectangleHeight((double) height, nextROI, shape);
store.setRectangleTheZ(
new NonNegativeInteger(zIndex), nextROI, shape);
store.setRectangleTheT(
new NonNegativeInteger(tIndex), nextROI, shape);
if (font != null) {
store.setRectangleFontSize(font, nextROI, shape);
}
Length l = new Length((double) lineWidth, UNITS.PIXEL);
store.setRectangleStrokeWidth(l, nextROI, shape);
double centerX = realX + (width / 2);
double centerY = realY + (height / 2);
store.setRectangleTransform(
getRotationTransform(angle), nextROI, shape);
if (row < divide - 1 || col < divide - 1) shape++;
}
}
}
else if (shapeType == LINE) {
store.setLineID(shapeID, nextROI, shape);
store.setLineX1((double) x, nextROI, shape);
store.setLineY1((double) y, nextROI, shape);
store.setLineX2((double) (x + width), nextROI, shape);
store.setLineY2((double) (y + height), nextROI, shape);
store.setLineTheZ(new NonNegativeInteger(zIndex), nextROI, shape);
store.setLineTheT(new NonNegativeInteger(tIndex), nextROI, shape);
if (font != null) {
store.setLineFontSize(font, nextROI, shape);
}
Length l = new Length((double) lineWidth, UNITS.PIXEL);
store.setLineStrokeWidth(l, nextROI, shape);
int centerX = x + (width / 2);
int centerY = y + (height / 2);
store.setLineTransform(getRotationTransform(angle), nextROI, shape);
}
else if (shapeType == CIRCLE || shapeType == ELLIPSE) {
double rx = width / 2;
double ry = shapeType == CIRCLE ? rx : height / 2;
store.setEllipseID(shapeID, nextROI, shape);
store.setEllipseX(x + rx, nextROI, shape);
store.setEllipseY(y + ry, nextROI, shape);
store.setEllipseRadiusX(rx, nextROI, shape);
store.setEllipseRadiusY(ry, nextROI, shape);
store.setEllipseTheZ(
new NonNegativeInteger(zIndex), nextROI, shape);
store.setEllipseTheT(
new NonNegativeInteger(tIndex), nextROI, shape);
if (font != null) {
store.setEllipseFontSize(font, nextROI, shape);
}
Length l = new Length((double) lineWidth, UNITS.PIXEL);
store.setEllipseStrokeWidth(l, nextROI, shape);
store.setEllipseTransform(
getRotationTransform(angle), nextROI, shape);
}
else if (shapeType == POLYGON || shapeType == FREE_SHAPE ||
shapeType == POLYLINE || shapeType == FREE_LINE)
{
StringBuffer points = new StringBuffer();
for (int point=0; point<xc.length; point++) {
points.append(xc[point]);
points.append(",");
points.append(yc[point]);
if (point < xc.length - 1) points.append(" ");
}
if (shapeType == POLYLINE || shapeType == FREE_LINE) {
store.setPolylineID(shapeID, nextROI, shape);
store.setPolylinePoints(points.toString(), nextROI, shape);
store.setPolylineTransform(
getRotationTransform(angle), nextROI, shape);
store.setPolylineTheZ(
new NonNegativeInteger(zIndex), nextROI, shape);
store.setPolylineTheT(
new NonNegativeInteger(tIndex), nextROI, shape);
if (font != null) {
store.setPolylineFontSize(font, nextROI, shape);
}
Length l = new Length((double) lineWidth, UNITS.PIXEL);
store.setPolylineStrokeWidth(l, nextROI, shape);
}
else {
store.setPolygonID(shapeID, nextROI, shape);
store.setPolygonPoints(points.toString(), nextROI, shape);
store.setPolygonTransform(
getRotationTransform(angle), nextROI, shape);
store.setPolygonTheZ(
new NonNegativeInteger(zIndex), nextROI, shape);
store.setPolygonTheT(
new NonNegativeInteger(tIndex), nextROI, shape);
if (font != null) {
store.setPolygonFontSize(font, nextROI, shape);
}
Length l = new Length((double) lineWidth, UNITS.PIXEL);
store.setPolygonStrokeWidth(l, nextROI, shape);
}
}
else {
if (shape == 0) nextROI--;
shape--;
}
}
}
}
return nextROI;
}
private void addPtyFiles() throws FormatException {
if (ptyStart != null && ptyEnd != null) {
// FV1000 version 2 gives the first .pty file, the last .pty and
// the file name pattern. Version 1 lists each .pty file individually.
// pattern is typically 's_C%03dT%03d.pty'
// build list of block indexes
if (ptyPattern == null) {
String dir =
ptyStart.substring(0, ptyStart.indexOf(File.separator) + 1);
ptyPattern = dir + "s_C%03dT%03d.pty";
}
String[] prefixes = ptyPattern.split("%03d");
// get first and last numbers for each block
int[] first = scanFormat(ptyPattern, ptyStart);
int[] last = scanFormat(ptyPattern, ptyEnd);
int[] lengths = new int[prefixes.length - 1];
int totalFiles = 1;
for (int i=0; i<first.length; i++) {
lengths[i] = last[i] - first[i] + 1;
totalFiles *= lengths[i];
}
// add each .pty file
for (int file=0; file<totalFiles; file++) {
int[] pos = FormatTools.rasterToPosition(lengths, file);
StringBuffer pty = new StringBuffer();
for (int block=0; block<prefixes.length; block++) {
pty.append(prefixes[block]);
if (block < pos.length) {
String num = String.valueOf(pos[block] + 1);
for (int q=0; q<3 - num.length(); q++) {
pty.append("0");
}
pty.append(num);
}
}
filenames.put(file, pty.toString());
}
}
}
// -- Helper methods --
private String findOIFFile(String baseFile) throws FormatException {
Location current = new Location(baseFile).getAbsoluteFile();
String parent = current.getParent();
Location tmp = new Location(parent).getParentFile();
parent = tmp.getAbsolutePath();
baseFile = current.getName();
if (baseFile == null || baseFile.indexOf("_") == -1) return null;
baseFile = baseFile.substring(0, baseFile.lastIndexOf("_"));
if (checkSuffix(current.getName(), new String[] {"roi", "lut"})) {
if (!new Location(tmp, baseFile + ".oif").exists() &&
!new Location(tmp, baseFile + ".OIF").exists() &&
baseFile.indexOf("_") >= 0)
{
// some metadata files have an extra underscore
baseFile = baseFile.substring(0, baseFile.lastIndexOf("_"));
}
}
baseFile += ".oif";
tmp = new Location(tmp, baseFile);
String oifFile = tmp.getAbsolutePath();
if (!tmp.exists()) {
oifFile = oifFile.substring(0, oifFile.lastIndexOf(".")) + ".OIF";
tmp = new Location(oifFile);
if (!tmp.exists()) {
baseFile = current.getParent();
baseFile = baseFile.substring(0, baseFile.lastIndexOf("."));
baseFile = baseFile.substring(0, baseFile.lastIndexOf("."));
tmp = new Location(baseFile + ".oif");
oifFile = tmp.getAbsolutePath();
if (!tmp.exists()) {
tmp = new Location(tmp.getParent(), tmp.getName().toUpperCase());
oifFile = tmp.getAbsolutePath();
if (!tmp.exists()) {
// check in parent directory
if (parent.endsWith(File.separator)) {
parent = parent.substring(0, parent.length() - 1);
}
String dir = parent.substring(parent.lastIndexOf(File.separator));
dir = dir.substring(0, dir.lastIndexOf("."));
tmp = new Location(parent);
oifFile = new Location(tmp, dir).getAbsolutePath();
if (!new Location(oifFile).exists()) {
throw new FormatException("OIF file not found");
}
}
}
}
}
return oifFile;
}
private String mapOIBFiles() throws FormatException, IOException {
String oifName = null;
String infoFile = null;
final List<String> list = poi.getDocumentList();
for (String name : list) {
if (name.endsWith("OibInfo.txt")) {
infoFile = name;
break;
}
}
if (infoFile == null) {
throw new FormatException("OibInfo.txt not found in " + currentId);
}
RandomAccessInputStream ras = poi.getDocumentStream(infoFile);
oibMapping = new HashMap<String, String>();
// set up file name mappings
String s = DataTools.stripString(ras.readString((int) ras.length()));
ras.close();
String[] lines = s.split("\n");
// sort the lines to ensure that the
// directory key is before the file names
Arrays.sort(lines);
String directoryKey = null, directoryValue = null, key = null, value = null;
for (String line : lines) {
line = line.trim();
if (line.indexOf("=") != -1) {
key = line.substring(0, line.indexOf("="));
value = line.substring(line.indexOf("=") + 1);
if (directoryKey != null && directoryValue != null) {
value = value.replaceAll(directoryKey, directoryValue);
}
value = removeGST(value);
if (key.startsWith("Stream")) {
value = sanitizeFile(value, "");
if (checkSuffix(value, OIF_SUFFIX)) oifName = value;
if (directoryKey != null && value.startsWith(directoryValue)) {
oibMapping.put(value, "Root Entry" + File.separator +
directoryKey + File.separator + key);
}
else {
oibMapping.put(value, "Root Entry" + File.separator + key);
}
}
else if (key.startsWith("Storage")) {
directoryKey = key;
directoryValue = value;
}
}
}
s = null;
return oifName;
}
private String sanitizeValue(String value) {
String f = value.replaceAll("\"", "");
f = f.replace('\\', File.separatorChar);
f = f.replace('/', File.separatorChar);
while (f.indexOf("GST") != -1) {
f = removeGST(f);
}
return f;
}
private String sanitizeFile(String file, String path) {
String f = sanitizeValue(file);
if (path.equals("")) return f;
if (path.endsWith(File.separator)) {
return path + f;
}
return path + File.separator + f;
}
private String removeGST(String s) {
if (s.indexOf("GST") != -1) {
String first = s.substring(0, s.indexOf("GST"));
int ndx = s.indexOf(File.separator) < s.indexOf("GST") ?
s.length() : s.indexOf(File.separator);
String last = s.substring(s.lastIndexOf("=", ndx) + 1);
return first + last;
}
return s;
}
private void initPOIService() throws FormatException, IOException {
try {
ServiceFactory factory = new ServiceFactory();
poi = factory.getInstance(POIService.class);
}
catch (DependencyException de) {
throw new FormatException("POI library not found", de);
}
poi.initialize(Location.getMappedId(getCurrentFile()));
}
private RandomAccessInputStream getFile(String name)
throws FormatException, IOException
{
if (isOIB) {
if (poi == null) {
initPOIService();
}
name = name.replace('\\', File.separatorChar);
name = name.replace('/', File.separatorChar);
String realName = oibMapping.get(name);
if (realName == null) {
throw new FormatException("File " + name + " not found.");
}
return poi.getDocumentStream(realName);
}
return new RandomAccessInputStream(name, 16);
}
private RandomAccessInputStream getPlane(int seriesIndex, int planeIndex) {
int file = planeIndex;
if (seriesIndex == 0) {
file = planeIndex / (getImageCount() / tiffs.size());
}
else file = planeIndex / (getImageCount() / previewNames.size());
String filename =
seriesIndex == 0 ? tiffs.get(file) : previewNames.get(file);
RandomAccessInputStream plane = null;
try {
plane = getFile(filename);
}
catch (FormatException e) { }
catch (IOException e) { }
return plane;
}
private boolean isPreviewName(String name) {
// "-R" in the file name indicates that this is a preview image
int index = name.indexOf("-R");
return index == name.length() - 9;
}
private String replaceExtension(String name, String oldExt, String newExt) {
if (!name.endsWith("." + oldExt)) {
return name;
}
return name.substring(0, name.length() - oldExt.length()) + newExt;
}
/* Return the numbers in the given string matching %..d style patterns */
private static int[] scanFormat(String pattern, String string)
throws FormatException
{
final List<Integer> percentOffsets = new ArrayList<Integer>();
int offset = -1;
for (;;) {
offset = pattern.indexOf('%', offset + 1);
if (offset < 0 || offset + 1 >= pattern.length()) {
break;
}
if (pattern.charAt(offset + 1) != '0') {
continue;
}
percentOffsets.add(offset);
}
int[] result = new int[percentOffsets.size()];
int patternOffset = 0;
offset = 0;
for (int i=0; i<result.length; i++) {
int percent = percentOffsets.get(i).intValue();
if (!string.regionMatches(offset, pattern, patternOffset,
percent - patternOffset))
{
throw new FormatException("String '" + string +
"' does not match format '" + pattern + "'");
}
offset += percent - patternOffset;
patternOffset = percent;
int endOffset = offset;
while (endOffset < string.length() &&
Character.isDigit(string.charAt(endOffset)))
{
endOffset++;
}
result[i] = Integer.parseInt(string.substring(offset, endOffset));
offset = endOffset;
while (++patternOffset < pattern.length() &&
pattern.charAt(patternOffset - 1) != 'd')
{
; /* do nothing */
}
}
int remaining = pattern.length() - patternOffset;
if (string.length() - offset != remaining ||
!string.regionMatches(offset, pattern, patternOffset, remaining))
{
throw new FormatException("String '" + string +
"' does not match format '" + pattern + "'");
}
return result;
}
private IniList getIniFile(String filename)
throws FormatException, IOException
{
LOGGER.debug("getIniFile procession: {}", filename);
RandomAccessInputStream stream = getFile(filename);
String data = stream.readString((int) stream.length());
if (!data.startsWith("[")) {
data = data.substring(data.indexOf("["), data.length());
}
data = DataTools.stripString(data);
BufferedReader reader = new BufferedReader(new StringReader(data));
stream.close();
IniList list = parser.parseINI(reader);
// most of the values will be wrapped in double quotes
for (IniTable table : list) {
LOGGER.debug("");
LOGGER.debug("[" + table.get(IniTable.HEADER_KEY) + "]");
String[] keys = table.keySet().toArray(new String[table.size()]);
for (String key : keys) {
String value = sanitizeValue(table.get(key));
LOGGER.debug(key + " = " + value);
table.put(key, value);
}
}
reader.close();
return list;
}
// -- Helper classes --
class ChannelData {
public boolean active;
public Double gain;
public Double voltage;
public String name;
public String emissionFilter;
public String excitationFilter;
public Double emWave;
public Double exWave;
public String dyeName;
public String barrierFilter;
}
class PlaneData {
public Double deltaT;
public Length positionX;
public Length positionY;
public Length positionZ;
}
}