001    //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/dbaseapi/DBFDataSection.java $
002    
003    /*----------------------------------------------------------------------------
004     This file is part of deegree, http://deegree.org/
005     Copyright (C) 2001-2009 by:
006     Department of Geography, University of Bonn
007     and
008     lat/lon GmbH
009    
010     This library is free software; you can redistribute it and/or modify it under
011     the terms of the GNU Lesser General Public License as published by the Free
012     Software Foundation; either version 2.1 of the License, or (at your option)
013     any later version.
014     This library is distributed in the hope that it will be useful, but WITHOUT
015     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
016     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
017     details.
018     You should have received a copy of the GNU Lesser General Public License
019     along with this library; if not, write to the Free Software Foundation, Inc.,
020     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021    
022     Contact information:
023    
024     lat/lon GmbH
025     Aennchenstr. 19, 53177 Bonn
026     Germany
027     http://lat-lon.de/
028    
029     Department of Geography, University of Bonn
030     Prof. Dr. Klaus Greve
031     Postfach 1147, 53001 Bonn
032     Germany
033     http://www.geographie.uni-bonn.de/deegree/
034    
035     e-mail: info@deegree.org
036     ----------------------------------------------------------------------------*/
037    
038    package org.deegree.io.dbaseapi;
039    
040    import java.io.IOException;
041    import java.io.OutputStream;
042    import java.text.SimpleDateFormat;
043    import java.util.ArrayList;
044    import java.util.Date;
045    import java.util.List;
046    import java.util.Locale;
047    
048    /**
049     * Class representing a record of the data section of a dBase III/IV file<BR>
050     * at the moment only the daata types character ("C") and numeric ("N") are supported
051     * 
052     * 
053     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
054     * @author last edited by: $Author: apoth $
055     * 
056     * @version $Revision: 31147 $, $Date: 2011-06-29 13:18:11 +0200 (Mi, 29 Jun 2011) $
057     */
058    public class DBFDataSection {
059    
060        /**
061         * length of one record in bytes
062         */
063        private int recordlength = 0;
064    
065        private FieldDescriptor[] fieldDesc = null;
066    
067        private List<ByteContainer> data = new ArrayList<ByteContainer>();
068    
069        /**
070         * constructor
071         */
072        public DBFDataSection( FieldDescriptor[] fieldDesc ) {
073    
074            this.fieldDesc = fieldDesc;
075    
076            // calculate length of the data section
077            recordlength = 0;
078            for ( int i = 0; i < this.fieldDesc.length; i++ ) {
079    
080                byte[] fddata = this.fieldDesc[i].getFieldDescriptor();
081    
082                recordlength += fddata[16];
083    
084                fddata = null;
085    
086            }
087    
088            recordlength++;
089    
090        }
091    
092        /**
093         * method: public setRecord(ArrayList recData) writes a data record to byte array representing the data section of
094         * the dBase file. The method gets the data type of each field in recData from fieldDesc wich has been set at the
095         * constructor.
096         */
097        public void setRecord( List<?> recData )
098                                throws DBaseException {
099    
100            setRecord( data.size(), recData );
101    
102        }
103    
104        /**
105         * method: public setRecord(int index, ArrayList recData) writes a data record to byte array representing the data
106         * section of the dBase file. The method gets the data type of each field in recData from fieldDesc wich has been
107         * set at the constructor. index specifies the location of the retrieved record in the datasection. if an invalid
108         * index is used an exception will be thrown
109         */
110        public void setRecord( int index, List<?> recData )
111                                throws DBaseException {
112    
113            ByteContainer datasec = new ByteContainer( recordlength );
114    
115            if ( ( index < 0 ) || ( index > data.size() ) )
116                throw new DBaseException( "invalid index: " + index );
117    
118            if ( recData.size() != this.fieldDesc.length )
119                throw new DBaseException( "invalid size of recData" );
120    
121            int offset = 0;
122    
123            datasec.data[offset] = 0x20;
124    
125            offset++;
126    
127            byte[] b = null;
128    
129            // write every field on the ArrayList to the data byte array
130            for ( int i = 0; i < recData.size(); i++ ) {
131                byte[] fddata = this.fieldDesc[i].getFieldDescriptor();
132                switch ( fddata[11] ) {
133    
134                // if data type is character
135                case (byte) 'C':
136    
137                    if ( recData.get( i ) == null ) {
138                        b = new byte[0];
139                    } else {
140                        b = recData.get( i ).toString().getBytes();
141                    }
142                    // TODO
143                    // Maybe skip this and and trim to b.length   
144                    if ( b.length > fddata[16] ) {
145                        throw new DBaseException( "string contains too many characters " + (String) recData.get( i ) );
146                    }
147                    for ( int j = 0; j < b.length; j++ )
148                        datasec.data[offset + j] = b[j];
149                    for ( int j = b.length; j < fddata[16]; j++ )
150                        datasec.data[offset + j] = 0x20;
151                    break;
152                case (byte) 'N':
153                    if ( recData.get( i ) != null && !( recData.get( i ) instanceof Number ) )
154                        throw new DBaseException( "invalid data type at field: " + i );
155                    if ( recData.get( i ) == null ) {
156                        b = new byte[0];
157                    } else {
158                        b = ( (Number) recData.get( i ) ).toString().getBytes();
159                    }
160                    if ( b.length > fddata[16] )
161                        throw new DBaseException( "string contains too many characters " + (String) recData.get( i ) );
162                    for ( int j = 0; j < b.length; j++ )
163                        datasec.data[offset + j] = b[j];
164                    for ( int j = b.length; j < fddata[16]; j++ )
165                        datasec.data[offset + j] = 0x0;
166                    break;
167                case (byte) 'D':
168                    if ( recData.get( i ) != null && !( recData.get( i ) instanceof Date ) )
169                        throw new DBaseException( "invalid data type at field: " + i );
170                    if ( recData.get( i ) == null ) {
171                        b = new byte[0];
172                    } else {
173                        SimpleDateFormat sdf_ = new SimpleDateFormat( "yyyy-MM-dd", Locale.getDefault() );
174                        b = sdf_.format( (Date) recData.get( i )  ).getBytes();
175                    }                
176                    if ( b.length > fddata[16] )
177                        throw new DBaseException( "string contains too many characters " + (Date) recData.get( i ) );
178                    for ( int j = 0; j < b.length; j++ )
179                        datasec.data[offset + j] = b[j];
180                    for ( int j = b.length; j < fddata[16]; j++ )
181                        datasec.data[offset + j] = 0x0;
182                    break;
183                  default: {
184                    System.out.println("TTTT" + (char)fddata[11] );
185                    throw new DBaseException( "data type not supported" );
186                }
187    
188                }
189    
190                offset += fddata[16];
191    
192            }
193    
194            // puts the record to the ArrayList (container)
195            data.add( index, datasec );
196    
197        }
198    
199        /**
200         * method: public byte[] getDataSection() returns the data section as a byte array.
201         */
202        public byte[] getDataSection() {
203    
204            // allocate memory for all datarecords on one array + 1 byte
205            byte[] outdata = new byte[data.size() * recordlength + 1];
206    
207            // set the file terminating byte
208            outdata[outdata.length - 1] = 0x1A;
209    
210            // get all records from the ArrayList and put it
211            // on a single array
212            int j = 0;
213            for ( int i = 0; i < data.size(); i++ ) {
214                ByteContainer bc = data.get( i );
215                for ( int k = 0; k < recordlength; k++ ) {
216                    outdata[j++] = bc.data[k];
217                }
218            }
219            return outdata;
220        }
221    
222        public void getDataSection( OutputStream os )
223                                throws IOException {
224    
225            // get all records from the ArrayList and write it into a stream
226            for ( int i = 0; i < data.size(); i++ ) {
227                ByteContainer bc = data.get( i );
228                for ( int k = 0; k < recordlength; k++ ) {
229                    os.write( bc.data[k] );
230                }
231    
232            }
233            // set the file terminating byte        
234            os.write( 0x1A );
235        }
236    
237        /**
238         * method: public int getNoOfRecords() returns the number of records within the container
239         */
240        public int getNoOfRecords() {
241            return data.size();
242        }
243    
244    }
245    
246    class ByteContainer {
247    
248        public byte[] data = null;
249    
250        public ByteContainer( int size ) {
251    
252            data = new byte[size];
253    
254        }
255    
256    }