org.gdms.driver.dbf.DBFDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.gdms.driver.dbf.DBFDriver.java

Source

/**
 * The GDMS library (Generic Datasource Management System)
 * is a middleware dedicated to the management of various kinds of
 * data-sources such as spatial vectorial data or alphanumeric. Based
 * on the JTS library and conform to the OGC simple feature access
 * specifications, it provides a complete and robust API to manipulate
 * in a SQL way remote DBMS (PostgreSQL, H2...) or flat files (.shp,
 * .csv...).
 *
 * Gdms is distributed under GPL 3 license. It is produced by the "Atelier SIG"
 * team of the IRSTV Institute <http://www.irstv.fr/> CNRS FR 2488.
 *
 * Copyright (C) 2007-2012 IRSTV FR CNRS 2488
 *
 * This file is part of Gdms.
 *
 * Gdms is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * Gdms 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Gdms. If not, see <http://www.gnu.org/licenses/>.
 *
 * For more information, please consult: <http://www.orbisgis.org/>
 *
 * or contact directly:
 * info@orbisgis.org
 */
package org.gdms.driver.dbf;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.orbisgis.progress.ProgressMonitor;

import org.gdms.data.DataSourceFactory;
import org.gdms.data.schema.DefaultMetadata;
import org.gdms.data.schema.DefaultSchema;
import org.gdms.data.schema.Metadata;
import org.gdms.data.schema.Schema;
import org.gdms.data.types.Constraint;
import org.gdms.data.types.DefaultTypeDefinition;
import org.gdms.data.types.InvalidTypeException;
import org.gdms.data.types.LengthConstraint;
import org.gdms.data.types.PrecisionConstraint;
import org.gdms.data.types.Type;
import org.gdms.data.types.TypeDefinition;
import org.gdms.data.types.TypeFactory;
import org.gdms.data.values.Value;
import org.gdms.driver.AbstractDataSet;
import org.gdms.driver.DataSet;
import org.gdms.driver.DriverException;
import org.gdms.driver.FileReadWriteDriver;
import org.gdms.driver.driverManager.DriverManager;
import org.gdms.source.SourceManager;

public final class DBFDriver extends AbstractDataSet implements FileReadWriteDriver {

    public static final String STRING = "String";
    public static final String DOUBLE = "Double";
    public static final String INTEGER = "Integer";
    public static final String LONG = "Long";
    public static final String DATE = "Date";
    public static final String BOOLEAN = "Boolean";
    public static final String LENGTH = "Length";
    public static final String PRECISION = "Precision";
    public static final String DRIVER_NAME = "Dbase driver";
    private DbaseFileReader dbaseReader;
    private DataSourceFactory dataSourceFactory;
    private Schema schema;
    private DefaultMetadata metadata;
    private File file;
    private static final Logger LOG = Logger.getLogger(DBFDriver.class);

    @Override
    public void setDataSourceFactory(DataSourceFactory dsf) {
        this.dataSourceFactory = dsf;
    }

    @Override
    public void createSource(String path, Metadata metadata, DataSourceFactory dataSourceFactory)
            throws DriverException {
        LOG.trace("Creating source file");
        try {
            FileOutputStream fos = new FileOutputStream(new File(path));
            DbaseFileHeader header = getHeader(metadata, 0);
            DbaseFileWriter writer = new DbaseFileWriter(header, fos.getChannel());
            writer.close();
        } catch (IOException e) {
            throw new DriverException(e);
        } catch (DbaseFileException e) {
            throw new DriverException(e);
        }
    }

    @Override
    public void writeFile(File file, DataSet dataSource, ProgressMonitor pm) throws DriverException {
        writeFile(file, new DefaultRowProvider(dataSource), pm);
    }

    public void writeFile(File file, RowProvider dataSource, ProgressMonitor pm) throws DriverException {
        LOG.trace("Writing source file");
        try {
            FileOutputStream fos = new FileOutputStream(file);
            DbaseFileHeader header = getHeader(dataSource.getMetadata(), (int) dataSource.getRowCount());
            DbaseFileWriter writer = new DbaseFileWriter(header, fos.getChannel());
            final int numRecords = header.getNumRecords();
            pm.startTask("Writing file", numRecords);
            for (int i = 0; i < numRecords; i++) {
                if (i >= 100 && i % 100 == 0) {
                    if (pm.isCancelled()) {
                        break;
                    } else {
                        pm.progressTo(i);
                    }
                }
                writer.write(dataSource.getRow(i));
            }
            pm.progressTo(numRecords);
            writer.close();
        } catch (IOException e) {
            throw new DriverException(e);
        } catch (DbaseFileException e) {
            throw new DriverException(e);
        }
        pm.endTask();
    }

