FixedWidthFlatfileTable.java :  » Database-DBMS » axion » org » axiondb » engine » tables » Java Open Source

Java Open Source » Database DBMS » axion 
axion » org » axiondb » engine » tables » FixedWidthFlatfileTable.java
/*
 * $Id: FixedWidthFlatfileTable.java,v 1.16 2005/12/20 18:32:28 ahimanikya Exp $
 * =======================================================================
 * Copyright (c) 2002-2005 Axion Development Team.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer.
 *
 * 2. 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.
 *
 * 3. The names "Tigris", "Axion", nor the names of its contributors may
 *    not be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. Products derived from this software may not be called "Axion", nor
 *    may "Tigris" or "Axion" appear in their names 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 THE COPYRIGHT
 * OWNER OR CONTRIBUTORS 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 org.axiondb.engine.tables;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import org.axiondb.AxionException;
import org.axiondb.Column;
import org.axiondb.DataType;
import org.axiondb.Database;
import org.axiondb.ExternalTable;
import org.axiondb.Row;
import org.axiondb.engine.rows.SimpleRow;
import org.axiondb.io.BufferedDataInputStream;
import org.axiondb.io.BufferedDataOutputStream;

/**
 * A disk-resident Fixed Width Flatfile {@link org.axiondb.Table}.
 * 
 * @version $Revision: 1.16 $ $Date: 2005/12/20 18:32:28 $
 * @author Ahimanikya Satapathy
 * @author Jonathan Giron
 */
public final class FixedWidthFlatfileTable extends BaseFlatfileTable {

    // TODO: Add Record Trailer byte size

    public static final String PROP_HEADERBYTESOFFSET = "HEADERBYTESOFFSET";

    private static final Set PROPERTY_KEYS = new HashSet(2);

    static {
        PROPERTY_KEYS.add(PROP_HEADERBYTESOFFSET);
    }

    public FixedWidthFlatfileTable(String name, Database db) throws AxionException {
        super(name, db, new FixedWidthFlatfileTableLoader());
        setType(ExternalTable.FW_TABLE_TYPE);
    }

    public void addColumn(Column col, boolean metaUpdateNeeded) throws AxionException {
        super.addColumn(col, metaUpdateNeeded);
        updateRecordLength();
    }

    protected Row getRowByOffset(int idToAssign, long ptr) throws AxionException {
        BufferedDataInputStream data = getInputStream();
        int colCount = getColumnCount();
        Row row = new SimpleRow(idToAssign, colCount);

        try {
            synchronized (data) {
                int colLength = 0;
                int linePtr = 0;
                char[] charArray = readLine(data, ptr);

                for (int i = 0; i < colCount; i++) {
                    colLength = getColumnSize(i);

                    String columnValue = new String(charArray, linePtr, colLength).trim();
                    if (columnValue.length() == 0) {
                        columnValue = null;
                    }

                    row = trySettingColumn(idToAssign, row, i, columnValue);
                    linePtr += colLength;
                }
            }
            return row;
        } catch (Exception e) {
            if (e instanceof AxionException) {
                throw (AxionException) e;
            }
            throw new AxionException(e);
        }
    }

    protected int nextLineLength(final long fileOffset) throws AxionException {
        try {
            int nextChar;
            int recLength = 0;
            BufferedDataInputStream data = getInputStream();

            data.seek(fileOffset);
            while (true) {
                nextChar = data.read();
                recLength++;

                if (isEOF(nextChar)) {
                    if (recLength > 1) {
                        //-- EOF reached but has some data
                        return recLength;
                    }
                    return EOF;
                }

                if (isEndOfRecord(recLength, nextChar, data)) {
                    recLength = (recLength == 1 ? recLength : _recordLength);
                    break;
                }
            }
            return recLength;

        } catch (Exception e) {
            throw new AxionException("Unable to parse data file: ", e);
        }
    }

    protected long ignoreRowsToSkip() throws AxionException {
        long offset = super.ignoreRowsToSkip();
        if (offset > 0) {
            return offset;
        } else if (_headerBytesOffset > 0) {
            return _headerBytesOffset;
        }
        return 0;
    }

    protected void initializeTable() throws AxionException {
        _lineCharArray = new char[_recordLength];
        super.initializeTable();
    }

