package org.das2.qds; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.das2.util.LoggerManager; import org.das2.qds.ops.Ops; /** * Extracts a subset of the source dataset by using a rank 1 subset of indeces on each index. * @author jbf */ public class SubsetDataSet extends AbstractDataSet { private static final Logger logger= LoggerManager.getLogger("qdataset"); QDataSet source; QDataSet[] sorts; int[] lens; boolean nonQube=false; private static List indgen( int start, int stop, int stride ) { ArrayList result= new ArrayList<>(); for ( int i=start; i *
  • index, with negative indices relative to the end. *
  • start:stop, with stop exclusive. *
  • start:stop:stride, incrementing stride elements, including negative. *
  • start-stopInclusive, where the trailing index is also included. * * If the spec starts with ~, then these indices are removed. For example:
      *
    • ~5, remove the 5th index. *
    • ~15:20, remove the 5 indices starting at 15. *
    • ~-1, remove the last index. *
    * Note if invert is present, then the indices cannot be reversed. * @param spec * @param dimlen, the amount added to negative indices. * @return the list of integers. * @throws ParseException */ public static int[] parseIndices( String spec, int dimlen ) throws ParseException { Pattern p1= Pattern.compile("(\\-?\\d+)\\-(\\-?\\d+)"); Pattern p2= Pattern.compile("(\\-?\\d+)?\\:(\\-?\\d+)?(\\:(\\-?\\d+)?)?"); boolean invert= spec.length()>1 && spec.charAt(0)=='~'; if ( invert ) { spec= spec.substring(1); } String[] ss= spec.split(","); List result= new ArrayList<>(); int charPos= 0; for (String s : ss) { Matcher m = p2.matcher(s); if (m.matches()) { int start= m.group(1)==null ? 0 : Integer.parseInt(m.group(1)); if ( start<0 ) start+= dimlen; int stop= m.group(2)==null ? dimlen : Integer.parseInt(m.group(2)); if ( stop<0 ) stop+= dimlen; int stride= m.group(4)==null ? 1 : Integer.parseInt(m.group(4)); List ii= indgen( start, stop, stride ); result.addAll( ii ); } else { m = p1.matcher(s); if (m.matches()) { int start= m.group(1)==null ? 0 : Integer.parseInt(m.group(1)); if ( start<0 ) start+= dimlen; int stop= m.group(2)==null ? dimlen : Integer.parseInt(m.group(2)); if ( stop<0 ) stop+= dimlen; List ii; if ( start>stop ) { ii= indgen( stop, start+1, 1 ); } else { ii= indgen( start, stop+1, 1 ); } result.addAll( ii ); } else { try { int ii = Integer.parseInt(s); if ( ii<0 ) ii+=dimlen; result.add(ii); } catch ( NumberFormatException ex ) { throw new ParseException("unable to parse: "+s,charPos); } } } charPos+= 1+s.length(); } int[] iresult; if ( invert ) { iresult= new int[dimlen-result.size()]; Collections.sort(result); int resultIndex= 0; int outputIndex= 0; int ii= result.get(resultIndex); for ( int i=0; i0 ) throw new IllegalArgumentException("unable to applyIndex on non-qube source dataset"); if ( idx.rank()==1 ) { QDataSet max= Ops.reduceMax( idx, 0 ); if ( max.value()>=lens[idim] ) { logger.log(Level.WARNING, "idx dataset contains maximum that is out-of-bounds: {0}", max); } } sorts[idim]= idx; lens[idim]= idx.length(); if ( idx.rank()>1 ) { throw new IllegalArgumentException("indexes must be rank 1"); } QDataSet dep= (QDataSet)property( "DEPEND_"+idim ); if ( dep==null ) { dep= (QDataSet) source.property( "DEPEND_"+idim ); } if ( dep!=null ) { SubsetDataSet dim= new SubsetDataSet( dep ); switch (dim.rank()) { case 1: dim.applyIndex(0,idx); break; case 2: dim.applyIndex(1,idx); break; default: throw new IllegalArgumentException("DEPEND_"+idim+" must be rank 1 or rank 2"); } putProperty("DEPEND_"+idim,dim); } for ( int i=idim+1; i=2 ) { SubsetDataSet dim= new SubsetDataSet( dep ); dim.applyIndex(idim,idx); putProperty("DEPEND_"+i,dim); } } if ( idim==0 ) { // DEPEND_1-4 can be rank 2, where the 0th dimension corresponds to DEPEND_0. for ( int i=1; i1 ) { SubsetDataSet dim= new SubsetDataSet( depi ); dim.applyIndex( 0, idx ); putProperty("DEPEND_"+i, dim ); } } } // support subset to get a subset of bundled datasets. QDataSet bundle= (QDataSet)property( "BUNDLE_"+idim ); if ( bundle==null ) { bundle= (QDataSet) source.property( "BUNDLE"+idim ); } if ( bundle!=null ) { SubsetDataSet b= new SubsetDataSet(bundle); b.applyIndex( 0, idx ); putProperty( "BUNDLE_"+idim, b ); } } @Override public int rank() { return source.rank(); } @Override public int length() { return lens[0]; } @Override public int length(int i) { return nonQube ? source.length(i) : lens[1]; } @Override public int length(int i, int j) { return nonQube ? source.length(i,j) : lens[2]; } @Override public int length(int i, int j, int k) { return nonQube ? source.length(i,j,k) : lens[3]; } @Override public double value() { return source.value(); } @Override public double value(int i) { return source.value((int)sorts[0].value(i)); } @Override public double value(int i0, int i1) { return source.value((int)sorts[0].value(i0),(int)sorts[1].value(i1)); } @Override public double value(int i0, int i1, int i2) { return source.value((int)sorts[0].value(i0),(int)sorts[1].value(i1),(int)sorts[2].value(i2)); } @Override public double value(int i0, int i1, int i2, int i3) { return source.value((int)sorts[0].value(i0),(int)sorts[1].value(i1),(int)sorts[2].value(i2),(int)sorts[3].value(i3)); } @Override public Object property(String name, int i) { Object v= properties.get(name); return v!=null ? v : source.property(name, ((int)sorts[0].value(i)) ); } @Override public Object property(String name) { Object v= properties.get(name); return v!=null ? v : source.property(name); } }