All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.Attribute Maven / Gradle / Ivy

Go to download

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-2014 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.nc2;

import ucar.ma2.*;

import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.nio.ByteBuffer;
import java.util.Map;

import net.jcip.annotations.Immutable;
import ucar.nc2.util.Indent;

/**
 * An Attribute has a name and a value, used for associating arbitrary metadata with a Variable or a Group.
 * The value can be a one dimensional array of Strings or numeric values.
 * 

* Attributes are immutable. * * @author caron */ @Immutable public class Attribute extends CDMNode { /** * Turn a list into a map * @param atts list of attributes * @return map of attributes by name */ static public Map makeMap(List atts) { int size = (atts == null) ? 1 : atts.size(); Map result = new HashMap<>(size); if (atts == null) return result; for (Attribute att : atts) result.put(att.getShortName(), att); return result; } /////////////////////////////////////////////////////////////////////////////////// /** * Get the data type of the Attribute value. * * @return DataType */ public DataType getDataType() { return dataType; } /** * True if value is an array (getLength() > 1) * * @return if its an array. */ public boolean isArray() { return (getLength() > 1); } /** * Get the length of the array of values * * @return number of elements in the array. */ public int getLength() { return nelems; } /** * Find whether the underlying data should be interpreted as unsigned. * Only affects byte, short, and int. * * @return true if the data is unsigned integer type. */ public boolean isUnsigned() { return isUnsigned || (values != null) && values.isUnsigned(); } /** * Get the value as an Array. * * @return Array of values. */ public Array getValues() { if (values == null && svalue != null) { values = Array.factory(String.class, new int[]{1}); values.setObject(values.getIndex(), svalue); } return values; } /** * Get the value as an Object. * * @param index which index * @return ith value as an Object. */ public Object getValue(int index) { if (isString()) return getStringValue(index); return getNumericValue(index); } /** * True if value is of type String and not null. * * @return if its a String and not null. */ public boolean isString() { return (dataType == DataType.STRING) && null != getStringValue(); } /** * Retrieve String value; only call if isString() is true. * * @return String if this is a String valued attribute, else null. * @see Attribute#isString */ public String getStringValue() { if (dataType != DataType.STRING) return null; return (svalue != null) ? svalue : _getStringValue(0); } /** * Retrieve ith String value; only call if isString() is true. * * @param index which index * @return ith String value (if this is a String valued attribute and index in range), else null. * @see Attribute#isString */ public String getStringValue(int index) { if (dataType != DataType.STRING) return null; if ((svalue != null) && (index == 0)) return svalue; return _getStringValue(index); } private String _getStringValue(int index) { if ((index < 0) || (index >= nelems)) return null; return (String) values.getObject(index); } /** * Retrieve numeric value. * Equivalent to getNumericValue(0) * * @return the first element of the value array, or null if its a String that cant be converted. */ public Number getNumericValue() { return getNumericValue(0); } /// these deal with array-valued attributes /** * Retrieve a numeric value by index. If its a String, it will try to parse it as a double. * * @param index the index into the value array. * @return Number value[index], or null if its a non-parsable String or * the index is out of range. */ public Number getNumericValue(int index) { if ((index < 0) || (index >= nelems)) return null; if (dataType == DataType.STRING) { try { return new Double(getStringValue(index)); } catch (NumberFormatException e) { return null; } } // LOOK can attributes be enum valued? for now, no if (dataType == DataType.BYTE) return values.getByte(index); else if (dataType == DataType.SHORT) return values.getShort(index); else if (dataType == DataType.INT) return values.getInt(index); else if (dataType == DataType.FLOAT) return values.getFloat(index); else if (dataType == DataType.DOUBLE) return values.getDouble(index); else if (dataType == DataType.LONG) return values.getLong(index); return null; } /** * CDL representation, not strict * * @return CDL representation */ @Override public String toString() { return toString(false); } /** * CDL representation, may be strict * @param strict if true, create strict CDL, escaping names * @return CDL representation */ public String toString( boolean strict) { Formatter f = new Formatter(); writeCDL(f, strict); return f.toString(); } /** * Write CDL representation into f * * @param f write into this * @param strict if true, create strict CDL, escaping names */ protected void writeCDL(Formatter f, boolean strict) { f.format("%s", strict ? NetcdfFile.makeValidCDLName(getShortName()) : getShortName()); if (isString()) { f.format(" = "); for (int i = 0; i < getLength(); i++) { if (i != 0) f.format(", "); String val = getStringValue(i); if (val != null) f.format("\"%s\"", NCdumpW.encodeString(val)); } } else { f.format(" = "); for (int i = 0; i < getLength(); i++) { if (i != 0) f.format(", "); f.format("%s", getNumericValue(i)); if (dataType == DataType.FLOAT) f.format("f"); else if (dataType == DataType.SHORT) { if (isUnsigned()) f.format("US"); else f.format("S"); } else if (dataType == DataType.BYTE) { if (isUnsigned()) f.format("UB"); else f.format("B"); } else if (dataType == DataType.LONG) { if (isUnsigned()) f.format("UL"); else f.format("L"); } else if (dataType == DataType.INT) { if (isUnsigned()) f.format("U"); } } } } /////////////////////////////////////////////////////////////////////////////// private String svalue; // optimization for common case of single String valued attribute private DataType dataType; private int nelems; // can be 0 or greater private Array values; private boolean isUnsigned; /** * Copy constructor * * @param name name of new Attribute * @param from copy value from here. */ public Attribute(String name, Attribute from) { super(name); if (name == null) throw new IllegalArgumentException("Trying to set name to null on "+this); this.dataType = from.dataType; this.nelems = from.nelems; this.svalue = from.svalue; this.values = from.values; this.isUnsigned = from.isUnsigned; setImmutable(true); } /** * Create a String-valued Attribute. * * @param name name of Attribute * @param val value of Attribute */ public Attribute(String name, String val) { super(name); if (name == null) throw new IllegalArgumentException("Trying to set name to null on "+this); setStringValue(val); setImmutable(true); } /** * Create a scalar numeric-valued Attribute. * * @param name name of Attribute * @param val value of Attribute */ public Attribute(String name, Number val) { this(name, val, false); } public Attribute(String name, Number val, boolean isUnsigned) { super(name); if (name == null) throw new IllegalArgumentException("Trying to set name to null on "+this); int[] shape = new int[1]; shape[0] = 1; DataType dt = DataType.getType(val.getClass()); Array vala = Array.factory(dt.getPrimitiveClassType(), shape); Index ima = vala.getIndex(); vala.setDouble(ima.set0(0), val.doubleValue()); setValues(vala); this.isUnsigned = isUnsigned; if (isUnsigned) vala.setUnsigned(true); setImmutable(true); } /** * Construct attribute with Array of values. * * @param name name of attribute * @param values array of values. */ public Attribute(String name, Array values) { this(name); setValues(values); setImmutable(true); } /** * Construct an empty attribute with no values * * @param name name of attribute * @param dataType type of Attribute. */ public Attribute(String name, DataType dataType, boolean isUnsigned) { this(name); this.dataType = dataType == DataType.CHAR ? DataType.STRING : dataType; this.nelems = 0; this.isUnsigned = isUnsigned; setImmutable(true); } /** * Construct attribute with list of String or Number values. * * @param name name of attribute * @param values list of values. must be String or Number, must all be the same type, and have at least 1 member */ public Attribute(String name, List values) { this(name); int n = values.size(); Object pa; Class c = values.get(0).getClass(); if (c == String.class) { String[] va = new String[n]; pa = va; for (int i = 0; i < n; i++) va[i] = (String) values.get(i); } else if (c == Integer.class) { int[] va = new int[n]; pa = va; for (int i = 0; i < n; i++) va[i] = (Integer) values.get(i); } else if (c == Double.class) { double[] va = new double[n]; pa = va; for (int i = 0; i < n; i++) va[i] = (Double) values.get(i); } else if (c == Float.class) { float[] va = new float[n]; pa = va; for (int i = 0; i < n; i++) va[i] = (Float) values.get(i); } else if (c == Short.class) { short[] va = new short[n]; pa = va; for (int i = 0; i < n; i++) va[i] = (Short) values.get(i); } else if (c == Byte.class) { byte[] va = new byte[n]; pa = va; for (int i = 0; i < n; i++) va[i] = (Byte) values.get(i); } else if (c == Long.class) { long[] va = new long[n]; pa = va; for (int i = 0; i < n; i++) va[i] = (Long) values.get(i); } else { throw new IllegalArgumentException("unknown type for Attribute = " + c.getName()); } setValues(Array.factory(c, new int[]{n}, pa)); setImmutable(true); } /** * A copy constructor using a ucar.unidata.util.Parameter. * Need to do this so ucar.unidata.geoloc package doesnt depend on ucar.nc2 library * * @param param copy info from here. */ public Attribute(ucar.unidata.util.Parameter param) { this(param.getName()); if (param.isString()) { setStringValue(param.getStringValue()); } else { double[] values = param.getNumericValues(); int n = values.length; Array vala = Array.factory(DataType.DOUBLE.getPrimitiveClassType(), new int[]{n}, values); setValues(vala); } setImmutable(true); } /** * set the value as a String, trimming trailing zeroes * * @param val value of Attribute */ private void setStringValue(String val) { if (val == null) throw new IllegalArgumentException("Attribute value cannot be null"); // get rid of trailing nul characters int len = val.length(); while ((len > 0) && (val.charAt(len - 1) == 0)) len--; if (len != val.length()) val = val.substring(0, len); this.svalue = val; this.nelems = 1; this.dataType = DataType.STRING; //values = Array.factory(String.class, new int[]{1}); //values.setObject(values.getIndex(), val); //setValues(values); } ////////////////////////////////////////// // the following make this mutable, but its restricted to subclasses, namely DODSAttribute /** * Constructor. Must also set value * * @param name name of Attribute */ protected Attribute(String name) { super(name); if (name == null) throw new IllegalArgumentException("Trying to set name to null on "+this); } /** * set the values from an Array * * @param arr value of Attribute */ protected void setValues(Array arr) { if (immutable) throw new IllegalStateException("Cant modify"); if (arr == null) { dataType = DataType.STRING; return; } if (DataType.getType(arr.getElementType()) == null) throw new IllegalArgumentException("Cant set Attribute with type " + arr.getElementType()); if (arr.getElementType() == char.class) { // turn CHAR into STRING ArrayChar carr = (ArrayChar) arr; if (carr.getRank() == 1) { // common case svalue = carr.getString(); this.nelems = 1; this.dataType = DataType.STRING; return; } // otherwise its an array of Strings arr = carr.make1DStringArray(); } // this should be a utility somewhere if (arr.getElementType() == ByteBuffer.class) { // turn OPAQUE into BYTE int totalLen = 0; arr.resetLocalIterator(); while (arr.hasNext()) { ByteBuffer bb = (ByteBuffer) arr.next(); totalLen += bb.limit(); } byte[] ba = new byte[totalLen]; int pos = 0; arr.resetLocalIterator(); while (arr.hasNext()) { ByteBuffer bb = (ByteBuffer) arr.next(); System.arraycopy(bb.array(), 0, ba, pos, bb.limit()); pos += bb.limit(); } arr = Array.factory(DataType.BYTE, new int[]{totalLen}, ba); } if (arr.getRank() > 1) arr = arr.reshape(new int[]{(int) arr.getSize()}); // make sure 1D this.values = arr; this.nelems = (int) arr.getSize(); this.dataType = DataType.getType(arr.getElementType()); } /** * Set the name of this Attribute. * Attribute names are unique within a NetcdfFile's global set, and within a Variable's set. * * @param name name of attribute */ public synchronized void setName(String name) { if (immutable) throw new IllegalStateException("Cant modify"); setShortName(name); } /* public synchronized void setUnsigned(boolean isUnsigned) { if (immutable) throw new IllegalStateException("Cant modify"); this.isUnsigned = isUnsigned; } */ /** * Instances which have same content are equal. */ @Override public boolean equals(Object o) { if (this == o) return true; if ((o == null) || !(o instanceof Attribute)) return false; final Attribute att = (Attribute) o; String name = getShortName(); if (!name.equals(att.getShortName())) return false; if (nelems != att.nelems) return false; if (!dataType.equals(att.dataType)) return false; if (isString()) return att.getStringValue().equals(getStringValue()); //if (svalue != null) return svalue.equals(att.getStringValue()); if (values != null) { for (int i = 0; i < getLength(); i++) { int r1 = isString() ? getStringValue(i).hashCode() : getNumericValue(i).hashCode(); int r2 = att.isString() ? att.getStringValue(i).hashCode() : att.getNumericValue(i).hashCode(); if (r1 != r2) return false; } } return true; } /** * Override Object.hashCode() to implement equals. */ @Override public int hashCode() { int result = 17; result = 37 * result + getShortName().hashCode(); result = 37 * result + nelems; result = 37 * result + getDataType().hashCode(); if (svalue != null) result = 37 * result + svalue.hashCode(); else if (values != null) { for (int i = 0; i < getLength(); i++) { int h = isString() ? getStringValue(i).hashCode() : getNumericValue(i).hashCode(); result = 37 * result + h; } } return result; } @Override public void hashCodeShow(Indent indent) { System.out.printf("%sAtt hash = %d%n", indent, hashCode()); System.out.printf("%s shortName '%s' = %d%n", indent, getShortName(), getShortName() == null ? -1 : getShortName().hashCode()); System.out.printf("%s nelems %s%n", indent, nelems); System.out.printf("%s dataType %s%n", indent, getDataType()); if (svalue != null) System.out.printf("%s svalue %s = %s%n", indent, svalue, svalue.hashCode()); else { indent.incr(); for (int i = 0; i < getLength(); i++) { if (isString()) System.out.printf("%s value %s = %s%n", indent, getStringValue(i), getStringValue(i).hashCode()); else System.out.printf("%s value %s = %s%n", indent, getValue(i), getValue(i).hashCode()); } indent.decr(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy