gt:             long plane = getTimestamp(file);
context: /*
   * #%L
   * OME Bio-Formats package for reading and converting biological file formats.
   * %%
   * Copyright (C) 2005 - 2012 Vanderbilt Integrative Cancer Center, and
   * 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.FileInputStream;
  import java.io.InputStreamReader;
  import java.io.IOException;
  import java.io.StringReader;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Vector;
  
  import loci.common.Constants;
  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.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.tiff.IFD;
  import loci.formats.tiff.TiffIFDEntry;
  import loci.formats.tiff.TiffParser;
  
  import ome.xml.model.primitives.NonNegativeInteger;
  import ome.xml.model.primitives.PositiveInteger;
  
  /**
   * BDReader is the file format reader for BD Pathway datasets.
   *
   * <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/in/BDReader.java">Trac</a>,
   * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/BDReader.java;hb=HEAD">Gitweb</a></dd></dl>
   *
   * @author Shawn Garbett  Shawn.Garbett a t Vanderbilt.edu
   */
  public class BDReader extends FormatReader {
  
    // -- Constants --
    private static final String EXPERIMENT_FILE = "Experiment.exp";
    private static final String[] META_EXT =
      {"drt", "dye", "exp", "plt", "txt", "geo", "ltp", "ffc", "afc", "mon",
      "xyz", "mac", "bmp", "roi", "adf"};
  
    // -- Fields --
    private Vector<String> metadataFiles = new Vector<String>();
    private Vector<String> channelNames = new Vector<String>();
    private Vector<String> wellLabels = new Vector<String>();
    private String plateName, plateDescription;
    private String[][] tiffs;
    private MinimalTiffReader reader;
  
    private String roiFile;
    private int[] emWave, exWave;
    private double[] gain, offset, exposure;
    private String binning, objective;
  
    private int wellRows, wellCols;
    private int fieldRows;
    private int fieldCols;
  
    // -- Constructor --
  
    /** Constructs a new ScanR reader. */
    public BDReader() {
      super("BD Pathway", new String[] {"exp", "tif"});
      domains = new String[] {FormatTools.HCS_DOMAIN};
      hasCompanionFiles = true;
      suffixSufficient = false;
      suffixNecessary = false;
      datasetDescription = "Multiple files (.exp, .dye, .ltp, ...) plus " +
        "one or more directories containing .tif and .bmp files";
    }
  
    // -- IFormatReader API methods --
  
    /* @see loci.formats.IFormatReader#isThisType(String, boolean) */
    public boolean isThisType(String name, boolean open) {
      if (name.endsWith(EXPERIMENT_FILE)) return true;
      if (!open) return false;
  
      String id = new Location(name).getAbsolutePath();
      try {
        id = locateExperimentFile(id);
      }
      catch (FormatException f) {
        return false;
      }
      catch (IOException f) {
        return false;
      }
      catch (NullPointerException e) {
        return false;
      }
  
      if (id.endsWith(EXPERIMENT_FILE)) { return true; }
  
      return super.isThisType(name, open);
    }
  
    /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */
    public boolean isThisType(RandomAccessInputStream stream) throws IOException {
      TiffParser p = new TiffParser(stream);
      IFD ifd = p.getFirstIFD();
      if (ifd == null) return false;
  
      String software = ifd.getIFDTextValue(IFD.SOFTWARE);
      if (software == null) return false;
  
      return software.trim().startsWith("MATROX Imaging Library");
    }
  
    /* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */
    public String[] getSeriesUsedFiles(boolean noPixels) {
      FormatTools.assertId(currentId, true, 1);
  
      Vector<String> files = new Vector<String>();
      for (String file : metadataFiles) {
        if (file != null) files.add(file);
      }
  
      if (!noPixels && tiffs != null) {
        int well = getSeries() / (fieldRows * fieldCols);
        for (int i = 0; i<tiffs[well].length; i++) {
          files.add(tiffs[well][i]);
        }
      }
  
      return files.toArray(new String[files.size()]);
    }
  
    /* @see loci.formats.IFormatReader#close(boolean) */
    public void close(boolean fileOnly) throws IOException {
      super.close(fileOnly);
      if (!fileOnly) {
        if (reader != null) reader.close();
        reader = null;
        tiffs = null;
        plateName = null;
        plateDescription = null;
        channelNames.clear();
        metadataFiles.clear();
        wellLabels.clear();
        wellRows = 0;
        wellCols = 0;
        fieldRows = 0;
        fieldCols = 0;
      }
    }
  
    /* @see loci.formats.IFormatReader#fileGroupOption(String) */
    public int fileGroupOption(String id) throws FormatException, IOException {
      return FormatTools.MUST_GROUP;
    }
  
    /* see loci.formats.IFormatReader#isSingleFile(String) */
    public boolean isSingleFile(String id) throws FormatException, IOException {
      return false;
    }
  
    /**
     * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int)
     */
    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);
  
      String file = getFilename(getSeries(), no);
      int field = getSeries() % (fieldRows * fieldCols);
      int fieldRow = field / fieldCols;
      int fieldCol = field % fieldCols;
  
      if (file != null) {
        try {
          reader.setId(file);
          if (fieldRows * fieldCols == 1) {
            reader.openBytes(0, buf, x, y, w, h);
          }
          else {
            // fields are stored together in a single image,
            // so we need to split them up
            int fx = x + (fieldCol * getSizeX());
            int fy = y + (fieldRow * getSizeY());
            reader.openBytes(0, buf, fx, fy, w, h);
          }
        }
        catch (FormatException e) {
          LOGGER.debug("Could not read file " + file, e);
          return buf;
        }
        catch (IOException e) {
          LOGGER.debug("Could not read file " + file, e);
          return buf;
        }
      }
  
      return buf;
    }
  
    /* @see loci.formats.IFormatReader#getOptimalTileWidth() */
    public int getOptimalTileWidth() {
      FormatTools.assertId(currentId, true, 1);
      return reader.getOptimalTileWidth();
    }
  
    /* @see loci.formats.IFormatReader#getOptimalTileHeight() */
    public int getOptimalTileHeight() {
      FormatTools.assertId(currentId, true, 1);
      return reader.getOptimalTileHeight();
    }
  
    // -- Internal FormatReader API methods --
  
    /* @see loci.formats.FormatReader#initFile(String) */
    protected void initFile(String id) throws FormatException, IOException {
      // make sure we have the experiment file
      id = locateExperimentFile(id);
      super.initFile(id);
      Location dir = new Location(id).getAbsoluteFile().getParentFile();
  
      for (String file : dir.list(true)) {
        Location f = new Location(dir, file);
        if (!f.isDirectory()) {
          if (checkSuffix(file, META_EXT)) {
            metadataFiles.add(f.getAbsolutePath());
          }
        }
        else {
          for (String well : f.list(true)) {
            Location wellFile = new Location(f, well);
            if (!wellFile.isDirectory()) {
              if (checkSuffix(well, META_EXT)) {
                metadataFiles.add(wellFile.getAbsolutePath());
              }
            }
          }
        }
      }
  
      // parse Experiment metadata
      IniList experiment = readMetaData(id);
  
      if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
        objective = experiment.getTable("Geometry").get("Name");
        IniTable camera = experiment.getTable("Camera");
        binning = camera.get("BinX") + "x" + camera.get("BinY");
  
        parseChannelData(dir);
  
        addGlobalMeta("Objective", objective);
        addGlobalMeta("Camera binning", binning);
      }
  
      Vector<String> uniqueRows = new Vector<String>();
      Vector<String> uniqueColumns = new Vector<String>();
  
      for (String well : wellLabels) {
        String row = well.substring(0, 1).trim();
        String column = well.substring(1).trim();
  
        if (!uniqueRows.contains(row) && row.length() > 0) uniqueRows.add(row);
        if (!uniqueColumns.contains(column) && column.length() > 0) {
          uniqueColumns.add(column);
        }
      }
  
      int nSlices = getSizeZ() == 0 ? 1 : getSizeZ();
      int nTimepoints = getSizeT();
      int nWells = wellLabels.size();
      int nChannels = getSizeC() == 0 ? channelNames.size() : getSizeC();
      if (nChannels == 0) nChannels = 1;
  
      tiffs = getTiffs(dir.getAbsolutePath());
  
      reader = new MinimalTiffReader();
      reader.setId(tiffs[0][0]);
  
      int sizeX = reader.getSizeX();
      int sizeY = reader.getSizeY();
      int pixelType = reader.getPixelType();
      boolean rgb = reader.isRGB();
      boolean interleaved = reader.isInterleaved();
      boolean indexed = reader.isIndexed();
      boolean littleEndian = reader.isLittleEndian();
  
      if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
        IniParser parser = new IniParser();
        for (String metadataFile : metadataFiles) {
          String filename = new Location(metadataFile).getName();
          if (!checkSuffix(metadataFile,
            new String[] {"txt", "bmp", "adf", "roi"}))
          {
            String data = DataTools.readFile(metadataFile);
            IniList ini =
              parser.parseINI(new BufferedReader(new StringReader(data)));
            HashMap<String, String> h = ini.flattenIntoHashMap();
            for (String key : h.keySet()) {
              addGlobalMeta(filename + " " + key, h.get(key));
            }
          }
        }
      }
  
      for (int i=0; i<core.length; i++) {
        core[i] = new CoreMetadata();
        core[i].sizeC = nChannels;
        core[i].sizeZ = nSlices;
        core[i].sizeT = nTimepoints;
        core[i].sizeX = sizeX / fieldCols;
        core[i].sizeY = sizeY / fieldRows;
        core[i].pixelType = pixelType;
        core[i].rgb = rgb;
        core[i].interleaved = interleaved;
        core[i].indexed = indexed;
        core[i].littleEndian = littleEndian;
        core[i].dimensionOrder = "XYZTC";
        core[i].imageCount = nSlices * nTimepoints * nChannels;
      }
  
      MetadataStore store = makeFilterMetadata();
      boolean populatePlanes =
        getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM;
      MetadataTools.populatePixels(store, this, populatePlanes);
  
      String plateAcqID = MetadataTools.createLSID("PlateAcquisition", 0, 0);
      store.setPlateAcquisitionID(plateAcqID, 0, 0);
      if (fieldRows * fieldCols > 0) {
        store.setPlateAcquisitionMaximumFieldCount(
          new PositiveInteger(fieldRows * fieldCols), 0, 0);
      }
      else {
        LOGGER.warn("Expected positive value for MaximumFieldCount; got {}",
          fieldRows * fieldCols);
      }
  
      for (int row=0; row<wellRows; row++) {
        for (int col=0; col<wellCols; col++) {
          int index = row * wellCols + col;
  
          store.setWellID(MetadataTools.createLSID("Well", 0, index), 0, index);
          store.setWellRow(new NonNegativeInteger(row), 0, index);
          store.setWellColumn(new NonNegativeInteger(col), 0, index);
        }
      }
  
      for (int i=0; i<getSeriesCount(); i++) {
        int well = i / (fieldRows * fieldCols);
        int field = i % (fieldRows * fieldCols);
  
        MetadataTools.setDefaultCreationDate(store, tiffs[well][0], i);
  
        String name = wellLabels.get(well);
        String row = name.substring(0, 1);
        Integer col = Integer.parseInt(name.substring(1));
  
        int index = (row.charAt(0) - 'A') * wellCols + col - 1;
  
        String wellSampleID =
          MetadataTools.createLSID("WellSample", 0, index, field);
        store.setWellSampleID(wellSampleID, 0, index, field);
        store.setWellSampleIndex(new NonNegativeInteger(i), 0, index, field);
  
        String imageID = MetadataTools.createLSID("Image", i);
        store.setWellSampleImageRef(imageID, 0, index, field);
        store.setImageID(imageID, i);
        store.setImageName(name + " Field #" + (field + 1), i);
  
        store.setPlateAcquisitionWellSampleRef(wellSampleID, 0, 0, i);
      }
  
      MetadataLevel level = getMetadataOptions().getMetadataLevel();
      if (level != MetadataLevel.MINIMUM) {
        String instrumentID = MetadataTools.createLSID("Instrument", 0);
        store.setInstrumentID(instrumentID, 0);
  
        String objectiveID = MetadataTools.createLSID("Objective", 0, 0);
        store.setObjectiveID(objectiveID, 0, 0);
        if (objective != null) {
          String[] tokens = objective.split(" ");
          String mag = tokens[0].replaceAll("[xX]", "");
          String na = null;
          int naIndex = 0;
          for (int i=0; i<tokens.length; i++) {
            if (tokens[i].equals("NA")) {
              naIndex = i + 1;
              na = tokens[naIndex];
              break;
            }
          }
  
          Integer magnification = new Integer(mag);
          if (magnification > 0) {
            store.setObjectiveNominalMagnification(
              new PositiveInteger(magnification), 0, 0);
          }
          else {
            LOGGER.warn(
              "Expected positive value for NominalMagnification; got {}",
              magnification);
          }
          if (na != null) {
            na = na.substring(0, 1) + "." + na.substring(1);
            store.setObjectiveLensNA(new Double(na), 0, 0);
          }
          if (naIndex + 1 < tokens.length) {
            store.setObjectiveManufacturer(tokens[naIndex + 1], 0, 0);
          }
        }
  
        // populate LogicalChannel data
        for (int i=0; i<getSeriesCount(); i++) {
          store.setImageInstrumentRef(instrumentID, i);
          store.setObjectiveSettingsID(objectiveID, i);
  
          for (int c=0; c<getSizeC(); c++) {
            store.setChannelName(channelNames.get(c), i, c);
            if (emWave[c] > 0) {
              store.setChannelEmissionWavelength(
                new PositiveInteger(emWave[c]), i, c);
            }
            else {
              LOGGER.warn(
                "Expected positive value for EmissionWavelength; got {}",
                emWave[c]);
            }
            if (exWave[c] > 0) {
              store.setChannelExcitationWavelength(
                new PositiveInteger(exWave[c]), i, c);
            }
            else {
              LOGGER.warn(
                "Expected positive value for ExcitationWavelength; got {}",
                exWave[c]);
            }
  
            String detectorID = MetadataTools.createLSID("Detector", 0, c);
            store.setDetectorID(detectorID, 0, c);
            store.setDetectorSettingsID(detectorID, i, c);
            store.setDetectorSettingsGain(gain[c], i, c);
            store.setDetectorSettingsOffset(offset[c], i, c);
            store.setDetectorSettingsBinning(getBinning(binning), i, c);
          }
  
          long firstPlane = 0;
          for (int p=0; p<getImageCount(); p++) {
            int[] zct = getZCTCoords(p);
            store.setPlaneExposureTime(exposure[zct[1]], i, p);
            String file = getFilename(i, p);
            if (file != null) {
  