    private DbaseFileHeader getHeader(Metadata m, int rowCount) throws DriverException, DbaseFileException {
        DbaseFileHeader header = new DbaseFileHeader();
        for (int i = 0; i < m.getFieldCount(); i++) {
            String fieldName = m.getFieldName(i);
            Type gdmsType = m.getFieldType(i);
            DBFType type = getDBFType(gdmsType);
            header.addColumn(fieldName, type.type, type.fieldLength, type.decimalCount);
        }
        header.setNumRecords(rowCount);

        return header;
    }

    @Override
    public Schema getSchema() throws DriverException {
        return schema;
    }

    @Override
    public DataSet getTable(String name) {
        if (!name.equals(DriverManager.DEFAULT_SINGLE_TABLE_NAME)) {
            return null;
        }
        return this;
    }

    @Override
    public Value getFieldValue(long rowIndex, int fieldId) throws DriverException {
        try {
            return dbaseReader.getFieldValue((int) rowIndex, fieldId);
        } catch (IOException e) {
            throw new DriverException(e);
        }
    }

    @Override
    public long getRowCount() throws DriverException {
        return dbaseReader.getHeader().getNumRecords();
    }

    @Override
    public Number[] getScope(int dimension) throws DriverException {
        return null;
    }

    @Override
    public Metadata getMetadata() throws DriverException {
        return schema.getTableByName(DriverManager.DEFAULT_SINGLE_TABLE_NAME);
    }

    @Override
    public void setFile(File file) {
        this.file = file;

        // building schema and metadata
        schema = new DefaultSchema("DBF" + file.getAbsolutePath().hashCode());
        metadata = new DefaultMetadata();
        schema.addTable(DriverManager.DEFAULT_SINGLE_TABLE_NAME, metadata);
    }

    @Override
    public boolean isOpen() {
        return dbaseReader != null;
    }

    private static class DBFType {

        char type;
        int fieldLength;
        int decimalCount;

        DBFType(char type, int fieldLength, int decimalCount) {
            super();
            this.type = type;
            this.fieldLength = fieldLength;
            this.decimalCount = decimalCount;
        }
    }

    private DBFType getDBFType(Type fieldType) throws DriverException {
        Constraint lengthConstraint = fieldType.getConstraint(Constraint.LENGTH);
        int length = Integer.MAX_VALUE;
        if (lengthConstraint != null) {
            length = Integer.parseInt(lengthConstraint.getConstraintValue());
        }

        Constraint decimalCountConstraint = fieldType.getConstraint(Constraint.PRECISION);
        int decimalCount = Integer.MAX_VALUE;
        if (decimalCountConstraint != null) {
            decimalCount = Integer.parseInt(decimalCountConstraint.getConstraintValue());
        }

        switch (fieldType.getTypeCode()) {
        case Type.BOOLEAN:
            return new DBFType('l', 1, 0);
        case Type.BYTE:
            return new DBFType('n', Math.min(3, length), 0);
        case Type.DATE:
            return new DBFType('d', 8, 0);
        case Type.DOUBLE:
        case Type.FLOAT:
            return new DBFType('f', Math.min(20, length), Math.min(18, decimalCount));
        case Type.INT:
            return new DBFType('n', Math.min(10, length), 0);
        case Type.LONG:
            return new DBFType('n', Math.min(18, length), 0);
        case Type.SHORT:
            return new DBFType('n', Math.min(5, length), 0);
        case Type.STRING:
            return new DBFType('c', Math.min(254, length), 0);
        default:
            throw new DriverException(
                    "Cannot store " + TypeFactory.getTypeName(fieldType.getTypeCode()) + " in dbase");
        }
    }

    @Override
    public void copy(File in, File out) throws IOException {
        FileUtils.copyFile(in, out);
    }

