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

Code
Comments
Other
Rev Date Author Line
4925 08 May 09 nicklas 1 /**
4925 08 May 09 nicklas 2   $Id$
4925 08 May 09 nicklas 3
4925 08 May 09 nicklas 4   Copyright (C) 2009 Nicklas Nordborg
4925 08 May 09 nicklas 5
4925 08 May 09 nicklas 6   This file is part of BASE - BioArray Software Environment.
4925 08 May 09 nicklas 7   Available at http://base.thep.lu.se/
4925 08 May 09 nicklas 8
4925 08 May 09 nicklas 9   BASE is free software; you can redistribute it and/or
4925 08 May 09 nicklas 10   modify it under the terms of the GNU General Public License
4925 08 May 09 nicklas 11   as published by the Free Software Foundation; either version 3
4925 08 May 09 nicklas 12   of the License, or (at your option) any later version.
4925 08 May 09 nicklas 13
4925 08 May 09 nicklas 14   BASE is distributed in the hope that it will be useful,
4925 08 May 09 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
4925 08 May 09 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4925 08 May 09 nicklas 17   GNU General Public License for more details.
4925 08 May 09 nicklas 18
4925 08 May 09 nicklas 19   You should have received a copy of the GNU General Public License
4925 08 May 09 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
4925 08 May 09 nicklas 21 */
4925 08 May 09 nicklas 22 package net.sf.basedb.util.export.spotdata;
4925 08 May 09 nicklas 23
4925 08 May 09 nicklas 24
4925 08 May 09 nicklas 25 import java.sql.SQLException;
4925 08 May 09 nicklas 26 import java.util.ArrayList;
4925 08 May 09 nicklas 27 import java.util.Arrays;
4925 08 May 09 nicklas 28 import java.util.Collection;
4925 08 May 09 nicklas 29 import java.util.LinkedHashMap;
4925 08 May 09 nicklas 30 import java.util.List;
4925 08 May 09 nicklas 31 import java.util.Map;
4925 08 May 09 nicklas 32
4925 08 May 09 nicklas 33 import net.sf.basedb.core.BioAssay;
4925 08 May 09 nicklas 34 import net.sf.basedb.core.BioAssaySet;
4925 08 May 09 nicklas 35 import net.sf.basedb.core.DatabaseException;
4925 08 May 09 nicklas 36 import net.sf.basedb.core.DbControl;
4925 08 May 09 nicklas 37 import net.sf.basedb.core.DynamicResultIterator;
4925 08 May 09 nicklas 38 import net.sf.basedb.core.DynamicSpotQuery;
4925 08 May 09 nicklas 39 import net.sf.basedb.core.ProgressReporter;
5373 03 Aug 10 nicklas 40 import net.sf.basedb.core.Type;
4925 08 May 09 nicklas 41 import net.sf.basedb.core.VirtualColumn;
4925 08 May 09 nicklas 42 import net.sf.basedb.core.VirtualTable;
4925 08 May 09 nicklas 43 import net.sf.basedb.core.query.Dynamic;
4925 08 May 09 nicklas 44 import net.sf.basedb.core.query.Expressions;
4925 08 May 09 nicklas 45 import net.sf.basedb.core.query.JoinType;
4925 08 May 09 nicklas 46 import net.sf.basedb.core.query.Restrictions;
4925 08 May 09 nicklas 47 import net.sf.basedb.core.query.Selects;
4925 08 May 09 nicklas 48 import net.sf.basedb.core.query.SqlResult;
5405 10 Sep 10 nicklas 49 import net.sf.basedb.core.signal.ThreadSignalHandler;
4925 08 May 09 nicklas 50 import net.sf.basedb.util.Values;
4925 08 May 09 nicklas 51 import net.sf.basedb.util.basefile.BaseFileWriter;
4925 08 May 09 nicklas 52 import net.sf.basedb.util.formatter.Formatter;
4925 08 May 09 nicklas 53 import net.sf.basedb.util.formatter.IdentifiableFormatter;
4925 08 May 09 nicklas 54 import net.sf.basedb.util.formatter.ToStringFormatter;
4925 08 May 09 nicklas 55
4925 08 May 09 nicklas 56 /**
4925 08 May 09 nicklas 57   A superclass for exporters that exports spot data to BASEfile format.
4925 08 May 09 nicklas 58   This class exposes several other configuration parameters that are 
4925 08 May 09 nicklas 59   specific for BASEfile:s. 
4925 08 May 09 nicklas 60
4925 08 May 09 nicklas 61   @author Nicklas
4925 08 May 09 nicklas 62   @version 2.12
4925 08 May 09 nicklas 63   @base.modified $Date$
4925 08 May 09 nicklas 64 */
4925 08 May 09 nicklas 65 public abstract class BaseFileExporter
4925 08 May 09 nicklas 66   extends AbstractBioAssaySetExporter
4925 08 May 09 nicklas 67 {
4925 08 May 09 nicklas 68
4925 08 May 09 nicklas 69   private Map<String, String> parameters;
4925 08 May 09 nicklas 70   private BaseFileWriter out;
6077 03 Aug 12 nicklas 71   private boolean autoCloseWriters;
4925 08 May 09 nicklas 72   
4925 08 May 09 nicklas 73   protected BaseFileExporter()
4925 08 May 09 nicklas 74   {
4925 08 May 09 nicklas 75     this.parameters = new LinkedHashMap<String, String>();
6077 03 Aug 12 nicklas 76     this.autoCloseWriters = true;
4925 08 May 09 nicklas 77   }
4925 08 May 09 nicklas 78   
4925 08 May 09 nicklas 79   /*
4925 08 May 09 nicklas 80     Configuration properties
4925 08 May 09 nicklas 81     ------------------------
4925 08 May 09 nicklas 82   */
4925 08 May 09 nicklas 83   /**
4925 08 May 09 nicklas 84     Set the stream were the exported data should be written.
4925 08 May 09 nicklas 85     It is expected that the given writer is a fresh writer and
4925 08 May 09 nicklas 86     that no data has been written to it yet.
4925 08 May 09 nicklas 87   */
4925 08 May 09 nicklas 88   public void setWriter(BaseFileWriter out)
4925 08 May 09 nicklas 89   {
4925 08 May 09 nicklas 90     this.out = out;
4925 08 May 09 nicklas 91   }
4925 08 May 09 nicklas 92   
4925 08 May 09 nicklas 93   /**
6077 03 Aug 12 nicklas 94     If this option is set then all writers are automatically closed
6077 03 Aug 12 nicklas 95     when all data has been writted to them. This setting is enabled by default.
6077 03 Aug 12 nicklas 96     @since 3.2
6077 03 Aug 12 nicklas 97   */
6077 03 Aug 12 nicklas 98   public void setAutoCloseWriters(boolean autoClose)
6077 03 Aug 12 nicklas 99   {
6077 03 Aug 12 nicklas 100     this.autoCloseWriters = autoClose;
6077 03 Aug 12 nicklas 101   }
6077 03 Aug 12 nicklas 102   
6077 03 Aug 12 nicklas 103   /**
4925 08 May 09 nicklas 104     Add a parameter that is exported in the 'settings' section, which
4925 08 May 09 nicklas 105     is the first section in the BASEfile. There can only be one value
4925 08 May 09 nicklas 106     for a given key. The parameters are written to the file in the 
4925 08 May 09 nicklas 107     order they are registered by this method.
4925 08 May 09 nicklas 108     
4925 08 May 09 nicklas 109     @param key The parameter key (if null this method call is ignored)
4925 08 May 09 nicklas 110     @param value The parameter value (may be null)
4925 08 May 09 nicklas 111   */
4925 08 May 09 nicklas 112   public void setParameter(String key, String value)
4925 08 May 09 nicklas 113   {
4925 08 May 09 nicklas 114     if (key == null) return;
4925 08 May 09 nicklas 115     parameters.put(key, value);
4925 08 May 09 nicklas 116   }
4925 08 May 09 nicklas 117   
4925 08 May 09 nicklas 118   /**
4925 08 May 09 nicklas 119     Get the current value of a 'settings' parameter.
4925 08 May 09 nicklas 120     @param key The parameter key
4925 08 May 09 nicklas 121     @return The value, or null if no values has been registered for
4925 08 May 09 nicklas 122       the key (or if the registered value is null)
4925 08 May 09 nicklas 123   */
4925 08 May 09 nicklas 124   public String getParameter(String key)
4925 08 May 09 nicklas 125   {
4925 08 May 09 nicklas 126     return parameters.get(key);
4925 08 May 09 nicklas 127   }
4925 08 May 09 nicklas 128   
4925 08 May 09 nicklas 129   @Override
4925 08 May 09 nicklas 130   public void addSpotField(DynamicField field)
4925 08 May 09 nicklas 131   {
4925 08 May 09 nicklas 132     super.addSpotField(field);
4925 08 May 09 nicklas 133   }
4925 08 May 09 nicklas 134   
4925 08 May 09 nicklas 135   /**
4925 08 May 09 nicklas 136     Add multiple spot fields in one go. A null collection is ignored and
4925 08 May 09 nicklas 137     so are null values in the collection.
4925 08 May 09 nicklas 138     @param fields A collection with the fields that should be added
4925 08 May 09 nicklas 139   */
4925 08 May 09 nicklas 140   public void addSpotFields(Collection<? extends DynamicField> fields)
4925 08 May 09 nicklas 141   {
4925 08 May 09 nicklas 142     if (fields == null) return;
4925 08 May 09 nicklas 143     for (DynamicField f : fields)
4925 08 May 09 nicklas 144     {
4925 08 May 09 nicklas 145       addSpotField(f);
4925 08 May 09 nicklas 146     }
4925 08 May 09 nicklas 147   }
4925 08 May 09 nicklas 148   
4925 08 May 09 nicklas 149   @Override
4925 08 May 09 nicklas 150   public List<DynamicField> getSpotFields()
4925 08 May 09 nicklas 151   {
4925 08 May 09 nicklas 152     return super.getSpotFields();
4925 08 May 09 nicklas 153   }
4925 08 May 09 nicklas 154   
4925 08 May 09 nicklas 155   @Override
4925 08 May 09 nicklas 156   public void addReporterField(DynamicField field)
4925 08 May 09 nicklas 157   {
4925 08 May 09 nicklas 158     super.addReporterField(field);
4925 08 May 09 nicklas 159   }
4925 08 May 09 nicklas 160   
4925 08 May 09 nicklas 161   /**
5373 03 Aug 10 nicklas 162     Add multiple reporter fields in one go. A null collection is ignored and
4925 08 May 09 nicklas 163     so are null values in the collection.
4925 08 May 09 nicklas 164     @param fields A collection with the fields that should be added
4925 08 May 09 nicklas 165   */
4925 08 May 09 nicklas 166   public void addReporterFields(Collection<? extends DynamicField> fields)
4925 08 May 09 nicklas 167   {
4925 08 May 09 nicklas 168     if (fields == null) return;
4925 08 May 09 nicklas 169     for (DynamicField f : fields)
4925 08 May 09 nicklas 170     {
4925 08 May 09 nicklas 171        addReporterField(f);
4925 08 May 09 nicklas 172     }
4925 08 May 09 nicklas 173   }
4925 08 May 09 nicklas 174
4925 08 May 09 nicklas 175   @Override
4925 08 May 09 nicklas 176   public List<DynamicField> getReporterFields()
4925 08 May 09 nicklas 177   {
4925 08 May 09 nicklas 178     return super.getReporterFields();
4925 08 May 09 nicklas 179   }
4925 08 May 09 nicklas 180   
5373 03 Aug 10 nicklas 181   /**
5373 03 Aug 10 nicklas 182     Adds an assay field to the exported file. If no fields has been added
5373 03 Aug 10 nicklas 183     the exporter will automaticall export the assay id and name and all 
5373 03 Aug 10 nicklas 184     experimental factor values.
5373 03 Aug 10 nicklas 185     @since 2.16
5373 03 Aug 10 nicklas 186   */
4925 08 May 09 nicklas 187   @Override
5373 03 Aug 10 nicklas 188   public void addAssayField(AssayField field)
5373 03 Aug 10 nicklas 189   {
5373 03 Aug 10 nicklas 190     super.addAssayField(field);
5373 03 Aug 10 nicklas 191   }
5373 03 Aug 10 nicklas 192
5373 03 Aug 10 nicklas 193   /**
5373 03 Aug 10 nicklas 194     Add multiple assay fields in one go. A null collection is ignored and
5373 03 Aug 10 nicklas 195     so are null values in the collection. If no fields has been added
5373 03 Aug 10 nicklas 196     the exporter will automaticall export the assay id and name and all 
5373 03 Aug 10 nicklas 197     experimental factor values.
5373 03 Aug 10 nicklas 198     @param fields A collection with the fields that should be added
5373 03 Aug 10 nicklas 199     @since 2.16
5373 03 Aug 10 nicklas 200   */
5373 03 Aug 10 nicklas 201   public void addAssayFields(Collection<? extends AssayField> fields)
5373 03 Aug 10 nicklas 202   {
5373 03 Aug 10 nicklas 203     if (fields == null) return;
5373 03 Aug 10 nicklas 204     for (AssayField f : fields)
5373 03 Aug 10 nicklas 205     {
5373 03 Aug 10 nicklas 206        addAssayField(f);
5373 03 Aug 10 nicklas 207     }
5373 03 Aug 10 nicklas 208   }
5373 03 Aug 10 nicklas 209
5373 03 Aug 10 nicklas 210   /**
5373 03 Aug 10 nicklas 211     @since 2.16
5373 03 Aug 10 nicklas 212   */
5373 03 Aug 10 nicklas 213   @Override
5373 03 Aug 10 nicklas 214   public List<AssayField> getAssayFields()
5373 03 Aug 10 nicklas 215   {
5373 03 Aug 10 nicklas 216     return super.getAssayFields();
5373 03 Aug 10 nicklas 217   }
5373 03 Aug 10 nicklas 218
5373 03 Aug 10 nicklas 219   
5373 03 Aug 10 nicklas 220   @Override
4925 08 May 09 nicklas 221   public void setAverageOnReporter(boolean averageOnReporter)
4925 08 May 09 nicklas 222   {
4925 08 May 09 nicklas 223     super.setAverageOnReporter(averageOnReporter);
4925 08 May 09 nicklas 224   }
4925 08 May 09 nicklas 225   @Override
4925 08 May 09 nicklas 226   public boolean getAverageOnReporter()
4925 08 May 09 nicklas 227   {
4925 08 May 09 nicklas 228     return super.getAverageOnReporter();
4925 08 May 09 nicklas 229   }
4925 08 May 09 nicklas 230   // --------------------------------------
4925 08 May 09 nicklas 231   
4925 08 May 09 nicklas 232   /*
4925 08 May 09 nicklas 233     From the AbstractBioAssaySetExporter class
4925 08 May 09 nicklas 234     ------------------------------------------
4925 08 May 09 nicklas 235   */
4925 08 May 09 nicklas 236   /**
4925 08 May 09 nicklas 237     Prepare the export by adding all experimental
5373 03 Aug 10 nicklas 238     factors as assay fields if no fields have been specified.
4925 08 May 09 nicklas 239   */
4925 08 May 09 nicklas 240   @Override
4925 08 May 09 nicklas 241   protected void beginExport()
4925 08 May 09 nicklas 242   {
5373 03 Aug 10 nicklas 243     if (getAssayFields() == null || getAssayFields().size() == 0)
5373 03 Aug 10 nicklas 244     {
5373 03 Aug 10 nicklas 245       addAssayField(ExportableFieldFactory.assayProperty("id", "id", Type.INT, null));
5373 03 Aug 10 nicklas 246       addAssayField(ExportableFieldFactory.assayProperty("name", "name", Type.STRING, null));
5373 03 Aug 10 nicklas 247       addExperimentalFactorsAsAssayFields();
5373 03 Aug 10 nicklas 248     }
4925 08 May 09 nicklas 249   }
4925 08 May 09 nicklas 250   /**
4925 08 May 09 nicklas 251     Export 'settings' and 'assays' sections.
4925 08 May 09 nicklas 252   */
4925 08 May 09 nicklas 253   @Override
4925 08 May 09 nicklas 254   protected boolean exportGlobalHeader()
4925 08 May 09 nicklas 255   {
4925 08 May 09 nicklas 256     exportSettingsSection();
4925 08 May 09 nicklas 257     exportAssaysSectionHeaders();
4925 08 May 09 nicklas 258     exportAssaysSectionData();
4925 08 May 09 nicklas 259     return true;
4925 08 May 09 nicklas 260   }
6077 03 Aug 12 nicklas 261   
6077 03 Aug 12 nicklas 262   @Override
6077 03 Aug 12 nicklas 263   protected void endExport(RuntimeException e) 
6077 03 Aug 12 nicklas 264   {
6077 03 Aug 12 nicklas 265     if (out != null && autoCloseWriters) out.close();
6077 03 Aug 12 nicklas 266     super.endExport(e);
6077 03 Aug 12 nicklas 267   }
4925 08 May 09 nicklas 268   // -------------------------------------------  
4925 08 May 09 nicklas 269
4925 08 May 09 nicklas 270   /**
4925 08 May 09 nicklas 271     Get the writer that writes the data to the file.
4925 08 May 09 nicklas 272   */
4925 08 May 09 nicklas 273   protected BaseFileWriter getBaseFileWriter()
4925 08 May 09 nicklas 274   {
4925 08 May 09 nicklas 275     return out;
4925 08 May 09 nicklas 276   }
4925 08 May 09 nicklas 277   
4925 08 May 09 nicklas 278   /**
4925 08 May 09 nicklas 279     Export the settings section which contains all parameter values
4925 08 May 09 nicklas 280     registered with {@link #setParameter(String, String)}. If a parameter
4925 08 May 09 nicklas 281     with key=section exists the value of that parameter is used as the 
4925 08 May 09 nicklas 282     name of this section, otherwise 'settings' is used as name.
4925 08 May 09 nicklas 283     If the 'section' parameter exists, it must be the first
4925 08 May 09 nicklas 284     parameter. It is not allowed in other places and will generate an
4925 08 May 09 nicklas 285     exception.
4925 08 May 09 nicklas 286     <p>
4925 08 May 09 nicklas 287     This method leaves the BASEfile writer in the header part in case the caller 
4925 08 May 09 nicklas 288     wants to add more headers. 
4925 08 May 09 nicklas 289     <p>
4925 08 May 09 nicklas 290     If no parameters has been registered this section is skipped.
4925 08 May 09 nicklas 291     
4925 08 May 09 nicklas 292     @return The number of parameters written, not including the 'section'
4925 08 May 09 nicklas 293       parameter
4925 08 May 09 nicklas 294   */
4925 08 May 09 nicklas 295   protected int exportSettingsSection()
4925 08 May 09 nicklas 296   {
4925 08 May 09 nicklas 297     BaseFileWriter out = getBaseFileWriter();
4925 08 May 09 nicklas 298     out.baseWriteBom();
4925 08 May 09 nicklas 299     if (parameters.isEmpty()) return 0;
4925 08 May 09 nicklas 300
4925 08 May 09 nicklas 301     boolean first = true;
4925 08 May 09 nicklas 302     int numParameters = 0;
4925 08 May 09 nicklas 303     for (Map.Entry<String, String> entry : parameters.entrySet())
4925 08 May 09 nicklas 304     {
4925 08 May 09 nicklas 305       String key = entry.getKey();
4925 08 May 09 nicklas 306       String value = entry.getValue();
4974 15 Jun 09 nicklas 307       String section = null;
4925 08 May 09 nicklas 308       if (first)
4925 08 May 09 nicklas 309       {
4974 15 Jun 09 nicklas 310         first = false;
4925 08 May 09 nicklas 311         if ("section".equals(key))
4925 08 May 09 nicklas 312         {
4974 15 Jun 09 nicklas 313           section = value;
4974 15 Jun 09 nicklas 314           key = null;
4925 08 May 09 nicklas 315         }
4925 08 May 09 nicklas 316         else
4925 08 May 09 nicklas 317         {
4974 15 Jun 09 nicklas 318           section = "settings";
4925 08 May 09 nicklas 319         }
4925 08 May 09 nicklas 320       }
4974 15 Jun 09 nicklas 321       if (section != null) out.baseBeginSection(section);
4974 15 Jun 09 nicklas 322       if (key != null)
4974 15 Jun 09 nicklas 323       {
4974 15 Jun 09 nicklas 324         ++numParameters;
4974 15 Jun 09 nicklas 325         out.basePrintHeader(key, value);
4974 15 Jun 09 nicklas 326       }
4925 08 May 09 nicklas 327     }
4925 08 May 09 nicklas 328     out.flush();
4925 08 May 09 nicklas 329     return numParameters;
4925 08 May 09 nicklas 330   }
4925 08 May 09 nicklas 331
4974 15 Jun 09 nicklas 332
4925 08 May 09 nicklas 333   /**
4925 08 May 09 nicklas 334     Exports headers for the 'assays' section which contains metadata 
4925 08 May 09 nicklas 335     information about the assays in the bioassay set. 
4925 08 May 09 nicklas 336     This method leaves the BASEfile writer in the header part in 
4925 08 May 09 nicklas 337     case the caller wants to add more headers. 
4925 08 May 09 nicklas 338   */
4925 08 May 09 nicklas 339   protected void exportAssaysSectionHeaders()
4925 08 May 09 nicklas 340   {
4925 08 May 09 nicklas 341     // Get configuration settings
4925 08 May 09 nicklas 342     BaseFileWriter out = getBaseFileWriter();
4925 08 May 09 nicklas 343     List<BioAssay> assays = getBioAssays();
4925 08 May 09 nicklas 344     List<AssayField> assayFields = getAssayFields();
4925 08 May 09 nicklas 345     
4925 08 May 09 nicklas 346     // Put together a list with the assay fields that are exported
5373 03 Aug 10 nicklas 347     int dataColumns = assayFields.size();
4925 08 May 09 nicklas 348     List<String> columns = new ArrayList<String>(dataColumns);
5373 03 Aug 10 nicklas 349     List<String> annotationColumns = new ArrayList<String>(dataColumns);
4925 08 May 09 nicklas 350     for (AssayField af : assayFields)
4925 08 May 09 nicklas 351     {
4925 08 May 09 nicklas 352       columns.add(af.getTitle());
5373 03 Aug 10 nicklas 353       if (af.isAnnotation()) annotationColumns.add(af.getTitle());
4925 08 May 09 nicklas 354     }
4925 08 May 09 nicklas 355     
4925 08 May 09 nicklas 356     // Write the headers
4925 08 May 09 nicklas 357     out.baseBeginSection("assays");
4925 08 May 09 nicklas 358     out.basePrintHeader("columns", Values.getString(columns, "\t", false));
5373 03 Aug 10 nicklas 359     out.basePrintHeader("annotationColumns", Values.getString(annotationColumns, "\t", false));
4925 08 May 09 nicklas 360     out.basePrintHeader("count", assays.size());
4925 08 May 09 nicklas 361     out.flush();
4925 08 May 09 nicklas 362   }
4925 08 May 09 nicklas 363   
4925 08 May 09 nicklas 364   /**
4925 08 May 09 nicklas 365     Export data for the 'assays' section that contains information
4925 08 May 09 nicklas 366     about the bioassays in the source bioassay set. Each data line
4925 08 May 09 nicklas 367     consists of the 'id', 'name' and annotations for a bioassay.
4925 08 May 09 nicklas 368   */
6875 20 Apr 15 nicklas 369   @SuppressWarnings({ "unchecked", "rawtypes" })
4925 08 May 09 nicklas 370   protected void exportAssaysSectionData()
4925 08 May 09 nicklas 371   {
4925 08 May 09 nicklas 372     // Get configuration settings
4925 08 May 09 nicklas 373     DbControl dc = getDbControl();
4925 08 May 09 nicklas 374     BaseFileWriter out = getBaseFileWriter();
4925 08 May 09 nicklas 375     List<BioAssay> assays = getBioAssays();
4925 08 May 09 nicklas 376     List<AssayField> assayFields = getAssayFields();
5373 03 Aug 10 nicklas 377     int dataColumns = assayFields.size();
4925 08 May 09 nicklas 378     
4925 08 May 09 nicklas 379     if (!out.isWritingData()) out.baseBeginDataPart();
4925 08 May 09 nicklas 380     for (BioAssay ba : assays)
4925 08 May 09 nicklas 381     {
4925 08 May 09 nicklas 382       Object[] data = new Object[dataColumns];
5373 03 Aug 10 nicklas 383       int index = 0;
4925 08 May 09 nicklas 384       for (AssayField at : assayFields)
4925 08 May 09 nicklas 385       {
6875 20 Apr 15 nicklas 386         Collection<?> values = at.getAssayValue(dc, ba);
4925 08 May 09 nicklas 387         Formatter f = at.getFormatter();
4925 08 May 09 nicklas 388         data[index++] = Values.getString(values, ",", true, f);
4925 08 May 09 nicklas 389       }
4925 08 May 09 nicklas 390       out.basePrintData(data);
4925 08 May 09 nicklas 391     }
4925 08 May 09 nicklas 392     out.flush();
4925 08 May 09 nicklas 393   }
4925 08 May 09 nicklas 394
4925 08 May 09 nicklas 395   /**
4925 08 May 09 nicklas 396     Start a new 'spot' section and export the standard headers:
4925 08 May 09 nicklas 397     'channels, 'assays', 'columns', 'assayFields' and 'count'.
4925 08 May 09 nicklas 398     This method leaves the BASEfile writer in the header part in
4925 08 May 09 nicklas 399     case the caller wants to add more headers.
4925 08 May 09 nicklas 400     
4925 08 May 09 nicklas 401     @param assays The bioassays that this section contains data for
4925 08 May 09 nicklas 402     @param spotCount The number of data lines that is going to be
4925 08 May 09 nicklas 403       written in the data part
4925 08 May 09 nicklas 404   */
4925 08 May 09 nicklas 405   protected void exportSpotSectionHeaders(List<BioAssay> assays, long spotCount)
4925 08 May 09 nicklas 406   {
4925 08 May 09 nicklas 407     // Get configuration settings
4925 08 May 09 nicklas 408     BioAssaySet source = getSource();
4925 08 May 09 nicklas 409     List<DynamicField> reporterFields = getReporterFields();
4925 08 May 09 nicklas 410     List<DynamicField> spotFields = getSpotFields();
4925 08 May 09 nicklas 411     BaseFileWriter out = getBaseFileWriter();
4925 08 May 09 nicklas 412     
4925 08 May 09 nicklas 413     // Write section 'spots' and headers
4925 08 May 09 nicklas 414     out.baseBeginSection("spots");
4925 08 May 09 nicklas 415     out.basePrintHeader("channels", source.getRawDataType().getChannels());
4925 08 May 09 nicklas 416     out.basePrintHeader("assays", Values.getString(assays, "\t", false, new IdentifiableFormatter("")));
4925 08 May 09 nicklas 417     ExportableFieldFormatter f = new ExportableFieldFormatter();
4925 08 May 09 nicklas 418     out.basePrintHeader("columns", Values.getString(reporterFields, "\t", false, f) + "\tassayData");
4925 08 May 09 nicklas 419     out.basePrintHeader("assayFields", Values.getString(spotFields, "\t", false, f));
4925 08 May 09 nicklas 420     out.basePrintHeader("count", spotCount);
4925 08 May 09 nicklas 421   }
4925 08 May 09 nicklas 422   
4925 08 May 09 nicklas 423   /**
4925 08 May 09 nicklas 424     Export the spot data of a 'spot' section. The given query should
4925 08 May 09 nicklas 425     return the given number of spot fields PLUS the spot position and 
4925 08 May 09 nicklas 426     column (in that order). Reporter data is expected to have been cached
4925 08 May 09 nicklas 427     by {@link #cacheReporterData()} and is copied to the output by
4925 08 May 09 nicklas 428     {@link #copyReporterFields(int, Object[], int)}.
4925 08 May 09 nicklas 429     <p>
4925 08 May 09 nicklas 430     A new data row is generated each time the position number changes. If each 
4925 08 May 09 nicklas 431     row should contain data from more than one assay an index map must be given.
4925 08 May 09 nicklas 432     The index map should map the bioassay column to the ordinal number of the
4925 08 May 09 nicklas 433     assay in the list of assays.
4925 08 May 09 nicklas 434     
4925 08 May 09 nicklas 435     @param assays A list of with the bioassays that the current spot
4925 08 May 09 nicklas 436       data section contains data for
4925 08 May 09 nicklas 437     @param spotQuery The query used to retreive the data
4925 08 May 09 nicklas 438     @param progress An optional progress reporter, if given progress
4925 08 May 09 nicklas 439       is reported from 0 to 100%
4925 08 May 09 nicklas 440     @param count The total number of spots must be given for the progress
4925 08 May 09 nicklas 441       reporting to work
4925 08 May 09 nicklas 442   */
6875 20 Apr 15 nicklas 443   @SuppressWarnings({ "unchecked", "rawtypes" })
4925 08 May 09 nicklas 444   protected void exportSpotSectionData(List<BioAssay> assays, DynamicSpotQuery spotQuery, 
4925 08 May 09 nicklas 445     ProgressReporter progress, long count)
4925 08 May 09 nicklas 446   {
4925 08 May 09 nicklas 447     // Get configuration options
4925 08 May 09 nicklas 448     DbControl dc = getDbControl();
4925 08 May 09 nicklas 449     BaseFileWriter out = getBaseFileWriter();
4925 08 May 09 nicklas 450     List<DynamicField> spotFields = getSpotFields();
4925 08 May 09 nicklas 451     int numSpotFields = spotFields.size();
4925 08 May 09 nicklas 452     int numReporterFields = getReporterFields().size();
4925 08 May 09 nicklas 453     
4925 08 May 09 nicklas 454     // Prepare temporary storage
4925 08 May 09 nicklas 455     int numAssaysPerRow = assays.size();
4925 08 May 09 nicklas 456     int numColumnsPerRow = numReporterFields + numAssaysPerRow * numSpotFields;
4925 08 May 09 nicklas 457     Object[] data = new Object[numColumnsPerRow];
4926 11 May 09 nicklas 458     // Map assay column number to index in the 'data' array
4925 08 May 09 nicklas 459     if (numAssaysPerRow > 1)
4925 08 May 09 nicklas 460     {
4926 11 May 09 nicklas 461       prepareAssayIndexMap(assays, numReporterFields, numSpotFields);
4925 08 May 09 nicklas 462     }
4925 08 May 09 nicklas 463     Formatter[] formatters = new Formatter[numSpotFields+1];
4925 08 May 09 nicklas 464     for (int i = 0; i < numSpotFields; ++i)
4925 08 May 09 nicklas 465     {
4925 08 May 09 nicklas 466       Formatter f = spotFields.get(i).getFormatter();
4925 08 May 09 nicklas 467       if (f == null) f = new ToStringFormatter();
4925 08 May 09 nicklas 468       formatters[i+1] = f; // Note +1 to avoid re-calc of index later
4925 08 May 09 nicklas 469     }
4925 08 May 09 nicklas 470
4925 08 May 09 nicklas 471     if (!out.isWritingData()) out.baseBeginDataPart();
4925 08 May 09 nicklas 472     int posIndex = numSpotFields + 1;
4925 08 May 09 nicklas 473     int colIndex = numSpotFields + 2;
4925 08 May 09 nicklas 474     int currentPosition = -1;
4925 08 May 09 nicklas 475     
4925 08 May 09 nicklas 476     // Execute the query
5229 04 Feb 10 nicklas 477     if (progress != null) progress.display(0, "Loading spot data...");
4925 08 May 09 nicklas 478     DynamicResultIterator it = spotQuery.iterate(dc);
4925 08 May 09 nicklas 479     try
4925 08 May 09 nicklas 480     {
4925 08 May 09 nicklas 481       long numDone = 0;
5229 04 Feb 10 nicklas 482       long progressInterval = Math.max(1 + count / 100, 50);
4925 08 May 09 nicklas 483       while (it.hasNext())
4925 08 May 09 nicklas 484       {
5405 10 Sep 10 nicklas 485         ThreadSignalHandler.checkInterrupted();
4925 08 May 09 nicklas 486         SqlResult result = it.next();
4925 08 May 09 nicklas 487         
4925 08 May 09 nicklas 488         int position = result.getInt(posIndex);
4925 08 May 09 nicklas 489         if (position != currentPosition)
4925 08 May 09 nicklas 490         {
4925 08 May 09 nicklas 491           if (currentPosition != -1) 
4925 08 May 09 nicklas 492           {
4925 08 May 09 nicklas 493             out.basePrintData(data);
4925 08 May 09 nicklas 494             Arrays.fill(data, null);
4925 08 May 09 nicklas 495             ++numDone;
5229 04 Feb 10 nicklas 496             if (progress != null && numDone % progressInterval == 0) 
4925 08 May 09 nicklas 497             {
4925 08 May 09 nicklas 498               int percent = count == 0 ? 0 : 10+(int)((90L * numDone) / count);
4925 08 May 09 nicklas 499               progress.display(percent, "Exporting spot data: " + 
4925 08 May 09 nicklas 500                 numDone + " of " + (count == 0 ? "unknown" : count) + " done");
4925 08 May 09 nicklas 501             }
4925 08 May 09 nicklas 502           }
4925 08 May 09 nicklas 503           copyReporterFields(position, data, 0);
4925 08 May 09 nicklas 504           currentPosition = position;
4925 08 May 09 nicklas 505         }
4925 08 May 09 nicklas 506         int index = numReporterFields;
4925 08 May 09 nicklas 507         if (numAssaysPerRow > 1)
4925 08 May 09 nicklas 508         {
4925 08 May 09 nicklas 509           short column = result.getShort(colIndex);
4926 11 May 09 nicklas 510           index = getAssayIndex(column);
4925 08 May 09 nicklas 511         }
4925 08 May 09 nicklas 512         for (int i = 1; i <= numSpotFields; ++i)
4925 08 May 09 nicklas 513         {
4925 08 May 09 nicklas 514           data[index] = formatters[i].format(result.getObject(i));
4925 08 May 09 nicklas 515           ++index;
4925 08 May 09 nicklas 516         }
4925 08 May 09 nicklas 517       }
4925 08 May 09 nicklas 518       // Print the final line
4925 08 May 09 nicklas 519       out.basePrintData(data);
4925 08 May 09 nicklas 520       out.flush();
4925 08 May 09 nicklas 521     }
4925 08 May 09 nicklas 522     catch (SQLException ex)
4925 08 May 09 nicklas 523     {
4925 08 May 09 nicklas 524       throw new DatabaseException(ex);
4925 08 May 09 nicklas 525     }
4925 08 May 09 nicklas 526     finally
4925 08 May 09 nicklas 527     {
4925 08 May 09 nicklas 528       if (it != null) it.close();
4925 08 May 09 nicklas 529     }
4925 08 May 09 nicklas 530   }
4925 08 May 09 nicklas 531   
4925 08 May 09 nicklas 532   /**
4925 08 May 09 nicklas 533     Get a configured query that counts the number of data lines that is about to
4925 08 May 09 nicklas 534     be written in a data section. The returned query finds this value by calculating
4925 08 May 09 nicklas 535     the number of unique positions or, if average on reporters is enabled, the number
4925 08 May 09 nicklas 536     of unique reporters.
4925 08 May 09 nicklas 537     
4925 08 May 09 nicklas 538     @param bioAssayRestriction If TRUE a restriction is added to the 
4925 08 May 09 nicklas 539       query to make it return data for a single bioassay only.
4925 08 May 09 nicklas 540       Use <code>query.setParameter("bioAssayColumn", (int)bioAssay.getDataCubeColumnNo(), Type.INT)</code> 
4925 08 May 09 nicklas 541       to set the value for the restriction
4925 08 May 09 nicklas 542     @return A configured query. Use {@link DynamicSpotQuery#count(DbControl)} to
4925 08 May 09 nicklas 543       get the count
4925 08 May 09 nicklas 544   */
4925 08 May 09 nicklas 545   protected DynamicSpotQuery getCountQuery(boolean bioAssayRestriction)
4925 08 May 09 nicklas 546   {
4925 08 May 09 nicklas 547     // Get configuration options
4925 08 May 09 nicklas 548     BioAssaySet source = getSource();
4925 08 May 09 nicklas 549     boolean forAverage = getAverageOnReporter();
4925 08 May 09 nicklas 550     
4925 08 May 09 nicklas 551     DynamicSpotQuery query = source.getSpotData();
4925 08 May 09 nicklas 552     query.setDistinct(true);
4925 08 May 09 nicklas 553     if (forAverage)
4925 08 May 09 nicklas 554     {
4925 08 May 09 nicklas 555       query.joinReporters(JoinType.INNER);
4925 08 May 09 nicklas 556       query.select(Selects.expression(Dynamic.column(VirtualTable.POSITION, VirtualColumn.REPORTER_ID), "rep", true));
4925 08 May 09 nicklas 557     }
4925 08 May 09 nicklas 558     else
4925 08 May 09 nicklas 559     {
4925 08 May 09 nicklas 560       query.select(Selects.expression(Dynamic.column(VirtualColumn.POSITION), "pos", true));
4925 08 May 09 nicklas 561     }
4925 08 May 09 nicklas 562     
4925 08 May 09 nicklas 563     // Optional restriction
4925 08 May 09 nicklas 564     if (bioAssayRestriction)
4925 08 May 09 nicklas 565     {
4925 08 May 09 nicklas 566       query.restrict(
4925 08 May 09 nicklas 567         Restrictions.eq(
4925 08 May 09 nicklas 568           Dynamic.column(VirtualColumn.COLUMN), 
4925 08 May 09 nicklas 569           Expressions.parameter("bioAssayColumn")
4925 08 May 09 nicklas 570         ));
4925 08 May 09 nicklas 571     }
4925 08 May 09 nicklas 572     return query;
4925 08 May 09 nicklas 573   }
4925 08 May 09 nicklas 574
4925 08 May 09 nicklas 575 }