001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/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: 24277 $, $Date: 2010-05-10 16:41:12 +0200 (Mo, 10 Mai 2010) $
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                    if ( b.length > fddata[16] )
143                        throw new DBaseException( "string contains too many characters " + (String) recData.get( i ) );
144                    for ( int j = 0; j < b.length; j++ )
145                        datasec.data[offset + j] = b[j];
146                    for ( int j = b.length; j < fddata[16]; j++ )
147                        datasec.data[offset + j] = 0x20;
148                    break;
149                case (byte) 'N':
150                    if ( recData.get( i ) != null && !( recData.get( i ) instanceof Number ) )
151                        throw new DBaseException( "invalid data type at field: " + i );
152                    if ( recData.get( i ) == null ) {
153                        b = new byte[0];
154                    } else {
155                        b = ( (Number) recData.get( i ) ).toString().getBytes();
156                    }
157                    if ( b.length > fddata[16] )
158                        throw new DBaseException( "string contains too many characters " + (String) recData.get( i ) );
159                    for ( int j = 0; j < b.length; j++ )
160                        datasec.data[offset + j] = b[j];
161                    for ( int j = b.length; j < fddata[16]; j++ )
162                        datasec.data[offset + j] = 0x0;
163                    break;
164                case (byte) 'D':
165                    if ( recData.get( i ) != null && !( recData.get( i ) instanceof Date ) )
166                        throw new DBaseException( "invalid data type at field: " + i );
167                    if ( recData.get( i ) == null ) {
168                        b = new byte[0];
169                    } else {
170                        SimpleDateFormat sdf_ = new SimpleDateFormat( "yyyy-MM-dd", Locale.getDefault() );
171                        b = sdf_.format( (Date) recData.get( i )  ).getBytes();
172                    }                
173                    if ( b.length > fddata[16] )
174                        throw new DBaseException( "string contains too many characters " + (Date) recData.get( i ) );
175                    for ( int j = 0; j < b.length; j++ )
176                        datasec.data[offset + j] = b[j];
177                    for ( int j = b.length; j < fddata[16]; j++ )
178                        datasec.data[offset + j] = 0x0;
179                    break;
180                  default: {
181                    System.out.println("TTTT" + (char)fddata[11] );
182                    throw new DBaseException( "data type not supported" );
183                }
184    
185                }
186    
187                offset += fddata[16];
188    
189            }
190    
191            // puts the record to the ArrayList (container)
192            data.add( index, datasec );
193    
194        }
195    
196        /**
197         * method: public byte[] getDataSection() returns the data section as a byte array.
198         */
199        public byte[] getDataSection() {
200    
201            // allocate memory for all datarecords on one array + 1 byte
202            byte[] outdata = new byte[data.size() * recordlength + 1];
203    
204            // set the file terminating byte
205            outdata[outdata.length - 1] = 0x1A;
206    
207            // get all records from the ArrayList and put it
208            // on a single array
209            int j = 0;
210            for ( int i = 0; i < data.size(); i++ ) {
211                ByteContainer bc = data.get( i );
212                for ( int k = 0; k < recordlength; k++ ) {
213                    outdata[j++] = bc.data[k];
214                }
215            }
216            return outdata;
217        }
218    
219        public void getDataSection( OutputStream os )
220                                throws IOException {
221    
222            // get all records from the ArrayList and write it into a stream
223            for ( int i = 0; i < data.size(); i++ ) {
224                ByteContainer bc = data.get( i );
225                for ( int k = 0; k < recordlength; k++ ) {
226                    os.write( bc.data[k] );
227                }
228    
229            }
230            // set the file terminating byte        
231            os.write( 0x1A );
232        }
233    
234        /**
235         * method: public int getNoOfRecords() returns the number of records within the container
236         */
237        public int getNoOfRecords() {
238            return data.size();
239        }
240    
241    }
242    
243    class ByteContainer {
244    
245        public byte[] data = null;
246    
247        public ByteContainer( int size ) {
248    
249            data = new byte[size];
250    
251        }
252    
253    }