src/core/net/sf/basedb/util/export/spotdata/BfsExporter.java

Code
Comments
Other
Rev Date Author Line
5199 15 Dec 09 nicklas 1 /**
5199 15 Dec 09 nicklas 2   $Id$
5199 15 Dec 09 nicklas 3
5199 15 Dec 09 nicklas 4   Copyright (C) 2009 Nicklas Nordborg
5199 15 Dec 09 nicklas 5
5199 15 Dec 09 nicklas 6   This file is part of BASE - BioArray Software Environment.
5199 15 Dec 09 nicklas 7   Available at http://base.thep.lu.se/
5199 15 Dec 09 nicklas 8
5199 15 Dec 09 nicklas 9   BASE is free software; you can redistribute it and/or
5199 15 Dec 09 nicklas 10   modify it under the terms of the GNU General Public License
5199 15 Dec 09 nicklas 11   as published by the Free Software Foundation; either version 3
5199 15 Dec 09 nicklas 12   of the License, or (at your option) any later version.
5199 15 Dec 09 nicklas 13
5199 15 Dec 09 nicklas 14   BASE is distributed in the hope that it will be useful,
5199 15 Dec 09 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
5199 15 Dec 09 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
5199 15 Dec 09 nicklas 17   GNU General Public License for more details.
5199 15 Dec 09 nicklas 18
5199 15 Dec 09 nicklas 19   You should have received a copy of the GNU General Public License
5199 15 Dec 09 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
5199 15 Dec 09 nicklas 21 */
5199 15 Dec 09 nicklas 22 package net.sf.basedb.util.export.spotdata;
5199 15 Dec 09 nicklas 23
5199 15 Dec 09 nicklas 24 import java.io.IOException;
5199 15 Dec 09 nicklas 25 import java.sql.SQLException;
5199 15 Dec 09 nicklas 26 import java.util.ArrayList;
5199 15 Dec 09 nicklas 27 import java.util.Arrays;
5199 15 Dec 09 nicklas 28 import java.util.Collection;
5199 15 Dec 09 nicklas 29 import java.util.Collections;
5199 15 Dec 09 nicklas 30 import java.util.HashSet;
5199 15 Dec 09 nicklas 31 import java.util.LinkedHashMap;
5199 15 Dec 09 nicklas 32 import java.util.List;
5199 15 Dec 09 nicklas 33 import java.util.Map;
5199 15 Dec 09 nicklas 34 import java.util.Set;
5199 15 Dec 09 nicklas 35
5199 15 Dec 09 nicklas 36 import net.sf.basedb.core.BioAssay;
5199 15 Dec 09 nicklas 37 import net.sf.basedb.core.DatabaseException;
5199 15 Dec 09 nicklas 38 import net.sf.basedb.core.DbControl;
5199 15 Dec 09 nicklas 39 import net.sf.basedb.core.DynamicPositionQuery;
5199 15 Dec 09 nicklas 40 import net.sf.basedb.core.DynamicResultIterator;
5199 15 Dec 09 nicklas 41 import net.sf.basedb.core.DynamicSpotQuery;
5199 15 Dec 09 nicklas 42 import net.sf.basedb.core.ProgressReporter;
5199 15 Dec 09 nicklas 43 import net.sf.basedb.core.Type;
5199 15 Dec 09 nicklas 44 import net.sf.basedb.core.query.SqlResult;
5405 10 Sep 10 nicklas 45 import net.sf.basedb.core.signal.ThreadSignalHandler;
5199 15 Dec 09 nicklas 46 import net.sf.basedb.util.ChainedProgressReporter;
5199 15 Dec 09 nicklas 47 import net.sf.basedb.util.Values;
5199 15 Dec 09 nicklas 48 import net.sf.basedb.util.bfs.AnnotationWriter;
5199 15 Dec 09 nicklas 49 import net.sf.basedb.util.bfs.DataWriter;
5199 15 Dec 09 nicklas 50 import net.sf.basedb.util.bfs.DataWriterFactory;
5199 15 Dec 09 nicklas 51 import net.sf.basedb.util.bfs.MetadataWriter;
5199 15 Dec 09 nicklas 52 import net.sf.basedb.util.bfs.MetadataModel.SectionEntry;
5199 15 Dec 09 nicklas 53 import net.sf.basedb.util.formatter.Formatter;
5199 15 Dec 09 nicklas 54 import net.sf.basedb.util.formatter.ToStringFormatter;
5199 15 Dec 09 nicklas 55
5199 15 Dec 09 nicklas 56 /**
5199 15 Dec 09 nicklas 57   Base class for exporting spotdata to BFS format. This class will
5199 15 Dec 09 nicklas 58   do most of the hard work. A subclass is required to:
5199 15 Dec 09 nicklas 59   <ul>
5199 15 Dec 09 nicklas 60   <li>
5199 15 Dec 09 nicklas 61   <li>create a list of data writers that will output the actual 
5199 15 Dec 09 nicklas 62     spot data (see {@link #createDataWriters()}).  
5199 15 Dec 09 nicklas 63   <li>rearrange the data that is loaded by the query to the 
5199 15 Dec 09 nicklas 64     correct data writer (see {@link #arrangeData(int, short, Object[][], Object[])}).
5199 15 Dec 09 nicklas 65   </ul>
5199 15 Dec 09 nicklas 66   
5199 15 Dec 09 nicklas 67   <p>
5199 15 Dec 09 nicklas 68   Before the exporter can be used it must be configured. The most important options are:
5199 15 Dec 09 nicklas 69   <ul>
5199 15 Dec 09 nicklas 70   <li>{@link #setDbControl(DbControl)} (required): Sets the DbControl that is used for database access.
5199 15 Dec 09 nicklas 71   <li>{@link #setSource(net.sf.basedb.core.BioAssaySet)} (required): Sets the bioassay set that data should
5199 15 Dec 09 nicklas 72     be exported from.
5199 15 Dec 09 nicklas 73   <li>{@link #setMetadataWriter(MetadataWriter)} (required): Sets the writer stream were BFS metadata is
5199 15 Dec 09 nicklas 74     written.
5199 15 Dec 09 nicklas 75   <li>{@link #setReporterAnnotationsWriter(AnnotationWriter)} and {@link #addReporterField(DynamicField)}
5199 15 Dec 09 nicklas 76     (optional): Sets the writer stream were reporter annoatations are written and selects which 
5199 15 Dec 09 nicklas 77     reporter annotations to export. If no writer is specified the reporter annotations file is not
5199 15 Dec 09 nicklas 78     created.
5199 15 Dec 09 nicklas 79   <li>{@link #setAssayAnnotationsWriter(AnnotationWriter)} and {@link #addAssayField(AssayField)}
5199 15 Dec 09 nicklas 80     (optional): Sets the writer stream were assay annotations is written and selects which
5199 15 Dec 09 nicklas 81     assay annotations to export. If no writer is specified the assay annotations file is not
5199 15 Dec 09 nicklas 82     created.
5319 20 Apr 10 nicklas 83   <li>{@link #setDataWriterFactory(DataWriterFactory)} and {@link #addSpotField(DynamicField)} 
5199 15 Dec 09 nicklas 84     (optional): Sets the factory that should be used to create output files and selects which
5199 15 Dec 09 nicklas 85     spot data to export. The number of files and the data arrangement depends on the subtype.
5199 15 Dec 09 nicklas 86   <li>{@link #setParameter(String, String...)} (optional): Additional parameters that are needed
5199 15 Dec 09 nicklas 87     by, for example, a plug-in that is about to process the exported data files.
5199 15 Dec 09 nicklas 88   </ul>
5199 15 Dec 09 nicklas 89   
5199 15 Dec 09 nicklas 90   
5199 15 Dec 09 nicklas 91   @author Nicklas
5199 15 Dec 09 nicklas 92   @version 2.15
5199 15 Dec 09 nicklas 93   @base.modified $Date$
5199 15 Dec 09 nicklas 94 */
5199 15 Dec 09 nicklas 95 public abstract class BfsExporter
5199 15 Dec 09 nicklas 96   extends AbstractBioAssaySetExporter
5199 15 Dec 09 nicklas 97 {
5199 15 Dec 09 nicklas 98
5319 20 Apr 10 nicklas 99   private boolean autoCloseWriters;
6875 20 Apr 15 nicklas 100   @SuppressWarnings("rawtypes")
5199 15 Dec 09 nicklas 101   private DataWriterFactory dataWriterFactory;
5199 15 Dec 09 nicklas 102   private Map<String, String[]> parameters;
5199 15 Dec 09 nicklas 103   private MetadataWriter metadataWriter;
5199 15 Dec 09 nicklas 104   private AnnotationWriter reporterWriter;
5199 15 Dec 09 nicklas 105   private AnnotationWriter assayWriter;
5199 15 Dec 09 nicklas 106   private List<DataWriter> dataWriters;
5199 15 Dec 09 nicklas 107   private List<SectionEntry> extraFiles;
5199 15 Dec 09 nicklas 108   private ChainedProgressReporter chainedProgress;
5199 15 Dec 09 nicklas 109   private long spotCount;
5199 15 Dec 09 nicklas 110   private Set<Integer> exportedPositions;
5199 15 Dec 09 nicklas 111   
5199 15 Dec 09 nicklas 112   protected BfsExporter()
5199 15 Dec 09 nicklas 113   {
5199 15 Dec 09 nicklas 114     this.parameters = new LinkedHashMap<String, String[]>();
5199 15 Dec 09 nicklas 115     this.extraFiles = new ArrayList<SectionEntry>();
5319 20 Apr 10 nicklas 116     this.autoCloseWriters = true;
5199 15 Dec 09 nicklas 117   }
5199 15 Dec 09 nicklas 118   
5199 15 Dec 09 nicklas 119   /*
5199 15 Dec 09 nicklas 120     Configuration properties
5199 15 Dec 09 nicklas 121     ------------------------
5199 15 Dec 09 nicklas 122   */
5199 15 Dec 09 nicklas 123   /**
5319 20 Apr 10 nicklas 124     If this option is set then all writers are automatically closed
5319 20 Apr 10 nicklas 125     when all data has been writted to them. This setting is enabled by default.
5319 20 Apr 10 nicklas 126   */
5319 20 Apr 10 nicklas 127   public void setAutoCloseWriters(boolean autoClose)
5319 20 Apr 10 nicklas 128   {
5319 20 Apr 10 nicklas 129     this.autoCloseWriters = autoClose;
5319 20 Apr 10 nicklas 130   }
5319 20 Apr 10 nicklas 131   
5319 20 Apr 10 nicklas 132   /**
5199 15 Dec 09 nicklas 133     Set the stream were the metadata should be written. This 
5199 15 Dec 09 nicklas 134     stream is required before it is possible to start the 
5199 15 Dec 09 nicklas 135     export. It is expected that the given writer is a fresh writer 
5199 15 Dec 09 nicklas 136     and that no data has been written to it yet.
5199 15 Dec 09 nicklas 137   */
5199 15 Dec 09 nicklas 138   public void setMetadataWriter(MetadataWriter metadataWriter)
5199 15 Dec 09 nicklas 139   {
5199 15 Dec 09 nicklas 140     this.metadataWriter = metadataWriter;
5199 15 Dec 09 nicklas 141   }
5199 15 Dec 09 nicklas 142   
5199 15 Dec 09 nicklas 143   /**
5199 15 Dec 09 nicklas 144     Set the stream were reporter annotations should be written. This is optional
5199 15 Dec 09 nicklas 145     and if not given, no reporter annotations file is generated. The
5199 15 Dec 09 nicklas 146     writer must return a filename from {@link AnnotationWriter#getFilename()}
5199 15 Dec 09 nicklas 147     since this is required in the metadata file. 
5199 15 Dec 09 nicklas 148     <p>
5199 15 Dec 09 nicklas 149     Use {@link #addReporterField(DynamicField)} to register reporter
5199 15 Dec 09 nicklas 150     fields to export. The position number is always exported.
5199 15 Dec 09 nicklas 151     
5199 15 Dec 09 nicklas 152     @param reporterWriter An annotation writer, or null if no reporter
5199 15 Dec 09 nicklas 153       annotation file should be created
5199 15 Dec 09 nicklas 154   */
5199 15 Dec 09 nicklas 155   public void setReporterAnnotationsWriter(AnnotationWriter reporterWriter)
5199 15 Dec 09 nicklas 156   {
5199 15 Dec 09 nicklas 157     this.reporterWriter = reporterWriter;
5199 15 Dec 09 nicklas 158   }
5199 15 Dec 09 nicklas 159   
5199 15 Dec 09 nicklas 160   /**
5199 15 Dec 09 nicklas 161     Set the stream were assay annotations should be written. This is optional
5199 15 Dec 09 nicklas 162     and if not given, no assay annotations file is generated. The
5199 15 Dec 09 nicklas 163     writer must return a filename from {@link AnnotationWriter#getFilename()}
5199 15 Dec 09 nicklas 164     since this is required in the metadata file. 
5199 15 Dec 09 nicklas 165     <p>
5199 15 Dec 09 nicklas 166     Use {@link #addAssayField(AssayField)} to register fields to export. 
5199 15 Dec 09 nicklas 167     The assay id is always exported.
5199 15 Dec 09 nicklas 168     
5199 15 Dec 09 nicklas 169     @param assayWriter An annotation writer, or null if no assay
5199 15 Dec 09 nicklas 170       annotations file should be created
5199 15 Dec 09 nicklas 171   */
5199 15 Dec 09 nicklas 172   public void setAssayAnnotationsWriter(AnnotationWriter assayWriter)
5199 15 Dec 09 nicklas 173   {
5199 15 Dec 09 nicklas 174     this.assayWriter = assayWriter;
5199 15 Dec 09 nicklas 175   }
5199 15 Dec 09 nicklas 176   
5199 15 Dec 09 nicklas 177   /**
5199 15 Dec 09 nicklas 178     Set the data writer factory to use for generating spot data files.
5199 15 Dec 09 nicklas 179     What files that are required depends on the BFS subtype. Eg. 
5199 15 Dec 09 nicklas 180     the serial format uses a file per assay and the matrix format
5199 15 Dec 09 nicklas 181     uses a file per spot field. A data writer factory is optional
5199 15 Dec 09 nicklas 182     and if not given, no spot data is exported.
5199 15 Dec 09 nicklas 183   
5199 15 Dec 09 nicklas 184     @param dataWriterFactory A data writer factory, or null if no spot
5199 15 Dec 09 nicklas 185       data files should be created
5199 15 Dec 09 nicklas 186   */
5199 15 Dec 09 nicklas 187   public void setDataWriterFactory(DataWriterFactory<?> dataWriterFactory)
5199 15 Dec 09 nicklas 188   {
5199 15 Dec 09 nicklas 189     this.dataWriterFactory = dataWriterFactory;
5199 15 Dec 09 nicklas 190   }
5199 15 Dec 09 nicklas 191   
5199 15 Dec 09 nicklas 192   /**
5199 15 Dec 09 nicklas 193     Add a parameter that is exported in the [parameters] section. 
5199 15 Dec 09 nicklas 194     There can be one or more values for a given key, which are then
5199 15 Dec 09 nicklas 195     separated by tab characters. The parameters are written to the file 
5199 15 Dec 09 nicklas 196     in the order they are registered by this method.
5199 15 Dec 09 nicklas 197     <p>
5199 15 Dec 09 nicklas 198     NOTE! Calling this method twice with the same key, overwrites the
5199 15 Dec 09 nicklas 199     existing parameters, but doesn't change the order they are written
5199 15 Dec 09 nicklas 200     to the file.
5199 15 Dec 09 nicklas 201     
5199 15 Dec 09 nicklas 202     @param key The parameter key (if null this method call is ignored)
5199 15 Dec 09 nicklas 203     @param values The parameter value (may be null)
5199 15 Dec 09 nicklas 204   */
5199 15 Dec 09 nicklas 205   public void setParameter(String key, String... values)
5199 15 Dec 09 nicklas 206   {
5199 15 Dec 09 nicklas 207     if (key == null) return;
5199 15 Dec 09 nicklas 208     parameters.put(key, values);
5199 15 Dec 09 nicklas 209   }
5199 15 Dec 09 nicklas 210   
5199 15 Dec 09 nicklas 211   /**
5199 15 Dec 09 nicklas 212     Get the current value of a parameter.
5199 15 Dec 09 nicklas 213     @param key The parameter key
5199 15 Dec 09 nicklas 214     @return The value, or null if no values has been registered for
5199 15 Dec 09 nicklas 215       the key (or if the registered value is null)
5199 15 Dec 09 nicklas 216   */
5199 15 Dec 09 nicklas 217   public String[] getParameter(String key)
5199 15 Dec 09 nicklas 218   {
5199 15 Dec 09 nicklas 219     return parameters.get(key);
5199 15 Dec 09 nicklas 220   }
5199 15 Dec 09 nicklas 221
5199 15 Dec 09 nicklas 222
5199 15 Dec 09 nicklas 223   @Override
5199 15 Dec 09 nicklas 224   public void addReporterField(DynamicField field)
5199 15 Dec 09 nicklas 225   {
5199 15 Dec 09 nicklas 226     super.addReporterField(field);
5199 15 Dec 09 nicklas 227   }
5199 15 Dec 09 nicklas 228
5199 15 Dec 09 nicklas 229   /**
5199 15 Dec 09 nicklas 230     Add multiple reporter fields in one go. A null collection is ignored and
5199 15 Dec 09 nicklas 231     so are null values in the collection.
5199 15 Dec 09 nicklas 232     @param fields A collection with the fields that should be added
5199 15 Dec 09 nicklas 233   */
5199 15 Dec 09 nicklas 234   public void addReporterFields(Collection<? extends DynamicField> fields)
5199 15 Dec 09 nicklas 235   {
5199 15 Dec 09 nicklas 236     if (fields == null) return;
5199 15 Dec 09 nicklas 237     for (DynamicField f : fields)
5199 15 Dec 09 nicklas 238     {
5199 15 Dec 09 nicklas 239        addReporterField(f);
5199 15 Dec 09 nicklas 240     }
5199 15 Dec 09 nicklas 241   }
5199 15 Dec 09 nicklas 242   
5199 15 Dec 09 nicklas 243   @Override
5199 15 Dec 09 nicklas 244   public List<DynamicField> getReporterFields()
5199 15 Dec 09 nicklas 245   {
5199 15 Dec 09 nicklas 246     return super.getReporterFields();
5199 15 Dec 09 nicklas 247   }
5199 15 Dec 09 nicklas 248   
5199 15 Dec 09 nicklas 249   @Override
5199 15 Dec 09 nicklas 250   public void addAssayField(AssayField field)
5199 15 Dec 09 nicklas 251   {
5199 15 Dec 09 nicklas 252     super.addAssayField(field);
5199 15 Dec 09 nicklas 253   }
5199 15 Dec 09 nicklas 254   
5199 15 Dec 09 nicklas 255   /**
5199 15 Dec 09 nicklas 256     Add multiple assay fields in one go. A null collection is ignored and
5199 15 Dec 09 nicklas 257     so are null values in the collection.
5199 15 Dec 09 nicklas 258     @param fields A collection with the fields that should be added
5199 15 Dec 09 nicklas 259   */
5199 15 Dec 09 nicklas 260   public void addAssayFields(Collection<? extends AssayField> fields)
5199 15 Dec 09 nicklas 261   {
5199 15 Dec 09 nicklas 262     if (fields == null) return;
5199 15 Dec 09 nicklas 263     for (AssayField f : fields)
5199 15 Dec 09 nicklas 264     {
5199 15 Dec 09 nicklas 265        addAssayField(f);
5199 15 Dec 09 nicklas 266     }
5199 15 Dec 09 nicklas 267   }
5199 15 Dec 09 nicklas 268   
5199 15 Dec 09 nicklas 269   @Override
5199 15 Dec 09 nicklas 270   public List<AssayField> getAssayFields()
5199 15 Dec 09 nicklas 271   {
5199 15 Dec 09 nicklas 272     return super.getAssayFields();
5199 15 Dec 09 nicklas 273   }
5199 15 Dec 09 nicklas 274   
5199 15 Dec 09 nicklas 275   /**
5199 15 Dec 09 nicklas 276     Add information about a spot field that should be exported.
5199 15 Dec 09 nicklas 277     @param spotField The spot field information
5199 15 Dec 09 nicklas 278   */
5319 20 Apr 10 nicklas 279   @Override
5319 20 Apr 10 nicklas 280   public void addSpotField(DynamicField spotField)
5199 15 Dec 09 nicklas 281   {
5199 15 Dec 09 nicklas 282     super.addSpotField(spotField);
5199 15 Dec 09 nicklas 283   }
5319 20 Apr 10 nicklas 284
5199 15 Dec 09 nicklas 285   /**
5319 20 Apr 10 nicklas 286     Add multiple spot fields in one go. A null collection is ignored and
5319 20 Apr 10 nicklas 287     so are null values in the collection.
5319 20 Apr 10 nicklas 288     @param fields A collection with the fields that should be added
5319 20 Apr 10 nicklas 289   */
5319 20 Apr 10 nicklas 290   public void addSpotFields(Collection<? extends DynamicField> fields)
5319 20 Apr 10 nicklas 291   {
5319 20 Apr 10 nicklas 292     if (fields == null) return;
5319 20 Apr 10 nicklas 293     for (DynamicField f : fields)
5319 20 Apr 10 nicklas 294     {
5319 20 Apr 10 nicklas 295        addSpotField(f);
5319 20 Apr 10 nicklas 296     }
5319 20 Apr 10 nicklas 297   }
5319 20 Apr 10 nicklas 298
5319 20 Apr 10 nicklas 299   @Override
5319 20 Apr 10 nicklas 300   public List<DynamicField> getSpotFields()
5319 20 Apr 10 nicklas 301   {
5319 20 Apr 10 nicklas 302     return super.getSpotFields();
5319 20 Apr 10 nicklas 303   }
5319 20 Apr 10 nicklas 304
5319 20 Apr 10 nicklas 305   /**
5199 15 Dec 09 nicklas 306     Add information about an extra data file that is included
5199 15 Dec 09 nicklas 307     as part of the BFS. This exporter will register this information
5199 15 Dec 09 nicklas 308     as an entry in the [files] section in the metadata file.
5199 15 Dec 09 nicklas 309     It will not check that the file actually exists or do any
5199 15 Dec 09 nicklas 310     other action with the file. 
5199 15 Dec 09 nicklas 311     <p>
5199 15 Dec 09 nicklas 312     File entries are written in the order they are registered by
5199 15 Dec 09 nicklas 313     this method. Multiple entries with the same key are allowed
5199 15 Dec 09 nicklas 314     (eg. the previous entry is not replaced).
5199 15 Dec 09 nicklas 315     <p>
5199 15 Dec 09 nicklas 316
5199 15 Dec 09 nicklas 317     @param key The key for the file
5199 15 Dec 09 nicklas 318     @param filename The name of the file
5199 15 Dec 09 nicklas 319   */
5199 15 Dec 09 nicklas 320   public void addExtraFile(String key, String filename)
5199 15 Dec 09 nicklas 321   {
5199 15 Dec 09 nicklas 322     extraFiles.add(new SectionEntry(key, filename));
5199 15 Dec 09 nicklas 323   }
5199 15 Dec 09 nicklas 324   
5199 15 Dec 09 nicklas 325   // --------------------------------------
5199 15 Dec 09 nicklas 326
5199 15 Dec 09 nicklas 327   /*
5199 15 Dec 09 nicklas 328     From the AbstractBioAssaySetExporter class
5199 15 Dec 09 nicklas 329     ------------------------------------------
5199 15 Dec 09 nicklas 330   */
5199 15 Dec 09 nicklas 331   @Override
5199 15 Dec 09 nicklas 332   protected void validate()
5199 15 Dec 09 nicklas 333   {
5199 15 Dec 09 nicklas 334     super.validate();
5199 15 Dec 09 nicklas 335     if (getMetadataWriter() == null) throw new NullPointerException("getMetadataWriter()");
5199 15 Dec 09 nicklas 336     AnnotationWriter aw = getReporterAnnotationsWriter();
5199 15 Dec 09 nicklas 337     if (aw != null && aw.getFilename() == null)
5199 15 Dec 09 nicklas 338     {
5199 15 Dec 09 nicklas 339       throw new NullPointerException("getReporterAnnotationsWriter().getFilename()");
5199 15 Dec 09 nicklas 340     }
5199 15 Dec 09 nicklas 341     aw = getAssayAnnotationsWriter();
5199 15 Dec 09 nicklas 342     if (aw != null && aw.getFilename() == null)
5199 15 Dec 09 nicklas 343     {
5199 15 Dec 09 nicklas 344       throw new NullPointerException("getAssayAnnotationsWriter().getFilename()");
5199 15 Dec 09 nicklas 345     }
5199 15 Dec 09 nicklas 346   }
5199 15 Dec 09 nicklas 347   
5199 15 Dec 09 nicklas 348   /**
5199 15 Dec 09 nicklas 349     Validates that all required properties has been set and
5199 15 Dec 09 nicklas 350     initialises other things that are needed.
5199 15 Dec 09 nicklas 351   */
5199 15 Dec 09 nicklas 352   @Override
5199 15 Dec 09 nicklas 353   protected void beginExport()
5199 15 Dec 09 nicklas 354   {
5199 15 Dec 09 nicklas 355     super.beginExport();
5199 15 Dec 09 nicklas 356     setProgress(0, "Initializing...");
5199 15 Dec 09 nicklas 357     ProgressReporter p = getProgressReporter();
5199 15 Dec 09 nicklas 358     if (p != null) chainedProgress = new ChainedProgressReporter(p);
5199 15 Dec 09 nicklas 359     try
5199 15 Dec 09 nicklas 360     {
5199 15 Dec 09 nicklas 361       if (getDataWriterFactory() != null) dataWriters = createDataWriters();
5213 12 Jan 10 nicklas 362       if (dataWriters == null) dataWriters = Collections.emptyList();
5199 15 Dec 09 nicklas 363     }
5199 15 Dec 09 nicklas 364     catch (IOException ex)
5199 15 Dec 09 nicklas 365     {
5199 15 Dec 09 nicklas 366       throw new RuntimeException(ex);
5199 15 Dec 09 nicklas 367     }
5199 15 Dec 09 nicklas 368   }
5199 15 Dec 09 nicklas 369   
5199 15 Dec 09 nicklas 370   /**
5199 15 Dec 09 nicklas 371     Export the metadata and assay annotations.
5199 15 Dec 09 nicklas 372   */
5199 15 Dec 09 nicklas 373   @Override
5199 15 Dec 09 nicklas 374   protected boolean exportGlobalHeader()
5199 15 Dec 09 nicklas 375   {
5199 15 Dec 09 nicklas 376     exportMetadata();
5199 15 Dec 09 nicklas 377     exportAssayAnnotations();
5199 15 Dec 09 nicklas 378     return true;
5199 15 Dec 09 nicklas 379   }
5199 15 Dec 09 nicklas 380   
5199 15 Dec 09 nicklas 381   /**
5199 15 Dec 09 nicklas 382     Export all spot data.
5199 15 Dec 09 nicklas 383   */
5199 15 Dec 09 nicklas 384   @Override
5199 15 Dec 09 nicklas 385   protected void exportSectionData()
5199 15 Dec 09 nicklas 386   {
5199 15 Dec 09 nicklas 387     if (chainedProgress != null) chainedProgress.setRange(15, 80);
5199 15 Dec 09 nicklas 388     spotCount = exportSpotData(chainedProgress, getSource().getNumSpots());
5199 15 Dec 09 nicklas 389   }
5199 15 Dec 09 nicklas 390
5199 15 Dec 09 nicklas 391   /**
5199 15 Dec 09 nicklas 392     Exports reporter annotations.
5199 15 Dec 09 nicklas 393   */
5199 15 Dec 09 nicklas 394   @Override
5199 15 Dec 09 nicklas 395   protected void exportGlobalFooter()
5199 15 Dec 09 nicklas 396   {
5199 15 Dec 09 nicklas 397     if (chainedProgress != null) chainedProgress.setRange(80, 95);
5199 15 Dec 09 nicklas 398     exportReporterAnnotations(chainedProgress, exportedPositions.size());
5199 15 Dec 09 nicklas 399   }
5199 15 Dec 09 nicklas 400
5199 15 Dec 09 nicklas 401   /**
5199 15 Dec 09 nicklas 402     Clean up.
5199 15 Dec 09 nicklas 403   */
5199 15 Dec 09 nicklas 404   @Override
5199 15 Dec 09 nicklas 405   protected void endExport(RuntimeException e)
5199 15 Dec 09 nicklas 406   {
5199 15 Dec 09 nicklas 407     if (e == null) setProgress(100, "Export complete. " + spotCount + " spots done");
5199 15 Dec 09 nicklas 408     if (exportedPositions != null) exportedPositions.clear();
5199 15 Dec 09 nicklas 409     exportedPositions = null;
5199 15 Dec 09 nicklas 410     super.endExport(e);
5199 15 Dec 09 nicklas 411   }
5199 15 Dec 09 nicklas 412   // -------------------------------------------  
5199 15 Dec 09 nicklas 413
5199 15 Dec 09 nicklas 414   /**
5199 15 Dec 09 nicklas 415     Get the BFS subtype. This method may be overridden by subclasses.
5199 15 Dec 09 nicklas 416     The default implementation returns null. The subtype is written to
5199 15 Dec 09 nicklas 417     the metadata file: {@link MetadataWriter#setSubtype(String)}.
5199 15 Dec 09 nicklas 418   */
5199 15 Dec 09 nicklas 419   public String getBFSSubtype()
5199 15 Dec 09 nicklas 420   {
5199 15 Dec 09 nicklas 421     return null;
5199 15 Dec 09 nicklas 422   }
5199 15 Dec 09 nicklas 423
5199 15 Dec 09 nicklas 424   /**
5199 15 Dec 09 nicklas 425     Get the writer were metadata should be printed.
5199 15 Dec 09 nicklas 426   */
5199 15 Dec 09 nicklas 427   protected MetadataWriter getMetadataWriter()
5199 15 Dec 09 nicklas 428   {
5199 15 Dec 09 nicklas 429     return metadataWriter;
5199 15 Dec 09 nicklas 430   }
5199 15 Dec 09 nicklas 431
5199 15 Dec 09 nicklas 432   /**
5199 15 Dec 09 nicklas 433     Get the writer were reporter annotations should be printed.
5199 15 Dec 09 nicklas 434     Can be null.
5199 15 Dec 09 nicklas 435   */
5199 15 Dec 09 nicklas 436   protected AnnotationWriter getReporterAnnotationsWriter()
5199 15 Dec 09 nicklas 437   {
5199 15 Dec 09 nicklas 438     return reporterWriter;
5199 15 Dec 09 nicklas 439   }
5199 15 Dec 09 nicklas 440
5199 15 Dec 09 nicklas 441   /**
5199 15 Dec 09 nicklas 442     Get the writer were assay annotations should be printed.
5199 15 Dec 09 nicklas 443     Can be null.
5199 15 Dec 09 nicklas 444   */
5199 15 Dec 09 nicklas 445   protected AnnotationWriter getAssayAnnotationsWriter()
5199 15 Dec 09 nicklas 446   {
5199 15 Dec 09 nicklas 447     return assayWriter;
5199 15 Dec 09 nicklas 448   }
5199 15 Dec 09 nicklas 449   
5199 15 Dec 09 nicklas 450   /**
5199 15 Dec 09 nicklas 451     Get the factory that creates data writers.
5199 15 Dec 09 nicklas 452     Can be null.
5199 15 Dec 09 nicklas 453   */
6875 20 Apr 15 nicklas 454   @SuppressWarnings("rawtypes")
5199 15 Dec 09 nicklas 455   protected DataWriterFactory getDataWriterFactory()
5199 15 Dec 09 nicklas 456   {
5199 15 Dec 09 nicklas 457     return dataWriterFactory;
5199 15 Dec 09 nicklas 458   }
5199 15 Dec 09 nicklas 459   
5199 15 Dec 09 nicklas 460   /**
5199 15 Dec 09 nicklas 461     Export the metadata file which contains information about the
5199 15 Dec 09 nicklas 462     other files (reporter annotations, assay annotations and spot data),
5199 15 Dec 09 nicklas 463     plug-in parameters, etc. Plug-in parameters are registered with
5199 15 Dec 09 nicklas 464     {@link #setParameter(String, String[])} and are written to
5199 15 Dec 09 nicklas 465     the [parameters] section. If no parameters has been registered this 
5199 15 Dec 09 nicklas 466     section is skipped.
5199 15 Dec 09 nicklas 467   */
5199 15 Dec 09 nicklas 468   protected void exportMetadata()
5199 15 Dec 09 nicklas 469   {
5199 15 Dec 09 nicklas 470     setProgress(5, "Exporting metadata...");
5199 15 Dec 09 nicklas 471     MetadataWriter out = getMetadataWriter();
5199 15 Dec 09 nicklas 472     out.setSubtype(getBFSSubtype());
5199 15 Dec 09 nicklas 473     
5199 15 Dec 09 nicklas 474
5199 15 Dec 09 nicklas 475     // [files]
5199 15 Dec 09 nicklas 476     out.bfsPrintSection("files", true, true);
5199 15 Dec 09 nicklas 477     // Reporter and assay annotation files
5199 15 Dec 09 nicklas 478     AnnotationWriter aw = getReporterAnnotationsWriter();
5199 15 Dec 09 nicklas 479     if (aw != null) out.bfsPrintValue("rdata", aw.getFilename());
5199 15 Dec 09 nicklas 480     aw = getAssayAnnotationsWriter();
5199 15 Dec 09 nicklas 481     if (aw != null) out.bfsPrintValue("pdata", aw.getFilename());
5199 15 Dec 09 nicklas 482     // Spot data files are labeled 'sdataN' with N = 1, 2, ...
5199 15 Dec 09 nicklas 483     int index = 1;
5199 15 Dec 09 nicklas 484     for (DataWriter dataWriter : dataWriters)
5199 15 Dec 09 nicklas 485     {
5199 15 Dec 09 nicklas 486       out.bfsPrintValue("sdata" + index, dataWriter.getFilename());
5199 15 Dec 09 nicklas 487       index++;
5199 15 Dec 09 nicklas 488     }
5199 15 Dec 09 nicklas 489     // Custom extra data files
5199 15 Dec 09 nicklas 490     for (SectionEntry extraFile : extraFiles)
5199 15 Dec 09 nicklas 491     {
5199 15 Dec 09 nicklas 492       out.bfsPrintValue(extraFile.getKey(), extraFile.getValues());
5199 15 Dec 09 nicklas 493     }
5199 15 Dec 09 nicklas 494   
5199 15 Dec 09 nicklas 495     // [parameters]
5199 15 Dec 09 nicklas 496     if (parameters.size() > 0)
5199 15 Dec 09 nicklas 497     {
5199 15 Dec 09 nicklas 498       out.bfsPrintSection("parameters", true, true);
5199 15 Dec 09 nicklas 499       for (Map.Entry<String, String[]> entry : parameters.entrySet())
5199 15 Dec 09 nicklas 500       {
5199 15 Dec 09 nicklas 501         String key = entry.getKey();
5199 15 Dec 09 nicklas 502         String[] values = entry.getValue();
5199 15 Dec 09 nicklas 503         out.bfsPrintValue(key, values);
5199 15 Dec 09 nicklas 504       }
5199 15 Dec 09 nicklas 505     }
5199 15 Dec 09 nicklas 506     
5199 15 Dec 09 nicklas 507     // [sdata]
5199 15 Dec 09 nicklas 508     List<DynamicField> spotFields = getSpotFields();
5199 15 Dec 09 nicklas 509     if (spotFields.size() > 0)
5199 15 Dec 09 nicklas 510     {
5199 15 Dec 09 nicklas 511       out.bfsPrintSection("sdata", true, true);
5199 15 Dec 09 nicklas 512       for (DynamicField spotField : spotFields)
5199 15 Dec 09 nicklas 513       {
5319 20 Apr 10 nicklas 514         Type spotType = spotField.getType();
5319 20 Apr 10 nicklas 515         out.bfsPrintValue(spotField.getTitle(), spotType == null ? "" : spotType.getStringValue());
5199 15 Dec 09 nicklas 516       }
5199 15 Dec 09 nicklas 517     }
5199 15 Dec 09 nicklas 518     out.flush();
5319 20 Apr 10 nicklas 519     if (autoCloseWriters) out.close();
5199 15 Dec 09 nicklas 520   }
5199 15 Dec 09 nicklas 521
5199 15 Dec 09 nicklas 522   /**
5199 15 Dec 09 nicklas 523     Export the reporter annotations. This method is ignored if
5199 15 Dec 09 nicklas 524     {@link #getReporterAnnotationsWriter()} returns null. The 
5199 15 Dec 09 nicklas 525     position is used as the value for the id column. Other columns
5199 15 Dec 09 nicklas 526     are exported as given by {@link #getReporterFields()}.
5199 15 Dec 09 nicklas 527     <p>
5199 15 Dec 09 nicklas 528     If a progress reporter is given, progress is updated between 
5199 15 Dec 09 nicklas 529     0 and 100%.
5199 15 Dec 09 nicklas 530     <p>
5199 15 Dec 09 nicklas 531     This method needs to be executed after {@link #exportSpotData(ProgressReporter, long)}
5199 15 Dec 09 nicklas 532     since we need to know which positions that was exported.
5199 15 Dec 09 nicklas 533     
5199 15 Dec 09 nicklas 534     @param progress An optional progress reporter
5199 15 Dec 09 nicklas 535     @param count The total number of reporters, or 0 if not known 
5199 15 Dec 09 nicklas 536   */
6875 20 Apr 15 nicklas 537   @SuppressWarnings({ "unchecked", "rawtypes" })
5199 15 Dec 09 nicklas 538   protected void exportReporterAnnotations(ProgressReporter progress, long count)
5199 15 Dec 09 nicklas 539   {
5199 15 Dec 09 nicklas 540     AnnotationWriter out = getReporterAnnotationsWriter();
5199 15 Dec 09 nicklas 541     if (out == null) return;
5199 15 Dec 09 nicklas 542
5199 15 Dec 09 nicklas 543     List<DynamicField> fields = getReporterFields();
5199 15 Dec 09 nicklas 544     int numReporterFields = fields.size();
5199 15 Dec 09 nicklas 545     DbControl dc = getDbControl();
5199 15 Dec 09 nicklas 546     int posIndex = numReporterFields + 1;
5199 15 Dec 09 nicklas 547     
5199 15 Dec 09 nicklas 548     // Initialize headers and formatters
5199 15 Dec 09 nicklas 549     String[] headers = new String[numReporterFields];
5199 15 Dec 09 nicklas 550     Formatter[] formatters = new Formatter[numReporterFields];
5199 15 Dec 09 nicklas 551     int index = 0;
5199 15 Dec 09 nicklas 552     for (DynamicField df : fields)
5199 15 Dec 09 nicklas 553     {
5199 15 Dec 09 nicklas 554       headers[index] = df.getTitle();
5199 15 Dec 09 nicklas 555       Formatter f = df.getFormatter();
5199 15 Dec 09 nicklas 556       if (f == null) f = new ToStringFormatter();
5199 15 Dec 09 nicklas 557       formatters[index] = f;
5199 15 Dec 09 nicklas 558       index++;
5199 15 Dec 09 nicklas 559     }
5199 15 Dec 09 nicklas 560     out.bfsPrintHeaders(headers);
5199 15 Dec 09 nicklas 561     
5199 15 Dec 09 nicklas 562     if (progress != null) progress.display(0, "Loading reporter annotations...");
5199 15 Dec 09 nicklas 563     DynamicPositionQuery query = getReporterQuery();
5199 15 Dec 09 nicklas 564     DynamicResultIterator it = query.iterate(dc);
5199 15 Dec 09 nicklas 565     Object[] data = new Object[numReporterFields];
5199 15 Dec 09 nicklas 566     try
5199 15 Dec 09 nicklas 567     {
5199 15 Dec 09 nicklas 568       long numDone = 0;
5319 20 Apr 10 nicklas 569       long progressInterval = Math.max(count > 0 ? 1 + count / 100 : 100, 100);
5199 15 Dec 09 nicklas 570       while (it.hasNext())
5199 15 Dec 09 nicklas 571       {
5405 10 Sep 10 nicklas 572         ThreadSignalHandler.checkInterrupted();
5199 15 Dec 09 nicklas 573         SqlResult result = it.next();
5199 15 Dec 09 nicklas 574         
5199 15 Dec 09 nicklas 575         int position = result.getInt(posIndex);
5199 15 Dec 09 nicklas 576         if (exportedPositions.contains(position))
5199 15 Dec 09 nicklas 577         {
5199 15 Dec 09 nicklas 578           index = 0;
5199 15 Dec 09 nicklas 579           for (int i = 1; i <= numReporterFields; ++i)
5199 15 Dec 09 nicklas 580           {
5199 15 Dec 09 nicklas 581             data[index] = formatters[index].format(result.getObject(i));
5199 15 Dec 09 nicklas 582             ++index;
5199 15 Dec 09 nicklas 583           }
5199 15 Dec 09 nicklas 584           out.bfsPrintData(position, data);
5199 15 Dec 09 nicklas 585           ++numDone;
5319 20 Apr 10 nicklas 586           if (progress != null && numDone % progressInterval == 0) 
5199 15 Dec 09 nicklas 587           {
5199 15 Dec 09 nicklas 588             int percent = count == 0 ? 0 : 10+(int)((90L * numDone) / count);
5199 15 Dec 09 nicklas 589             progress.display(percent, "Exporting reporter annotations: " + 
5199 15 Dec 09 nicklas 590               numDone + " of " + (count == 0 ? "unknown" : count) + " done");
5199 15 Dec 09 nicklas 591           }
5199 15 Dec 09 nicklas 592         }
5199 15 Dec 09 nicklas 593       }
5199 15 Dec 09 nicklas 594       out.flush();
5199 15 Dec 09 nicklas 595       if (progress != null) 
5199 15 Dec 09 nicklas 596       {
5199 15 Dec 09 nicklas 597         progress.display(100, numDone + " reporter annotations exported.");
5199 15 Dec 09 nicklas 598       }
5199 15 Dec 09 nicklas 599     }
5199 15 Dec 09 nicklas 600     catch (SQLException e)
5199 15 Dec 09 nicklas 601     {
5199 15 Dec 09 nicklas 602       throw new DatabaseException(e);
5199 15 Dec 09 nicklas 603     }
5199 15 Dec 09 nicklas 604     finally
5199 15 Dec 09 nicklas 605     {
5199 15 Dec 09 nicklas 606       if (it != null) it.close();
5319 20 Apr 10 nicklas 607       if (out != null && autoCloseWriters) out.close();
5199 15 Dec 09 nicklas 608     }
5199 15 Dec 09 nicklas 609   }
5199 15 Dec 09 nicklas 610   
5199 15 Dec 09 nicklas 611   
5199 15 Dec 09 nicklas 612   /**
5199 15 Dec 09 nicklas 613     Export the assay annotations. This method is ignored if
5199 15 Dec 09 nicklas 614     {@link #getAssayAnnotationsWriter()} returns null. The 
5199 15 Dec 09 nicklas 615     assay ID is used as the value for the id column. Other columns
5199 15 Dec 09 nicklas 616     are exported as given by {@link #getAssayFields()}.
5199 15 Dec 09 nicklas 617   */
6875 20 Apr 15 nicklas 618   @SuppressWarnings({ "unchecked", "rawtypes" })
5199 15 Dec 09 nicklas 619   protected void exportAssayAnnotations()
5199 15 Dec 09 nicklas 620   {
5199 15 Dec 09 nicklas 621     AnnotationWriter out = getAssayAnnotationsWriter();
5199 15 Dec 09 nicklas 622     if (out == null) return;
5199 15 Dec 09 nicklas 623     
5199 15 Dec 09 nicklas 624     setProgress(10, "Exporting assay annotations...");
5199 15 Dec 09 nicklas 625     List<AssayField> fields = getAssayFields();
5199 15 Dec 09 nicklas 626     int numFields = fields.size();
5199 15 Dec 09 nicklas 627     DbControl dc = getDbControl();
5199 15 Dec 09 nicklas 628
5199 15 Dec 09 nicklas 629     String[] headers = new String[numFields];
5199 15 Dec 09 nicklas 630     Formatter[] formatters = new Formatter[numFields];
5199 15 Dec 09 nicklas 631     
5199 15 Dec 09 nicklas 632     int index = 0;
5199 15 Dec 09 nicklas 633     for (AssayField df : fields)
5199 15 Dec 09 nicklas 634     {
5199 15 Dec 09 nicklas 635       headers[index] = df.getTitle();
5199 15 Dec 09 nicklas 636       Formatter f = df.getFormatter();
5199 15 Dec 09 nicklas 637       if (f == null) f = new ToStringFormatter();
5199 15 Dec 09 nicklas 638       formatters[index] = f;
5199 15 Dec 09 nicklas 639       index++;
5199 15 Dec 09 nicklas 640     }
5199 15 Dec 09 nicklas 641     out.bfsPrintHeaders(headers);
5199 15 Dec 09 nicklas 642     
5199 15 Dec 09 nicklas 643     List<BioAssay> assays = getBioAssays();
5199 15 Dec 09 nicklas 644     Object[] data = new Object[numFields];
5199 15 Dec 09 nicklas 645
5199 15 Dec 09 nicklas 646     for (BioAssay ba : assays)
5199 15 Dec 09 nicklas 647     {
5405 10 Sep 10 nicklas 648       ThreadSignalHandler.checkInterrupted();
5199 15 Dec 09 nicklas 649       index = 0;
5199 15 Dec 09 nicklas 650       for (int i = 1; i <= numFields; ++i)
5199 15 Dec 09 nicklas 651       {
5199 15 Dec 09 nicklas 652         Collection<?> values = fields.get(index).getAssayValue(dc, ba);
5199 15 Dec 09 nicklas 653         data[index] = values == null || values.size() == 0 ? 
5199 15 Dec 09 nicklas 654             null : Values.getString(values, ", ", true, formatters[index]);
5199 15 Dec 09 nicklas 655         ++index;
5199 15 Dec 09 nicklas 656       }
5199 15 Dec 09 nicklas 657       out.bfsPrintData(ba.getId(), data);
5199 15 Dec 09 nicklas 658     }
5199 15 Dec 09 nicklas 659     out.flush();
5319 20 Apr 10 nicklas 660     if (autoCloseWriters) out.close();
5199 15 Dec 09 nicklas 661   }
5199 15 Dec 09 nicklas 662   
5199 15 Dec 09 nicklas 663   /**
5199 15 Dec 09 nicklas 664     Export spot data. This method needs to be executed even if no spot
5199 15 Dec 09 nicklas 665     fields has been selected. The reason is that we need to find out
5199 15 Dec 09 nicklas 666     the positions that are used in the bioassay set when exporting
5199 15 Dec 09 nicklas 667     reporter annotations. The only exception is if also no reporter
5199 15 Dec 09 nicklas 668     annotations are exported.
5199 15 Dec 09 nicklas 669
5199 15 Dec 09 nicklas 670     @param progress An optional progress reporter
5199 15 Dec 09 nicklas 671     @param count The expected number of spots or 0 if not known
5199 15 Dec 09 nicklas 672     @return The number of spots processed
5199 15 Dec 09 nicklas 673   */
6875 20 Apr 15 nicklas 674   @SuppressWarnings({ "unchecked", "rawtypes" })
5199 15 Dec 09 nicklas 675   protected long exportSpotData(ProgressReporter progress, long count)
5199 15 Dec 09 nicklas 676   {
5199 15 Dec 09 nicklas 677     exportedPositions = new HashSet<Integer>();
5199 15 Dec 09 nicklas 678     DbControl dc = getDbControl();
5199 15 Dec 09 nicklas 679     List<DynamicField> spotFields = getSpotFields();
5199 15 Dec 09 nicklas 680     int numSpotFields = spotFields.size();
5199 15 Dec 09 nicklas 681     if (numSpotFields == 0 && getReporterFields().size() == 0) return 0;
5199 15 Dec 09 nicklas 682
5199 15 Dec 09 nicklas 683     Formatter[] formatters = new Formatter[numSpotFields];
5199 15 Dec 09 nicklas 684     Object[] spotData = new Object[numSpotFields];
5199 15 Dec 09 nicklas 685     for (int i = 0; i < numSpotFields; ++i)
5199 15 Dec 09 nicklas 686     {
5199 15 Dec 09 nicklas 687       Formatter f = spotFields.get(i).getFormatter();
5199 15 Dec 09 nicklas 688       if (f == null) f = new ToStringFormatter();
5199 15 Dec 09 nicklas 689       formatters[i] = f; 
5199 15 Dec 09 nicklas 690     }
5199 15 Dec 09 nicklas 691     
5199 15 Dec 09 nicklas 692     Object[][] data = new Object[dataWriters.size()][];
5199 15 Dec 09 nicklas 693     int index = 0;
5199 15 Dec 09 nicklas 694     for (DataWriter dw : dataWriters)
5199 15 Dec 09 nicklas 695     {
5199 15 Dec 09 nicklas 696       data[index] = new Object[dw.getColumnCount()];
5199 15 Dec 09 nicklas 697       ++index;
5199 15 Dec 09 nicklas 698     }
5199 15 Dec 09 nicklas 699     
5199 15 Dec 09 nicklas 700     int posIndex = numSpotFields + 1;
5199 15 Dec 09 nicklas 701     int colIndex = numSpotFields + 2;
5199 15 Dec 09 nicklas 702     int currentPosition = -1;
5199 15 Dec 09 nicklas 703
5199 15 Dec 09 nicklas 704     // Execute the query
5199 15 Dec 09 nicklas 705     if (progress != null) progress.display(0, "Loading spot data...");
5199 15 Dec 09 nicklas 706     DynamicSpotQuery spotQuery = getSpotQuery(false);
5199 15 Dec 09 nicklas 707     DynamicResultIterator it = spotQuery.iterate(dc);
5199 15 Dec 09 nicklas 708     long numDone = 0;
5199 15 Dec 09 nicklas 709     try
5199 15 Dec 09 nicklas 710     {
5319 20 Apr 10 nicklas 711       long progressInterval = Math.max(count > 0 ? 1 + count / 100 : 100, 100);
5199 15 Dec 09 nicklas 712       while (it.hasNext())
5199 15 Dec 09 nicklas 713       {
5405 10 Sep 10 nicklas 714         ThreadSignalHandler.checkInterrupted();
5199 15 Dec 09 nicklas 715         SqlResult result = it.next();
5199 15 Dec 09 nicklas 716         
5199 15 Dec 09 nicklas 717         int position = result.getInt(posIndex);
5199 15 Dec 09 nicklas 718         short column = result.getShort(colIndex);
5199 15 Dec 09 nicklas 719         if (position != currentPosition)
5199 15 Dec 09 nicklas 720         {
5199 15 Dec 09 nicklas 721           if (currentPosition != -1) 
5199 15 Dec 09 nicklas 722           {
5199 15 Dec 09 nicklas 723             // print out data for the old position
5199 15 Dec 09 nicklas 724             index = 0;
5199 15 Dec 09 nicklas 725             for (DataWriter writer : dataWriters)
5199 15 Dec 09 nicklas 726             {
5199 15 Dec 09 nicklas 727               writer.bfsPrintData(data[index]);
5199 15 Dec 09 nicklas 728               Arrays.fill(data[index], null);
5199 15 Dec 09 nicklas 729               index++;
5199 15 Dec 09 nicklas 730             }
5223 26 Jan 10 nicklas 731             exportedPositions.add(currentPosition);
5199 15 Dec 09 nicklas 732           }
5199 15 Dec 09 nicklas 733           currentPosition = position;
5199 15 Dec 09 nicklas 734         }
5199 15 Dec 09 nicklas 735         
5199 15 Dec 09 nicklas 736         // Copy formatted spot field values into the spotData array...
5199 15 Dec 09 nicklas 737         for (int i = 0; i < spotData.length; ++i)
5199 15 Dec 09 nicklas 738         {
5199 15 Dec 09 nicklas 739           spotData[i] = formatters[i].format(result.getObject(i+1));
5199 15 Dec 09 nicklas 740         }
5199 15 Dec 09 nicklas 741         // ... and let the subclass arrange the data into the output array
5213 12 Jan 10 nicklas 742         if (numSpotFields > 0) arrangeData(position, column, data, spotData);
5199 15 Dec 09 nicklas 743         
5199 15 Dec 09 nicklas 744         ++numDone;
5319 20 Apr 10 nicklas 745         if (progress != null && numDone % progressInterval == 0) 
5199 15 Dec 09 nicklas 746         {
5199 15 Dec 09 nicklas 747           int percent = count == 0 ? 0 : 10+(int)((90L * numDone) / count);
5199 15 Dec 09 nicklas 748           progress.display(percent, "Exporting spot data: " + 
5199 15 Dec 09 nicklas 749             numDone + " of " + (count == 0 ? "unknown" : count) + " done");
5199 15 Dec 09 nicklas 750         }
5199 15 Dec 09 nicklas 751       }
5199 15 Dec 09 nicklas 752       
5199 15 Dec 09 nicklas 753       // Print out the final data line
5199 15 Dec 09 nicklas 754       exportedPositions.add(currentPosition);
5199 15 Dec 09 nicklas 755       index = 0;
5199 15 Dec 09 nicklas 756       for (DataWriter writer : dataWriters)
5199 15 Dec 09 nicklas 757       {
5199 15 Dec 09 nicklas 758         writer.bfsPrintData(data[index]);
5199 15 Dec 09 nicklas 759         writer.flush();
5319 20 Apr 10 nicklas 760         if (autoCloseWriters) writer.close();
5199 15 Dec 09 nicklas 761         index++;
5199 15 Dec 09 nicklas 762       }
5199 15 Dec 09 nicklas 763     }
5199 15 Dec 09 nicklas 764     catch (SQLException ex)
5199 15 Dec 09 nicklas 765     {
5199 15 Dec 09 nicklas 766       throw new DatabaseException(ex);
5199 15 Dec 09 nicklas 767     }
5199 15 Dec 09 nicklas 768     finally
5199 15 Dec 09 nicklas 769     {
5199 15 Dec 09 nicklas 770       if (it != null) it.close();
5199 15 Dec 09 nicklas 771     }
5199 15 Dec 09 nicklas 772     if (progress != null) progress.display(100, "Exported spot data: " + numDone + " spots");
5199 15 Dec 09 nicklas 773     return numDone;
5199 15 Dec 09 nicklas 774   }
5199 15 Dec 09 nicklas 775   
5199 15 Dec 09 nicklas 776   /**
5199 15 Dec 09 nicklas 777     Utility method for calling creating a data writer. Calls
5199 15 Dec 09 nicklas 778     {@link DataWriterFactory#createDataWriter(Object, String)}.
5199 15 Dec 09 nicklas 779     @return A data writer, or null if no factory has been specified
5199 15 Dec 09 nicklas 780   */
5199 15 Dec 09 nicklas 781   @SuppressWarnings("unchecked")
5199 15 Dec 09 nicklas 782   protected DataWriter createDataWriter(Object owner, String suggestedFilename)
5199 15 Dec 09 nicklas 783     throws IOException
5199 15 Dec 09 nicklas 784   {
5199 15 Dec 09 nicklas 785     DataWriter writer = null;
5199 15 Dec 09 nicklas 786     if (dataWriterFactory != null) 
5199 15 Dec 09 nicklas 787     {
5199 15 Dec 09 nicklas 788       writer = dataWriterFactory.createDataWriter(owner, suggestedFilename);
5199 15 Dec 09 nicklas 789     }
5199 15 Dec 09 nicklas 790     return writer;
5199 15 Dec 09 nicklas 791   }
5199 15 Dec 09 nicklas 792   
5199 15 Dec 09 nicklas 793   /**
5199 15 Dec 09 nicklas 794     Create the exact number of data writers that is needed to export the
5199 15 Dec 09 nicklas 795     desired data. Each data writer needs to specify a filename 
5199 15 Dec 09 nicklas 796     ({@link DataWriter#getFilename()} and the number of data columns 
5199 15 Dec 09 nicklas 797     ({@link DataWriter#getColumnCount()}) it is going to write.
5199 15 Dec 09 nicklas 798     <p>
5199 15 Dec 09 nicklas 799     
5199 15 Dec 09 nicklas 800     This method must be implemented by a subclass and is called once from
5199 15 Dec 09 nicklas 801     the {@link #beginExport()} method unless no data writer factory has been
5199 15 Dec 09 nicklas 802     set up.
5199 15 Dec 09 nicklas 803     
5199 15 Dec 09 nicklas 804     @return A list with data writers
5199 15 Dec 09 nicklas 805     @throws IOException If there is a problem with creating the writers
5199 15 Dec 09 nicklas 806   */
5199 15 Dec 09 nicklas 807   protected abstract List<DataWriter> createDataWriters()
5199 15 Dec 09 nicklas 808     throws IOException;
5199 15 Dec 09 nicklas 809   
5199 15 Dec 09 nicklas 810   /**
5199 15 Dec 09 nicklas 811     Arrange the data that was returned by the query into the data writers
5199 15 Dec 09 nicklas 812     array. The data array is a two-demensional array. The first index
5199 15 Dec 09 nicklas 813     represents a datawriter and goes from 0 to the one below the number
5199 15 Dec 09 nicklas 814     of data writers returned by {@link #createDataWriters()}. The size of
5199 15 Dec 09 nicklas 815     the second index is determined by the number of columns for the
5199 15 Dec 09 nicklas 816     data writer (eg. {@link DataWriter#getColumnCount()}.
5199 15 Dec 09 nicklas 817     <p>
5199 15 Dec 09 nicklas 818     
5199 15 Dec 09 nicklas 819     This method must be implemented by a subclass which should copy
5199 15 Dec 09 nicklas 820     data from the spotdata array into the data array. The data array
5199 15 Dec 09 nicklas 821     is flushed to the data writers every time the position coordinate
5199 15 Dec 09 nicklas 822     changes. Thus, this method can be called multiple times with
5199 15 Dec 09 nicklas 823     different column (=assay) coordinates.
5199 15 Dec 09 nicklas 824     
5199 15 Dec 09 nicklas 825     @param position The current position of the spot data
5199 15 Dec 09 nicklas 826     @param column The current column of the spot data
5199 15 Dec 09 nicklas 827     @param data The data array to copy the data to
5199 15 Dec 09 nicklas 828     @param spotData The current row of spot data, in the same order
5199 15 Dec 09 nicklas 829       as {@link #getSpotFields()}
5199 15 Dec 09 nicklas 830   */
5199 15 Dec 09 nicklas 831   protected abstract void arrangeData(int position, short column, 
5199 15 Dec 09 nicklas 832     Object[][] data, Object[] spotData);
5199 15 Dec 09 nicklas 833   
5199 15 Dec 09 nicklas 834 }