    private void loadInternalMetadata() throws DriverException {
        metadata.clear();
        DbaseFileHeader header = dbaseReader.getHeader();

        for (int i = 0; i < header.getNumFields(); i++) {
            String fieldsName = header.getFieldName(i);
            final int type = getFieldType(i);
            Type fieldType;
            try {
                switch (type) {
                case Type.STRING:
                    fieldType = TypeFactory.createType(Type.STRING, STRING,
                            new LengthConstraint(header.getFieldLength(i)));
                    break;
                case Type.INT:
                    fieldType = TypeFactory.createType(Type.INT, INTEGER,
                            new LengthConstraint(header.getFieldLength(i)));
                    break;
                case Type.LONG:
                    fieldType = TypeFactory.createType(Type.LONG, LONG,
                            new LengthConstraint(header.getFieldLength(i)));
                    break;
                case Type.DOUBLE:
                    fieldType = TypeFactory.createType(Type.DOUBLE, DOUBLE,
                            new Constraint[] { new LengthConstraint(header.getFieldLength(i)),
                                    new PrecisionConstraint(header.getFieldDecimalCount(i)) });
                    break;
                case Type.BOOLEAN:
                    fieldType = TypeFactory.createType(Type.BOOLEAN, BOOLEAN);
                    break;
                case Type.DATE:
                    fieldType = TypeFactory.createType(Type.DATE, DATE);
                    break;
                default:
                    throw new DriverException("Unknown dbf driver type: " + type);
                }
            } catch (InvalidTypeException e) {
                throw new DriverException(e);
            }
            metadata.addField(fieldsName, fieldType);
        }
    }

    private int getFieldType(int i) throws DriverException {
        DbaseFileHeader header = dbaseReader.getHeader();
        char fieldType = header.getFieldType(i);

        switch (fieldType) {
        // (L)logical (T,t,F,f,Y,y,N,n)
        case 'l':
        case 'L':
            return Type.BOOLEAN;
        // (C)character (String)
        case 'c':
        case 'C':
            return Type.STRING;
        // (D)date (Date)
        case 'd':
        case 'D':
            return Type.DATE;
        // (F)floating (Double)
        case 'n':
        case 'N':
            if ((header.getFieldDecimalCount(i) == 0)) {
                if ((header.getFieldLength(i) >= 0) && (header.getFieldLength(i) < 10)) {
                    return Type.INT;
                } else {
                    return Type.LONG;
                }
            }
        case 'f':
        case 'F': // floating point number
            return Type.DOUBLE;
        default:
            throw new DriverException("Unknown field type");
        }
    }

    @Override
    public void open() throws DriverException {
        LOG.trace("Opening file");
        try {
            FileInputStream fis = new FileInputStream(file);
            dbaseReader = new DbaseFileReader(fis.getChannel());
            loadInternalMetadata();
        } catch (IOException e) {
            throw new DriverException(e);
        }
    }

    @Override
    public void close() throws DriverException {
        LOG.trace("Closing file");
        try {
            dbaseReader.close();
        } catch (IOException e) {
            throw new DriverException(e);
        } finally {
            dbaseReader = null;
        }
    }

    @Override
    public String getDriverId() {
        return DRIVER_NAME;
    }

    @Override
    public TypeDefinition[] getTypesDefinitions() {
        return new TypeDefinition[] {
                new DefaultTypeDefinition(STRING, Type.STRING, new int[] { Constraint.LENGTH }),
                new DefaultTypeDefinition(INTEGER, Type.INT, new int[] { Constraint.LENGTH }),
                new DefaultTypeDefinition(DOUBLE, Type.DOUBLE,
                        new int[] { Constraint.LENGTH, Constraint.PRECISION }),
                new DefaultTypeDefinition(BOOLEAN, Type.BOOLEAN), new DefaultTypeDefinition(DATE, Type.DATE) };
    }

    @Override
    public boolean isCommitable() {
        return true;
    }

    @Override
    public int getSupportedType() {
        return SourceManager.FILE;
    }

    @Override
    public int getType() {
        return SourceManager.FILE;
    }

    @Override
    public String validateMetadata(Metadata metadata) {
        return null;
    }

    @Override
    public String[] getFileExtensions() {
        return new String[] { "dbf" };
    }

    @Override
    public String getTypeDescription() {
        return "dBase file";
    }

    @Override
    public String getTypeName() {
        return "DBF";
    }
}