    protected boolean isEndOfRecord(int recLength, int nextChar, BufferedDataInputStream data) throws IOException {
        if (isEOF(nextChar)) {
            return true;
        }
      
      if (recLength >= _recordLength){
        if (!("".equals(_lineSep)) && _lineSep.charAt(0) != nextChar) {
          throw new IOException("Corrupted data, record delimeter not found at specified record length.");
        } else {
                return true;
            }
      }

        if (!("".equals(_lineSep)) && _lineSep.charAt(0) == nextChar) {
            char[] charBuf = _lineSep.toCharArray();
            // Look ahead to see whether the following chars match EOL.
            long lastDataFileOffset = data.getPos();
            for (int i = 1, I =_lineSep.length(); i < I; i++) {
                if (charBuf[i] != (char) data.read()) {
                    data.seek(lastDataFileOffset);
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    public boolean loadExternalTable(Properties props) throws AxionException {
        context = new FixedwidthTableOrganizationContext();
        return super.loadExternalTable(props);
    }

    public Properties getTableProperties() {
        return context.getTableProperties();
    }

    protected void parseTableProperties(ObjectInputStream in) throws AxionException {
        try {
            _lineSep = in.readUTF();
            _isFirstLineHeader = Boolean.valueOf(in.readUTF()).booleanValue();
            in.readUTF(); // _eol will be computed , keep this for older version metadata
            _headerBytesOffset = in.readInt();
            _recordLength = in.readInt();
            _fileName = in.readUTF();

            context = new FixedwidthTableOrganizationContext();
            context.updateProperties();
            context.readOrSetDefaultProperties(context.getTableProperties());
            createOrLoadDataFile();
            _lineCharArray = new char[_recordLength];
        } catch (IOException e) {
            throw new AxionException("Unable to parse meta file for table " + getName(), e);
        }
    }

    protected synchronized void renameTableFiles(String oldName, String name) {
        super.renameTableFiles(oldName, name);
        updateRecordLength();
    }

    private void updateRecordLength() {
        if (context != null) {
            _recordLength = 0;
            for (int i = 0, I = getColumnCount(); i < I; i++) {
                _recordLength += getColumnSize(i);
            }
            _lineCharArray = new char[_recordLength];
        }
    }

    protected void writeHeader(BufferedDataOutputStream dataFile) throws AxionException {
        if (_isFirstLineHeader) {
            try {
                for (int i = 0, I = getColumnCount(); i < I; i++) {
                    dataFile.write(writeColumn(i, getColumn(i).getName()));
                }
                dataFile.write(_lineSep.getBytes());
            } catch (IOException ioex) {
                throw new AxionException("Unable to write header for table: " + getName(), ioex);
            }
        }
    }

    protected void writeRow(BufferedDataOutputStream out, Row row) throws AxionException {
        Object colValue = null;
        DataType type = null;

        try {
            for (int i = 0, I = getColumnCount(); i < I; i++) {
                colValue = row.get(i);
                type = getColumn(i).getDataType();
                out.write(writeColumn(i, type.toString(colValue)));
            }

            // write new line
            out.write(_lineSep.getBytes());
        } catch (IOException e) {
            throw new AxionException("Error writing row: " + row, e);
        }
    }

    protected void writeTableProperties(ObjectOutputStream out) throws AxionException {
        try {
            if (_lineSep != null && _fileName != null) {
                out.writeUTF(_lineSep);
                out.writeUTF(Boolean.toString(_isFirstLineHeader));
                out.writeUTF(_lineSep);
                out.writeInt(_headerBytesOffset);
                out.writeInt(_recordLength);
                out.writeUTF(_fileName);
            }
        } catch (IOException e) {
            throw new AxionException("Unable to write meta file for table " + getName(), e);
        }
    }

    private int getColumnSize(int index) {
        return getColumn(index).getDataType().getColumnDisplaySize();
    }

    private char[] readLine(BufferedDataInputStream data, long fileOffset) throws AxionException {
        Arrays.fill(_lineCharArray, FILLER);
        int recLength = 0;
        try {
            int nextChar;
            data.seek(fileOffset);

            while (true) {
                nextChar = data.read();
                if (isEndOfRecord(recLength, nextChar, data)) {
                    if (recLength == 0) {
                        throw new AxionException("Empty line detected - invalid.");
                    } else if (_lineSep.length() == 0 && !isEOF(nextChar)) {
                        // If this is a packed data file, move pointer back by one - since
                        // there isn't a record delimiter, our call to data.read() has set
                        // the file pointer to the first char of the next record.
                        data.seek(data.getPos() - 1);
                    }
                    break;
                }
                _lineCharArray[recLength++] = ((char) nextChar);
            }

            return _lineCharArray;

        } catch (IOException ioex) {
            throw new AxionException("Unable to parse data file: ", ioex);
        }
    }

    private byte[] writeColumn(int colIndex, String value) {
        if (value == null) {
            value = " ";
        }

        byte byteData[] = new byte[getColumnSize(colIndex)];
        Arrays.fill(byteData, (byte) FILLER);
        byte colValue[] = value.getBytes();

        // truncate if required
        int len = colValue.length <= byteData.length ? colValue.length : byteData.length;
        System.arraycopy(colValue, 0, byteData, 0, len);
        return byteData;
    }

    private class FixedwidthTableOrganizationContext extends BaseFlatfileTableOrganizationContext {
        public Set getPropertyKeys() {
            Set baseKeys = super.getPropertyKeys();
            Set keys = new HashSet(baseKeys.size() + PROPERTY_KEYS.size());
            keys.addAll(baseKeys);
            keys.addAll(PROPERTY_KEYS);

            return keys;
        }

        public void readOrSetDefaultProperties(Properties props) throws AxionException {
            super.readOrSetDefaultProperties(props);

            // compute record length; if not set yet
            for (int i = 0, I = getColumnCount(); i < I; i++) {
                _recordLength += getColumnSize(i);
            }
            _recordLength += _lineSep.length();

            try {
                _headerBytesOffset = Integer.parseInt(props.getProperty(PROP_HEADERBYTESOFFSET));
            } catch (NumberFormatException e) {
                // optional argument, ignore if not set
            }

            // IsFirstLineHeader will override HeaderBytesOffset.
            _isFirstLineHeader = Boolean.valueOf(props.getProperty(PROP_ISFIRSTLINEHEADER.toUpperCase(), "false")).booleanValue();
        }

        public void updateProperties() {
            super.updateProperties();
            _props.setProperty(PROP_LOADTYPE, ExternalTableFactory.TYPE_FIXEDWIDTH);
            _props.setProperty(PROP_HEADERBYTESOFFSET, Integer.toString(_headerBytesOffset));
        }
    }

    private int _headerBytesOffset = 0;
    private char[] _lineCharArray;
    private int _recordLength;

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.