3030
3131package org .scijava .table .io ;
3232
33- import java .io .File ;
3433import java .io .IOException ;
3534import java .util .ArrayList ;
3635import java .util .Arrays ;
4645import org .scijava .io .AbstractIOPlugin ;
4746import org .scijava .io .handle .DataHandle ;
4847import org .scijava .io .handle .DataHandleService ;
49- import org .scijava .io .location .FileLocation ;
5048import org .scijava .io .location .Location ;
49+ import org .scijava .log .LogService ;
5150import org .scijava .plugin .Parameter ;
5251import org .scijava .plugin .Plugin ;
5352import org .scijava .table .DefaultGenericTable ;
5453import org .scijava .table .GenericTable ;
5554import org .scijava .table .Table ;
56- import org .scijava .util .FileUtils ;
5755
5856/**
5957 * Plugin for reading/writing {@link Table}s.
6058 *
6159 * @author Leon Yang
60+ * @author Curtis Rueden
6261 */
6362@ SuppressWarnings ("rawtypes" )
6463@ Plugin (type = TableIOPlugin .class , priority = Priority .LOW )
@@ -69,96 +68,55 @@ public class DefaultTableIOPlugin extends AbstractIOPlugin<Table> implements
6968 @ Parameter
7069 private DataHandleService dataHandleService ;
7170
71+ @ Parameter
72+ private LogService log ;
73+
7274 // FIXME: The "txt" extension is extremely general and will conflict with
7375 // other plugins. Consider another way to check supportsOpen/Close.
7476 private static final Set <String > SUPPORTED_EXTENSIONS = //
7577 Collections .unmodifiableSet (new HashSet <>(//
7678 Arrays .asList ("csv" , "txt" , "prn" , "dif" , "rtf" )));
7779
80+ // -- TableIOPlugin methods --
81+
7882 @ Override
79- public boolean supportsOpen (final Location source ) {
80- if (!(source instanceof FileLocation )) return false ;
81- final File file = ((FileLocation ) source ).getFile ();
82- // NB: For opening, the file must exist or it's not readable.
83- return file .exists () && supportsFile (file );
83+ public GenericTable open (final Location source , final TableIOOptions options )
84+ throws IOException
85+ {
86+ return open (source , options .values );
8487 }
8588
8689 @ Override
87- public boolean supportsSave (final Location source ) {
88- if (!(source instanceof FileLocation )) return false ;
89- final File file = ((FileLocation ) source ).getFile ();
90- // NB: For saving, it's OK if the file does not exist yet.
91- return supportsFile (file );
90+ public void save (final Table table , final Location destination ,
91+ final TableIOOptions options ) throws IOException
92+ {
93+ save (table , destination , options .values );
9294 }
9395
94- /**
95- * Process a given line into a list of tokens.
96- */
97- private ArrayList <String > processRow (final String line , final char separator ,
98- final char quote ) throws IOException
99- {
100- final ArrayList <String > row = new ArrayList <>();
101- final StringBuilder sb = new StringBuilder ();
102- int idx = 0 ;
103- int start = idx ;
104- while (idx < line .length ()) {
105- if (line .charAt (idx ) == quote ) {
106- sb .append (line , start , idx );
107- boolean quoted = true ;
108- idx ++;
109- start = idx ;
110- // find quoted string
111- while (idx < line .length ()) {
112- if (line .charAt (idx ) == quote ) {
113- sb .append (line , start , idx );
114- if (idx + 1 < line .length () && line .charAt (idx + 1 ) == quote ) {
115- sb .append (quote );
116- idx += 2 ;
117- start = idx ;
118- }
119- else {
120- idx ++;
121- start = idx ;
122- quoted = false ;
123- break ;
124- }
125- }
126- else {
127- idx ++;
128- }
129- }
130- if (quoted ) {
131- throw new IOException (String .format (
132- "Unbalanced quote at position %d: %s" , idx , line ));
133- }
134- }
135- else if (line .charAt (idx ) == separator ) {
136- sb .append (line , start , idx );
137- row .add (sb .toString ());
138- sb .setLength (0 );
139- idx ++;
140- start = idx ;
141- }
142- else {
143- idx ++;
144- }
96+ // -- IOPlugin methods --
97+
98+ @ Override
99+ public boolean supportsOpen (final Location source ) {
100+ if (!supportsLocation (source )) return false ;
101+ try {
102+ return dataHandleService .exists (source );
103+ }
104+ catch (IOException exc ) {
105+ log .debug (exc );
106+ return false ;
145107 }
146- sb .append (line , start , idx );
147- row .add (sb .toString ());
148- return row ;
149108 }
150109
151110 @ Override
152- public GenericTable open (final Location source , final TableIOOptions options )
153- throws IOException
154- {
155- return open (source , options .values );
111+ public boolean supportsSave (final Location source ) {
112+ return supportsLocation (source );
156113 }
157114
115+ // -- Helper methods --
116+
158117 private GenericTable open (final Location source ,
159118 final TableIOOptions .Values options ) throws IOException
160119 {
161-
162120 final GenericTable table = new DefaultGenericTable ();
163121
164122 try (final DataHandle <? extends Location > handle = //
@@ -240,46 +198,9 @@ private GenericTable open(final Location source,
240198 return table ;
241199 }
242200
243- private static Function <String , ?> getParser (final String content ,
244- final int column , final TableIOOptions .Values options )
245- {
246- final ColumnTableIOOptions .Values colOptions = options .column (column );
247- if (colOptions != null ) return colOptions .parser ();
248- if (options .guessParser ()) return guessParser (content );
249- return options .parser ();
250- }
251-
252- static Function <String , ?> guessParser (final String content ) {
253- try {
254- final Function <String , ?> function = s -> Double .valueOf (s .replace (
255- "infinity" , "Infinity" ).replace ("Nan" , "NaN" ));
256- function .apply (content );
257- return function ;
258- }
259- catch (final NumberFormatException ignored ) {}
260- if (content .equalsIgnoreCase ("true" ) || content .equalsIgnoreCase ("false" )) {
261- return Boolean ::valueOf ;
262- }
263- return String ::valueOf ;
264- }
265-
266- @ Override
267- public void save (final Table table , final Location destination ,
268- final TableIOOptions options ) throws IOException
269- {
270- save (table , destination , options .values );
271- }
272-
273- private boolean supportsFile (final File file ) {
274- if (file .isDirectory ()) return false ;
275- final String ext = FileUtils .getExtension (file ).toLowerCase ();
276- return SUPPORTED_EXTENSIONS .contains (ext );
277- }
278-
279201 private void save (final Table table , final Location destination ,
280202 final TableIOOptions .Values options ) throws IOException
281203 {
282-
283204 try (final DataHandle <Location > handle = //
284205 dataHandleService .create (destination ))
285206 {
@@ -338,10 +259,84 @@ else if (table.getColumnCount() > 0) {
338259 sb .setLength (0 );
339260 }
340261 }
262+ }
341263
264+ private static boolean supportsLocation (Location location ) {
265+ final String name = location .getName ();
266+ if (name == null ) return false ;
267+ final int dot = name .lastIndexOf ('.' );
268+ if (dot < 0 ) return false ;
269+ String extension = name .substring (dot + 1 ).toLowerCase ();
270+ return SUPPORTED_EXTENSIONS .contains (extension );
342271 }
343272
344- private Function <Object , String > getFormatter (
273+ /**
274+ * Process a given line into a list of tokens.
275+ */
276+ private static ArrayList <String > processRow (final String line , final char separator ,
277+ final char quote ) throws IOException
278+ {
279+ final ArrayList <String > row = new ArrayList <>();
280+ final StringBuilder sb = new StringBuilder ();
281+ int idx = 0 ;
282+ int start = idx ;
283+ while (idx < line .length ()) {
284+ if (line .charAt (idx ) == quote ) {
285+ sb .append (line , start , idx );
286+ boolean quoted = true ;
287+ idx ++;
288+ start = idx ;
289+ // find quoted string
290+ while (idx < line .length ()) {
291+ if (line .charAt (idx ) == quote ) {
292+ sb .append (line , start , idx );
293+ if (idx + 1 < line .length () && line .charAt (idx + 1 ) == quote ) {
294+ sb .append (quote );
295+ idx += 2 ;
296+ start = idx ;
297+ }
298+ else {
299+ idx ++;
300+ start = idx ;
301+ quoted = false ;
302+ break ;
303+ }
304+ }
305+ else {
306+ idx ++;
307+ }
308+ }
309+ if (quoted ) {
310+ throw new IOException (String .format (
311+ "Unbalanced quote at position %d: %s" , idx , line ));
312+ }
313+ }
314+ else if (line .charAt (idx ) == separator ) {
315+ sb .append (line , start , idx );
316+ row .add (sb .toString ());
317+ sb .setLength (0 );
318+ idx ++;
319+ start = idx ;
320+ }
321+ else {
322+ idx ++;
323+ }
324+ }
325+ sb .append (line , start , idx );
326+ row .add (sb .toString ());
327+ return row ;
328+ }
329+
330+ private static Function <String , ?> getParser (final String content ,
331+ final int column , final TableIOOptions .Values options )
332+ {
333+ final ColumnTableIOOptions .Values colOptions = options .column (column );
334+ if (colOptions != null ) return colOptions .parser ();
335+ if (options .guessParser ()) return guessParser (content );
336+ return options .parser ();
337+ }
338+
339+ private static Function <Object , String > getFormatter (
345340 final TableIOOptions .Values options , final int i )
346341 {
347342 final ColumnTableIOOptions .Values columnOptions = options .column (i );
@@ -358,7 +353,7 @@ private Function<Object, String> getFormatter(
358353 * @param str string to quote
359354 * @return string, possibly quoted
360355 */
361- private String tryQuote (final String str , final char separator ,
356+ private static String tryQuote (final String str , final char separator ,
362357 final char quote )
363358 {
364359 if (str == null || str .length () == 0 ) return "" + quote + quote ;
@@ -368,4 +363,18 @@ private String tryQuote(final String str, final char separator,
368363 if (str .indexOf (separator ) != -1 ) return quote + str + quote ;
369364 return str ;
370365 }
366+
367+ static Function <String , ?> guessParser (final String content ) {
368+ try {
369+ final Function <String , ?> function = s -> Double .valueOf (s .replace (
370+ "infinity" , "Infinity" ).replace ("Nan" , "NaN" ));
371+ function .apply (content );
372+ return function ;
373+ }
374+ catch (final NumberFormatException ignored ) {}
375+ if (content .equalsIgnoreCase ("true" ) || content .equalsIgnoreCase ("false" )) {
376+ return Boolean ::valueOf ;
377+ }
378+ return String ::valueOf ;
379+ }
371380}
0 commit comments