/** * AnalyzerBeans * Copyright (C) 2014 Neopost - Customer Information Management * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.eobjects.analyzer.connection; import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.util.List; import org.eobjects.analyzer.util.ReadObjectBuilder; import org.apache.metamodel.UpdateableDataContext; import org.apache.metamodel.csv.CsvConfiguration; import org.apache.metamodel.csv.CsvDataContext; import org.apache.metamodel.util.FileHelper; import org.apache.metamodel.util.FileResource; import org.apache.metamodel.util.Resource; import org.apache.metamodel.util.SerializableRef; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Datastore implementation for CSV files. */ public final class CsvDatastore extends UsageAwareDatastore<UpdateableDataContext> implements FileDatastore, ResourceDatastore, UpdateableDatastore { private static final long serialVersionUID = 1L; private static final Logger logger = LoggerFactory.getLogger(CsvDatastore.class); /** * The value is '\\uFFFF', the "not a character" value which should not * occur in any valid Unicode string. */ public static final char NOT_A_CHAR = '\uFFFF'; public static final char DEFAULT_QUOTE_CHAR = NOT_A_CHAR; public static final char DEFAULT_SEPARATOR_CHAR = CsvConfiguration.DEFAULT_SEPARATOR_CHAR; private final SerializableRef<Resource> _resourceRef; private final String _filename; private final Character _quoteChar; private final Character _separatorChar; private final Character _escapeChar; private final String _encoding; private final boolean _failOnInconsistencies; private final boolean _multilineValues; private final int _headerLineNumber; public CsvDatastore(String name, Resource resource) { this(name, resource, resource.getName(), CsvConfiguration.DEFAULT_QUOTE_CHAR, CsvConfiguration.DEFAULT_SEPARATOR_CHAR, CsvConfiguration.DEFAULT_ESCAPE_CHAR, FileHelper.DEFAULT_ENCODING, true, CsvConfiguration.DEFAULT_COLUMN_NAME_LINE); } public CsvDatastore(String name, String filename) { this(name, filename, CsvConfiguration.DEFAULT_QUOTE_CHAR, CsvConfiguration.DEFAULT_SEPARATOR_CHAR, FileHelper.DEFAULT_ENCODING); } public CsvDatastore(String name, String filename, Character quoteChar, Character separatorChar, String encoding) { this(name, filename, quoteChar, separatorChar, encoding, true); } public CsvDatastore(String name, String filename, Character quoteChar, Character separatorChar, String encoding, boolean failOnInconsistencies) { this(name, filename, quoteChar, separatorChar, encoding, failOnInconsistencies, CsvConfiguration.DEFAULT_COLUMN_NAME_LINE); } public CsvDatastore(String name, String filename, Character quoteChar, Character separatorChar, String encoding, boolean failOnInconsistencies, int headerLineNumber) { this(name, null, filename, quoteChar, separatorChar, CsvConfiguration.DEFAULT_ESCAPE_CHAR, encoding, failOnInconsistencies, headerLineNumber); } public CsvDatastore(String name, Resource resource, CsvConfiguration c) { this(name, resource, resource.getName(), c.getQuoteChar(), c.getSeparatorChar(), c.getEscapeChar(), c .getEncoding(), c.isFailOnInconsistentRowLength(), c.isMultilineValues(), c.getColumnNameLineNumber()); } public CsvDatastore(String name, Resource resource, String filename, Character quoteChar, Character separatorChar, Character escapeChar, String encoding, boolean failOnInconsistencies, int headerLineNumber) { this(name, resource, filename, quoteChar, separatorChar, escapeChar, encoding, failOnInconsistencies, true, headerLineNumber); } public CsvDatastore(String name, Resource resource, String filename, Character quoteChar, Character separatorChar, Character escapeChar, String encoding, boolean failOnInconsistencies, boolean multilineValues, int headerLineNumber) { super(name); _filename = filename; if (resource == null) { resource = new FileResource(filename); } _resourceRef = new SerializableRef<Resource>(resource); _quoteChar = quoteChar; _separatorChar = separatorChar; _escapeChar = escapeChar; _encoding = encoding; _failOnInconsistencies = failOnInconsistencies; _multilineValues = multilineValues; if (headerLineNumber < 0) { headerLineNumber = CsvConfiguration.NO_COLUMN_NAME_LINE; } _headerLineNumber = headerLineNumber; } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { ReadObjectBuilder.create(this, CsvDatastore.class).readObject(stream); } public String getEncoding() { return _encoding; } @Override public String getFilename() { return _filename; } public Character getQuoteChar() { return _quoteChar; } public Character getEscapeChar() { return _escapeChar; } public Character getSeparatorChar() { return _separatorChar; } @Override public Resource getResource() { if (_resourceRef == null) { return null; } return _resourceRef.get(); } @Override protected UsageAwareDatastoreConnection<UpdateableDataContext> createDatastoreConnection() { final UpdateableDataContext dataContext; final Resource resource = getResource(); if (resource == null) { logger.warn("Resource was not available, a local file reference will be created with path: {}", _filename); dataContext = new CsvDataContext(new File(_filename), getCsvConfiguration()); } else { dataContext = new CsvDataContext(resource, getCsvConfiguration()); } return new UpdateableDatastoreConnectionImpl<UpdateableDataContext>(dataContext, this); } public CsvConfiguration getCsvConfiguration() { final char separatorChar = _separatorChar == null ? DEFAULT_SEPARATOR_CHAR : _separatorChar; final char quoteChar = _quoteChar == null ? DEFAULT_QUOTE_CHAR : _quoteChar; final char escapeChar = _escapeChar == null ? CsvConfiguration.DEFAULT_ESCAPE_CHAR : _escapeChar; final String encoding = _encoding == null ? FileHelper.UTF_8_ENCODING : _encoding; final CsvConfiguration configuration = new CsvConfiguration(_headerLineNumber, encoding, separatorChar, quoteChar, escapeChar, _failOnInconsistencies, _multilineValues); return configuration; } @Override public UpdateableDatastoreConnection openConnection() { DatastoreConnection connection = super.openConnection(); return (UpdateableDatastoreConnection) connection; } @Override public PerformanceCharacteristics getPerformanceCharacteristics() { return new PerformanceCharacteristicsImpl(false, true); } public boolean isFailOnInconsistencies() { return _failOnInconsistencies; } public boolean isMultilineValues() { return _multilineValues; } public int getHeaderLineNumber() { return _headerLineNumber; } @Override protected void decorateIdentity(List<Object> identifiers) { super.decorateIdentity(identifiers); identifiers.add(_filename); identifiers.add(_encoding); identifiers.add(_quoteChar); identifiers.add(_escapeChar); identifiers.add(_separatorChar); identifiers.add(_failOnInconsistencies); identifiers.add(_multilineValues); identifiers.add(_headerLineNumber); } @Override public String toString() { return "CsvDatastore[name=" + getName() + ", filename=" + _filename + ", quoteChar='" + _quoteChar + "', separatorChar='" + _separatorChar + "', encoding=" + _encoding + ", headerLineNumber=" + _headerLineNumber + "]"; } }