package org.autoplot.jythonsupport; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.text.ParseException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import org.das2.datum.Datum; import org.das2.datum.DatumUtil; import org.das2.datum.EnumerationUnits; import org.das2.datum.InconvertibleUnitsException; import org.das2.datum.Units; import org.das2.datum.UnitsConverter; import org.das2.qds.ops.Ops; import org.das2.qds.DataSetIterator; import org.das2.qds.IndexListDataSetIterator; import org.das2.qds.QubeDataSetIterator; import org.python.core.Py; import org.python.core.PyFloat; import org.python.core.PyInteger; import org.python.core.PyIterator; import org.python.core.PyJavaInstance; import org.python.core.PyList; import org.python.core.PyLong; import org.python.core.PyNone; import org.python.core.PyObject; import org.python.core.PyReflectedFunction; import org.python.core.PySequence; import org.python.core.PySlice; import org.python.core.PyString; import org.das2.qds.ArrayDataSet; import org.das2.qds.DDataSet; import org.das2.qds.DataSetOps; import org.das2.qds.DataSetUtil; import org.das2.qds.MutablePropertyDataSet; import org.das2.qds.QDataSet; import org.das2.qds.SemanticOps; import org.das2.qds.TrimStrideWrapper; import org.das2.qds.WritableDataSet; import org.das2.qds.ops.CoerceUtil; /** * PyQDataSet wraps a QDataSet to provide Python operator overloading and * indexing. For example, the Python plus "+" operator is implemented in the * method "__add__", and ds[0,:] is implemented in __getitem__ and __setitem__. * The class PyQDataSetAdapter is responsible for creating the PyQDataSet when * a QDataSet is brought into the Python interpreter. * * @author jbf */ public final class PyQDataSet extends PyJavaInstance { private static final Logger logger= Logger.getLogger("jython"); WritableDataSet ds; MutablePropertyDataSet mpds; QDataSet rods; // read-only dataset Units units; // indicates if the units have been set. If the data is a bundle, this should not be set. int serialNumber; private static final AtomicInteger _seq= new AtomicInteger(1000); public PyQDataSet( ) { throw new IllegalArgumentException("no-arg constructor is not supported"); } /** * Note getDataSet will always provide a writable dataset. * @param ds */ public PyQDataSet(QDataSet ds) { super(ds); this.serialNumber= _seq.incrementAndGet(); if (ds instanceof WritableDataSet && !((WritableDataSet)ds).isImmutable() ) { this.ds = (WritableDataSet) ds; this.mpds= (MutablePropertyDataSet) ds; this.rods= ds; } else if ( ds instanceof MutablePropertyDataSet && !((MutablePropertyDataSet)ds).isImmutable() ) { this.ds = null; this.mpds= (MutablePropertyDataSet) ds; this.rods= ds; } else if (ds.rank() == 0) { this.ds = null; this.rods = ds; } else { logger.fine("read-only dataset will not support writing."); this.ds= null; this.rods= ds; } this.units= (Units)ds.property(QDataSet.UNITS); } public QDataSet getQDataSet() { return this.rods; } /** * return the serial number. * @return */ public int getSerialNumber() { return serialNumber; } /* plus, minus, multiply, divide */ @Override public PyQDataSet __add__(PyObject arg0) { if ( arg0 instanceof PyInteger && !arg0.__nonzero__() ) { // special check to support "sum" which starts with zero. return this; } QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.add(rods, that)); } @Override public PyObject __radd__(PyObject arg0) { return __add__(arg0); } @Override public PyObject __sub__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.subtract(rods, that)); } @Override public PyObject __rsub__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.subtract(that, rods)); } @Override public PyObject __mul__( PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.multiply(rods, that)); } @Override public PyObject __rmul__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.multiply(that, rods)); } @Override public PyObject __div__( PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.divide(rods, that)); } @Override public PyObject __rdiv__( PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.divide(that, rods)); } @Override public PyObject __floordiv__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.div(rods, that)); } @Override public PyObject __mod__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.mod(rods, that)); } @Override public PyObject __rfloordiv__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.div(that, rods)); } @Override public PyObject __rmod__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.mod(that, rods)); } /* unary negate and plus operator */ @Override public PyObject __pos__() { return this; } @Override public PyObject __neg__() { return new PyQDataSet(Ops.negate(rods)); } @Override public PyObject __abs__() { return new PyQDataSet(Ops.abs(rods)); } /* pow operator (**) */ @Override public PyObject __pow__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.pow(rods, that)); } @Override public PyObject __rpow__(PyObject arg0) { QDataSet that = coerce_ds(arg0); return new PyQDataSet(Ops.pow(that, rods)); } @Override public boolean __nonzero__() { if ( this.rods.rank()>0 ) { throw new IllegalArgumentException("data must be rank 0"); } Units u= (Units)this.rods.property(QDataSet.UNITS); if ( u!=null && u.getOffsetUnits()!=u ) { throw new IllegalArgumentException("data must be dimensionless or a ratiometric datum."); } else { return this.rods.value()!=0; } } @Override public PyObject __int__() { if ( rods.rank()>0 ) { throw Py.TypeError("PyQDataSet with rank="+rods.rank()+" found where rank 0 was expected"); } return Py.newInteger((int)rods.value()); } @Override public PyFloat __float__() { if ( rods.rank()>0 ) { throw Py.TypeError("PyQDataSet with rank="+rods.rank()+" found where rank 0 was expected"); } return Py.newFloat(rods.value()); } @Override public PyLong __long__() { if ( rods.rank()>0 ) { throw Py.TypeError("PyQDataSet with rank="+rods.rank()+" found where rank 0 was expected"); } return Py.newLong((int)rods.value()); } private final static Map binaryInfixMethods; static { binaryInfixMethods= new HashMap<>(); //TODO: what is this? binaryInfixMethods.put( "gt", new PyReflectedFunction("gt") ); for ( Method m: BinaryInfixOps.class.getMethods() ) { PyReflectedFunction func= binaryInfixMethods.get(m.getName()); if ( func==null ) { func= new PyReflectedFunction(m.getName()); binaryInfixMethods.put( m.getName(), func ); } func.addMethod(m); } } @Override public PyObject __ge__(PyObject o) { PyObject r= BinaryInfixOps.ge( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .ge operator"); } else { return r; } } @Override public PyObject __gt__(PyObject o) { PyObject r= BinaryInfixOps.gt( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .gt operator"); } else { return r; } } @Override public PyObject __le__(PyObject o) { PyObject r= BinaryInfixOps.le( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .le operator"); } else { return r; } } @Override public PyObject __lt__(PyObject o) { PyObject r= BinaryInfixOps.lt( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .lt operator"); } else { return r; } } @Override public PyObject __eq__(PyObject o) { PyObject r= BinaryInfixOps.eq( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .eq operator"); } else { return r; } } @Override public PyObject __ne__(PyObject o) { PyObject r= BinaryInfixOps.ne( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .ne operator"); } else { return r; } } @Override public PyObject __and__(PyObject o) { PyObject r= BinaryInfixOps.and( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .and operator"); } else { return r; } } @Override public PyObject __or__(PyObject o) { PyObject r= BinaryInfixOps.or( this, o ); if ( r instanceof PyQDataSet ) { throw new IllegalArgumentException("use .or operator"); } else { return r; } } @Override public PyObject __findattr__(String name) { PyReflectedFunction func= binaryInfixMethods.get(name); if ( func!=null ) { return func; } else { return super.__findattr__(name); } } @Override public void __delattr__(String attr) { if ( binaryInfixMethods.remove(attr)==null ) { super.__delattr__(attr); } } @Override public void __setattr__(String name, PyObject value) { if ( binaryInfixMethods.containsKey(name) ) { binaryInfixMethods.remove(name); } super.__setattr__(name, value); } @Override public PyObject invoke(String name) { PyReflectedFunction func= binaryInfixMethods.get(name); if ( func!=null ) { return func.__call__(this); } else { return super.invoke(name); } } @Override public PyObject invoke(String name, PyObject arg1) { PyReflectedFunction func= binaryInfixMethods.get(name); if ( func!=null ) { return func.__call__(this,arg1); } else if ( name.equals("property") ) { if ( arg1 instanceof PyString ) { return Py.java2py(this.rods.property(arg1.toString())); } else { return super.invoke(name,arg1); } } else { return super.invoke(name,arg1); } } /** * experiment with making the dataset mutable, even after it has been * made immutable. */ private void makeMutable() { logger.log(Level.FINE, "makeMutable called using: {0}", rods); if ( ds==null ) { this.ds= Ops.copy(rods); this.mpds= ds; this.rods= ds; } else { this.ds= Ops.copy(ds); this.mpds= ds; this.rods= ds; } } @Override public PyObject invoke(String name, PyObject arg1, PyObject arg2) { PyReflectedFunction func= binaryInfixMethods.get(name); if ( func!=null ) { return func.__call__(this,arg1,arg2); } else { switch (name) { case "putProperty": if ( mpds==null || this.mpds.isImmutable() ) { makeMutable(); } this.putProperty( (PyString)arg1, arg2 ); return Py.None; case "property": if ( arg1 instanceof PyString && arg2 instanceof PyInteger ) { return Py.java2py(this.rods.property(arg1.toString(),((PyInteger)arg2).getValue())); } else { return super.invoke(name,arg1,arg2); } default: return super.invoke(name,arg1,arg2); } } } @Override public PyObject invoke(String name, PyObject[] args, String[] keywords) { PyReflectedFunction func= binaryInfixMethods.get(name); if ( func!=null ) { return func.__call__(this,args,keywords); } else { return super.invoke(name,args,keywords); } } @Override public PyObject invoke(String name, PyObject[] args) { PyReflectedFunction func= binaryInfixMethods.get(name); if ( func!=null ) { return func.__call__(this); } else { return super.invoke(name,args); } } protected static Number getNumber( Object po ) { if ( po instanceof QDataSet ) { QDataSet qpo= (QDataSet)po; if ( qpo.rank()==0 ) { return qpo.value(); } else { throw Py.TypeError("QDataSet with rank>0 found where number was expected"); } } else if ( po instanceof PyQDataSet ) { PyQDataSet pqd= ((PyQDataSet)po); QDataSet qpo= pqd.rods; if ( qpo.rank()==0 ) { return qpo.value(); } else { throw Py.TypeError("PyQDataSet with rank>0 found where number was expected"); } } else if ( po instanceof PyObject ) { Object result= ((PyObject)po).__tojava__( Number.class ); if ( result==Py.NoConversion ) { throw Py.TypeError("can't convert to number: "+((PyObject)po).__repr__() ); } return (Number) result; } else { if ( po instanceof Number ) { return (Number) po; } else { throw Py.TypeError("can't convert to number: "+po ); } } } @Override public int __len__() { return rods.length(); } /** * bug 1623, where we might want to interpret the PyList as * a rank 1 dataset. * @param arg0 any python object * @return the same object, or rank 1 PyQDataSet when PyList can be adapted. */ private PyObject maybeAdaptList( PyObject arg0 ) { if ( arg0 instanceof PyList ) { PyList list= ((PyList)arg0); if ( list.size()>0 ) { Object o= list.get(0); if ( o instanceof Number ) { arg0= new PyQDataSet( PyQDataSetAdapter.adaptList(list) ); } else if ( o instanceof QDataSet || o instanceof Datum ) { arg0= new PyQDataSet( PyQDataSetAdapter.adaptList(list) ); } else if ( o instanceof PyInteger || o instanceof PyFloat || o instanceof PyLong ) { arg0= new PyQDataSet( PyQDataSetAdapter.adaptList(list) ); } } } return arg0; } /* accessor and mutator */ /** * This implements the Python indexing, such as data[4,:,3:5]. Note this * includes many QDataSet operations: a single index represents a slice, a * range like 3:5 is a trim, an array is a sort, and a colon leaves a dimension * alone. See http://autoplot.org/developer.python.indexing * * TODO: preserve metadata * TODO: verify all index types. * @param arg0 various python types http://autoplot.org/developer.python.indexing * @return element or subset of data. */ @Override public PyObject __getitem__(PyObject arg0) { if ( arg0 instanceof PyList ) { arg0= maybeAdaptList((PyList)arg0); } Object o = arg0.__tojava__(QDataSet.class); if (o == null || o == Py.NoConversion) { if (arg0 instanceof PySlice) { PySlice slice = (PySlice) arg0; Number start = (Number) getNumber( slice.start ); Number stop = (Number) getNumber(slice.stop ); Number step = (Number) getNumber(slice.step ); if ( step==null || step.equals(1) ) { if ( start==null ) start= 0; if ( stop==null ) stop= rods.length(); if ( start.intValue()<0 ) start= rods.length() + start.intValue(); // support negative indices if ( stop.intValue()<0 ) stop= rods.length() + stop.intValue(); return new PyQDataSet( rods.trim( start.intValue(), stop.intValue() ) ); } else { TrimStrideWrapper wds= new TrimStrideWrapper(rods); wds.setTrim( 0, start, stop, step ); // supports negative indices return new PyQDataSet(wds); } } else if (arg0.isNumberType()) { int idx = ((Number) arg0.__tojava__(Number.class)).intValue(); if ( idx<0 ) { idx= rods.length()+idx; // support negative indices } QDataSet sds= rods.slice(idx); //TODO: why is this not the writable dataset, or a copy of it? //TODO: properties and context. return new PyQDataSet( sds ); } else if (arg0.isSequenceType()) { PySequence slices = (PySequence) arg0; if ( slices.__len__()==2 && slices.__getitem__(1) instanceof PyInteger ) { // sf 3473406: optimize for ds[:,0] to use unbundle if ( slices.__getitem__(0) instanceof PySlice ) { int index= ((Number)slices.__getitem__(1).__tojava__( Number.class )).intValue(); if ( index<0 ) index= rods.length(0) + index; // support negative indices QDataSet unb1= DataSetOps.unbundle( rods, index, false ); PySlice slice = (PySlice) slices.__getitem__(0); if ( slice.start instanceof PyNone && slice.stop instanceof PyNone && slice.step instanceof PyNone ) { return new PyQDataSet( unb1 ); } else if ( slice.step instanceof PyNone || ((Number)slice.step.__tojava__(Number.class)).intValue()==1 ) { // use native trim if possible. int start= slice.start.isNumberType() ? ((Number)slice.start.__tojava__( Number.class )).intValue() : 0; int stop= slice.stop.isNumberType() ? ((Number)slice.stop.__tojava__( Number.class )).intValue() : unb1.length(); if ( start<0 ) start= unb1.length()+start; if ( stop<0 ) stop= unb1.length()+stop; return new PyQDataSet( unb1.trim(start,stop) ); } } } if ( slices.__len__()>rods.rank() ) { throw new IllegalArgumentException("rank "+slices.__len__()+" access on a rank "+rods.rank()+" dataset" ); } Map bundleProps= new HashMap(); QDataSet[] lists= new QDataSet[slices.__len__()]; boolean allLists= true; boolean betterBeAllLists= false; QubeDataSetIterator iter = new QubeDataSetIterator(rods); for (int i = 0; i < slices.__len__(); i++) { PyObject a = slices.__getitem__(i); if ( ! ( a instanceof PyQDataSet ) && !( a instanceof PyInteger || a instanceof PyFloat ) ) { allLists= false; } QubeDataSetIterator.DimensionIteratorFactory fit= null; if (a instanceof PySlice) { PySlice slice = (PySlice) a; Number start = (Number) getNumber(slice.start); // TODO: for the 0th index and step=1, native trim can be used. Number stop = (Number) getNumber(slice.stop); Number step = (Number) getNumber(slice.step); fit = new QubeDataSetIterator.StartStopStepIteratorFactory(start, stop, step); // supports negative indices } else if ( a instanceof PyQDataSet ) { Object o2 = a.__tojava__(QDataSet.class); QDataSet that = (QDataSet) o2; switch (that.rank()) { case 0: int idx = (int)(that.value()); fit = new QubeDataSetIterator.SingletonIteratorFactory(idx); // supports negative indices break; case 1: fit = new QubeDataSetIterator.IndexListIteratorFactory(that); // supports negative indices break; default: betterBeAllLists= true; break; } lists[i]= ((PyQDataSet)a).rods; } else if (a.isNumberType()) { int idx = ((Number) getNumber( a )).intValue(); fit = new QubeDataSetIterator.SingletonIteratorFactory(idx); // supports negative indices if ( i==rods.rank()-1 ) { QDataSet bds= (QDataSet) rods.property( "BUNDLE_"+i ); if ( bds!=null && rods.property( "DEPEND_"+i )==null ) { // https://sourceforge.net/p/autoplot/bugs/1478/ DataSetUtil.sliceProperties( bds, idx, bundleProps ); } } lists[i]= DataSetUtil.asDataSet( idx ); } else { QDataSet that = coerce_ds(a); fit = new QubeDataSetIterator.IndexListIteratorFactory(that); // supports negative indices lists[i]= DataSetUtil.asDataSet( that ); } if ( fit!=null ) iter.setIndexIteratorFactory(i, fit); } if ( betterBeAllLists && !allLists ) { throw new IllegalArgumentException("index error, because all indeces must be lists."); } ArrayDataSet result; if ( allLists && lists.length == rods.rank() ) { lists = checkIndexBundle( lists ); result= DataSetOps.applyIndexAllLists( rods, lists ); // supports negative indices } else { result= iter.createEmptyDs(); QubeDataSetIterator resultIter = new QubeDataSetIterator(result); while (iter.hasNext()) { iter.next(); double d = iter.getValue(rods); resultIter.next(); resultIter.putValue(result, d); } } DataSetUtil.copyDimensionProperties( rods, result ); if ( !bundleProps.isEmpty() ) { DataSetUtil.putProperties( bundleProps, result ); } return new PyQDataSet(result); } else { throw Py.TypeError("invalid index type: "+arg0); } } else { QDataSet that = (QDataSet) o; DataSetIterator iter = new QubeDataSetIterator(rods); QDataSet dep0= null; if ( that.rank()>1 && ( SemanticOps.isBundle(that) || SemanticOps.isLegacyBundle(that) ) ) { for ( int j=0; j1 ) { WritableDataSet result= DataSetOps.applyIndexAllLists( rods, new QDataSet[] { that } ); DataSetUtil.copyDimensionProperties( rods, result ); return new PyQDataSet(result); } else { QubeDataSetIterator.DimensionIteratorFactory fit = new QubeDataSetIterator.IndexListIteratorFactory(that); ((QubeDataSetIterator)iter).setIndexIteratorFactory(0, fit); dep0= (QDataSet) rods.property(QDataSet.DEPEND_0); if ( dep0!=null ) { dep0= DataSetOps.applyIndex( dep0, 0, that, false ); } } DDataSet result= iter.createEmptyDs(); //TODO: look at DataSetOps.applyIndex sometime QubeDataSetIterator resultIter = new QubeDataSetIterator(result); while (iter.hasNext()) { iter.next(); double d = iter.getValue(rods); resultIter.next(); resultIter.putValue(result, d); } if ( dep0!=null && dep0.length()==result.length() ) { result.putProperty( QDataSet.DEPEND_0, dep0 ); // yeah, we did it right! } DataSetUtil.copyDimensionProperties( rods, result ); return new PyQDataSet(result); } } /** * returns null if the data does not have a bundle, otherwise the bundle is returned. * @return */ private QDataSet getBundle() { QDataSet bds; if ( this.ds.rank()==1 ) { bds = (QDataSet) this.ds.property(QDataSet.BUNDLE_0); } else { bds = (QDataSet) this.ds.property(QDataSet.BUNDLE_1); } return bds; } /** * Assign the values to the indeces specified. * Note, if the value has units and the PyQDataSet does not yet have units, * then the units are assigned. * See http://autoplot.org/developer.python.indexing * @param arg0 the indeces * @param arg1 the values to assign */ @Override public void __setitem__(PyObject arg0, PyObject arg1) { if ( ds==null || ds.isImmutable() ) { makeMutable(); } DataSetIterator iter = new QubeDataSetIterator(ds); if ( arg0 instanceof PyList ) { arg0= maybeAdaptList((PyList)arg0); } if (!arg0.isSequenceType()) { PyObject a = arg0; QubeDataSetIterator.DimensionIteratorFactory fit; if (a instanceof PySlice) { PySlice slice = (PySlice) a; Integer start = ( slice.start==Py.None ) ? null : getInteger( slice.start ); Integer stop = ( slice.stop==Py.None ) ? null : getInteger( slice.stop ); Integer step = ( slice.step==Py.None ) ? null : getInteger( slice.step ); fit = new QubeDataSetIterator.StartStopStepIteratorFactory(start, stop, step); } else if (a.isNumberType()) { int idx = ( (Number) a.__tojava__(Number.class) ).intValue(); fit = new QubeDataSetIterator.SingletonIteratorFactory(idx); } else { Object o = a.__tojava__(QDataSet.class); QDataSet that = (QDataSet) o; fit = new QubeDataSetIterator.IndexListIteratorFactory(that); } ((QubeDataSetIterator) iter).setIndexIteratorFactory(0, fit); } else if (arg0 instanceof PyQDataSet) { Object o = arg0.__tojava__(QDataSet.class); QDataSet that = (QDataSet) o; if (ds.rank() > 1) { if ( SemanticOps.isRank1Bundle(that) ) { for ( int k=0; kmaxRank ) maxRank=rods1.rank(); } } if (allLists) { QDataSet val = coerceDsInternal(arg1); lists = checkIndexBundle(lists); if (units == null) { // see repeat code below. Return requires repetition. logger.fine("resetting units based on values assigned"); Units u = SemanticOps.getUnits(val); if ( u!=Units.dimensionless ) { if ( getBundle()==null ) { this.ds.putProperty(QDataSet.UNITS, u); units = u; } else { MutablePropertyDataSet bds= (MutablePropertyDataSet) getBundle(); if ( bds!=null && ds.rank()==2 ) { int column= (int)lists[1].value() ; Units columnUnits= (Units)bds.property( QDataSet.UNITS, column ); if ( columnUnits==null ) { bds.putProperty( QDataSet.UNITS, column, u ); } } } } } if ( maxRank==0 ) { switch ( ds.rank() ) { case 0: ds.putValue(val.value()); break; case 1: ds.putValue(((int)lists[0].value()),val.value()); break; case 2: ds.putValue(((int)lists[0].value()),((int)lists[1].value()),val.value()); break; case 3: ds.putValue(((int)lists[0].value()),((int)lists[1].value()),((int)lists[2].value()),val.value()); break; case 4: ds.putValue(((int)lists[0].value()), ((int)lists[1].value()), ((int)lists[2].value()), ((int)lists[3].value()),val.value()); break; } } else { setItemAllLists( ds, lists, val); } return; } else { int[] qubeDims = DataSetUtil.qubeDims(ds); for (int i = 0; i < slices.__len__(); i++) { PyObject a = slices.__getitem__(i); QubeDataSetIterator.DimensionIteratorFactory fit; if (a instanceof PySlice) { PySlice slice = (PySlice) a; // TODO: why not the same as 75 lines prior? Integer start = (Integer) slice.start.__tojava__(Integer.class); Integer stop = (Integer) slice.stop.__tojava__(Integer.class); Integer step = (Integer) slice.step.__tojava__(Integer.class); fit = new QubeDataSetIterator.StartStopStepIteratorFactory(start, stop, step); } else if (a.isNumberType() && !(a instanceof PyQDataSet)) { if (a instanceof PyFloat) { throw new IllegalArgumentException("float used to index array"); } int idx = (Integer) a.__tojava__(Integer.class); if (idx < 0) { if (i == 0 || qubeDims != null) { idx = i == 0 ? (ds.length() + idx) : (qubeDims[i] + idx); } else { throw new IllegalArgumentException("negative index not supported for non-qube."); } } fit = new QubeDataSetIterator.SingletonIteratorFactory(idx); } else { QDataSet that = coerce_ds(a); if (that.rank() == 0) { fit = new QubeDataSetIterator.SingletonIteratorFactory((int) that.value()); } else { fit = new QubeDataSetIterator.IndexListIteratorFactory(that); } } ((QubeDataSetIterator) iter).setIndexIteratorFactory(i, fit); } } } QDataSet val = coerceDsInternal(arg1); if ( units==null ) { // see repeat code above. logger.fine("resetting units based on values assigned"); Units u= SemanticOps.getUnits(val); if ( getBundle()==null && u!=Units.dimensionless ) { this.ds.putProperty(QDataSet.UNITS,u); units= u; } } // figure out what the fill value will be. Number fill= (Number)val.property(QDataSet.FILL_VALUE); if ( fill!=null ) { if ( this.ds.property(QDataSet.FILL_VALUE)!=null ) { fill= (Number)this.ds.property(QDataSet.FILL_VALUE); } } else { fill= (Number)this.ds.property(QDataSet.FILL_VALUE); } boolean resultHasFill= false; if ( fill==null ) { fill= -1e38; } double dfill= fill.doubleValue(); // see org.das2.qds.ops.CoerceUtil, make version that makes iterators. if ( val.rank()==0 ) { if ( Ops.valid(val).value()==0 ) { while (iter.hasNext()) { iter.next(); iter.putValue(ds, dfill ); resultHasFill= true; } } else { while (iter.hasNext()) { iter.next(); iter.putRank0Value(ds, val ); } } } else if ( val.rank()!=iter.rank() ) { throw new IllegalArgumentException("not supported, couldn't reconcile ranks in set[" + val + "]=" + iter ); } else { QDataSet wds= DataSetUtil.weightsDataSet(val); QubeDataSetIterator it = new QubeDataSetIterator(val); if ( SemanticOps.isBundle(val) && !DataSetUtil.oneUnit(val) ) { while (it.hasNext()) { it.next(); if ( !iter.hasNext() ) throw new IllegalArgumentException("assigned dataset has too many elements"); iter.next(); double w = it.getValue(wds); if ( w==0 ) { iter.putValue(ds, dfill); resultHasFill= true; } else { iter.putRank0Value( ds, it.getRank0Value(val) ); } } } else { UnitsConverter uc; if ( units==null ) { //TODO: how did this work before? uc= UnitsConverter.IDENTITY; } else { try { uc= SemanticOps.getUnits(val).getConverter(units); } catch ( InconvertibleUnitsException ex ) { uc= UnitsConverter.IDENTITY; } } while (it.hasNext()) { it.next(); if ( !iter.hasNext() ) throw new IllegalArgumentException("assigned dataset has too many elements"); iter.next(); double w = it.getValue(wds); if ( w==0 ) { double d = dfill; iter.putValue(ds, d); resultHasFill= true; } else { double d = uc.convert(it.getValue(val)); iter.putValue(ds, d); } } } if ( iter.hasNext() ) { iter.next(); // allow off-by-one so that assignment of diffs is allowed. if ( iter.hasNext() ) { throw new IllegalArgumentException("assigned dataset has too few elements"); } else { logger.log(Level.FINE, "allowing suspect dataset assignment, where there is an extra element which was not assigned: {0}", iter); } } } if ( resultHasFill ) { Number tfill= (Number)this.ds.property(QDataSet.FILL_VALUE); if ( tfill==null ) { logger.fine("add FILL_VALUE to dataset"); this.ds.putProperty( QDataSet.FILL_VALUE, fill ); } } } /** * the output of the where command of a rank N dataset is a rank 2 dataset * idx[M,N] where M cases where the condition was true. This means that * the first dataset might be a bundle of indeces, and here we break them * out to N datasets. * @see https://github.com/autoplot/dev/blob/master/bugs/2134/indexWithRank2_case5.jy * @param lists * @return */ private QDataSet[] checkIndexBundle(QDataSet[] lists) { if ( lists.length