package org.autoplot.html;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.das2.datum.EnumerationUnits;
import org.das2.datum.TimeParser;
import org.das2.datum.Units;
import org.das2.util.LoggerManager;
import org.das2.qds.AbstractDataSet;
import org.das2.qds.DDataSet;
import org.das2.qds.QDataSet;
import org.das2.qds.ops.Ops;
/**
* Generic class for converting a table of ASCII strings to QDataSet stream.
* This supports streaming sources by reporting QDataSet records as they arrive.
* @author jbf
*/
public class AsciiTableStreamer implements Iterator {
private static final Logger logger= LoggerManager.getLogger("apdss.html");
QDataSet desc = null; // bundle descriptor
List units = null;
Units defaultUnits= null;
List labels = null;
List names = null;
List format= null;
List fillValues= null;
List records;
QDataSet recordDescriptor;
int fieldCount= -1;
boolean initializedFields= false;
/**
* true indicates that there may be more records, while false asserts no more records.
*/
boolean hasNextRecord= true;
public AsciiTableStreamer() {
records= Collections.synchronizedList( new LinkedList() );
}
protected void setUnits(String units) {
this.defaultUnits= Units.lookupUnits(units);
}
private void setUnitsAndFormat( List values ) {
for (int i = 0; i < fieldCount; i++) {
String field = values.get(i).trim();
boolean isTime= false;
try {
if ( TimeParser.isIso8601String(field) ) { // allow ISO8601 times.
Units.cdfTT2000.parse(field);
isTime= true;
} else if ( field.matches("\\d+/\\d+/\\d+") ) {
Units.cdfTT2000.parse(field);
isTime= true;
}
} catch (ParseException ex) {
Logger.getLogger(AsciiTableStreamer.class.getName()).log(Level.SEVERE, null, ex);
}
if ( units.get(i)==null ) {
if ( field.contains("$") ) {
units.set(i,Units.dollars);
format.set(i,"%.2f");
} else if ( field.endsWith("%") ) {
units.set(i,Units.percent);
format.set(i,null);
} else if ( isTime ) {
units.set(i,Units.us2000);
format.set(i,null);
} else {
try {
Integer.parseInt(field);
units.set(i,Units.dimensionless);
format.set(i,"%d");
} catch ( NumberFormatException ex ) {
try {
Double.parseDouble(field);
units.set(i,Units.dimensionless);
format.set(i,null);
} catch ( NumberFormatException ex2 ) {
String[] ss= field.split("\\s",-2); // "3.4 sec"
if ( ss.length>1 ) {
try {
Double.parseDouble(ss[0]);
units.set(i,Units.lookupUnits( field.substring( ss[0].length() ).trim() ) );
format.set(i,null);
} catch ( NumberFormatException ex3 ) {
units.set( i, new EnumerationUnits("default") );
format.set(i,null);
}
} else {
units.set( i, new EnumerationUnits("default") );
format.set(i,null);
}
}
}
}
}
}
}
public void addRecord(List values) {
if ( units==null ) {
String s= values.get(Math.min(1,values.size()-1)).trim();
if ( s.length()>0 && Character.isAlphabetic(s.charAt(0)) ) {
addHeader(values);
return;
}
}
if ( fieldCount==-1 ) {
return;
}
if ( initializedFields==false ) {
setUnitsAndFormat(values);
initializedFields= true;
}
DDataSet result= DDataSet.createRank1(fieldCount);
for (int i = 0; i < fieldCount; i++) {
String field = values.get(i).trim();
if ( field.trim().length()==0 ) {
result.putValue( i, fillValues.get(i) );
} else {
try {
Units u= units.get(i);
double d;
if ( u instanceof EnumerationUnits ) {
d= ((EnumerationUnits)u).createDatum(field).doubleValue(u);
} else {
d= u.parse( field ).doubleValue( u );
}
result.putValue( i, d);
} catch (ParseException ex) {
result.putValue( i, fillValues.get(i) );
}
}
}
if ( desc==null ) {
desc= getBundleDescriptor();
}
sendRecord( result );
}
/**
* indicate that no more records will be added. This can only be set to
* false.
* @param t this must be set to false.
*/
public void setHasNext( boolean t ) {
hasNextRecord= false;
}
private void sendRecord( QDataSet result ) {
records.add(result);
}
protected void initialize(List values) {
fieldCount = values.size();
units = new ArrayList<>(fieldCount);
for (int i = 0; i < fieldCount; i++) {
units.add(i, defaultUnits ); // null here means we can reset.
}
format= new ArrayList<>(fieldCount);
labels = new ArrayList<>(fieldCount);
names = new ArrayList<>(fieldCount);
fillValues= new ArrayList<>(fieldCount);
if (labels.isEmpty()) {
for (int i = 0; i < fieldCount; i++) {
labels.add(i, values.get(i));
names.add(i, Ops.safeName(values.get(i)));
format.add("");
fillValues.add(-1e38);
}
}
}
/**
* return true if the header has been set.
* @return true if the header has been set.
*/
public boolean hasHeader() {
return this.fieldCount>-1;
}
protected void addHeader(List values) {
if (fieldCount == -1) {
initialize(values);
}
}
protected void addUnits(List units) {
}
protected void addUnits(int icol, String units) {
}
private QDataSet getBundleDescriptor() {
return new AbstractDataSet() {
@Override
public int rank() {
return 2;
}
@Override
public Object property(String name, int i) {
switch (name) {
case QDataSet.LABEL:
return labels.get(i);
case QDataSet.NAME:
return names.get(i);
case QDataSet.FORMAT:
return format.get(i);
case QDataSet.UNITS:
return units.get(i);
default:
break;
}
return property(name);
}
@Override
public double value(int i0, int i1) {
return 0;
}
@Override
public int length() {
return labels.size();
}
@Override
public int length(int i) {
return 0;
}
};
}
@Override
public boolean hasNext() {
if ( !records.isEmpty() ) return true;
while ( !initializedFields && hasNextRecord ) {
Thread.yield();
}
while ( records.isEmpty() && hasNextRecord ) {
Thread.yield();
}
return hasNextRecord;
}
@Override
public QDataSet next() {
while ( records.isEmpty() ) {
Thread.yield();
}
QDataSet result= records.remove(0);
//result.putProperty(QDataSet.BUNDLE_1, desc);
return result;
}
@Override
public void remove() {
}
}