All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eobjects.metamodel.excel.ExcelDataContext Maven / Gradle / Ivy

There is a newer version: 5.3.6
Show newest version
/**
 * eobjects.org MetaModel
 * Copyright (C) 2010 eobjects.org
 *
 * 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.metamodel.excel;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;

import org.apache.poi.POIXMLDocument;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.QueryPostprocessDataContext;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.MutableSchema;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.util.FileHelper;
import org.eobjects.metamodel.util.LazyRef;
import org.eobjects.metamodel.util.Ref;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link DataContext} implementation to use for Excel spreadsheets.
 * 
 * This DataContext supports both the "old" .xls format and the "new" .xlsx
 * format, and saves the user the trouble of figuring out which one to use,
 * simply by detecting it at runtime and delegating to the appropriate
 * implementation.
 */
public final class ExcelDataContext extends QueryPostprocessDataContext implements UpdateableDataContext {

    private static final Logger logger = LoggerFactory.getLogger(ExcelDataContext.class);

    private final Object WRITE_LOCK = new Object();

    private final File _file;
    private final ExcelConfiguration _configuration;
    private SpreadsheetReaderDelegate _spreadsheetReaderDelegate;

    /**
     * Constructs an Excel DataContext based on a file, with default
     * configuration
     * 
     * @param file
     */
    public ExcelDataContext(File file) {
        this(file, new ExcelConfiguration());
    }

    /**
     * Constructs an Excel DataContext based on a file and a custom
     * configuration.
     * 
     * @param file
     * @param configuration
     */
    public ExcelDataContext(File file, ExcelConfiguration configuration) {
        if (file == null) {
            throw new IllegalArgumentException("File cannot be null");
        }
        if (configuration == null) {
            throw new IllegalArgumentException("ExcelConfiguration cannot be null");
        }
        if (file.exists() && !file.canRead()) {
            throw new IllegalArgumentException("Cannot read from file");
        }
        _file = file;
        _configuration = configuration;
    }

    /**
     * Gets the Excel configuration used.
     * 
     * @return an excel configuration.
     */
    public ExcelConfiguration getConfiguration() {
        return _configuration;
    }

    /**
     * Gets the Excel file being read.
     * 
     * @return a file.
     */
    public File getFile() {
        return _file;
    }

    @Override
    protected String getMainSchemaName() throws MetaModelException {
        return _file.getName();
    }

    @Override
    public DataSet materializeMainSchemaTable(Table table, Column[] columns, int maxRows) {

        Ref inputStreamRef = getInputStreamRef();
        InputStream inputStream = null;
        try {
            SpreadsheetReaderDelegate delegate = getSpreadsheetReaderDelegate(inputStreamRef);
            inputStream = inputStreamRef.get();
            DataSet dataSet = delegate.executeQuery(inputStream, table, columns, maxRows);
            return dataSet;
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new MetaModelException("Unexpected exception while materializing main schema table", e);
        } finally {
            FileHelper.safeClose(inputStream);
        }
    }

    @Override
    protected Schema getMainSchema() throws MetaModelException {
        if (!_file.exists()) {
            logger.info("File does not exist, returning empty schema");
            return new MutableSchema(getMainSchemaName());
        }
        Ref inputStreamRef = getInputStreamRef();
        InputStream inputStream = null;
        try {
            SpreadsheetReaderDelegate delegate = getSpreadsheetReaderDelegate(inputStreamRef);
            inputStream = inputStreamRef.get();
            Schema schema = delegate.createSchema(inputStream, getMainSchemaName());
            assert getMainSchemaName().equals(schema.getName());
            return schema;
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new MetaModelException("Unexpected exception while building main schema", e);
        } finally {
            FileHelper.safeClose(inputStream);
        }
    }

    /**
     * Convenient method for testing and inspecting internal state.
     * 
     * @return the class of the spreadsheet reader delegate.
     */
    protected Class getSpreadsheetReaderDelegateClass() {
        if (_spreadsheetReaderDelegate != null) {
            return _spreadsheetReaderDelegate.getClass();
        }
        return null;
    }

    private SpreadsheetReaderDelegate getSpreadsheetReaderDelegate(Ref inputStream)
            throws MetaModelException {
        if (_spreadsheetReaderDelegate == null) {
            synchronized (this) {
                if (_spreadsheetReaderDelegate == null) {
                    try {
                        if (POIXMLDocument.hasOOXMLHeader(inputStream.get())) {
                            _spreadsheetReaderDelegate = new XlsxSpreadsheetReaderDelegate(_configuration);
                        } else {
                            _spreadsheetReaderDelegate = new DefaultSpreadsheetReaderDelegate(_configuration);
                        }
                    } catch (IOException e) {
                        logger.error("Could not identify spreadsheet type, using default", e);
                        _spreadsheetReaderDelegate = new DefaultSpreadsheetReaderDelegate(_configuration);
                    }
                }
            }
        }
        return _spreadsheetReaderDelegate;
    }

    private InputStream getInputStream() throws MetaModelException {
        try {
            InputStream inputStream = new FileInputStream(_file);
            if (!inputStream.markSupported()) {
                inputStream = new PushbackInputStream(inputStream, 8);
            }
            return inputStream;
        } catch (FileNotFoundException e) {
            throw new MetaModelException("Spreadsheet file was not found", e);
        }
    }

    private LazyRef getInputStreamRef() throws MetaModelException {
        final LazyRef inputStreamRef = new LazyRef() {
            @Override
            public InputStream fetch() {
                InputStream inputStream = getInputStream();
                return inputStream;
            }
        };
        return inputStreamRef;
    }

    protected void notifyTablesModified() {
        LazyRef inputStreamRef = getInputStreamRef();
        try {
            getSpreadsheetReaderDelegate(inputStreamRef).notifyTablesModified(inputStreamRef);
        } finally {
            if (inputStreamRef.isFetched()) {
                FileHelper.safeClose(inputStreamRef.get());
            }
        }
    }

    @Override
    public void executeUpdate(UpdateScript update) {
        ExcelUpdateCallback updateCallback = new ExcelUpdateCallback(this);
        synchronized (WRITE_LOCK) {
            try {
                update.run(updateCallback);
            } finally {
                updateCallback.close();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy