4925 |
08 May 09 |
nicklas |
1 |
/** |
4925 |
08 May 09 |
nicklas |
$Id$ |
4925 |
08 May 09 |
nicklas |
3 |
|
4925 |
08 May 09 |
nicklas |
Copyright (C) 2009 Nicklas Nordborg |
4925 |
08 May 09 |
nicklas |
5 |
|
4925 |
08 May 09 |
nicklas |
This file is part of BASE - BioArray Software Environment. |
4925 |
08 May 09 |
nicklas |
Available at http://base.thep.lu.se/ |
4925 |
08 May 09 |
nicklas |
8 |
|
4925 |
08 May 09 |
nicklas |
BASE is free software; you can redistribute it and/or |
4925 |
08 May 09 |
nicklas |
modify it under the terms of the GNU General Public License |
4925 |
08 May 09 |
nicklas |
as published by the Free Software Foundation; either version 3 |
4925 |
08 May 09 |
nicklas |
of the License, or (at your option) any later version. |
4925 |
08 May 09 |
nicklas |
13 |
|
4925 |
08 May 09 |
nicklas |
BASE is distributed in the hope that it will be useful, |
4925 |
08 May 09 |
nicklas |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
4925 |
08 May 09 |
nicklas |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4925 |
08 May 09 |
nicklas |
GNU General Public License for more details. |
4925 |
08 May 09 |
nicklas |
18 |
|
4925 |
08 May 09 |
nicklas |
You should have received a copy of the GNU General Public License |
4925 |
08 May 09 |
nicklas |
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 |
Wraps a writer stream and provides methods for easier |
4925 |
08 May 09 |
nicklas |
creation of BASEfiles. All methods that start with |
4925 |
08 May 09 |
nicklas |
<code>base</code> are used for BASEfile specified formatting. |
4925 |
08 May 09 |
nicklas |
It is also possible to use the regular {@link PrintWriter} |
4925 |
08 May 09 |
nicklas |
methods for special cases. |
4925 |
08 May 09 |
nicklas |
<p> |
4925 |
08 May 09 |
nicklas |
A BASEfile is divided in sections. Each section has |
4925 |
08 May 09 |
nicklas |
a header part and a data part. To start a new section |
4925 |
08 May 09 |
nicklas |
use {@link #baseBeginSection(String)}. When a section has been |
4925 |
08 May 09 |
nicklas |
started use {@link #basePrintHeader(String, Object)} or one |
4925 |
08 May 09 |
nicklas |
of the overloaded methods to print headers for that section. |
4925 |
08 May 09 |
nicklas |
To end the header part and start the data part, use |
4925 |
08 May 09 |
nicklas |
{@link #baseBeginDataPart()} and then {@link #basePrintData(Object...)} |
4925 |
08 May 09 |
nicklas |
to print data lines. When all data has been printed |
4925 |
08 May 09 |
nicklas |
either start a new section or end the current section |
4925 |
08 May 09 |
nicklas |
with {@link #baseEndSection()}. |
4925 |
08 May 09 |
nicklas |
48 |
|
4925 |
08 May 09 |
nicklas |
@author Nicklas |
4925 |
08 May 09 |
nicklas |
@version 2.12 |
4925 |
08 May 09 |
nicklas |
@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 |
Create a new BASEfile writer that is writing it's output to the |
4925 |
08 May 09 |
nicklas |
given writer. The file prefix 'BASEfile' is automatically written. |
4925 |
08 May 09 |
nicklas |
@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 |
@return The name of the current section, or null if no |
4925 |
08 May 09 |
nicklas |
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 |
@return TRUE if this writer is currently in the header part of a section, |
4925 |
08 May 09 |
nicklas |
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 |
@return TRUE if this writer is currently in the data part of a section, |
4925 |
08 May 09 |
nicklas |
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 |
The metadata prefix is written to the beginning of |
4925 |
08 May 09 |
nicklas |
each line that is not a data line. The default value |
4925 |
08 May 09 |
nicklas |
is an empty string. |
4925 |
08 May 09 |
nicklas |
<p> |
4925 |
08 May 09 |
nicklas |
Tip! Settting this value to '#' makes it possible to send |
4925 |
08 May 09 |
nicklas |
a BASEfile to gnuplot. |
4925 |
08 May 09 |
nicklas |
@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 |
Change the metadata prefix string. |
4925 |
08 May 09 |
nicklas |
@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 |
Write the beginning-of-file marker (BASEfile) to the output. |
4925 |
08 May 09 |
nicklas |
This method must be called before it is possible to writer other |
4925 |
08 May 09 |
nicklas |
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 |
Start a new section in the BASEfile. If the current |
4925 |
08 May 09 |
nicklas |
section hasn't been ended, {@link #baseEndSection()} |
4925 |
08 May 09 |
nicklas |
is automatically called to end it. After this call the |
4925 |
08 May 09 |
nicklas |
BASEfile is ready for writing headers to the new section. |
4925 |
08 May 09 |
nicklas |
<p> |
4925 |
08 May 09 |
nicklas |
If the beginning-of-file-marker hasn't been written the |
4925 |
08 May 09 |
nicklas |
{@link #baseWriteBom()} is automatically called. |
4925 |
08 May 09 |
nicklas |
148 |
|
4925 |
08 May 09 |
nicklas |
@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 |
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 |
Prints a header in the BASEfile. This call is ignored if |
4925 |
08 May 09 |
nicklas |
no section has been started or if the current section is |
4930 |
14 May 09 |
nicklas |
currently in data writing mode. Line breaks (\n) and |
4930 |
14 May 09 |
nicklas |
carriage returns (\r) in the value are converted to |
4930 |
14 May 09 |
nicklas |
the literal string values '\n' and '\r'. |
4925 |
08 May 09 |
nicklas |
@param key The header key, 'section' is not allowed |
4925 |
08 May 09 |
nicklas |
@param value The value, if null the configured null value |
4925 |
08 May 09 |
nicklas |
is used, otherwise the toString() method is called |
4925 |
08 May 09 |
nicklas |
@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 |
End the header part of the current section (by writing a single % on a line |
4925 |
08 May 09 |
nicklas |
by itself) and start the data part. The call to this method is ignored if |
4925 |
08 May 09 |
nicklas |
no section has been started or if the data part of the current section |
4925 |
08 May 09 |
nicklas |
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 |
Print a data line to the BASEfile. Each value is separated by the |
4925 |
08 May 09 |
nicklas |
data separator string ('tab' by default). Null values are replaced |
4925 |
08 May 09 |
nicklas |
with the null value string (empty string by default). Non-null objects |
4925 |
08 May 09 |
nicklas |
are converted to strings values by calling their toString() method. |
4925 |
08 May 09 |
nicklas |
<p> |
4925 |
08 May 09 |
nicklas |
This call is ignored if the data part has not been started. |
4925 |
08 May 09 |
nicklas |
@param data The values to write |
4925 |
08 May 09 |
nicklas |
@see #isWritingData() |
4925 |
08 May 09 |
nicklas |
@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 |
Escape new line and carrige return characters in the input |
4930 |
14 May 09 |
nicklas |
to the literal string '\n' and '\r' in the output. |
4930 |
14 May 09 |
nicklas |
@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 |
} |