/*
* Copyright (c) 2016, Metron, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Metron, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.metsci.glimpse.dnc.facc;
import static com.google.common.base.Objects.equal;
import static com.metsci.glimpse.util.io.StreamOpener.resource;
import static java.lang.Integer.parseInt;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FaccIo
{
public static final String faccPath = "facc/";
public static final String faccFeaturesFile = "facc-features.csv";
public static final String faccAttrsFile = "facc-attrs.csv";
public static final String faccAttrValsFile = "facc-attr-vals.csv";
public static Reader faccReader( String filename ) throws IOException
{
return new BufferedReader( new InputStreamReader( resource.openForRead( faccPath + filename ) ) );
}
public static Map<String,FaccFeature> readFaccFeatures( ) throws IOException
{
try ( Reader reader = faccReader( faccFeaturesFile ) )
{
return readFaccFeatures( reader );
}
}
public static Map<String,FaccFeature> readFaccFeatures( Reader reader ) throws IOException
{
List<List<String>> rows = parseFaccCsvFile( reader );
// Skip header row
rows = rows.subList( 1, rows.size( ) );
Map<String,FaccFeature> features = new HashMap<>( );
for ( List<String> tokens : rows )
{
String fcode = tokens.get( 0 ).toUpperCase( );
String name = tokens.get( 1 );
String text = tokens.get( 2 );
features.put( fcode, new FaccFeature( fcode, name, text ) );
}
return features;
}
public static Map<String,FaccAttr> readFaccAttrs( ) throws IOException
{
Map<String,Map<Object,Object>> valueLookups = readFaccValues( );
try ( Reader reader = faccReader( faccAttrsFile ) )
{
return readFaccAttrs( reader, valueLookups );
}
}
public static Map<String,FaccAttr> readFaccAttrs( Reader reader, Map<String,Map<Object,Object>> valueLookups ) throws IOException
{
List<List<String>> rows = parseFaccCsvFile( reader );
// Skip header row
rows = rows.subList( 1, rows.size( ) );
Map<String,FaccAttr> attrs = new HashMap<>( );
for ( List<String> tokens : rows )
{
String code = tokens.get( 0 ).toLowerCase( );
String name = tokens.get( 12 );
String text = tokens.get( 13 );
boolean hasCodedValues = tokens.get( 14 ).equalsIgnoreCase( "Coded" );
Map<Object,Object> valueLookup = ( hasCodedValues ? valueLookups.get( code ) : null );
attrs.put( code, new FaccAttr( code, name, text, valueLookup ) );
}
return attrs;
}
public static Map<String,Map<Object,Object>> readFaccValues( ) throws IOException
{
try ( Reader reader = faccReader( faccAttrValsFile ) )
{
return readFaccValues( reader );
}
}
public static Map<String,Map<Object,Object>> readFaccValues( Reader reader ) throws IOException
{
List<List<String>> rows = parseFaccCsvFile( reader );
// Skip header row
rows = rows.subList( 1, rows.size( ) );
Map<String,Map<Object,Object>> valueLookups = new HashMap<>( );
for ( List<String> tokens : rows )
{
String attr = tokens.get( 0 ).toLowerCase( );
Integer valueCode = parseInt( tokens.get( 1 ) );
String valueText = tokens.get( 2 );
Map<Object,Object> valueLookup = valueLookups.computeIfAbsent( attr, ( k ) -> new HashMap<>( ) );
valueLookup.put( valueCode, valueText );
}
return valueLookups;
}
/**
* Parses a FACC CSV file, honoring newlines inside quotes
*/
public static List<List<String>> parseFaccCsvFile( Reader reader ) throws IOException
{
List<List<String>> rows = new ArrayList<>( );
List<String> row = new ArrayList<>( );
StringBuilder token = new StringBuilder( );
Character c = readChar( reader );
boolean quoted = false;
while ( c != null )
{
Character cNext = readChar( reader );
// Collapse EOL to a single char
if ( ( equal( c, '\n' ) && equal( cNext, '\r' ) ) || ( equal( c, '\r' ) && equal( cNext, '\n' ) ) )
{
c = '\n';
cNext = readChar( reader );
}
if ( quoted )
{
if ( equal( c, '"' ) && !equal( cNext, '"' ) )
{
// Lone quote char (not two in a row) -- end quoted region
quoted = false;
}
else if ( equal( c, '"' ) && equal( cNext, '"' ) )
{
// Two quote chars in a row -- skip the second one
cNext = readChar( reader );
token.append( c );
}
else
{
token.append( c );
}
}
else
{
if ( equal( c, '\n' ) || equal( c, '\r' ) )
{
// Unquoted newline -- end both token and row
row.add( token.toString( ).trim( ) );
token.setLength( 0 );
rows.add( row );
row = new ArrayList<>( );
}
else if ( equal( c, ',' ) )
{
// Unquoted delimiter -- end token
row.add( token.toString( ).trim( ) );
token.setLength( 0 );
}
else if ( equal( c, '"' ) )
{
quoted = true;
}
else
{
token.append( c );
}
}
c = cNext;
}
// EOF
if ( token.length( ) > 0 )
{
row.add( token.toString( ) );
}
if ( !row.isEmpty( ) )
{
rows.add( row );
}
return rows;
}
protected static Character readChar( Reader reader ) throws IOException
{
int v = reader.read( );
if ( v == -1 )
{
return null;
}
else
{
return ( char ) v;
}
}
}