src/core/net/sf/basedb/util/bfs/MetadataWriter.java

Code
Comments
Other
Rev Date Author Line
5193 27 Nov 09 nicklas 1 /**
5193 27 Nov 09 nicklas 2   $Id$
5193 27 Nov 09 nicklas 3
5193 27 Nov 09 nicklas 4   Copyright (C) 2009 Nicklas Nordborg
5193 27 Nov 09 nicklas 5
5193 27 Nov 09 nicklas 6   This file is part of BASE - BioArray Software Environment.
5193 27 Nov 09 nicklas 7   Available at http://base.thep.lu.se/
5193 27 Nov 09 nicklas 8
5193 27 Nov 09 nicklas 9   BASE is free software; you can redistribute it and/or
5193 27 Nov 09 nicklas 10   modify it under the terms of the GNU General Public License
5193 27 Nov 09 nicklas 11   as published by the Free Software Foundation; either version 3
5193 27 Nov 09 nicklas 12   of the License, or (at your option) any later version.
5193 27 Nov 09 nicklas 13
5193 27 Nov 09 nicklas 14   BASE is distributed in the hope that it will be useful,
5193 27 Nov 09 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
5193 27 Nov 09 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
5193 27 Nov 09 nicklas 17   GNU General Public License for more details.
5193 27 Nov 09 nicklas 18
5193 27 Nov 09 nicklas 19   You should have received a copy of the GNU General Public License
5193 27 Nov 09 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
5193 27 Nov 09 nicklas 21 */
5193 27 Nov 09 nicklas 22 package net.sf.basedb.util.bfs;
5193 27 Nov 09 nicklas 23
5384 13 Aug 10 nicklas 24 import java.io.FileNotFoundException;
5219 18 Jan 10 nicklas 25 import java.io.FileOutputStream;
5219 18 Jan 10 nicklas 26 import java.io.IOException;
5198 15 Dec 09 nicklas 27 import java.io.OutputStream;
5198 15 Dec 09 nicklas 28 import java.io.OutputStreamWriter;
5193 27 Nov 09 nicklas 29 import java.io.PrintWriter;
5193 27 Nov 09 nicklas 30 import java.io.Writer;
7714 21 May 19 nicklas 31 import java.nio.charset.StandardCharsets;
5193 27 Nov 09 nicklas 32 import java.util.HashSet;
5193 27 Nov 09 nicklas 33 import java.util.Set;
5193 27 Nov 09 nicklas 34
5210 11 Jan 10 nicklas 35 import net.sf.basedb.core.File;
5193 27 Nov 09 nicklas 36 import net.sf.basedb.util.encode.EncoderDecoder;
5193 27 Nov 09 nicklas 37 import net.sf.basedb.util.encode.TabCrLfEncoderDecoder;
5193 27 Nov 09 nicklas 38 import net.sf.basedb.util.formatter.Formatter;
5193 27 Nov 09 nicklas 39 import net.sf.basedb.util.formatter.ToStringFormatter;
5193 27 Nov 09 nicklas 40
5193 27 Nov 09 nicklas 41 /**
5193 27 Nov 09 nicklas 42   Writer implementation for writing BFS metadata files. The start
5193 27 Nov 09 nicklas 43   of file marker (BFSformat) is written immediately at object 
5193 27 Nov 09 nicklas 44   construction. Use the {@link #bfsPrintSection(String, boolean, boolean)}
5193 27 Nov 09 nicklas 45   to start a new section, and {@link #bfsPrintValue(String, String...)}
5193 27 Nov 09 nicklas 46   to print a section entry. It is not recommended that any of
5193 27 Nov 09 nicklas 47   the superclass methods (eg. {@link PrintWriter} methods} are used
5193 27 Nov 09 nicklas 48   directly as it can create an invalid BFS metadata file.
5193 27 Nov 09 nicklas 49   <p>
5193 27 Nov 09 nicklas 50   
5193 27 Nov 09 nicklas 51   This class can be subclassed to get specific functionality in specific
5193 27 Nov 09 nicklas 52   use cases.
5193 27 Nov 09 nicklas 53
5193 27 Nov 09 nicklas 54   @author Nicklas
5193 27 Nov 09 nicklas 55   @version 2.15
5193 27 Nov 09 nicklas 56   @base.modified $Date$
5193 27 Nov 09 nicklas 57 */
5193 27 Nov 09 nicklas 58 public class MetadataWriter
5193 27 Nov 09 nicklas 59   extends PrintWriter
5193 27 Nov 09 nicklas 60 {
5193 27 Nov 09 nicklas 61
5193 27 Nov 09 nicklas 62   public static final String BOF_MARKER = "BFSformat";
5193 27 Nov 09 nicklas 63
5198 15 Dec 09 nicklas 64   /**
5198 15 Dec 09 nicklas 65     Utility method for creating a metadata writer when you have an
5198 15 Dec 09 nicklas 66     output stream.
5198 15 Dec 09 nicklas 67     @param out The output stream the metadata writer should print to
5198 15 Dec 09 nicklas 68     @param filename Optional, the name of the file the output stream
5198 15 Dec 09 nicklas 69       is printing to
5198 15 Dec 09 nicklas 70   */
5198 15 Dec 09 nicklas 71   public static MetadataWriter create(OutputStream out, String filename)
5198 15 Dec 09 nicklas 72   {
5384 13 Aug 10 nicklas 73     if (out == null) throw new NullPointerException("out");
5198 15 Dec 09 nicklas 74     MetadataWriter writer = new MetadataWriter(
7714 21 May 19 nicklas 75         new OutputStreamWriter(out, StandardCharsets.UTF_8));
5198 15 Dec 09 nicklas 76     writer.setFilename(filename);
5198 15 Dec 09 nicklas 77     return writer;
5198 15 Dec 09 nicklas 78   }
5198 15 Dec 09 nicklas 79   
5210 11 Jan 10 nicklas 80   /**
5210 11 Jan 10 nicklas 81     Utility method for creating a metadata writer to a file in the BASE
5210 11 Jan 10 nicklas 82     file system. The character set and MIME type on the file are automatically 
5210 11 Jan 10 nicklas 83     updated.
5210 11 Jan 10 nicklas 84     @param file The file in the BASE file system
5210 11 Jan 10 nicklas 85   */
5210 11 Jan 10 nicklas 86   public static MetadataWriter create(File file)
5210 11 Jan 10 nicklas 87   {
5384 13 Aug 10 nicklas 88     if (file == null) throw new NullPointerException("file");
5210 11 Jan 10 nicklas 89     file.setCharacterSet("UTF-8");
5210 11 Jan 10 nicklas 90     file.setMimeType("text/plain");
5210 11 Jan 10 nicklas 91     return create(file.getUploadStream(false), file.getName());
5210 11 Jan 10 nicklas 92   }
5210 11 Jan 10 nicklas 93
5219 18 Jan 10 nicklas 94   /**
5219 18 Jan 10 nicklas 95     Utility method for creating a metadata writer to a file in the native
5219 18 Jan 10 nicklas 96     file system. If the file doesn't exists, it is created.
5219 18 Jan 10 nicklas 97     @param file The file in the native file system
5219 18 Jan 10 nicklas 98   */
5219 18 Jan 10 nicklas 99   public static MetadataWriter create(java.io.File file)
5219 18 Jan 10 nicklas 100     throws IOException
5219 18 Jan 10 nicklas 101   {
5384 13 Aug 10 nicklas 102     if (file == null) throw new NullPointerException("file");
5384 13 Aug 10 nicklas 103     if (!file.createNewFile() && !file.exists()) 
5384 13 Aug 10 nicklas 104     {
5384 13 Aug 10 nicklas 105       throw new FileNotFoundException(file.toString());
5384 13 Aug 10 nicklas 106     }
5219 18 Jan 10 nicklas 107     return create(new FileOutputStream(file), file.getName());
5219 18 Jan 10 nicklas 108   }
5219 18 Jan 10 nicklas 109
5210 11 Jan 10 nicklas 110   
5193 27 Nov 09 nicklas 111   private final EncoderDecoder encoder;
5198 15 Dec 09 nicklas 112   private String filename;
5194 04 Dec 09 nicklas 113   private String subtype;
5193 27 Nov 09 nicklas 114   private Set<String> usedSections;
5193 27 Nov 09 nicklas 115   private Set<String> usedKeys;
5193 27 Nov 09 nicklas 116   private String currentSection;
5193 27 Nov 09 nicklas 117   private boolean uniqueKeys;
5193 27 Nov 09 nicklas 118   private int lineCount;
5193 27 Nov 09 nicklas 119   private int sectionCount;
5193 27 Nov 09 nicklas 120   private int valueCount;
5193 27 Nov 09 nicklas 121   
5193 27 Nov 09 nicklas 122   /**
5193 27 Nov 09 nicklas 123     Create a new BFS metadata writer.
5193 27 Nov 09 nicklas 124     
5193 27 Nov 09 nicklas 125     @param out The parent writer which this writer will print to
5193 27 Nov 09 nicklas 126   */
5194 04 Dec 09 nicklas 127   public MetadataWriter(Writer out)
5193 27 Nov 09 nicklas 128   {
5193 27 Nov 09 nicklas 129     super(out);
5193 27 Nov 09 nicklas 130     this.usedSections = new HashSet<String>();
5193 27 Nov 09 nicklas 131     this.usedKeys = new HashSet<String>();
5193 27 Nov 09 nicklas 132     this.encoder = new TabCrLfEncoderDecoder(true);
5193 27 Nov 09 nicklas 133   }
5193 27 Nov 09 nicklas 134   
5193 27 Nov 09 nicklas 135   /**
5198 15 Dec 09 nicklas 136     Get the file name that this writer is printing to.
5198 15 Dec 09 nicklas 137     @return The file name or null if not known
5198 15 Dec 09 nicklas 138   */
5198 15 Dec 09 nicklas 139   public String getFilename()
5198 15 Dec 09 nicklas 140   {
5198 15 Dec 09 nicklas 141     return filename;
5198 15 Dec 09 nicklas 142   }
5198 15 Dec 09 nicklas 143   
5198 15 Dec 09 nicklas 144   /**
5198 15 Dec 09 nicklas 145     Set the file name that this writer is printing to.
5198 15 Dec 09 nicklas 146   */
5198 15 Dec 09 nicklas 147   public void setFilename(String filename)
5198 15 Dec 09 nicklas 148   {
5198 15 Dec 09 nicklas 149     this.filename = filename;
5198 15 Dec 09 nicklas 150   }
5198 15 Dec 09 nicklas 151   
5198 15 Dec 09 nicklas 152   /**
5193 27 Nov 09 nicklas 153     Get the BFS subtype.
5193 27 Nov 09 nicklas 154     @return The unencoded subtype, or null if no subtype was specified
5193 27 Nov 09 nicklas 155       when the object was created
5193 27 Nov 09 nicklas 156   */
5193 27 Nov 09 nicklas 157   public String getSubtype()
5193 27 Nov 09 nicklas 158   {
5193 27 Nov 09 nicklas 159     return subtype;
5193 27 Nov 09 nicklas 160   }
5193 27 Nov 09 nicklas 161
5193 27 Nov 09 nicklas 162   /**
5194 04 Dec 09 nicklas 163     Set the BFS subtype for this writer. This method must be called
5194 04 Dec 09 nicklas 164     before any printing method is called.
5194 04 Dec 09 nicklas 165     @param subtype The subtype
5194 04 Dec 09 nicklas 166     @throws IllegalStateException If data has already been written
5194 04 Dec 09 nicklas 167   */
5194 04 Dec 09 nicklas 168   public void setSubtype(String subtype)
5194 04 Dec 09 nicklas 169   {
5194 04 Dec 09 nicklas 170     if (getLineCount() > 0)
5194 04 Dec 09 nicklas 171     {
5194 04 Dec 09 nicklas 172       throw new IllegalStateException("Can't change the subtype after data has been printed");
5194 04 Dec 09 nicklas 173     }
5194 04 Dec 09 nicklas 174     this.subtype = subtype;
5194 04 Dec 09 nicklas 175   }
5194 04 Dec 09 nicklas 176   
5194 04 Dec 09 nicklas 177   /**
5193 27 Nov 09 nicklas 178     Get the name of the current section.
5193 27 Nov 09 nicklas 179     @return The unencoded name of the section, or null if no
5193 27 Nov 09 nicklas 180       section has been started yet
5193 27 Nov 09 nicklas 181   */
5193 27 Nov 09 nicklas 182   public String getCurrentSection()
5193 27 Nov 09 nicklas 183   {
5193 27 Nov 09 nicklas 184     return currentSection;
5193 27 Nov 09 nicklas 185   }
5193 27 Nov 09 nicklas 186
5193 27 Nov 09 nicklas 187   /**
5193 27 Nov 09 nicklas 188     Checks if the current section requires entry names to be unique
5193 27 Nov 09 nicklas 189     or not.
5193 27 Nov 09 nicklas 190   */
5193 27 Nov 09 nicklas 191   public boolean getUniqueKeys()
5193 27 Nov 09 nicklas 192   {
5193 27 Nov 09 nicklas 193     return uniqueKeys;
5193 27 Nov 09 nicklas 194   }
5193 27 Nov 09 nicklas 195   
5193 27 Nov 09 nicklas 196   /**
5193 27 Nov 09 nicklas 197     Checks if the current section already has an entry for the given
5193 27 Nov 09 nicklas 198     key or not.
5193 27 Nov 09 nicklas 199     @param key The key to check
5193 27 Nov 09 nicklas 200     @return TRUE if the current section already has an entry for this key,
5193 27 Nov 09 nicklas 201       FALSE otherwise
5193 27 Nov 09 nicklas 202   */
5193 27 Nov 09 nicklas 203   public boolean isUsedKey(String key)
5193 27 Nov 09 nicklas 204   {
5193 27 Nov 09 nicklas 205     return usedKeys.contains(key);
5193 27 Nov 09 nicklas 206   }
5193 27 Nov 09 nicklas 207   
5193 27 Nov 09 nicklas 208   /**
5193 27 Nov 09 nicklas 209     Encode a value for use in a BFS file. There is usually no need
5193 27 Nov 09 nicklas 210     to call this method, since values are automatically encoded.
5193 27 Nov 09 nicklas 211     @param value The value to encode, may be null
5193 27 Nov 09 nicklas 212     @return The encoded value
5193 27 Nov 09 nicklas 213   */
5193 27 Nov 09 nicklas 214   public String encodeValue(String value)
5193 27 Nov 09 nicklas 215   {
5193 27 Nov 09 nicklas 216     return encoder.encode(value);
5193 27 Nov 09 nicklas 217   }
5193 27 Nov 09 nicklas 218
5193 27 Nov 09 nicklas 219   /**
5193 27 Nov 09 nicklas 220     Get the number of lines that has been written to this
5193 27 Nov 09 nicklas 221     file so far, including all comment lines, empty lines,
5193 27 Nov 09 nicklas 222     sections headers, etc.
5193 27 Nov 09 nicklas 223   */
5193 27 Nov 09 nicklas 224   public int getLineCount()
5193 27 Nov 09 nicklas 225   {
5193 27 Nov 09 nicklas 226     return lineCount;
5193 27 Nov 09 nicklas 227   }
5193 27 Nov 09 nicklas 228   
5193 27 Nov 09 nicklas 229   /**
5193 27 Nov 09 nicklas 230     Get the number of sections that has been written to this
5193 27 Nov 09 nicklas 231     file so far.
5193 27 Nov 09 nicklas 232   */
5193 27 Nov 09 nicklas 233   public int getSectionCount()
5193 27 Nov 09 nicklas 234   {
5193 27 Nov 09 nicklas 235     return sectionCount;
5193 27 Nov 09 nicklas 236   }
5193 27 Nov 09 nicklas 237   
5193 27 Nov 09 nicklas 238   /**
5193 27 Nov 09 nicklas 239     Get the number of values that has been written to the current
5193 27 Nov 09 nicklas 240     section so far.
5193 27 Nov 09 nicklas 241   */
5193 27 Nov 09 nicklas 242   public int getValueCount()
5193 27 Nov 09 nicklas 243   {
5193 27 Nov 09 nicklas 244     return valueCount;
5193 27 Nov 09 nicklas 245   }
5193 27 Nov 09 nicklas 246   
5193 27 Nov 09 nicklas 247   /**
5193 27 Nov 09 nicklas 248     Print one or more empty lines to the metadata file.
5193 27 Nov 09 nicklas 249     @param num The number of empty lines to print. If 0 or negative
5193 27 Nov 09 nicklas 250       nothing is written
5193 27 Nov 09 nicklas 251   */
5193 27 Nov 09 nicklas 252   public void bfsPrintEmptyLine(int num)
5193 27 Nov 09 nicklas 253   {
5194 04 Dec 09 nicklas 254     if (getLineCount() == 0) internalPrintBofMarker();
5193 27 Nov 09 nicklas 255     for (int i = 0; i < num; ++i)
5193 27 Nov 09 nicklas 256     {
5193 27 Nov 09 nicklas 257       print("\n");
5193 27 Nov 09 nicklas 258     }
5194 04 Dec 09 nicklas 259     lineCount += num;
5193 27 Nov 09 nicklas 260   }
5193 27 Nov 09 nicklas 261   
5193 27 Nov 09 nicklas 262   /**
5193 27 Nov 09 nicklas 263     Prints one or more comment lines to the metadata file. A null
5193 27 Nov 09 nicklas 264     comment is ignored. If the comment contains line-breaks and/or
5193 27 Nov 09 nicklas 265     carriage returns, it will be split and written to multiple lines.
5193 27 Nov 09 nicklas 266     @param comment The comment string
5193 27 Nov 09 nicklas 267   */
5193 27 Nov 09 nicklas 268   public void bfsPrintComment(String comment)
5193 27 Nov 09 nicklas 269   {
5193 27 Nov 09 nicklas 270     if (comment == null) return;
5194 04 Dec 09 nicklas 271     if (getLineCount() == 0) internalPrintBofMarker();
5193 27 Nov 09 nicklas 272     String[] lines = comment.split("\\n|\\r|\\r\\n");
5193 27 Nov 09 nicklas 273     for (String line : lines)
5193 27 Nov 09 nicklas 274     {
5193 27 Nov 09 nicklas 275       internalPrintComment(line);
5193 27 Nov 09 nicklas 276     }
5193 27 Nov 09 nicklas 277   }
5193 27 Nov 09 nicklas 278   
5193 27 Nov 09 nicklas 279   /**
5193 27 Nov 09 nicklas 280     Start a new section.
5193 27 Nov 09 nicklas 281
5193 27 Nov 09 nicklas 282     @param name The name of the section, null is not allowed
5193 27 Nov 09 nicklas 283     @param unique TRUE if this section must be unique within the metadata
5193 27 Nov 09 nicklas 284       file
5193 27 Nov 09 nicklas 285     @param uniqueKeys TRUE if all keys in this section must be unique
5193 27 Nov 09 nicklas 286     @throws NullPointerException If the section name is null
5193 27 Nov 09 nicklas 287     @throws IllegalArgumentException If the name is not unique and
5193 27 Nov 09 nicklas 288       the writer requires unique sections
5193 27 Nov 09 nicklas 289   */
5193 27 Nov 09 nicklas 290   public void bfsPrintSection(String name, boolean unique, boolean uniqueKeys)
5193 27 Nov 09 nicklas 291   {
5194 04 Dec 09 nicklas 292     if (getLineCount() == 0) internalPrintBofMarker();
5193 27 Nov 09 nicklas 293     if (name == null) throw new NullPointerException("name");
5193 27 Nov 09 nicklas 294     if (unique)
5193 27 Nov 09 nicklas 295     {
5193 27 Nov 09 nicklas 296       if (usedSections.contains(name))
5193 27 Nov 09 nicklas 297       {
5193 27 Nov 09 nicklas 298         throw new IllegalArgumentException("Section '" + name + "' is not unique");
5193 27 Nov 09 nicklas 299       }
5193 27 Nov 09 nicklas 300     }
5193 27 Nov 09 nicklas 301     usedSections.add(name);
5193 27 Nov 09 nicklas 302     
5193 27 Nov 09 nicklas 303     this.currentSection = name;
5193 27 Nov 09 nicklas 304     this.uniqueKeys = uniqueKeys;
5193 27 Nov 09 nicklas 305     usedKeys.clear();
5193 27 Nov 09 nicklas 306     internalPrintSection(encodeValue(name));
5193 27 Nov 09 nicklas 307   }
5193 27 Nov 09 nicklas 308   
5193 27 Nov 09 nicklas 309   /**
5193 27 Nov 09 nicklas 310     Prints a section entry. A section must have been started with
5193 27 Nov 09 nicklas 311     {@link #bfsPrintSection(String, boolean, boolean)} before this
5193 27 Nov 09 nicklas 312     method is invoked.
5193 27 Nov 09 nicklas 313     
5193 27 Nov 09 nicklas 314     @param key The entry key; null is not allowed and it may not
5193 27 Nov 09 nicklas 315       start with '[' or '#'
5193 27 Nov 09 nicklas 316     @param values An array of string values, each value will be encoded
5193 27 Nov 09 nicklas 317       independently and separated with a tab
5193 27 Nov 09 nicklas 318     @throws NullPointerException If the key is null
5193 27 Nov 09 nicklas 319     @throws IllegalArgumentException If the key starts with '[' or
5193 27 Nov 09 nicklas 320       '#', or if the key is not unique within a section that requires
5193 27 Nov 09 nicklas 321       unique keys
5193 27 Nov 09 nicklas 322     @throws IllegalStateException If no section has been started
5193 27 Nov 09 nicklas 323   */
5193 27 Nov 09 nicklas 324   public void bfsPrintValue(String key, String... values)
5193 27 Nov 09 nicklas 325   {
5193 27 Nov 09 nicklas 326     bfsPrintValue(key, null, values);
5193 27 Nov 09 nicklas 327   }
5193 27 Nov 09 nicklas 328
5193 27 Nov 09 nicklas 329   /**
5193 27 Nov 09 nicklas 330     Prints a section entry. A section must have been started with
5193 27 Nov 09 nicklas 331     {@link #bfsPrintSection(String, boolean, boolean)} before this
5193 27 Nov 09 nicklas 332     method is invoked.
5193 27 Nov 09 nicklas 333     
5193 27 Nov 09 nicklas 334     @param key The entry key; null is not allowed and it may not
5193 27 Nov 09 nicklas 335       start with '[' or '#'
5193 27 Nov 09 nicklas 336     @param values An array of objects, each value will be be converted to
5193 27 Nov 09 nicklas 337       a string by calling the toString() method
5193 27 Nov 09 nicklas 338     @throws NullPointerException If the key is null
5193 27 Nov 09 nicklas 339     @throws IllegalArgumentException If the key starts with '[' or
5193 27 Nov 09 nicklas 340       '#', or if the key is not unique within a section that requires
5193 27 Nov 09 nicklas 341       unique keys
5193 27 Nov 09 nicklas 342     @throws IllegalStateException If no section has been started
5193 27 Nov 09 nicklas 343   */
6125 14 Sep 12 nicklas 344   @SuppressWarnings("unchecked")
5193 27 Nov 09 nicklas 345   public <T> void bfsPrintValue(String key, T... values)
5193 27 Nov 09 nicklas 346   {
5193 27 Nov 09 nicklas 347     bfsPrintValue(key, null, values);
5193 27 Nov 09 nicklas 348   }
5193 27 Nov 09 nicklas 349   
5193 27 Nov 09 nicklas 350   /**
5193 27 Nov 09 nicklas 351     Prints a section entry. A section must have been started with
5193 27 Nov 09 nicklas 352     {@link #bfsPrintSection(String, boolean, boolean)} before this
5193 27 Nov 09 nicklas 353     method is invoked.
5193 27 Nov 09 nicklas 354     
5193 27 Nov 09 nicklas 355     @param key The entry key; null is not allowed and it may not
5193 27 Nov 09 nicklas 356       start with '[' or '#'
5193 27 Nov 09 nicklas 357     @param formatter A formatter that should be used to convert the
5193 27 Nov 09 nicklas 358       values to strings, or null to simply use the toString() method
5193 27 Nov 09 nicklas 359     @param values An array of objects, each value will be encoded
5193 27 Nov 09 nicklas 360       independently and separated with a tab
5193 27 Nov 09 nicklas 361     @throws NullPointerException If the key is null
5193 27 Nov 09 nicklas 362     @throws IllegalArgumentException If the key starts with '[' or
5193 27 Nov 09 nicklas 363       '#', or if the key is not unique within a section that requires
5193 27 Nov 09 nicklas 364       unique keys
5193 27 Nov 09 nicklas 365     @throws IllegalStateException If no section has been started
5193 27 Nov 09 nicklas 366   */
6125 14 Sep 12 nicklas 367   @SuppressWarnings({"unchecked"})
5193 27 Nov 09 nicklas 368   public <T> void bfsPrintValue(String key, Formatter<T> formatter, T... values)
5193 27 Nov 09 nicklas 369   {
5193 27 Nov 09 nicklas 370     if (sectionCount == 0) throw new IllegalStateException("No section has been started");
5193 27 Nov 09 nicklas 371     if (key == null) throw new NullPointerException("key");
5193 27 Nov 09 nicklas 372     if (key.startsWith("[") || key.startsWith("#"))
5193 27 Nov 09 nicklas 373     {
5193 27 Nov 09 nicklas 374       throw new IllegalArgumentException("A key may not start with [ or #: '" + key + "'");
5193 27 Nov 09 nicklas 375     }
5193 27 Nov 09 nicklas 376     if (getUniqueKeys())
5193 27 Nov 09 nicklas 377     {
5193 27 Nov 09 nicklas 378       if (isUsedKey(key))
5193 27 Nov 09 nicklas 379       {
5193 27 Nov 09 nicklas 380         throw new IllegalArgumentException("Key '" + key + 
5193 27 Nov 09 nicklas 381           "' is not unique in section '" + getCurrentSection() + "'");
5193 27 Nov 09 nicklas 382       }
5193 27 Nov 09 nicklas 383     }
5193 27 Nov 09 nicklas 384     usedKeys.add(key);
5193 27 Nov 09 nicklas 385     
5193 27 Nov 09 nicklas 386     if (formatter == null) formatter = new ToStringFormatter<T>();
5193 27 Nov 09 nicklas 387     StringBuilder value = new StringBuilder();
5193 27 Nov 09 nicklas 388     if (values != null)
5193 27 Nov 09 nicklas 389     {
5193 27 Nov 09 nicklas 390       String sep = "";
5193 27 Nov 09 nicklas 391       for (T v : values)
5193 27 Nov 09 nicklas 392       {
5193 27 Nov 09 nicklas 393         value.append(sep);
5193 27 Nov 09 nicklas 394         value.append(encodeValue(formatter.format(v)));
5193 27 Nov 09 nicklas 395         sep = "\t";
5193 27 Nov 09 nicklas 396       }
5193 27 Nov 09 nicklas 397     }
5193 27 Nov 09 nicklas 398     internalPrintValue(encodeValue(key), value.toString());
5193 27 Nov 09 nicklas 399   }
5193 27 Nov 09 nicklas 400   
5193 27 Nov 09 nicklas 401   /**
5193 27 Nov 09 nicklas 402     Prints the beginning-of-file marker. Eg.
5193 27 Nov 09 nicklas 403     <code>BFSformat&lt;tab&gt;subtype</code>
5193 27 Nov 09 nicklas 404   */
5193 27 Nov 09 nicklas 405   protected void internalPrintBofMarker()
5193 27 Nov 09 nicklas 406   {
5193 27 Nov 09 nicklas 407     print(BOF_MARKER);
5193 27 Nov 09 nicklas 408     String subtype = getSubtype();
5193 27 Nov 09 nicklas 409     if (subtype != null) print("\t" + encodeValue(subtype));
5193 27 Nov 09 nicklas 410     print("\n");
5193 27 Nov 09 nicklas 411     lineCount++;
5193 27 Nov 09 nicklas 412   }
5193 27 Nov 09 nicklas 413   
5193 27 Nov 09 nicklas 414   /**
5193 27 Nov 09 nicklas 415     Print a single-line comment. This method should not be
5193 27 Nov 09 nicklas 416     called with comment string that is null or contain
5193 27 Nov 09 nicklas 417     line-breaks. Updates the line counter.
5193 27 Nov 09 nicklas 418   */
5193 27 Nov 09 nicklas 419   protected void internalPrintComment(String comment)
5193 27 Nov 09 nicklas 420   {
5193 27 Nov 09 nicklas 421     print("#" + comment + "\n");
5193 27 Nov 09 nicklas 422     lineCount++;
5193 27 Nov 09 nicklas 423   }
5193 27 Nov 09 nicklas 424   
5193 27 Nov 09 nicklas 425   /**
5193 27 Nov 09 nicklas 426     Prints a section header assuming that the name has already
5193 27 Nov 09 nicklas 427     been encoded. Updates line and section counters and resets
5193 27 Nov 09 nicklas 428     the value counter.
5193 27 Nov 09 nicklas 429   */
5193 27 Nov 09 nicklas 430   protected void internalPrintSection(String encodedName)
5193 27 Nov 09 nicklas 431   {
5193 27 Nov 09 nicklas 432     print("[" + encodedName + "]\n");
5193 27 Nov 09 nicklas 433     lineCount++;
5193 27 Nov 09 nicklas 434     sectionCount++;
5193 27 Nov 09 nicklas 435     valueCount = 0;
5193 27 Nov 09 nicklas 436   }
5193 27 Nov 09 nicklas 437   
5193 27 Nov 09 nicklas 438   /**
5193 27 Nov 09 nicklas 439     Prints a section entry assuming that the key and value has
5193 27 Nov 09 nicklas 440     already been encoded. Updates the line and value counters.
5193 27 Nov 09 nicklas 441   */
5193 27 Nov 09 nicklas 442   protected void internalPrintValue(String encodedKey, String encodedValue)
5193 27 Nov 09 nicklas 443   {
5193 27 Nov 09 nicklas 444     print(encodedKey + "\t" + encodedValue + "\n");
5193 27 Nov 09 nicklas 445     lineCount++;
5193 27 Nov 09 nicklas 446     valueCount++;
5193 27 Nov 09 nicklas 447   }
5193 27 Nov 09 nicklas 448
5193 27 Nov 09 nicklas 449 }