/*
 * Copyright 1996-2015 United States Government as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gsfc.nssdc.cdf;

import java.util.*;
import java.io.File;
import java.nio.charset.*;
import java.lang.reflect.*;
import gsfc.nssdc.cdf.util.*;

/**
 * This class describes a CDF global or variable attribute entry. <P>
 *
 * <B>Note:</B> In the Java CDF API there is no concept of an rEntry since r
 *              variables are not supported.  Only z variables are supported
 *              since it is far superior and efficient than r variables.
 *
 * @version 1.0
 * @version 2.0 03/18/05  Selection of current CDF, attribute and entry are done 
 *                        as part of operations passed to JNI.
 *                        JNI call is synchronized so only one process is
 *                        allowed in a JVM, due to multi-thread safety.
 *                        The select method will never be called.
 * @author Phil Williams, QSS Group Inc/RITSS <BR>
 *         Mike Liu, RSTX
 *
 * @see gsfc.nssdc.cdf.Attribute
 *
 */
public class Entry extends Object implements CDFObject, CDFConstants {

    /**
     * The entry id
     */
    private long id;
    /**
     * The attribute id that this entry belongs to
     */
    private long attrID;
    /**
     * The CDF id that this entry belongs to
     */
    private long cdfID;

    /**
     * The scope of the entry
     */
    private long scope;

    /**
     * The CDF data type of the entry data
     */
    private long dataType;
    private long xdataType;   // a temp one
    
    /**
     * The number of elements for this entry
     */
    private long numElements = -1;
    private long xnumElements;   // a temp one

    /**
     * The number of strings for this entry
     */
    private long numStrings;
    private long xnumStrings;   // a temp one

    /**
     * The entry data
     */
    private Object data = null;
    private Object xdata = null;

    /**
     * Since the data is a generic object and is set on-the-fly, this holds
     * the actual signature of the data object.  This is used in 
     * getArgument within cdfNativeLibrary.c
     */
    private String dataSignature;
    private String xdataSignature;

    /**
     * The attribute to which this entry belongs
     */
    private Attribute myAttribute;

    /**
     * Construct a basic attribute entry.
     *
     * @param myAttribute The attribute to which this entry belongs
     * @param id Entry id <P>
     * @return Newly created Entry object
     */
    private Entry(Attribute myAttribute, long id) {
	this.myAttribute = myAttribute;
	this.id = id;
	this.scope = myAttribute.getScope();
    }

