package org.das2.qds;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.das2.datum.Datum;
import org.das2.datum.DatumRange;
import org.das2.datum.DatumUtil;
import org.das2.datum.Units;
import org.das2.util.LoggerManager;
import org.das2.util.StringTools;
import org.das2.util.monitor.NullProgressMonitor;
import org.das2.util.monitor.ProgressMonitor;
import static org.das2.qds.DataSetOps.grid;
import static org.das2.qds.DataSetOps.slice1;
import static org.das2.qds.DataSetOps.slice2;
import static org.das2.qds.DataSetOps.slice3;
import org.das2.qds.filters.ApplyIndexEditorPanel;
import org.das2.qds.ops.Ops;
import org.das2.qds.util.BinAverage;
import org.das2.qds.util.Reduction;
/**
* Implement process chain like "|cleanData()|accum()", performing each command of the sequence.
* @author jbf
*/
public class OperationsProcessor {
private static final Logger logger= LoggerManager.getLogger("qdataset.ops");
/**
* pop off the single or double quotes delimiting a string, if found.
* @param s a string argument, possibly surrounded with quotes.
* @return the string without the quotes.
*/
private static String getStringArg( String s ) {
String comp= s.trim();
if ( comp.startsWith("'") && comp.endsWith("'") ) {
comp= comp.substring(1,comp.length()-1);
} else if ( comp.startsWith("\"") && comp.endsWith("\"") ) {
comp= comp.substring(1,comp.length()-1);
}
return comp;
}
/**
* container for the logic for slicing at an index vs slicing at a datum. If the string is
* an integer, then we return the index. If the index is a string, then we need to
* find the corresponding index to the rank 0 dataset elsewhere.
* If the string argument is not parseable, then deft is returned.
* @param arg String that encodes a datum position or index.
* @param deft default value.
* @return an integer index or a dataset indicating the index.
*/
public static Object getArgumentIndex( String arg, int deft ) {
try {
int idx= Integer.parseInt(arg);
return idx;
} catch ( NumberFormatException ex ) {
arg= arg.trim();
if ( arg.length()>2 && arg.startsWith("'") && arg.endsWith("'") ) {
arg= arg.substring(1,arg.length()-1);
}
if ( arg.length()>2 && arg.startsWith("\"") && arg.endsWith("\"") ) {
arg= arg.substring(1,arg.length()-1);
}
try {
QDataSet ds= Ops.dataset( arg );
return ds;
} catch ( IllegalArgumentException ex2 ) {
return deft;
}
}
}
/**
* @deprecated
* @see #process(org.das2.qds.QDataSet, java.lang.String, org.das2.util.monitor.ProgressMonitor)
* @param c process string like "slice0(9)|histogram()"
* @param fillDs The dataset loaded from the data source controller, with initial filters (like fill) applied.
* @param mon monitor for the processing.
* @return
* @throws Exception
*/
public static QDataSet sprocess( String c, QDataSet fillDs, ProgressMonitor mon ) throws Exception {
return process( fillDs, c, mon );
}
/**
* process implements the poorly-named filters string / process string of Autoplot, allowing
* clients to "pipe" data through a chain of operations. For example, the filters string
* "|slice0(9)|histogram()" will slice on the ninth index and then take a histogram of that
* result. See http://www.papco.org/wiki/index.php/DataReductionSpecs (TODO: wiki page was lost,
* which could probably be recovered.) There's a big problem here:
* if the command is not recognized, then it is ignored. We should probably change this,
* but the change should be at a major version change in case it breaks things.
* @param ds The dataset loaded from the data source controller, with initial filters (like fill) applied.
* @param c process string like "slice0(9)|histogram()"
* @param mon monitor for the processing.
* @throws ParseException when the string cannot be parsed
* @throws Exception when a function cannot be processed (e.g. index out of bounds)
* @return the dataset after the process string is applied.
* @see http://autoplot.org/developer.dataset.filters
* @see http://autoplot.org/developer.panel_rank_reduction
* @see org.das2.qds.filters.FilterEditorPanel
* @see https://sourceforge.net/p/autoplot/bugs/2378/
*/
public static QDataSet process( QDataSet ds, String c, ProgressMonitor mon ) throws Exception {
logger.log(Level.FINE, "process({0},{1})", new Object[] { c, ds } );
boolean sprocessCache= "true".equals( System.getProperty("referenceCaching2","false") );
if ( mon==null ) mon= new NullProgressMonitor();
QDataSet ds0= ds;
int i=1;
//Scanner s= new Scanner( c );
//s.useDelimiter("[\\(\\),]");
long t0= System.currentTimeMillis();
String[] commands= StringTools.guardedSplit( c, "\\|", '\'' );
String cmd="";
try {
mon.started();
for ( String command : commands ) {
if ( command.trim().length()==0 ) continue;
Scanner s= new Scanner( command );
s.useDelimiter("[\\(\\),]");
cmd= "|"+s.next();
cmd= cmd.replaceAll( "\\|\\s*", "|" ); // https://sourceforge.net/p/autoplot/feature-requests/288/
i= c.indexOf(cmd,i);
logger.log(Level.FINER, " cmd \"{0}\"", cmd );
if ( cmd.length()==0 ) continue;
mon.setProgressMessage("performing "+cmd.substring(1));
if ( logger.isLoggable(Level.FINEST) ) { // this has proved useful for debugging.
System.err.println( "---------------------" );
System.err.println( ds );
System.err.println( "dep0=" + ds.property(QDataSet.DEPEND_0) );
System.err.println( "bundle0=" + ds.property(QDataSet.BUNDLE_0) );
System.err.println( "dep1=" + ds.property(QDataSet.DEPEND_1) );
System.err.println( "bundle1=" + ds.property(QDataSet.BUNDLE_1) );
System.err.println( " the next command is "+ cmd );
}
if ( cmd.startsWith("|slices") && cmd.length()==7 ) { // multi dimensional slice
int[] dims= DataSetUtil.qubeDims(ds);
Pattern skipPattern= Pattern.compile("\\'\\:?\\'");
Pattern skipPattern2= Pattern.compile("\\:");
List