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

org.tinfour.gis.shapefile.DbfField Maven / Gradle / Ivy

/* --------------------------------------------------------------------
 * Copyright (C) 2018  Gary W. Lucas.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ---------------------------------------------------------------------
 */

 /*
 * -----------------------------------------------------------------------
 *
 * Revision History:
 * Date     Name         Description
 * ------   ---------    -------------------------------------------------
 * 11/2018  G. Lucas     Created  
 *
 * Notes:
 *
 * -----------------------------------------------------------------------
 */
package org.tinfour.gis.shapefile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.tinfour.io.BufferedRandomAccessReader;

/**
 * Provides definitions and access methods for a DBF field.
 * 

* A number of features are not yet supported. Only the data types C * (character), N (numeric) and F (floating-point) are supported. Only * iso-8859-1 character sets are supported. * */ public class DbfField { protected final String name; protected final char fieldType; protected final int dataAddress; protected final int fieldLength; protected final int fieldDecimalCount; protected final int offset; protected final StringBuilder builder; protected final boolean numeric; protected final boolean isIntegral; /** * Constructs an instance based on specifications from the DBF file header * * @param name the name of the field * @param fieldType the type of the field * @param dataAddress not supported at this time * @param fieldLength the length of the field * @param fieldDecimalCount the number of decimal digits in the field * @param offset the offset of the field within the fixed-length record */ DbfField(String name, char fieldType, int dataAddress, int fieldLength, int fieldDecimalCount, int offset) { this.name = name; this.fieldType = fieldType; this.dataAddress = dataAddress; this.fieldLength = fieldLength; this.fieldDecimalCount = fieldDecimalCount; this.offset = offset; this.builder = new StringBuilder(fieldLength + 1); this.numeric = fieldType == 'N' || fieldType == 'F'; isIntegral = fieldType == 'N' && fieldDecimalCount == 0; } /** * Read specifications from the DBF header create an instance of a DbfField. * * @param brad a read-only random access file * @param offset the file offset position * @return a valid instance * @throws IOException in the event of an unrecoverable I/O condition. */ static DbfField load(BufferedRandomAccessReader brad, int offset) throws IOException { String name = brad.readAscii(11); char fieldType = (char) brad.readUnsignedByte(); int dataAddress = brad.readInt(); int fieldLength = brad.readUnsignedByte(); int fieldDecimalCount = brad.readUnsignedByte(); if (fieldType == 'F' || fieldType == 'N' && fieldDecimalCount > 0) { return new DbfFieldDouble( name, fieldType, dataAddress, fieldLength, fieldDecimalCount, offset); } if (fieldType == 'N' && fieldDecimalCount == 0) { return new DbfFieldInt( name, fieldType, dataAddress, fieldLength, fieldDecimalCount, offset); } if (fieldType == 'L') { return new DbfFieldLogical( name, fieldType, dataAddress, fieldLength, fieldDecimalCount, offset); } if (fieldType == 'D') { return new DbfFieldDate( name, fieldType, dataAddress, fieldLength, fieldDecimalCount, offset); } return new DbfField( name, fieldType, dataAddress, fieldLength, fieldDecimalCount, offset); } /** * Read the content of the DBF file for the indicated record file position. * The record file position indicates the offset to the beginning of the * fixed-length record within the DBF file. The DbfField instance includes an * offset within that record so that it can perform a read operation. * * @param brad a valid instance * @param recordFilePos the position of the beginning of the record * @throws IOException in the event of an unrecoverable I/O condition */ void read(BufferedRandomAccessReader brad, long recordFilePos) throws IOException { brad.seek(recordFilePos + offset); builder.setLength(0); if (isNumeric()) { for (int i = 0; i < getFieldLength(); i++) { char c = (char) brad.readUnsignedByte(); if (!Character.isWhitespace(c)) { builder.append((char) c); } } } else { int lastNonBlank = 0; for (int i = 0; i < getFieldLength(); i++) { char c = (char) brad.readUnsignedByte(); if (!Character.isWhitespace(c)) { lastNonBlank = i + 1; } builder.append((char) c); } builder.setLength(lastNonBlank); } } /** * Indicates whether the field supports integral data value type. * * @return true if the field can return an integer; otherwise, false */ public boolean isIntegral() { return isIntegral; } /** * Gets the integer value of the field, if it is of an integral type, * otherwise returns a zero. * * @return a valid integral value, or a zero if undefined. */ public int getInteger() { if (isIntegral) { return Integer.parseInt(builder.toString()); } return 0; } /** * Gets the double value stored in the field during the most recent read * operation, if any * * @return a valid double or a NaN if this is not a numeric field */ public double getDouble() { if (isNumeric() && builder.length() > 0) { return Double.parseDouble(builder.toString()); } return Double.NaN; } /** * Gets the string value stored in the field during the most recent read * operation. * * @return a valid string, potentially empty */ public String getString() { return builder.toString(); } /** * Gets the name of the data field * * @return a valid string */ public String getName() { return name; } /** * Indicates the type of the data field as defined in the DBF spec. * * @return a single character as defined in the DBF spec. */ public char getFieldType() { return fieldType; } /** * Indicates the overall length of the data field * * @return the fieldLength */ public int getFieldLength() { return fieldLength; } /** * For numeric fields, indicates the number of decimal digits. * * @return the fieldDecimalCount */ public int getFieldDecimalCount() { return fieldDecimalCount; } /** * Indicates if the field defines a numeric value * * @return the numeric */ public boolean isNumeric() { return numeric; } /** * Gets a logical value. * * @return true if the field contains a logical value of TRUE; otherwise * false. */ public boolean getLogicalValue() { return false; } @Override public String toString() { return String.format("DbfField (%s %2d.%-2d) %s", fieldType, fieldLength, fieldDecimalCount, name); } /** * Gets the data for the field as an Object of the appropriate class. For the * DbdField base class, the return value is a string. For others, the return * type may vary. * * @return an object, potentially null if the record has not been read. */ public Object getApplicationData() { return builder.toString(); } /** * Indicates whether the value in the field was encoded using engineering * notation (e.g. scientific notation). The return value will be false for all * field types except floating-point types. * * @return true if engineering notation was used; otherwise false. */ public boolean usesEngineeringNotation() { return false; } /** * Gets the unique values for this field as a list of strings. * * @param dbf the DBF with which this field is associated * @return if successful, a valid list of unique values * @throws IOException in the event of an unrecoverable IO condition. */ public List getUniqueValues(DbfFileReader dbf) throws IOException { int nRecords = dbf.getRecordCount(); List list = new ArrayList(nRecords); if (nRecords == 0) { return list; } // Investigate: Because we are going to sort these strings anyway, // perhaps this logic would be better served by a Java tree rather // than a map. HashMap map = new HashMap(); String sMin = ""; String sMax = ""; for (int i = 1; i <= nRecords; i++) { dbf.readField(i, this); String s = getString(); if (i == 1) { map.put(s, s); list.add(s); sMin = s; sMax = s; } else if (!map.containsKey(s)) { map.put(s, s); list.add(s); if (s.compareTo(sMin) < 0) { sMin = s; } if (s.compareTo(sMax) > 0) { sMax = s; } if (sMin.length() > 20) { sMin = sMin.substring(0, 17) + "..."; } if (sMax.length() > 20) { sMax = sMax.substring(0, 17) + "..."; } } } return list; } /** * Indicates whether the field contains a null value. * Because the interpretation of null fields is not well defined by the * DBF file specification, this method should be used with care. * @return true if the field is empty, otherwise false. */ public boolean isNull(){ return builder.length()==0; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy