src/core/net/sf/basedb/util/basefile/BaseFileWriter.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.basefile;
4925 08 May 09 nicklas 23
4925 08 May 09 nicklas 24 import java.io.PrintWriter;
4925 08 May 09 nicklas 25 import java.io.Writer;
4930 14 May 09 nicklas 26 import java.util.regex.Pattern;
4925 08 May 09 nicklas 27
4925 08 May 09 nicklas 28 import net.sf.basedb.core.InvalidDataException;
4926 11 May 09 nicklas 29 import net.sf.basedb.util.export.TableWriter;
4925 08 May 09 nicklas 30
4925 08 May 09 nicklas 31 /**
4925 08 May 09 nicklas 32   Wraps a writer stream and provides methods for easier
4925 08 May 09 nicklas 33   creation of BASEfiles. All methods that start with
4925 08 May 09 nicklas 34   <code>base</code> are used for BASEfile specified formatting.
4925 08 May 09 nicklas 35   It is also possible to use the regular {@link PrintWriter}
4925 08 May 09 nicklas 36   methods for special cases.
4925 08 May 09 nicklas 37   <p>
4925 08 May 09 nicklas 38   A BASEfile is divided in sections. Each section has 
4925 08 May 09 nicklas 39   a header part and a data part. To start a new section
4925 08 May 09 nicklas 40   use {@link #baseBeginSection(String)}. When a section has been 
4925 08 May 09 nicklas 41   started use {@link #basePrintHeader(String, Object)} or one
4925 08 May 09 nicklas 42   of the overloaded methods to print headers for that section.
4925 08 May 09 nicklas 43   To end the header part and start the data part, use
4925 08 May 09 nicklas 44   {@link #baseBeginDataPart()} and then {@link #basePrintData(Object...)}
4925 08 May 09 nicklas 45   to print data lines. When all data has been printed
4925 08 May 09 nicklas 46   either start a new section or end the current section 
4925 08 May 09 nicklas 47   with {@link #baseEndSection()}.
4925 08 May 09 nicklas 48   
4925 08 May 09 nicklas 49   @author Nicklas
4925 08 May 09 nicklas 50   @version 2.12
4925 08 May 09 nicklas 51   @base.modified $Date$
4925 08 May 09 nicklas 52 */
4925 08 May 09 nicklas 53 public class BaseFileWriter
4926 11 May 09 nicklas 54   extends TableWriter
4925 08 May 09 nicklas 55 {
4925 08 May 09 nicklas 56   private String metadataPrefix = "";
4925 08 May 09 nicklas 57   
4925 08 May 09 nicklas 58   private String currentSection;
4925 08 May 09 nicklas 59   private boolean hasBom = false;
4925 08 May 09 nicklas 60   private boolean headerPart = false;
4925 08 May 09 nicklas 61   private boolean dataPart = false;
4925 08 May 09 nicklas 62   
4925 08 May 09 nicklas 63   /**
4925 08 May 09 nicklas 64     Create a new BASEfile writer that is writing it's output to the
4925 08 May 09 nicklas 65     given writer. The file prefix 'BASEfile' is automatically written.
4925 08 May 09 nicklas 66     @param out The writer to write the data to
4925 08 May 09 nicklas 67   */
4925 08 May 09 nicklas 68   public BaseFileWriter(Writer out)
4925 08 May 09 nicklas 69   {
4925 08 May 09 nicklas 70     super(out);
4925 08 May 09 nicklas 71   }
4925 08 May 09 nicklas 72   
4925 08 May 09 nicklas 73   /**
4925 08 May 09 nicklas 74     @return The name of the current section, or null if no
4925 08 May 09 nicklas 75       section has been started.
4925 08 May 09 nicklas 76   */
4925 08 May 09 nicklas 77   public String getCurrentSection()
4925 08 May 09 nicklas 78   {
4925 08 May 09 nicklas 79     return currentSection;
4925 08 May 09 nicklas 80   }
4925 08 May 09 nicklas 81   
4925 08 May 09 nicklas 82   /**
4925 08 May 09 nicklas 83     @return TRUE if this writer is currently in the header part of a section,
4925 08 May 09 nicklas 84       FALSE if no section is started or if the writer is in the data part
4925 08 May 09 nicklas 85   */
4925 08 May 09 nicklas 86   public boolean isWritingHeaders()
4925 08 May 09 nicklas 87   {
4925 08 May 09 nicklas 88     return headerPart;
4925 08 May 09 nicklas 89   }
4925 08 May 09 nicklas 90   
4925 08 May 09 nicklas 91   /**
4925 08 May 09 nicklas 92     @return TRUE if this writer is currently in the data part of a section,
4925 08 May 09 nicklas 93       FALSE if no section is started or if the writer is in the header part
4925 08 May 09 nicklas 94   */
4925 08 May 09 nicklas 95   public boolean isWritingData()
4925 08 May 09 nicklas 96   {
4925 08 May 09 nicklas 97     return dataPart;
4925 08 May 09 nicklas 98   }
4925 08 May 09 nicklas 99   
4925 08 May 09 nicklas 100   protected void setMode(boolean headerPart, boolean dataPart)
4925 08 May 09 nicklas 101   {
4925 08 May 09 nicklas 102     this.headerPart = headerPart;
4925 08 May 09 nicklas 103     this.dataPart = dataPart;
4925 08 May 09 nicklas 104   }
4925 08 May 09 nicklas 105   
4925 08 May 09 nicklas 106   /**
4925 08 May 09 nicklas 107     The metadata prefix is written to the beginning of 
4925 08 May 09 nicklas 108     each line that is not a data line. The default value
4925 08 May 09 nicklas 109     is an empty string. 
4925 08 May 09 nicklas 110     <p>
4925 08 May 09 nicklas 111     Tip! Settting this value to '#' makes it possible to send
4925 08 May 09 nicklas 112     a BASEfile to gnuplot.
4925 08 May 09 nicklas 113     @return The current value of the metadata prefix
4925 08 May 09 nicklas 114   */
4925 08 May 09 nicklas 115   public String getMetadataPrefix()
4925 08 May 09 nicklas 116   {
4925 08 May 09 nicklas 117     return metadataPrefix;
4925 08 May 09 nicklas 118   }
4925 08 May 09 nicklas 119   
4925 08 May 09 nicklas 120   /**
4925 08 May 09 nicklas 121     Change the metadata prefix string.
4925 08 May 09 nicklas 122     @param metaDataPrefix The new metadata prefix
4925 08 May 09 nicklas 123   */
4925 08 May 09 nicklas 124   public void setMetadataPrefix(String metaDataPrefix)
4925 08 May 09 nicklas 125   {
4925 08 May 09 nicklas 126     this.metadataPrefix = metaDataPrefix;
4925 08 May 09 nicklas 127   }
4925 08 May 09 nicklas 128   
4925 08 May 09 nicklas 129   /**
4925 08 May 09 nicklas 130     Write the beginning-of-file marker (BASEfile) to the output.
4925 08 May 09 nicklas 131     This method must be called before it is possible to writer other
4925 08 May 09 nicklas 132     data to the file.
4925 08 May 09 nicklas 133   */
4925 08 May 09 nicklas 134   public void baseWriteBom()
4925 08 May 09 nicklas 135   {
4925 08 May 09 nicklas 136     println(getMetadataPrefix() + "BASEfile");
4925 08 May 09 nicklas 137     hasBom = true;
4925 08 May 09 nicklas 138   }
4925 08 May 09 nicklas 139   
4925 08 May 09 nicklas 140   /**
4925 08 May 09 nicklas 141     Start a new section in the BASEfile. If the current
4925 08 May 09 nicklas 142     section hasn't been ended, {@link #baseEndSection()}
4925 08 May 09 nicklas 143     is automatically called to end it. After this call the
4925 08 May 09 nicklas 144     BASEfile is ready for writing headers to the new section.
4925 08 May 09 nicklas 145     <p>
4925 08 May 09 nicklas 146     If the beginning-of-file-marker hasn't been written the
4925 08 May 09 nicklas 147     {@link #baseWriteBom()} is automatically called.
4925 08 May 09 nicklas 148     
4925 08 May 09 nicklas 149     @param name The name of the section
4925 08 May 09 nicklas 150   */
4925 08 May 09 nicklas 151   public void baseBeginSection(String name)
4925 08 May 09 nicklas 152   {
4925 08 May 09 nicklas 153     if (!hasBom) baseWriteBom();
4925 08 May 09 nicklas 154     baseEndSection();
4930 14 May 09 nicklas 155     println(getMetadataPrefix() + "section\t" + baseEscape(name));
4925 08 May 09 nicklas 156     setMode(true, false);
5384 13 Aug 10 nicklas 157     currentSection = name;
4925 08 May 09 nicklas 158   }
4925 08 May 09 nicklas 159   
4925 08 May 09 nicklas 160   /**
4925 08 May 09 nicklas 161     Ends the current section by writing an empty line.
4925 08 May 09 nicklas 162   */
4925 08 May 09 nicklas 163   public void baseEndSection()
4925 08 May 09 nicklas 164   {
4925 08 May 09 nicklas 165     if (isWritingHeaders()) 
4925 08 May 09 nicklas 166     {
4925 08 May 09 nicklas 167       println(getMetadataPrefix() + "%");
4925 08 May 09 nicklas 168       println();
4925 08 May 09 nicklas 169     }
4925 08 May 09 nicklas 170     else if (isWritingData())
4925 08 May 09 nicklas 171     {
4925 08 May 09 nicklas 172       println();
4925 08 May 09 nicklas 173     }
4925 08 May 09 nicklas 174     setMode(false, false);
5384 13 Aug 10 nicklas 175     currentSection = null;
4925 08 May 09 nicklas 176   }
4925 08 May 09 nicklas 177
4925 08 May 09 nicklas 178   /**
4925 08 May 09 nicklas 179     Prints a header in the BASEfile. This call is ignored if
4925 08 May 09 nicklas 180     no section has been started or if the current section is
4930 14 May 09 nicklas 181     currently in data writing mode. Line breaks (\n) and
4930 14 May 09 nicklas 182     carriage returns (\r) in the value are converted to
4930 14 May 09 nicklas 183     the literal string values '\n' and '\r'.
4925 08 May 09 nicklas 184     @param key The header key, 'section' is not allowed
4925 08 May 09 nicklas 185     @param value The value, if null the configured null value
4925 08 May 09 nicklas 186       is used, otherwise the toString() method is called
4925 08 May 09 nicklas 187     @throws InvalidDataException If the key is 'section'
4925 08 May 09 nicklas 188   */
4925 08 May 09 nicklas 189   public <T> void basePrintHeader(String key, Object value)
4925 08 May 09 nicklas 190   {
4925 08 May 09 nicklas 191     if (!isWritingHeaders()) return;
4925 08 May 09 nicklas 192     if ("section".equals(key))
4925 08 May 09 nicklas 193     {
4925 08 May 09 nicklas 194       throw new InvalidDataException("'section' is not allowed as a parameter");
4925 08 May 09 nicklas 195     }
4925 08 May 09 nicklas 196     String valueToWrite = value == null ? getNullValue() : value.toString();
4930 14 May 09 nicklas 197     println(getMetadataPrefix() + key + "\t" + baseEscape(valueToWrite));
4925 08 May 09 nicklas 198   }
4925 08 May 09 nicklas 199
4925 08 May 09 nicklas 200   /**
4925 08 May 09 nicklas 201     End the header part of the current section (by writing a single % on a line 
4925 08 May 09 nicklas 202     by itself) and start the data part. The call to this method is ignored if 
4925 08 May 09 nicklas 203     no section has been started or if the data part of the current section
4925 08 May 09 nicklas 204     has alredy been started.
4925 08 May 09 nicklas 205   */
4925 08 May 09 nicklas 206   public void baseBeginDataPart()
4925 08 May 09 nicklas 207   {
4925 08 May 09 nicklas 208     if (isWritingData() || !isWritingHeaders()) return;
4925 08 May 09 nicklas 209     println(getMetadataPrefix() + "%");
4925 08 May 09 nicklas 210     setMode(false, true);
4925 08 May 09 nicklas 211   }
4925 08 May 09 nicklas 212   
4925 08 May 09 nicklas 213   /**
4925 08 May 09 nicklas 214     Print a data line to the BASEfile. Each value is separated by the
4925 08 May 09 nicklas 215     data separator string ('tab' by default). Null values are replaced 
4925 08 May 09 nicklas 216     with the null value string (empty string by default). Non-null objects
4925 08 May 09 nicklas 217     are converted to strings values by calling their toString() method.
4925 08 May 09 nicklas 218     <p>
4925 08 May 09 nicklas 219     This call is ignored if the data part has not been started.
4925 08 May 09 nicklas 220     @param data The values to write
4925 08 May 09 nicklas 221     @see #isWritingData()
4925 08 May 09 nicklas 222     @see #baseBeginDataPart()
4925 08 May 09 nicklas 223   */
4925 08 May 09 nicklas 224   public void basePrintData(Object... data)
4925 08 May 09 nicklas 225   {
4925 08 May 09 nicklas 226     if (!isWritingData()) return;
4926 11 May 09 nicklas 227     tablePrintData(data);
4925 08 May 09 nicklas 228   }
4925 08 May 09 nicklas 229   
4930 14 May 09 nicklas 230   private Pattern r = Pattern.compile("\r");
4974 15 Jun 09 nicklas 231   private Pattern n = Pattern.compile("\n");
4930 14 May 09 nicklas 232
4930 14 May 09 nicklas 233   /**
4930 14 May 09 nicklas 234     Escape new line and carrige return characters in the input
4930 14 May 09 nicklas 235     to the literal string '\n' and '\r' in the output.
4930 14 May 09 nicklas 236     @param in The input string
4930 14 May 09 nicklas 237   */
4930 14 May 09 nicklas 238   public String baseEscape(String in)
4930 14 May 09 nicklas 239   {
4974 15 Jun 09 nicklas 240     in = r.matcher(in).replaceAll("\\\\r");
4930 14 May 09 nicklas 241     in = n.matcher(in).replaceAll("\\\\n");
4930 14 May 09 nicklas 242     return in;
4930 14 May 09 nicklas 243   }
4930 14 May 09 nicklas 244   
4925 08 May 09 nicklas 245 }