    /**
     * Get the attribute entry or entries for the given attribute. 
     *
     * @param myAttribute The attribute from which entry is retrieved
     * @param id The entry id <P>
     *
     * @return The requested attribute entry <P>
     * @throws CDFException CDFException If there is a problem getting the entry
     */
    protected static Entry retrieve(Attribute myAttribute, long id) 
	throws CDFException
    {

synchronized(myAttribute) {
	Entry theEntry = new Entry(myAttribute, id);

	Vector cmds = new Vector();
	Vector item0 = new Vector();
	Vector item1 = new Vector();
	Vector item2 = new Vector();
        Vector itemA = new Vector();
	Vector itemB = new Vector();
	Vector itemC = new Vector(); 

        cmds.addElement(Long.valueOf(SELECT_));
          cmds.addElement(Long.valueOf(CDF_));
            itemA.addElement("cdfID");
            itemA.addElement("J");
          cmds.addElement(itemA);
          cmds.addElement(Long.valueOf(ATTR_));
            itemB.addElement("attrID");
            itemB.addElement("J");
          cmds.addElement(itemB);
          cmds.addElement(((theEntry.scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_) : Long.valueOf(zENTRY_)));
            itemC.addElement("id"); 
            itemC.addElement("J");
          cmds.addElement(itemC);

        cmds.addElement(Long.valueOf(GET_));
          cmds.addElement(((theEntry.scope == GLOBAL_SCOPE) ?
			   Long.valueOf(gENTRY_DATATYPE_) : 
			   Long.valueOf(zENTRY_DATATYPE_)));
            item0.addElement("dataType");
            item0.addElement("J");
          cmds.addElement(item0);
          cmds.addElement(((theEntry.scope == GLOBAL_SCOPE) ?
			   Long.valueOf(gENTRY_NUMELEMS_) :
			   Long.valueOf(zENTRY_NUMELEMS_)));
            item1.addElement("numElements");
            item1.addElement("J");
          cmds.addElement(item1);
          if (theEntry.scope != GLOBAL_SCOPE) {
            cmds.addElement(Long.valueOf(zENTRY_NUMSTRINGS_));
              item2.addElement("numStrings");
              item2.addElement("J");
            cmds.addElement(item2);
          }
        cmds.addElement(Long.valueOf(NULL_));

	theEntry.cdfID = myAttribute.getMyCDF().getID();
	theEntry.attrID = myAttribute.getID();

	theEntry.myAttribute.getMyCDF().executeCommand((CDFObject)theEntry, 
						       cmds);
        /* Make sure the exception is thrown if a bad status is returned
           while JNI does not issue it. */
/*
	long status = theEntry.myAttribute.getMyCDF().getStatus();
        if (status < CDF_WARN) throw new CDFException(status);
*/
        if (theEntry.scope == VARIABLE_SCOPE && 
            (theEntry.dataType == CDF_CHAR || theEntry.dataType == CDF_UCHAR)) {
          if (theEntry.numStrings < 1) theEntry.numStrings = 1;
        }
        /********************/
	/* Get the data out */
        /********************/
	theEntry.buildDataObject();
	cmds.removeAllElements();
	item0.removeAllElements();
	itemA.removeAllElements();
	itemB.removeAllElements();
	itemC.removeAllElements();

        cmds.addElement(Long.valueOf(SELECT_));
          cmds.addElement(Long.valueOf(CDF_));
            itemA.addElement("cdfID");
            itemA.addElement("J");
          cmds.addElement(itemA);
          cmds.addElement(Long.valueOf(ATTR_));
            itemB.addElement("attrID");
            itemB.addElement("J");
          cmds.addElement(itemB);
          cmds.addElement(((theEntry.scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_) : Long.valueOf(zENTRY_)));
            itemC.addElement("id");
            itemC.addElement("J");
          cmds.addElement(itemC);
	
	cmds.addElement(Long.valueOf(GET_));
	  cmds.addElement(((theEntry.scope == GLOBAL_SCOPE) ?
			   Long.valueOf(gENTRY_DATA_) : 
			   Long.valueOf(zENTRY_DATA_)));
	    item0.addElement("data");
	    item0.addElement("Ljava/lang/Object;");
	  cmds.addElement(item0);
	cmds.addElement(Long.valueOf(NULL_));

	theEntry.myAttribute.getMyCDF().
	    executeCommand((CDFObject)theEntry, cmds);

	myAttribute.addEntry(theEntry, (int)id);

	return theEntry;
    }
}

    /**
     * Creates a new global or variable attribute entry.  One can create as
     * many global and variable entries as needed.
     *
     * The following example creates four entries for the global attribute
     * "Project":
     * <PRE>
     *     Attribute project  = Attribute.create(cdf, "Project", GLOBAL_SCOPE);
     *     Entry.create(project, 0, CDF_CHAR, "Project name: IMAGE");
     *     Entry.create(project, 1, CDF_CHAR, "Description 1");
     *     Entry.create(project, 2, CDF_CHAR, "Description 2");
     * </PRE>
     *
     * The following example creates a variable attribute entry for the
     * variable "Longitude" associated with the attribute "VALIDMIN": 
     * <PRE>
     *     Variable longitude = cdf.getVariable("Longitude"); 
     *     Attribute validMin = Attribute.create(cdf, "VALIDMIN", 
     *                                           VARIABLE_SCOPE);
     *     Entry.create(validMin, longitude.getID(), CDF_INT2, 
     *                  new Short((short)10)); 
     *             OR 
     *     longitude.putEntry(validMin, CDF_INT2, new Short((short)180));
     * </PRE>
     *
     * @param myAttribute the attribute to which this entry belongs <P>
     * @param id the entry id <P>
     * @param dataType the CDF data type for this entry that should be one
     *                 of the following: 
     *                 <UL>
     *                    <LI>CDF_BYTE     - 1-byte, signed integer
     *                    <LI>CDF_CHAR     - 1-byte, signed character
     *                    <LI>CDF_INT1     - 1-byte, signed integer
     *                    <LI>CDF_UCHAR    - 1-byte, unsigned character
     *                    <LI>CDF_UINT1    - 1-byte, unsigned integer
     *                    <LI>CDF_INT2     - 2-byte, signed integer
     *                    <LI>CDF_UNIT2    - 2-byte, unsigned integer
     *                    <LI>CDF_INT4     - 4-byte, signed integer
     *                    <LI>CDF_UINT4    - 4-byte, unsigned integer
     *                    <LI>CDF_INT8     - 8-byte, signed integer
     *                    <LI>CDF_REAL4    - 4-byte, floating point 
     *                    <LI>CDF_FLOAT    - 4-byte, floating point 
     *                    <LI>CDF_REAL8    - 8-byte, floating point 
     *                    <LI>CDF_DOUBLE   - 8-byte, floating point 
     *                    <LI>CDF_EPOCH    - 8-byte, floating point 
     *                    <LI>CDF_EPOCH16  - 2*8-byte, floating point
     *                    <LI>CDF_TIME_TT2000  - 8-byte, signed integer
     *                 </UL>
     * @param data the entry data to be added <P>
     * Note: From CDF V3.7.0, the string typed data of variable entry can 
     *       be an array of strings. Data from previous versions can only
     *       be a single string.
     *
     * @return newly created attribute entry <P>
     * @throws CDFException CDFException if there is a problem creating an entry
     */
    public static Entry create(Attribute myAttribute, long id, 
			       long dataType, Object data) 
	throws CDFException
    {

synchronized(myAttribute) {
	
	if (id < 0) {
	    throw new CDFException(BAD_ENTRY_NUM);
	}
	if ((myAttribute.getMaxEntryNumber() < id) &&
	    (myAttribute.getScope() == VARIABLE_SCOPE)) {
	    throw new CDFException(NO_SUCH_VAR);
	}

        // If there is an entry already, delete and recreate it.
        // This will take care of the situation when the user changes the
        // data type and numElements of an existing entry.

        if (myAttribute.getMaxEntryNumber() > id) {
            try { Entry entry = (Entry) myAttribute.getEntry(id);
                  if (entry != null) entry.delete(); }
            catch (CDFException e) {}
        }

	Entry theEntry         = new Entry(myAttribute, id);
	theEntry.dataType      = dataType;
/*
	theEntry.data          = data;
	theEntry.dataSignature = CDFUtils.getSignature(data);
        theEntry.numElements   = CDFUtils.getNumElements(dataType, data);
*/
	Vector cmds  = new Vector();
	Vector item0 = new Vector();
	Vector item1 = new Vector();
	Vector item2 = new Vector();
	Vector item3 = new Vector();
	Vector itemA = new Vector();
	Vector itemB = new Vector();
	Vector itemC = new Vector();

        cmds.addElement(Long.valueOf(SELECT_));
          cmds.addElement(Long.valueOf(CDF_));
            itemA.addElement("cdfID");
            itemA.addElement("J");
          cmds.addElement(itemA);
          cmds.addElement(Long.valueOf(ATTR_));
            itemB.addElement("attrID");
            itemB.addElement("J");
          cmds.addElement(itemB);
          cmds.addElement(((theEntry.scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_) : Long.valueOf(zENTRY_)));
            itemC.addElement("id");
            itemC.addElement("J");
          cmds.addElement(itemC);

          if ((theEntry.scope == VARIABLE_SCOPE ||
               theEntry.scope == GLOBAL_SCOPE) &&
              (dataType == CDF_CHAR || dataType == CDF_UCHAR)) {
            if (CDFUtils.getSignature(data).indexOf("[") != -1) {
              if (CDFUtils.getSignature(data).indexOf("[B") != -1) {
                theEntry.data          = data;
                theEntry.numStrings    = 1;
                theEntry.numElements   = (long) ((byte[])data).length;
                theEntry.dataSignature = CDFUtils.getSignature(theEntry.data);
              } else {
                theEntry.numStrings    = (long) ((String[])data).length;
                theEntry.data          = (Object) CDFUtils.mergeFromStrings(data);
                String oneStr          = (String)(theEntry.data);
                byte[] bytes;
                try {
                  bytes      = oneStr.getBytes("UTF-8");
                } catch (Exception ee) {
                  bytes      = oneStr.getBytes();
                }
                theEntry.numElements   = (long) bytes.length;
                theEntry.dataSignature = CDFUtils.getSignature(theEntry.data);
              }
            } else {
              try {
                byte[] bytes           = ((String)data).getBytes("UTF-8");
                theEntry.data          = bytes;
                theEntry.numElements   = bytes.length;
	      } catch (Exception ee) {
                theEntry.data          = data;
		theEntry.numElements   = CDFUtils.getNumElements(dataType,
                                                                 theEntry.data);
	      }
              theEntry.numStrings    = (long) CDFUtils.getNumStrings(theEntry.data);
              theEntry.dataSignature = CDFUtils.getSignature(theEntry.data);
            }
          } else {
            theEntry.data          = data;
	    theEntry.numElements   = CDFUtils.getNumElements(dataType, data);
            theEntry.numStrings    = 1;
            theEntry.dataSignature = CDFUtils.getSignature(theEntry.data);
          }

	cmds.addElement(Long.valueOf(PUT_));
	  cmds.addElement(((theEntry.scope == GLOBAL_SCOPE) ?
			   Long.valueOf(gENTRY_DATA_) : Long.valueOf(zENTRY_DATA_)));
	    item0.addElement("dataType");
	    item0.addElement("J");
	  cmds.addElement(item0);
	    item1.addElement("numElements");
	    item1.addElement("J");
	  cmds.addElement(item1);
	    item2.addElement("data");
	    item2.addElement("Ljava/lang/Object;");
	  cmds.addElement(item2);
	cmds.addElement(Long.valueOf(NULL_));

	theEntry.attrID = myAttribute.getID();
	theEntry.cdfID = myAttribute.getMyCDF().getID();

	myAttribute.getMyCDF().
	    executeCommand((CDFObject)theEntry, cmds);

	// Setup the connection to myAttribute
	myAttribute.addEntry(theEntry, (int) id);

	return theEntry;
    }
}

