/*
* ImageI/O-Ext - OpenSource Java Image translation Library
* http://www.geo-solutions.it/
* http://java.net/projects/imageio-ext/
* (C) 2007 - 2009, GeoSolutions
*
* 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;
* either version 3 of the License, or (at your option) any later version.
*
* 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.
*/
package it.geosolutions.imageio.utilities;
import java.lang.ref.SoftReference;
/**
* This class is responsible for converting a sequence of chars into a a double
* number.
*
* <p>
* It is a utility class uses by the {@link AsciiGridRaster} class for
* converting the input {@link String} into numbers. This class is highly
* optimized for performances reasons.
*
* <p>
* This class is not thread safe!!!
*
*
* <p>
* <strong>Usage</strong>
* <p>
*
* <pre>
* final StringToDouble converter = new StringToDouble();
* // If I need to load 10 samples, I need to count 9 spaces
* while (...) {
* // /////////////////////////////////////////////////////////////////
* //
* // Read the char
* //
* // /////////////////////////////////////////////////////////////////
* ch = read();
*
*
* // /////////////////////////////////////////////////////////////////
* //
* // Push the char into the converter
* //
* // /////////////////////////////////////////////////////////////////
* if (converter.pushChar(ch)) {
*
* // /////////////////////////////////////////////////////////////////
* //
* // Get the new value
* //
* // /////////////////////////////////////////////////////////////////
* value = converter.getValue();
*
* // /////////////////////////////////////////////////////////////////
* //
* // Consume the new value
* //
* // /////////////////////////////////////////////////////////////////
* ...
* ...
* ...
*
* } else {
* // /////////////////////////////////////////////////////////////////
* //
* // Let's check the EOF.
* //
* // /////////////////////////////////////////////////////////////////
* if (converter.isEof()) {
* ...
* ...
* ...
* }
* }
* }
* </pre>
*
* <p>
* It is worth to point out that when using this class it would be great to add
* to the JVM the hint to augment the lifetime of {@link SoftReference} objects
* since the underlying pool is based on them.
* <p>
*
* @author Daniele Romagnoli, GeoSolutions.
* @author Simone Giannecchini, GeoSolutions.
*/
public final class StringToDouble {
// /**
// * Default number of {@link StringToDouble} object to keep in the pool.
// *
// * <p>
// * I use this value also when enlarging the pool.
// */
// private static final int CONVERTER_NUM = 50;
//
// /**
// * Static pool of double converters.
// */
// static List pool;
//
// static {
// pool = Collections.synchronizedList(new ArrayList(CONVERTER_NUM));
// enlargePool(CONVERTER_NUM);
// }
//
// private static void enlargePool(final int num) {
// for (int i = 0; i < num; i++)
// pool.add(new SoftReference(new StringToDouble()));
// }
// variables for arithmetic operations
private double value = 0.0;
private int prevCh = -1;
private boolean eof = false;
private final StringBuilder builder = new StringBuilder();
/**
* Constructor.
*
*/
private StringToDouble() {
}
/**
* Resets the converter.
*
* <p>
* This method should be called each time a new value is consumed by using
* {@link StringToDouble#compute()} method.
*
*/
public void reset() {
// Resetting Values
value = 0.0;
eof = false;
prevCh = -1;
builder.setLength(0);
}
/**
* Pushes a new character to this converter.
*
* <p>
* The converter parses the char and check if we have a new value. The value
* is not computed unless requested for performance reasons. Often it is
* needed to throw away values that are not needed because of subsampling or
* the like, hence it is a waste of resource explicitly computing them.
*
* <p>
* It is worth to point out that after getting a false value it is crucial
* to check the {@link StringToDouble#isEof()} to see if we reached to eof
* condition.
*
* @param newChar
* to parse.
* @return true if there is a value to get, false otherwise.
* @see {@link StringToDouble#isEof()}
*/
public boolean pushChar(final int newChar) {
boolean retVal = false;
// check if we read a white space or similar
if ((newChar != 32) && (newChar != 10) && (newChar != 13)
&& (newChar != 9) && (newChar != 0)) {
if ((prevCh == 32) // ' '
|| (prevCh == 10) // '\r'
|| (prevCh == 13) // '\n'
|| (prevCh == 9) // '\t'
|| (prevCh == 0)) {
// //
//
// End of white spaces. I need to convert read bytes
// in a double value and I set it as a sample of the
// raster, if subsampling allows it.
//
// //
// we found a value
retVal = true;
value = compute();
reset();
}
// //
//
// Analysis of current byte for next value
//
// //
switch (newChar) {
case 48: // '0'
case 49: // '1'
case 50: // '2'
case 51: // '3'
case 52: // '4'
case 53: // '5'
case 54: // '6'
case 55: // '7'
case 56: // '8'
case 57: // '9'
builder.append(newChar - 48);
retVal = true;
break;
case 44: // ',' generated by ArcGIS in some environments
case 46: // '.'
builder.append('.');
retVal = true;
break;
case 45: // '-'
builder.append('-');
retVal = true;
break;
case 43: // '+'
builder.append('+');
retVal = true;
break;
case 42: // '*' NoData in GRASS Format
retVal = true;
value = Double.NaN;
break;
case 69: // 'E'
case 101: // 'e'
builder.append('E');
retVal = true;
break;
case -1:
retVal = true;
eof = true;
break;
default:
throw new NumberFormatException(new StringBuilder(
"Invalid data value was found. ASCII CODE : ").append(
newChar).toString());
}
}
prevCh = newChar; // store this byte for some checks
return retVal;
}
/**
* Returns a value, if any was advertised by the
* {@link StringToDouble#pushChar(int)} method.
*
* @return
*
* @return the computed value;
*/
public double compute() {
if (!Double.isNaN(value)) {
value = Double.parseDouble(builder.toString());
builder.setLength(0);
}
return value;
}
/**
* Did we find an EOF?
*
*/
boolean isEof() {
return eof;
}
/**
* Retrieves a poole {@link StringToDouble} object.
*
*/
public static StringToDouble acquire() {
// synchronized (pool) {
// SoftReference r;
// Object o;
// while (pool.size() > 0) {
// r = (SoftReference) pool.remove(0);
// o = r.get();
// if (o != null) {
// StringToDouble stf = (StringToDouble) o;
// stf.reset();
// return stf;
// }
//
// }
// // we did not find any
// enlargePool(CONVERTER_NUM - 1);
return new StringToDouble();
// }
}
/**
* Reacquire a pooled {@link StringToDouble}.
*
* @param c
*/
public static void release(StringToDouble c) {
c.builder.setLength(0);
c.builder.trimToSize();
// synchronized (pool) {
// pool.add(new SoftReference(c));
// }
}
}