ucar.ma2.Section Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of netcdf Show documentation
Show all versions of netcdf Show documentation
The NetCDF-Java Library is a Java interface to NetCDF files,
as well as to many other types of scientific data formats.
The newest version!
/*
* Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.ma2;
import java.util.*;
/**
* A section of multidimensional array indices.
* Represented as List.
* Immutable if makeImmutable() was called.
*
* @author caron
*/
public class Section {
private List list;
private boolean immutable = false;
/**
* Create Section from a shape array, assumes 0 origin.
*
* @param shape array of lengths for each Range. 0 = EMPTY, < 0 = VLEN
*/
public Section(int[] shape) {
list = new ArrayList();
for (int i = 0; i < shape.length; i++) {
if (shape[i] > 0 )
list.add(new Range(shape[i]));
else if (shape[i] == 0 )
list.add(Range.EMPTY);
else
list.add(Range.VLEN);
}
}
/**
* Create Section from a shape and origin arrays.
*
* @param origin array of start for each Range
* @param shape array of lengths for each Range
* @throws InvalidRangeException if origin < 0, or shape < 1.
*/
public Section(int[] origin, int[] shape) throws InvalidRangeException {
list = new ArrayList();
for (int i = 0; i < shape.length; i++) {
list.add(shape[i] > 0 ? new Range(origin[i], origin[i] + shape[i] - 1) : Range.EMPTY);
}
}
/**
* Create Section from a shape, origin, and stride arrays.
*
* @param origin array of start for each Range
* @param size array of lengths for each Range (last = origin + size -1)
* @param stride stride between consecutive elements, must be > 0
* @throws InvalidRangeException if origin < 0, or shape < 1.
*/
public Section(int[] origin, int[] size, int[] stride) throws InvalidRangeException {
list = new ArrayList();
for (int i = 0; i < size.length; i++) {
list.add(size[i] > 0 ? new Range(origin[i], origin[i] + size[i] - 1, stride[i]) : Range.EMPTY);
}
}
/**
* Create Section from a List.
*
* @param from the list of Range
*/
public Section(List from) {
list = new ArrayList(from);
}
/**
* Copy Constructor.
* Returned copy is mutable
*
* @param from the Section to copy
*/
public Section(Section from) {
list = new ArrayList(from.getRanges());
}
/**
* Create Section from a List.
*
* @param from the list of Range
* @param shape use this as default shape if any of the ranges are null.
* @throws InvalidRangeException if shape and range list done match
*/
public Section(List from, int[] shape) throws InvalidRangeException {
list = new ArrayList(from);
setDefaults(shape);
}
/**
* Return a Section guarenteed to be non null, with no null Ranges, and within the bounds set by shape.
* A section with no nulls is called "filled".
* If s is already filled, return it, otherwise return a new Section, filled from the shape.
*
* @param s the original Section, may be null or not filled
* @param shape use this as default shape if any of the ranges are null.
* @return a filled Section
* @throws InvalidRangeException if shape and s and shape rank dont match, or if s has invalid range compared to shape
*/
static public Section fill(Section s, int[] shape) throws InvalidRangeException {
// want all
if (s == null)
return new Section(shape);
String errs = s.checkInRange(shape);
if (errs != null)
throw new InvalidRangeException(errs);
// if s is already filled, use it
boolean ok = true;
for (int i = 0; i < shape.length; i++)
ok &= (s.getRange(i) != null);
if (ok) return s;
// fill in any nulls
return new Section(s.getRanges(), shape);
}
/* public Section subsection(int startElement, int endElement) throws InvalidRangeException {
int[] shape = getShape();
Index index = Index.factory( shape);
index.setCurrentCounter(startElement);
int[] startShape = index.getCurrentCounter();
index.setCurrentCounter(endElement);
int[] endShape = index.getCurrentCounter();
Section result = new Section();
for (int i=0; i
* The sectionSpec string uses fortran90 array section syntax, namely:
*
* sectionSpec := dims
* dims := dim | dim, dims
* dim := ':' | slice | start ':' end | start ':' end ':' stride
* slice := INTEGER
* start := INTEGER
* stride := INTEGER
* end := INTEGER
*
* where nonterminals are in lower case, terminals are in upper case, literals are in single quotes.
*
* Meaning of index selector :
* ':' = all
* slice = hold index to that value
* start:end = all indices from start to end inclusive
* start:end:stride = all indices from start to end inclusive with given stride
*
*
*
* @param sectionSpec the token to parse, eg "(1:20,:,3,10:20:2)", parenthesis optional
* @throws InvalidRangeException when the Range is illegal
* @throws IllegalArgumentException when sectionSpec is misformed
*/
public Section(String sectionSpec) throws InvalidRangeException {
list = new ArrayList();
Range range;
StringTokenizer stoke = new StringTokenizer(sectionSpec, "(),");
while (stoke.hasMoreTokens()) {
String s = stoke.nextToken().trim();
if (s.equals(":"))
range = null; // all
else if (s.indexOf(':') < 0) { // just a number : slice
try {
int index = Integer.parseInt(s);
range = new Range(index, index);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(" illegal selector: " + s + " part of <" + sectionSpec + ">");
}
} else { // gotta be "start : end" or "start : end : stride"
StringTokenizer stoke2 = new StringTokenizer(s, ":");
String s1 = stoke2.nextToken();
String s2 = stoke2.nextToken();
String s3 = stoke2.hasMoreTokens() ? stoke2.nextToken() : null;
try {
int index1 = Integer.parseInt(s1);
int index2 = Integer.parseInt(s2);
int stride = (s3 != null) ? Integer.parseInt(s3) : 1;
range = new Range(index1, index2, stride);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(" illegal selector: " + s + " part of <" + sectionSpec + ">");
}
}
list.add(range);
}
}
/**
* Create a new Section by compacting each Range.
* first = first/stride, last=last/stride, stride=1.
*
* @return compacted Section
* @throws InvalidRangeException elements must be nonnegative, 0 <= first <= last
*/
public Section compact() throws InvalidRangeException {
List results = new ArrayList(getRank());
for (Range r : list) {
results.add(r.compact());
}
return new Section(results);
}
/**
* Create a new Section by compacting each Range.
* first = first/stride, last=last/stride, stride=1.
*
* @return compacted Section
* @throws InvalidRangeException elements must be nonnegative, 0 <= first <= last
*/
public Section removeVlen() throws InvalidRangeException {
boolean need = false;
for (Range r : list) {
if (r == Range.VLEN) need = true;
}
if (!need) return this;
List results = new ArrayList(getRank());
for (Range r : list) {
if (r != Range.VLEN) results.add(r);
}
return new Section(results);
}
/**
* Create a new Section by composing with a Section that is reletive to this Section.
*
* @param want Section reletive to this one. If null, return this. If individual ranges are null, use corresponding Range in this.
* @return new Section, composed
* @throws InvalidRangeException if want.getRank() not equal to this.getRank(), or invalid component Range
*/
public Section compose(Section want) throws InvalidRangeException {
// all nulls
if (want == null) return this; // LOOK maybe a copy ??
if (want.getRank() != getRank())
throw new InvalidRangeException("Invalid Section rank");
// check individual nulls
List results = new ArrayList(getRank());
for (int j = 0; j < list.size(); j++) {
Range base = list.get(j);
Range r = want.getRange(j);
if (r == null)
results.add(base);
else
results.add(base.compose(r));
}
return new Section(results);
}
/**
* Create a new Section by intersection with another Section
*
* @param other Section other section
* @return new Section, composed
* @throws InvalidRangeException if want.getRank() not equal to this.getRank(), or invalid component Range
*/
public Section intersect(Section other) throws InvalidRangeException {
if (other.getRank() != getRank())
throw new InvalidRangeException("Invalid Section rank");
// check individual nulls
List results = new ArrayList(getRank());
for (int j = 0; j < list.size(); j++) {
Range base = list.get(j);
Range r = other.getRange(j);
results.add(base.intersect(r));
}
return new Section(results);
}
public int offset(Section intersect) throws InvalidRangeException {
if (intersect.getRank() != getRank())
throw new InvalidRangeException("Invalid Section rank");
int result = 0;
int stride = 1;
for (int j = list.size() - 1; j >= 0; j--) {
Range base = list.get(j);
Range r = intersect.getRange(j);
int offset = base.index(r.first());
result += offset * stride;
stride *= base.length();
}
return result;
}
/**
* Create a new Section by union with another Section
*
* @param other Section other section
* @return new Section, union of the two
* @throws InvalidRangeException if want.getRank() not equal to this.getRank(), or invalid component Range
*/
public Section union(Section other) throws InvalidRangeException {
if (other.getRank() != getRank())
throw new InvalidRangeException("Invalid Section rank");
List results = new ArrayList(getRank());
for (int j = 0; j < list.size(); j++) {
Range base = list.get(j);
Range r = other.getRange(j);
results.add(base.union(r));
}
return new Section(results);
}
/**
* Create a new Section by shifting each range by newOrigin.first()
* The result is then a reletive offset from the newOrigin.
*
* @param newOrigin this becomes the origin of the result
* @return new Section, shifted
* @throws InvalidRangeException if want.getRank() not equal to this.getRank()
*/
public Section shiftOrigin(Section newOrigin) throws InvalidRangeException {
if (newOrigin.getRank() != getRank())
throw new InvalidRangeException("Invalid Section rank");
// check individual nulls
List results = new ArrayList(getRank());
for (int j = 0; j < list.size(); j++) {
Range base = list.get(j);
Range r = newOrigin.getRange(j);
results.add(base.shiftOrigin(r.first()));
}
return new Section(results);
}
/**
* See if this Section intersects with another Section. ignores strides
*
* @param other another section
* @return true if intersection is non-empty
* @throws InvalidRangeException if want.getRank() not equal to this.getRank()
*/
public boolean intersects(Section other) throws InvalidRangeException {
if (other.getRank() != getRank())
throw new InvalidRangeException("Invalid Section rank");
for (int j = 0; j < list.size(); j++) {
Range base = list.get(j);
Range r = other.getRange(j);
if (!base.intersects(r))
return false;
}
return true;
}
/**
* See if this Section contains another Section. ignores strides
* Must have same rank and last >= other.last.
* @param other another section
* @return true if its a subset
*/
public boolean contains(Section other) {
if (other.getRank() != getRank())
return false;
for (int j = 0; j < list.size(); j++) {
Range base = list.get(j);
Range r = other.getRange(j);
if (base.first() > r.first())
return false;
if (base.last() < r.last())
return false;
}
return true;
}
/**
* Convert List of Ranges to String Spec.
* Inverse of new Section(String sectionSpec)
*
* @return index section String specification
*/
public String toString() {
StringBuilder sbuff = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
Range r = list.get(i);
if (i > 0) sbuff.append(",");
if (r == null)
sbuff.append(":");
else
sbuff.append(r.toString());
}
return sbuff.toString();
}
/**
* No-arg Constructor
*/
public Section() {
list = new ArrayList();
}
// these make it mutable
/**
* Append a null Range to the Section - meaning "all"
*
* @return this
*/
public Section appendRange() {
if (immutable) throw new IllegalStateException("Cant modify");
list.add(null);
return this;
}
/**
* Append a Range to the Section
*
* @return this
*/
public Section appendRange(Range r) {
if (immutable) throw new IllegalStateException("Cant modify");
list.add(r);
return this;
}
/**
* Append a new Range(0,size-1) to the Section
*
* @param size add this Range
* @return this
* @throws InvalidRangeException if size < 1
*/
public Section appendRange(int size) throws InvalidRangeException {
if (immutable) throw new IllegalStateException("Cant modify");
if (size > 0)
list.add(new Range(size));
else if (size == 0)
list.add( Range.EMPTY);
else
list.add( Range.VLEN);
return this;
}
/**
* Append a new Range(first, last) to the Section
*
* @param first starting index
* @param last last index, inclusive
* @return this
* @throws InvalidRangeException if last < first
*/
public Section appendRange(int first, int last) throws InvalidRangeException {
if (immutable) throw new IllegalStateException("Cant modify");
if (last < 0)
list.add(Range.VLEN);
else
list.add(new Range(first, last));
return this;
}
/**
* Append a new Range(first,last,stride) to the Section
*
* @param first starting index
* @param last last index, inclusive
* @param stride stride
* @return this
* @throws InvalidRangeException if last < first
*/
public Section appendRange(int first, int last, int stride) throws InvalidRangeException {
if (immutable) throw new IllegalStateException("Cant modify");
list.add(new Range(first, last, stride));
return this;
}
/**
* Append a new Range(name,first,last,stride) to the Section
*
* @param name name of Range
* @param first starting index
* @param last last index, inclusive
* @param stride stride
* @return this
* @throws InvalidRangeException if last < first
*/
public Section appendRange(String name, int first, int last, int stride) throws InvalidRangeException {
if (immutable) throw new IllegalStateException("Cant modify");
list.add(new Range(name, first, last, stride));
return this;
}
/**
* Insert a range at the specified index in the list.
*
* @param index insert here in the list, existing ranges at or after this index get shifted by one
* @param r insert this Range
* @return this
* @throws IndexOutOfBoundsException if bad index
*/
public Section insertRange(int index, Range r) {
if (immutable) throw new IllegalStateException("Cant modify");
list.add(index, r);
return this;
}
/**
* Remove a range at the specified index in the list.
*
* @param index remove here in the list, existing ranges after this index get shifted by one
* @return this
* @throws IndexOutOfBoundsException if bad index
*/
public Section removeRange(int index) {
if (immutable) throw new IllegalStateException("Cant modify");
list.remove(index);
return this;
}
/**
* Set the range at the specified index in the list, previous Range is discarded
*
* @param index list index, must be in interval [0,size).
* @param r insert this Range
* @return this
* @throws IndexOutOfBoundsException if bad index
*/
public Section setRange(int index, Range r) {
if (immutable) throw new IllegalStateException("Cant modify");
list.set(index, r);
return this;
}
/**
* Replace a range at the specified index in the list.
*
* @param index replace here in the list.
* @param r use this Range
* @return this
* @throws IndexOutOfBoundsException if bad index
*/
public Section replaceRange(int index, Range r) {
if (immutable) throw new IllegalStateException("Cant modify");
list.set(index, r);
return this;
}
/**
* Remove any ranges of length 1
*
* @return new Section if any Range had length 1, else this
*/
public Section reduce() {
if (immutable) throw new IllegalStateException("Cant modify");
boolean needed = false;
for (Range r : list) {
if (r.length() == 1) needed = true;
}
if (!needed) return this;
List newList = new ArrayList( list.size());
for (Range r : list) {
if (r.length() > 1)
newList.add(r);
}
return new Section(newList);
}
/**
* If any of the ranges are null, which means "all", set the Range from the
* corresponding length in shape[].
*
* @param shape default length for each Range; must have matching rank.
* @throws InvalidRangeException if rank is wrong
*/
public void setDefaults(int[] shape) throws InvalidRangeException {
if (immutable) throw new IllegalStateException("Cant modify");
if (shape.length != list.size())
throw new InvalidRangeException(" shape[] must have same rank as Section");
// check that any individual Range is null
for (int i = 0; i < shape.length; i++) {
Range r = list.get(i);
if (r == null) {
if (shape[i] > 0 )
list.set(i, new Range(shape[i]));
else if (shape[i] == 0 )
list.set(i, Range.EMPTY);
else
list.set(i, Range.VLEN);
}
}
}
/**
* Makes the object immutable, so can be safely shared
*
* @return this Section
*/
public Section makeImmutable() {
if (immutable) return this;
immutable = true;
list = Collections.unmodifiableList(list);
return this;
}
// end mutable methods
public boolean isImmutable() {
return immutable;
}
public boolean isStrided() {
for (Range r : list) {
if (r != null && r.stride() != 1) return false;
}
return true;
}
/**
* Get shape array using the Range.length() values.
*
* @return int[] shape
*/
public int[] getShape() {
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i).length();
}
return result;
}
/**
* Get origin array using the Range.first() values.
*
* @return int[] origin
*/
public int[] getOrigin() {
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i).first();
}
return result;
}
/**
* Get stride array using the Range.stride() values.
*
* @return int[] origin
*/
public int[] getStride() {
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i).stride();
}
return result;
}
/**
* Get origin of the ith Range
*
* @param i index of Range
* @return origin of ith Range
*/
public int getOrigin(int i) {
return list.get(i).first();
}
/**
* Get length of the ith Range
*
* @param i index of Range
* @return length of ith Range
*/
public int getShape(int i) {
return list.get(i).length();
}
/**
* Get stride of the ith Range
*
* @param i index of Range
* @return stride of ith Range
*/
public int getStride(int i) {
return list.get(i).stride();
}
/**
* Get rank - number of Ranges.
*
* @return rank
*/
public int getRank() {
return list.size();
}
/**
* Compute total number of elements represented by the section.
* Any VLEN Ranges are effectively skipped.
* @return total number of elements
*/
public long computeSize() {
long product = 1;
for (int ii = list.size() - 1; ii >= 0; ii--) {
Range r = list.get(ii);
if (r != Range.VLEN)
product *= r.length();
}
return product;
}
/**
* Get the list of Ranges.
*
* @return the List
*/
public List getRanges() {
return list;
}
/**
* Get the ith Range
*
* @param i index into the list of Ranges
* @return ith Range
*/
public Range getRange(int i) {
return list.get(i);
}
/**
* Find a Range by its name.
*
* @param rangeName find this Range
* @return named Range or null
*/
public Range find(String rangeName) {
for (Range r : list) {
if (rangeName.equals(r.getName())) return r;
}
return null;
}
public Section addRangeNames(List rangeNames) throws InvalidRangeException {
if (rangeNames.size() != getRank())
throw new InvalidRangeException("Invalid number of Range Names");
int count = 0;
Section result = new Section();
for (Range r : getRanges()) {
Range nr = new Range(rangeNames.get(count++), r);
result.appendRange(nr);
}
return result;
}
/**
* Check if this Section is legal for the given shape.
* [Note: modified by dmh to address the case of unlimited
* where the size is zero]
*
* @param shape range must fit within this shape, rank must match.
* @return error message if illegal, null if all ok
*/
public String checkInRange(int shape[]) {
if (list.size() != shape.length)
return "Number of ranges in section (" + list.size() + ") must be = " + shape.length;
for (int i = 0; i < list.size(); i++) {
Range r = list.get(i);
if (r == null) continue;
if (r == Range.VLEN) continue;
if (r.last() >= shape[i])
return "Illegal Range for dimension " + i + ": last requested " + r.last() + " > max " + (shape[i] - 1);
}
return null;
}
/**
* Is this section equivilent to the given shape.
* All non-null ranges must have origin 0 and length = shape[i]
*
* @param shape the given shape.
* @return true if equivilent
* @throws InvalidRangeException if setion rank doesnt match shape length
*/
public boolean equivalent(int[] shape) throws InvalidRangeException {
if (getRank() != shape.length)
throw new InvalidRangeException("Invalid Section rank");
for (int i = 0; i < list.size(); i++) {
Range r = list.get(i);
if (r == null) continue;
if (r.first() != 0) return false;
if (r.length() != shape[i]) return false;
}
return true;
}
/**
* Sections with equals Ranges are equal.
*/
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Section)) return false;
Section os = (Section) o;
if (getRank() != os.getRank()) return false;
for (int i = 0; i < getRank(); i++) {
Range r = getRange(i);
Range or = os.getRange(i);
if ((r == null) && (or != null)) return false;
if ((or == null) && (r != null)) return false;
if (r == null) continue; // then or is also null
if (!r.equals(or)) return false;
}
return true;
}
/**
* Override Object.hashCode() to agree with equals.
*/
public int hashCode() {
int result = 17;
for (Range r : list)
if (r != null) result += 37 * result + r.hashCode();
return result;
}
public Iterator getIterator(int[] shape) {
return new Iterator(shape);
}
public class Iterator {
private int[] odo = new int[getRank()];
private int[] stride = new int[getRank()];
private long done, total;
Iterator(int[] shape) {
int ss = 1;
for (int i = getRank() - 1; i >= 0; i--) {
odo[i] = getRange(i).first();
stride[i] = ss;
ss *= shape[i];
}
done = 0;
total = Index.computeSize(getShape()); // total in the section
}
public boolean hasNext() {
return done < total;
}
public int next() {
int next = currentElement();
done++;
if (done < total) incr(); // increment for next call
return next;
}
private void incr() {
int digit = getRank() - 1;
while (digit >= 0) {
Range r = getRange(digit);
odo[digit] += r.stride();
if (odo[digit] <= r.last())
break; // normal exit
odo[digit] = r.first(); // else, carry
digit--;
assert digit >= 0; // catch screw-ups
}
}
private int currentElement() {
int value = 0;
for (int ii = 0; ii < getRank(); ii++)
value += odo[ii] * stride[ii];
return value;
}
}
}