/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * (C) 2010, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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. * * This file is based on an origional contained in the GISToolkit project: * http://gistoolkit.sourceforge.net/ */ package org.geotoolkit.data.dbf; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; /** * A DbaseFileReader is used to read a dbase III format file. The general use of * this class is: <CODE><PRE> * DbaseFileHeader header = ... * WritableFileChannel out = new FileOutputStream("thefile.dbf").getChannel(); * DbaseFileWriter w = new DbaseFileWriter(header,out); * while ( moreRecords ) { * w.write( getMyRecord() ); * } * w.close(); * </PRE></CODE> You must supply the <CODE>moreRecords</CODE> and * <CODE>getMyRecord()</CODE> logic... * * @author Ian Schneider * @module */ public class DbaseFileWriter implements Closeable{ private DbaseFileHeader header; private DbaseFieldFormatter formatter; WritableByteChannel channel; private ByteBuffer buffer; private Charset charset; /** * Create a DbaseFileWriter using the specified header and writing to the * given channel. * * @param header * The DbaseFileHeader to write. * @param out * The Channel to write to. * @throws IOException * If errors occur while initializing. */ public DbaseFileWriter(final DbaseFileHeader header, final WritableByteChannel out) throws IOException { this(header, out, null); } /** * Create a DbaseFileWriter using the specified header and writing to the * given channel. * * @param header * The DbaseFileHeader to write. * @param out * The Channel to write to. * @param charset The charset the dbf is (will be) encoded in * @throws IOException * If errors occur while initializing. */ public DbaseFileWriter(final DbaseFileHeader header, final WritableByteChannel out, final Charset charset) throws IOException { header.writeHeader(out); this.header = header; this.channel = out; this.charset = charset == null ? Charset.defaultCharset() : charset; this.formatter = new DbaseFieldFormatter(this.charset); init(); } private void init() throws IOException { buffer = ByteBuffer.allocateDirect(header.getRecordLength()); } private void write() throws IOException { buffer.position(0); int r = buffer.remaining(); while ((r -= channel.write(buffer)) > 0) { // do nothing } } /** * Write a single dbase record. * * @param record * The entries to write. * @throws IOException * If IO error occurs. * @throws DbaseFileException * If the entry doesn't comply to the header. */ public void write(final Object[] record) throws IOException, DbaseFileException { if (record.length != header.getNumFields()) { throw new DbaseFileException("Wrong number of fields " + record.length + " expected " + header.getNumFields()); } buffer.position(0); // put the 'not-deleted' marker buffer.put((byte) ' '); for (int i = 0; i < header.getNumFields(); i++) { String fieldString = header.getField(i).string(record[i], formatter); if (header.getFieldLength(i) != fieldString.getBytes(charset.name()).length) { // System.out.println(i + " : " + header.getFieldName(i)+" value // = "+fieldString+""); buffer.put(new byte[header.getFieldLength(i)]); } else { buffer.put(fieldString.getBytes(charset.name())); } } write(); } /** * Release resources associated with this writer. <B>Highly recommended</B> * * @throws IOException * If errors occur. */ @Override public void close() throws IOException { // IANS - GEOT 193, bogus 0x00 written. According to dbf spec, optional // eof 0x1a marker is, well, optional. Since the original code wrote a // 0x00 (which is wrong anyway) lets just do away with this :) // - produced dbf works in OpenOffice and ArcExplorer java, so it must // be okay. // buffer.position(0); // buffer.put((byte) 0).position(0).limit(1); // write(); if (channel.isOpen()) { channel.close(); } buffer = null; channel = null; formatter = null; } @Override public boolean isClosed() { return !channel.isOpen(); } }