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

com.googlecode.paradox.data.TableData Maven / Gradle / Ivy

There is a newer version: 1.6.3
Show newest version
/*
 * TableData.java 03/14/2009 Copyright (C) 2009 Leonardo Alves da Costa This program 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. 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 General Public License for more details. You should have received a
 * copy of the GNU General Public License along with this program. If not, see .
 */
package com.googlecode.paradox.data;

import com.googlecode.paradox.ParadoxConnection;
import com.googlecode.paradox.data.table.value.FieldValue;
import com.googlecode.paradox.metadata.ParadoxField;
import com.googlecode.paradox.metadata.ParadoxTable;
import com.googlecode.paradox.utils.SQLStates;
import com.googlecode.paradox.utils.Utils;
import com.googlecode.paradox.utils.filefilters.TableFilter;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import static com.googlecode.paradox.utils.Utils.clear;
import static com.googlecode.paradox.utils.Utils.flip;
import static com.googlecode.paradox.utils.Utils.position;

/**
 * Utility class for loading table files.
 *
 * @author Leonardo Alves da Costa
 * @version 1.1
 * @since 1.0
 */
public final class TableData extends AbstractParadoxData {

    /**
     * Utility class.
     */
    private TableData() {
        super();
    }

    /**
     * List all database tables.
     *
     * @param currentSchema the current schema file.
     * @param connection    the database connection.
     * @return all {@link ParadoxTable}.
     * @throws SQLException in case of failures.
     */
    public static List listTables(final File currentSchema, final ParadoxConnection connection) throws
            SQLException {
        final ArrayList tables = new ArrayList<>();
        final File[] fileList = currentSchema.listFiles(new TableFilter());
        if (fileList != null) {
            for (final File file : fileList) {
                final ParadoxTable table = TableData.loadTableHeader(file, connection);
                tables.add(table);
            }
        }
        return tables;
    }

    /**
     * Gets all tables within a pattern.
     *
     * @param schema     the schema directory.
     * @param pattern    the pattern.
     * @param connection the database connection.
     * @return the tables filtered.
     * @throws SQLException in case of failures.
     */
    public static List listTables(final File schema, final String pattern,
                                                final ParadoxConnection connection) throws SQLException {
        final List tables = new ArrayList<>();
        final File[] fileList = schema.listFiles(new TableFilter(Utils.removeDb(pattern)));
        if (fileList != null) {
            Arrays.sort(fileList);
            for (final File file : fileList) {
                final ParadoxTable table = TableData.loadTableHeader(file, connection);
                tables.add(table);
            }
        }
        return tables;
    }

    /**
     * Load the table data from file.
     *
     * @param table  the table to read.
     * @param fields the fields to read.
     * @return the row values.
     * @throws SQLException in case of failures.
     */
    public static List> loadData(final ParadoxTable table,
                                                  final Collection fields) throws SQLException {
        final List> ret = new ArrayList<>();

        if (table.isEncrypted()) {
            throw new SQLException("Unsupported encrypted table files.");
        }

        final int blockSize = table.getBlockSizeBytes();
        final int recordSize = table.getRecordSize();
        final int headerSize = table.getHeaderSize();
        final ByteBuffer buffer = ByteBuffer.allocate(blockSize);

        try (FileInputStream fs = new FileInputStream(table.getFile()); FileChannel channel = fs.getChannel()) {
            if (table.getUsedBlocks() == 0) {
                return ret;
            }
            long nextBlock = table.getFirstBlock();
            do {
                buffer.order(ByteOrder.LITTLE_ENDIAN);
                channel.position(headerSize + ((nextBlock - 1) * blockSize));

                clear(buffer);
                channel.read(buffer);
                flip(buffer);

                nextBlock = buffer.getShort() & 0xFFFF;

                // The block number.
                buffer.getShort();

                final int addDataSize = buffer.getShort();
                final int rowsInBlock = (addDataSize / recordSize) + 1;

                buffer.order(ByteOrder.BIG_ENDIAN);

                for (int loop = 0; loop < rowsInBlock; loop++) {
                    ret.add(TableData.readRow(table, fields, buffer));
                }
            } while (nextBlock != 0);
        } catch (final IOException e) {
            throw new SQLException(e.getMessage(), SQLStates.INVALID_IO.getValue(), e);
        }
        return ret;
    }

    /**
     * Fix the buffer position based on file version ID.
     *
     * @param table      the Paradox table.
     * @param buffer     the buffer to fix.
     * @param fieldsSize the field list.
     */
    private static void fixTablePositionByVersion(final ParadoxTable table, final Buffer buffer,
                                                  final int fieldsSize) {
        if (table.getVersionId() > 4) {
            if (table.getVersionId() == 0xC) {
                position(buffer, 0x78 + 261 + 4 + (6 * fieldsSize));
            } else {
                position(buffer, 0x78 + 83 + (6 * fieldsSize));
            }
        } else {
            position(buffer, 0x58 + 83 + (6 * fieldsSize));
        }
    }