    /**
     *  Build appropriate data object and its signature so that the 
     *  JNI code can allocate appropriate amount of space.
     */
    private void buildDataObject()
    {
	if ((dataType == CDF_CHAR) || (dataType == CDF_UCHAR)) {
	    data = new String();
	    dataSignature = "Ljava/lang/String;";

	} else {
	    switch ((int)dataType) {
	    case (int)CDF_BYTE:
	    case (int)CDF_INT1:
		if (numElements > 1) {
		    dataSignature = "[B";
		    data = new byte [(int)numElements];
	        } else {
		    dataSignature = "Ljava/lang/Byte;";
		    data = new Byte((byte)0);
		}
		break;
	    case (int)CDF_INT2:
	    case (int)CDF_UINT1:
		if (numElements > 1) {
		    dataSignature = "[S";
		    data = new short [(int)numElements];
		} else {
		    dataSignature = "Ljava/lang/Short;";
		    data = new Short((short)0);
		}
		break;
	    case (int)CDF_INT4:
	    case (int)CDF_UINT2:
		if (numElements > 1) {
		    dataSignature = "[I";
		    data = new int [(int)numElements];
		} else {
		    dataSignature = "Ljava/lang/Integer;";
		    data = new Integer(0);
		}
		break;
	    case (int)CDF_UINT4:
		if (numElements > 1) {
		    data = new long [(int)numElements];
		    dataSignature = "[J";
		} else {
		    dataSignature = "Ljava/lang/Long;";
		    data = Long.valueOf(0L);
		}
		break;
	    case (int)CDF_INT8:
	    case (int)CDF_TIME_TT2000:
		if (numElements > 1) {
		    data = new long [(int)numElements];
		    dataSignature = "[J";
		} else {
		    dataSignature = "Ljava/lang/Long;";
		    data = Long.valueOf(0L);
		}
		break;
	    case (int)CDF_REAL4:
	    case (int)CDF_FLOAT:
		if (numElements > 1) {
		    dataSignature = "[F";
		    data = new float [(int)numElements];
		} else {
		    dataSignature = "Ljava/lang/Float;";
		    data = new Float(0);
		}
		break;
	    case (int)CDF_REAL8:
	    case (int)CDF_DOUBLE:
	    case (int)CDF_EPOCH:
		if (numElements > 1) {
		    dataSignature = "[D";
		    data = new double [(int)numElements];
		} else {
		    dataSignature = "Ljava/lang/Double;";
		    data = new Double(0);
		}
		break;
            case (int)CDF_EPOCH16:
                dataSignature = "[D";
                data = new double [2*(int)numElements];
                break;
	    default:
		break;
	    } /* switch (dataType) */
	}
    }

    /**
     * Deletes this entry.
     *
     * @throws CDFException CDFException if there is a problem deleting this entry
     */
    public synchronized void delete() throws CDFException {

	Vector cmds = new Vector();
	Vector item0 = new Vector();
        Vector itemA = new Vector();
        Vector itemB = new Vector();

        cmds.addElement(Long.valueOf(SELECT_));
          cmds.addElement(Long.valueOf(CDF_));
            itemA.addElement("cdfID");
            itemA.addElement("J");
          cmds.addElement(itemA);
          cmds.addElement(Long.valueOf(ATTR_));
            itemB.addElement("attrID");
            itemB.addElement("J");
          cmds.addElement(itemB);
          cmds.addElement(((scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_) : Long.valueOf(zENTRY_)));
            item0.addElement("id");
            item0.addElement("J");      
          cmds.addElement(item0);

	cmds.addElement(Long.valueOf(DELETE_));
	  cmds.addElement(((scope == GLOBAL_SCOPE) ?
			   Long.valueOf(gENTRY_) : Long.valueOf(zENTRY_)));
	cmds.addElement(Long.valueOf(NULL_));

        id = getID();
	cdfID = myAttribute.getMyCDF().getID();
	attrID = myAttribute.getID();

	myAttribute.getMyCDF().executeCommand((CDFObject)this, cmds);

	myAttribute.removeEntry(this);
    }
	

    /**
     * Gets the CDF data type of this entry.  See the description of the
     * create method for the CDF data types supported by the CDF library.
     *
     * @return the CDF data type of this entry
     */
    public synchronized long getDataType() {
	return dataType;
    }
    
    /**
     * Gets the scope of the attribute this entry in in.
     *
     * @return the scope of this entry
     */
    public synchronized long getScope() {
	return scope;
    }
    
    /**
     * Gets the number of elements in this entry.  For CDF_CHAR, it 
     * returns the number of characters stored.  
     * <PRE>
     *     Entry data        Number of elements
     *     ----------        ------------------
     *     10                1
     *     20.8              1
     *     10 20 30          3
     *     20.8 20.9         2
     *     "Upper Limits"    12
     * </PRE> <P>
     *
     * @return the number of elements stored in this entry
     */
    public synchronized long getNumElements() {
	return numElements;
    }

    /**
     * Gets the number of strings in the data. If the data is not string
     * type, 0 is returned. For global attribute entry, it always returns 1.
     *
     * @return the string number for this entry 
     */
    public synchronized long getNumStrings() {

        if (dataType == CDF_CHAR || dataType == CDF_UCHAR) {
          if (scope == VARIABLE_SCOPE) return numStrings;
          else return 1L;
        } else
          return 0L;
    }
 
    /**
     * Gets the data for this entry. Build a new data object so a duplicated
     * variable will have its own copy of the entry data.
     *
     * @return the data for this entry
     * Note: From CDF V3.7.0, the string typed data of variable entry can 
     *       return an array of strings. Data from previous versions only
     *       returns a single string.
     *
     * @throws CDFException An exception is thrown if the operation fails
     *
     */
    public synchronized Object getData() throws CDFException {

      try {
	if ((dataType == CDF_CHAR) || (dataType == CDF_UCHAR)) {
            String signature = CDFUtils.getSignature(data);
            if (scope == GLOBAL_SCOPE) {
              if (signature.indexOf("[B") != -1) {
	        try {
	          return new String((byte[])data, "UTF-8");
		} catch (Exception ee) {
	          return new String((byte[])data);
		}
              } else {
                return (String) data;
	      }
            } else {
              if (numStrings < 2) {
                if (signature.indexOf("[B") != -1) {
                  return new String((byte[])data, "UTF-8");
                } else {
                  String encoding = System.getProperty("file.encoding").
                                    toUpperCase();
                  if (encoding.indexOf("UTF-8") == -1 &&
                      encoding.indexOf("UTF8") == -1)
                    return (String) data;
                  else
                    return new String(((String)data).getBytes(), "UTF-8");
                }
              } else {
                if (signature.indexOf("[B") != -1) {
                  return CDFUtils.breakIntoStrings(new String((byte[])data));
                } else
                  return CDFUtils.breakIntoStrings((String)data.toString());
              }
            }
	} else {
	  switch ((int)dataType) {
	    case (int)CDF_BYTE:
	    case (int)CDF_INT1:
		if (numElements > 1) {
		    byte[] data2 = new byte [(int)numElements];
		    for (int i=0;i<(int)numElements;++i)
			data2[i] = ((byte[]) data)[i];
		    return data2;
	        } else {
		    Byte data2 = new Byte(((Byte)data).byteValue());
		    return data2;
		}
	    case (int)CDF_INT2:
	    case (int)CDF_UINT1:
		if (numElements > 1) {
		    short[] data2 = new short [(int)numElements];
		    for (int i=0;i<(int)numElements;++i)
			data2[i] = ((short[]) data)[i];
		    return data2;
		} else {
		    Short data2 = new Short(((Short)data).shortValue());
		    return data2;
		}
	    case (int)CDF_INT4:
	    case (int)CDF_UINT2:
		if (numElements > 1) {
		    int[] data2 = new int [(int)numElements];
		    for (int i=0;i<(int)numElements;++i)
			data2[i] = ((int[]) data)[i];
		    return data2;
		} else {
		    Integer data2 = new Integer(((Integer)data).intValue());
		    return data2;
		}
	    case (int)CDF_UINT4:
	    case (int)CDF_INT8:
	    case (int)CDF_TIME_TT2000:
		if (numElements > 1) {
		    long[] data2 = new long [(int)numElements];
		    for (int i=0;i<(int)numElements;++i)
			data2[i] = ((long[]) data)[i];
		    return data2;
		} else {
		    Long data2 = Long.valueOf(((Long)data).longValue());
		    return data2;
		}
	    case (int)CDF_REAL4:
	    case (int)CDF_FLOAT:
		if (numElements > 1) {
		    float[] data2 = new float [(int)numElements];
		    for (int i=0;i<(int)numElements;++i)
			data2[i] = ((float[]) data)[i];
		    return data2;
		} else {
		    Float data2 = new Float(((Float)data).floatValue());
		    return data2;
		}
	    case (int)CDF_REAL8:
	    case (int)CDF_DOUBLE:
	    case (int)CDF_EPOCH:
		if (numElements > 1) {
		    double[] data2 = new double [(int)numElements];
		    for (int i=0;i<(int)numElements;++i)
			data2[i] = ((double[]) data)[i];
		    return data2;
		} else {
		    Double data2 = new Double(((Double)data).doubleValue());
		    return data2;
		}
            case (int)CDF_EPOCH16:
                double[] data2 = new double [2*(int)numElements];
		for (int i=0;i<(int)numElements;++i) {
		   data2[2*i] = ((double[]) data)[2*i];
		   data2[2*i+1] = ((double[]) data)[2*i+1];
		}
		return data2;
	    default:
		break;
	    /* switch (dataType) */
	  }
	}
	return null;
      } catch (Exception ex) {
        System.out.println("*** EXCEPTION: "+ex);
        throw new CDFException(DATATYPE_MISMATCH);
      }
    }
	
    /**
     * Gets the ID of this entry.
     *
     * @return the ID/number of this entry  
     */
    public synchronized long getID() {

	return myAttribute.getEntryID(this);

    }
    
    /**
     * Gets the name of this entry.  Since an entry doesn't have its own 
     * name, the string representation of this entry ID is returned. <P>
     * 
     * This method overrides the getName() method defined in the Java Object 
     * class.  If this method is called explicitly or implicitly (i.e. just
     * the entry name by itself), it returns the string representation of the 
     * entry ID.  
     *
     * @return string representation of this attribute entry ID
     */
    public synchronized String getName() {
	return Long.toString(id);
    }

    /**
     * This method is here as a placeholder since the Entry class
     * implements the CDFObject interface that includes "rename".  
     *
     * @param name - not applicable <P>
     * @throws CDFException CDFException - not applicable 
     */
    public synchronized void rename(String name) throws CDFException {
    }
    /**
     * Update the data specification (data type and number of elements)
     * of the entry.
     *
     * @param  dataType the data type of the entry data
     * @param  numElements the number of elements of the entry data
     * @throws CDFException An exception is thrown if the operation fails
     */
    public synchronized void updateDataSpec(long dataType, long numElements) 
	throws CDFException {

        Vector cmds  = new Vector();
        Vector item0 = new Vector();
        Vector item1 = new Vector();
	Vector itemA = new Vector();
	Vector itemB = new Vector();
	Vector itemC = new Vector();

        cmds.addElement(Long.valueOf(SELECT_));
          cmds.addElement(Long.valueOf(CDF_));
            itemA.addElement("cdfID");
            itemA.addElement("J");
          cmds.addElement(itemA);
          cmds.addElement(Long.valueOf(ATTR_));
            itemB.addElement("attrID");
            itemB.addElement("J");
          cmds.addElement(itemB);
          cmds.addElement(((scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_) :
                           Long.valueOf(zENTRY_)));
            itemC.addElement("id");
            itemC.addElement("J");
	  cmds.addElement(itemC);

        cmds.addElement(Long.valueOf(PUT_));
          cmds.addElement(((scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_DATASPEC_) : 
			   Long.valueOf(zENTRY_DATASPEC_)));
            item0.addElement("xdataType");
            item0.addElement("J");
          cmds.addElement(item0);
            item1.addElement("xnumElements");
            item1.addElement("J");
          cmds.addElement(item1);
        cmds.addElement(Long.valueOf(NULL_));

        id = getID();
	attrID = myAttribute.getID();
	cdfID = myAttribute.getMyCDF().getID();
        this.xdataType = dataType;
        this.xnumElements = numElements;

        myAttribute.getMyCDF().executeCommand((CDFObject)this, cmds);

        this.dataType = xdataType;
        this.numElements = xnumElements;
	data = null;
	buildDataObject();

	cmds.removeAllElements();
        item0.removeAllElements();
	itemA.removeAllElements();
	itemB.removeAllElements();
	itemC.removeAllElements();

        cmds.addElement(Long.valueOf(SELECT_));
          cmds.addElement(Long.valueOf(CDF_));
            itemA.addElement("cdfID");
            itemA.addElement("J");
          cmds.addElement(itemA);
          cmds.addElement(Long.valueOf(ATTR_));
            itemB.addElement("attrID");
            itemB.addElement("J");
          cmds.addElement(itemB);
          cmds.addElement(((scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_) : Long.valueOf(zENTRY_)));
            itemC.addElement("id");
            itemC.addElement("J");
          cmds.addElement(itemC);

        cmds.addElement(Long.valueOf(GET_));
          cmds.addElement(((scope == GLOBAL_SCOPE) ?
                           Long.valueOf(gENTRY_DATA_) : Long.valueOf(zENTRY_DATA_)));
            item0.addElement("data");
            item0.addElement("Ljava/lang/Object;");
          cmds.addElement(item0);
        cmds.addElement(Long.valueOf(NULL_));

        myAttribute.getMyCDF().executeCommand((CDFObject)this, cmds);

    }

    /**
     * Put the entry data into the CDF.
     *
     * @param  dataType the data type of the entry data
     * @param  data the entry data to write
     * Note: From CDF V3.7.0, the string typed data of variable entry can 
     *       be an array of strings. Data from previous versions can only
     *       be a single string.
     * @throws CDFException An exception is thrown if the operation fails
     *
     */
    public synchronized void putData(long dataType, Object data) 
        throws CDFException {

        Vector cmds  = new Vector();
        Vector item0 = new Vector();
        Vector item1 = new Vector();
	Vector item2 = new Vector();
	Vector itemA = new Vector();
	Vector itemB = new Vector();
	Vector itemC = new Vector();

	try {
          cmds.addElement(Long.valueOf(SELECT_));
            cmds.addElement(Long.valueOf(CDF_));
              itemA.addElement("cdfID");
              itemA.addElement("J");
            cmds.addElement(itemA);
            cmds.addElement(Long.valueOf(ATTR_));
              itemB.addElement("attrID");
              itemB.addElement("J");
            cmds.addElement(itemB);
            cmds.addElement(((scope == GLOBAL_SCOPE) ?
                             Long.valueOf(gENTRY_) : Long.valueOf(zENTRY_)));
              itemC.addElement("id");
              itemC.addElement("J");
            cmds.addElement(itemC);
            
          if ((this.scope == VARIABLE_SCOPE || this.scope == GLOBAL_SCOPE) &&
              (dataType == CDF_CHAR || dataType == CDF_UCHAR)) {
            if (CDFUtils.getSignature(data).indexOf("[") != -1) {
              this.numStrings    = (long) ((String[])data).length;
              this.data         = (Object) CDFUtils.mergeFromStrings(data);
              String oneStr     = (String)(this.data);
              byte[] bytes;
              try {
                bytes      = oneStr.getBytes("UTF-8");
              } catch (Exception ee) {
                bytes      = oneStr.getBytes();
              }
              this.numElements   = (long) bytes.length;
              this.dataSignature = CDFUtils.getSignature(this.data);
            } else {
	      try {
                this.data          = new String(((String)data).getBytes(), "UTF-8");
	      } catch (Exception ee) {
                this.data          = data;
	      }
              this.numElements   = CDFUtils.getNumElements(dataType, this.data);
              this.numStrings    = (long) CDFUtils.getNumStrings(this.data);
              this.dataSignature = CDFUtils.getSignature(this.data);
            }
          } else {
            this.data          = data;
            this.numElements   = CDFUtils.getNumElements(dataType, data);
            this.numStrings    = 1;
            this.dataSignature = CDFUtils.getSignature(this.data);
          }

          cmds.addElement(Long.valueOf(PUT_));
            cmds.addElement(((scope == GLOBAL_SCOPE) ?
                             Long.valueOf(gENTRY_DATA_) :
                             Long.valueOf(zENTRY_DATA_)));
              item0.addElement("dataType");
              item0.addElement("J");
            cmds.addElement(item0);
              item1.addElement("numElements");
              item1.addElement("J");
            cmds.addElement(item1);
              item2.addElement("data");
              item2.addElement("Ljava/lang/Object;");
            cmds.addElement(item2);

          cmds.addElement(Long.valueOf(NULL_));

          id = getID();
          this.xdataType = this.dataType;
          this.xnumElements = this.numElements;
          this.xnumStrings = this.numStrings;
          this.xdataSignature = this.dataSignature;
          this.xdata = this.data;
          this.dataType = dataType;
          this.numElements = CDFUtils.getNumElements(dataType, data);
          this.dataSignature = CDFUtils.getSignature(data);
          this.data = data;

	  attrID = myAttribute.getID();
	  cdfID = myAttribute.getMyCDF().getID();

          myAttribute.getMyCDF().executeCommand((CDFObject)this, cmds);

	} catch (CDFException ex) {
	  this.dataType = this.xdataType;
	  this.numElements = this.xnumElements;
	  this.data = this.xdata;
	  this.dataSignature = this.xdataSignature;
	  throw new CDFException(ex.getCurrentStatus());
	}
    }

    /**
     * Selects this entry.  There is no need to build the entire
     * select cmd vector since this is handled in the JNI native method
     * in cdfNativeLibrary.c.
     *
     * @throws CDFException CDFException if there was a problem in selecting the
     *            current entry
     */
    protected synchronized final void select() throws CDFException {
        Vector cmds = new Vector();
	id = getID();
        // Make sure that the id is up to date
	
        cmds.addElement(Long.valueOf(NULL_)); // Select this entry

        myAttribute.getMyCDF().executeCommand((CDFObject)this, cmds);

    }

}