    /**
     * Gets the table header from a file.
     *
     * @param file       the {@link File} to read.
     * @param connection the database connection.
     * @return the {@link ParadoxTable}.
     * @throws SQLException in case of reading errors.
     */
    private static ParadoxTable loadTableHeader(final File file, final ParadoxConnection connection) throws
            SQLException {
        final ParadoxTable table = new ParadoxTable(file, file.getName(), connection);
        ByteBuffer buffer = ByteBuffer.allocate(2048);
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        try (FileInputStream fs = new FileInputStream(file); FileChannel channel = fs.getChannel()) {
            channel.read(buffer);
            flip(buffer);

            table.setRecordSize(buffer.getShort() & 0xFFFF);
            table.setHeaderSize(buffer.getShort() & 0xFFFF);
            table.setType(buffer.get());
            table.setBlockSize(buffer.get());
            table.setRowCount(buffer.getInt());
            table.setUsedBlocks(buffer.getShort());
            table.setTotalBlocks(buffer.getShort());
            table.setFirstBlock(buffer.getShort());
            table.setLastBlock(buffer.getShort());

            position(buffer, 0x21);
            table.setFieldCount(buffer.getShort());
            table.setPrimaryFieldCount(buffer.getShort());

            // Check for encrypted file.
            position(buffer, 0x25);
            int value = buffer.getInt();

            position(buffer, 0x38);
            table.setWriteProtected(buffer.get());
            table.setVersionId(buffer.get());

            // Paradox version 4.x and up.
            if (value == 0xFF00FF00 && table.getVersionId() > 4) {
                position(buffer, 0x5c);
                value = buffer.getInt();
            }
            table.setEncryptedData(value);

            position(buffer, 0x49);
            table.setAutoIncrementValue(buffer.getInt());
            table.setFirstFreeBlock(buffer.getShort());

            position(buffer, 0x55);
            table.setReferentialIntegrity(buffer.get());

            parseVersionID(buffer, table);

            final List fields = TableData.parseTableFields(table, buffer);

            // Restart the buffer with all table header
            channel.position(0);
            buffer = ByteBuffer.allocate(table.getHeaderSize());
            channel.read(buffer);

            TableData.fixTablePositionByVersion(table, buffer, fields.size());
            TableData.parseTableFieldsName(table, buffer, fields);
            TableData.parseTableFieldsOrder(table, buffer);
        } catch (final IOException e) {
            throw new SQLException(e.getMessage(), SQLStates.INVALID_IO.getValue(), e);
        }
        return table;
    }

    /**
     * Read fields attributes.
     *
     * @param table  the Paradox table.
     * @param buffer the buffer to read of.
     * @return the Paradox field list.
     * @throws SQLException in case of parse errors.
     */
    private static List parseTableFields(final ParadoxTable table, final ByteBuffer buffer) throws
            SQLException {
        final List fields = new ArrayList<>();
        for (int loop = 0; loop < table.getFieldCount(); loop++) {
            final ParadoxField field = new ParadoxField(loop + 1);
            field.setType(buffer.get());
            field.setSize(buffer.get() & 0xff);
            field.setTableName(table.getName());
            field.setTable(table);
            fields.add(field);
        }
        return fields;
    }

    /**
     * Parse the Paradox fields name.
     *
     * @param table  the Paradox table.
     * @param buffer the buffer to read of.
     * @param fields the field list.
     */
    private static void parseTableFieldsName(final ParadoxTable table, final ByteBuffer buffer,
                                             final List fields) {
        for (int loop = 0; loop < table.getFieldCount(); loop++) {
            final ByteBuffer name = ByteBuffer.allocate(261);

            while (true) {
                final byte c = buffer.get();
                if (c == 0) {
                    break;
                }
                name.put(c);
            }
            flip(name);
            fields.get(loop).setName(table.getCharset().decode(name).toString());
        }
        table.setFields(fields);
    }

    /**
     * Parse the fields order.
     *
     * @param table  the Paradox table.
     * @param buffer the buffer to read of.
     */
    private static void parseTableFieldsOrder(final ParadoxTable table, final ByteBuffer buffer) {
        final List fieldsOrder = new ArrayList<>();
        for (int loop = 0; loop < table.getFieldCount(); loop++) {
            fieldsOrder.add(buffer.getShort());
        }
        table.setFieldsOrder(fieldsOrder);
    }

    /**
     * Read a entire row.
     *
     * @param table  the table to read of.
     * @param fields the fields to read.
     * @param buffer the buffer to read of.
     * @return the row.
     * @throws SQLException in case of parse errors.
     */
    private static List readRow(final ParadoxTable table, final Collection fields,
                                            final ByteBuffer buffer) throws SQLException {
        final List row = new ArrayList<>();

        for (final ParadoxField field : table.getFields()) {
            final FieldValue fieldValue = FieldFactory.parse(table, buffer, field);

            // Field filter
            if (fields.contains(field) && (fieldValue != null)) {
                fieldValue.setField(field);
                row.add(fieldValue);
            }
        }
        return row;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy