/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Copyright by the Board of Trustees of the University of Illinois.         *
  * All rights reserved.                                                      *
  *                                                                           *
  * This file is part of HDF5.  The full HDF5 copyright notice, including     *
  * terms governing use, modification, and redistribution, is contained in    *
  * the files COPYING and Copyright.html.  COPYING can be found at the root   *
  * of the source code distribution tree; Copyright.html can be found at the  *
  * root level of an installed copy of the electronic HDF5 document set and   *
  * is linked from the top-level documents page.  It can also be found at     *
  * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html.  If you do not have     *
  * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*****************************************************************************
   FILE
   tattr.cpp - HDF5 C++ testing the functionalities associated with the
        C attribute interface (H5A)

 ***************************************************************************/

#include <string>

#ifdef OLD_HEADER_FILENAME
#include <iostream.h>
#else
#include <iostream>
#endif

#include "testhdf5.h"	// C test header file
#include "H5Cpp.h"	// C++ API header file

#ifndef H5_NO_NAMESPACE
using namespace H5;
#endif

#include "h5cpputil.h"	// C++ utilility header file

const string	FILENAME("tattr.h5");
const size_t ATTR_NAME_LEN = 16;
const size_t ATTR_MAX_DIMS = 7;
const string	ATTR_TMP_NAME("temp_name");

/* 3-D dataset with fixed dimensions */
const string SPACE1_NAME("Space1");
const int SPACE1_RANK = 3;
const int SPACE1_DIM1 = 3;
const int SPACE1_DIM2 = 15;
const int SPACE1_DIM3 = 13;

/* Dataset Information */
const string DSET1_NAME("Dataset1");
const string DSET2_NAME("Dataset2");

/* Group Information */
const string GROUP1_NAME("/Group1");

/* Attribute Rank & Dimensions */
const string ATTR1_NAME("Attr1");
const int ATTR1_RANK = 1;
const int ATTR1_DIM1 = 3;
int attr_data1[ATTR1_DIM1]={512,-234,98123}; /* Test data for 1st attribute */

const string ATTR2_NAME("Attr2");
const int ATTR2_RANK = 2;
const int ATTR2_DIM1 = 2;
const int ATTR2_DIM2 = 2;
int attr_data2[ATTR2_DIM1][ATTR2_DIM2]={{7614,-416},{197814,-3}}; /* Test data for 2nd attribute */

const string ATTR3_NAME("Attr3");
const int ATTR3_RANK = 3;
const int ATTR3_DIM1 = 2;
const int ATTR3_DIM2 = 2;
const int ATTR3_DIM3 = 2;
double attr_data3[ATTR3_DIM1][ATTR3_DIM2][ATTR3_DIM3]={{{2.3,-26.1},{0.123,-10.0}},{{981724.2,-0.91827},{2.0,23.0}}}; /* Test data for 3rd attribute */

const string ATTR4_NAME("Attr4");
const int ATTR4_RANK = 2;
const int ATTR4_DIM1 = 2;
const int ATTR4_DIM2 = 2;

const string ATTR4_FIELDNAME1("i");
const string ATTR4_FIELDNAME2("d");
const string ATTR4_FIELDNAME3("c");
size_t attr4_field1_off=0;
size_t attr4_field2_off=0;
size_t attr4_field3_off=0;
struct attr4_struct {
    int i;
    double d;
    char c;
 } attr_data4[ATTR4_DIM1][ATTR4_DIM2]={{{3,-26.1,'d'},{-100000, 0.123,'3'}},
    {{-23,981724.2,'Q'},{0,2.0,'\n'}}}; // Test data for 4th attribute

const string ATTR5_NAME("Attr5");
const int ATTR5_RANK = 0;
float attr_data5 = (float)-5.123;    	// Test data for 5th attribute

/* Info for another attribute */
#define ATTR1A_NAME  "Attr1_a"
int attr_data1a[ATTR1_DIM1]={256,11945,-22107};

/****************************************************************
**
**  test_attr_basic_write(): Test basic write attribute.
**      Tests integer attributes on both datasets and groups
** 
****************************************************************/
static void 
test_attr_basic_write(void)
{
    hsize_t dims1[] = {SPACE1_DIM1, SPACE1_DIM2, SPACE1_DIM3};
    hsize_t dims2[] = {ATTR1_DIM1};
    hsize_t dims3[] = {ATTR2_DIM1,ATTR2_DIM2};
    int     read_data1[ATTR1_DIM1]={0}; // Buffer for reading 1st attribute
    int     i;

    // Output message about test being performed 
    MESSAGE(5, ("Testing Basic Attribute Writing Functions\n"));

    try { 
	// Create file
	H5File fid1 (FILENAME, H5F_ACC_TRUNC);

	// Create dataspace for dataset
	DataSpace ds_space (SPACE1_RANK, dims1);

	// Create a dataset
	DataSet dataset = fid1.createDataSet(DSET1_NAME, PredType::NATIVE_UCHAR, ds_space);

	// Create dataspace for attribute
	DataSpace att_space (ATTR1_RANK, dims2);

	// Create an attribute for the dataset
	Attribute ds_attr1 = dataset.createAttribute (ATTR1_NAME, PredType::NATIVE_INT, att_space);

	// Try creating an attribute that already exists.  This should fail 
	// since two attributes cannot have the same name.  If an exception 
	// is not thrown for this action by createAttribute, then throw an 
	// invalid action exception.
	try {
	    Attribute invalid_attr = dataset.createAttribute (ATTR1_NAME, PredType::NATIVE_INT, att_space); 

	    // continuation here, that means no exception has been thrown
	    throw InvalidActionException("H5File::createDataSet", "Library allowed overwrite of existing dataset");
	}
	catch (AttributeIException E) // catching invalid creating attribute
        {} // do nothing, exception expected

	// Write attribute information
	ds_attr1.write (PredType::NATIVE_INT, attr_data1);

	// Read attribute information immediately, without closing attribute
	ds_attr1.read (PredType::NATIVE_INT, read_data1);

	// Verify values read in
	for(i=0; i<ATTR1_DIM1; i++)
	    if(attr_data1[i]!=read_data1[i])
		TestErrPrintf("%d: attribute data different: attr_data1[%d]=%d,read_data1[%d]=%d\n",__LINE__,i,attr_data1[i],i,read_data1[i]);

	// Create another attribute for this dataset
	Attribute ds_attr2 = dataset.createAttribute (ATTR1A_NAME, PredType::NATIVE_INT, att_space);

	// Write attribute information
	ds_attr2.write (PredType::NATIVE_INT, attr_data1a);

	// Read attribute information immediately, without closing attribute
	ds_attr2.read (PredType::NATIVE_INT, read_data1);

	// Verify values read in
	for(i=0; i<ATTR1_DIM1; i++)
	    if(attr_data1a[i]!=read_data1[i])
		TestErrPrintf("%d: attribute data different: attr_data1a[%d]=%d,read_data1[%d]=%d\n",__LINE__,i,attr_data1a[i],i,read_data1[i]);

	// Close both attributes
	ds_attr1.close();
	ds_attr2.close();

	/*
	 *		Test attribute with group
	 */

	// Create group in file fid1
	Group group = fid1.createGroup (GROUP1_NAME);

	// Create dataspace for attribute
	DataSpace sid3(ATTR2_RANK, dims3);

	// Create an attribute for the group
	Attribute gr_attr = group.createAttribute (ATTR2_NAME, PredType::NATIVE_INT, sid3);

	// Check storage size for attribute
	hsize_t attr_size = gr_attr.getStorageSize();
	verify_val((long)attr_size, (long)(ATTR2_DIM1*ATTR2_DIM2*sizeof(int)),
			"Attribute::getStorageSize",__LINE__,__FILE__);

	// Try to create the same attribute again (should fail)
	try {
	    Attribute invalid_attr = group.createAttribute (ATTR2_NAME, PredType::NATIVE_INT, sid3);
            // continuation here, that means no exception has been thrown
            throw InvalidActionException("H5Group::createAttribute", 
			"Attempting to create an existing attribute");
	}
        catch (AttributeIException E) // catching invalid creating attribute
        {} // do nothing, exception expected

	// Write attribute information
	gr_attr.write (PredType::NATIVE_INT, attr_data2);

	// Check storage size for attribute
	attr_size = gr_attr.getStorageSize();
	verify_val((long)attr_size, (long)(ATTR2_DIM1*ATTR2_DIM2*sizeof(int)), 
			"Attribute::getStorageSize",  __LINE__, __FILE__);
    } // end try block

    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_basic_write() */

/****************************************************************
**
**  test_attr_rename(): Test renaming attribute function.
** 
****************************************************************/
static void 
test_attr_rename(void)
{
    int read_data1[ATTR1_DIM1]={0}; // Buffer for reading the attribute
    int i;

	// Output message about test being performed
    MESSAGE(5, ("Testing Rename Attribute Function\n"));

    try {
	// Open file
	H5File fid1(FILENAME, H5F_ACC_RDWR);

	// Open the dataset
	DataSet dataset = fid1.openDataSet(DSET1_NAME);

	// Check rename

	// change attribute name
	dataset.renameAttr(ATTR1_NAME, ATTR_TMP_NAME);

	// Open attribute again
	Attribute attr1(dataset.openAttribute(ATTR_TMP_NAME));

	// Verify new attribute name
	string attr_name = attr1.getName();
	verify_val(attr_name, ATTR_TMP_NAME, "Attribute::getName", __LINE__, __FILE__);

	// Read attribute information immediately, without closing attribute
	attr1.read (PredType::NATIVE_INT, read_data1);

	// Verify values read in
	for(i=0; i<ATTR1_DIM1; i++)
	    if(attr_data1[i]!=read_data1[i])
		TestErrPrintf("%d: attribute data different: attr_data1[%d]=%d,read_data1[%d]=%d\n",__LINE__,i,attr_data1[i],i,read_data1[i]);

	// Close attribute
    	attr1.close();

	// Open the second attribute
	Attribute attr2(dataset.openAttribute(ATTR1A_NAME));

	// Verify second attribute name
	string attr2_name = attr2.getName();
	verify_val(attr2_name, ATTR1A_NAME, "Attribute::getName", __LINE__, __FILE__);

	// Read attribute information immediately, without closing attribute
	attr2.read (PredType::NATIVE_INT, read_data1);

	// Verify values read in
	for(i=0; i<ATTR1_DIM1; i++)
	    if(attr_data1a[i]!=read_data1[i])
		TestErrPrintf("%d: attribute data different: attr_data1a[%d]=%d,read_data1[%d]=%d\n",__LINE__,i,attr_data1a[i],i,read_data1[i]);

	// Close attribute 
	attr2.close();

	// Change first attribute back to the original name
	dataset.renameAttr(ATTR_TMP_NAME, ATTR1_NAME);

    } // end try block
    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_rename() */

/********************************************************************
**
**  test_attr_basic_read(): Test basic read attribute.
** 
********************************************************************/
static void 
test_attr_basic_read(void)
{
    int i, j;

    // Output message about test being performed
    MESSAGE(5, ("Testing Basic Attribute Reading Functions\n"));

    try { 
	// Open file
	H5File fid1(FILENAME, H5F_ACC_RDWR);

	// Open the dataset
	DataSet dataset = fid1.openDataSet(DSET1_NAME);

	// Verify the correct number of attributes
	int num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 2, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Open an attribute for the dataset
	Attribute ds_attr=dataset.openAttribute(ATTR1_NAME);

	// Read attribute information
	int read_data1[ATTR1_DIM1]={0}; // Buffer for reading 1st attribute
	ds_attr.read(PredType::NATIVE_INT, &read_data1);

	// Verify values read in
	for(i=0; i<ATTR1_DIM1; i++)
            if(attr_data1[i]!=read_data1[i]) 
		TestErrPrintf("%d: attribute data different: attr_data1[%d]=%d, read_data1[%d]=%d\n",__LINE__,i,attr_data1[i],i,read_data1[i]);

	/*
	 *		Test attribute with group
	 */
	// Open the group
	Group group = fid1.openGroup(GROUP1_NAME);

	// Verify the correct number of attributes
	num_attrs = group.getNumAttrs();
	verify_val(num_attrs, 1, "H5Group::getNumAttrs", __LINE__, __FILE__);

	// Open an attribute for the group
	Attribute gr_attr = group.openAttribute(ATTR2_NAME);

	// Buffer for reading 2nd attribute
	int read_data2[ATTR2_DIM1][ATTR2_DIM2]={{0}};

	// Read attribute information
	gr_attr.read(PredType::NATIVE_INT, read_data2);

	// Verify values read in
	for(i=0; i<ATTR2_DIM1; i++)
            for(j=0; j<ATTR2_DIM2; j++)
        	if(attr_data2[i][j]!=read_data2[i][j]) {
		    TestErrPrintf("%d: attribute data different: attr_data2[%d][%d]=%d, read_data2[%d][%d]=%d\n",__LINE__, i,j,attr_data2[i][j],i,j,read_data1[i]);
		}
    } // end try block
    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_basic_read() */

/****************************************************************
**
**  test_attr_compound_write(): Tests compound datatype attributes
** 
****************************************************************/
static void 
test_attr_compound_write(void)
{

	// Output message about test being performed
    MESSAGE(5, ("Testing Multiple Attribute Functions\n"));

    try { 
	// Create file
	H5File fid1(FILENAME.c_str(), H5F_ACC_TRUNC);

	// Create dataspace for dataset
	hsize_t		dims1[] = {SPACE1_DIM1, SPACE1_DIM2, SPACE1_DIM3};
	DataSpace sid1(SPACE1_RANK, dims1);

	// Create a dataset
	DataSet dataset = fid1.createDataSet(DSET1_NAME, PredType::NATIVE_UCHAR,sid1);

	// Create the attribute datatype. 
	CompType comp_type(sizeof(struct attr4_struct));

	attr4_field1_off = HOFFSET(struct attr4_struct, i);
	comp_type.insertMember(ATTR4_FIELDNAME1, attr4_field1_off, PredType::NATIVE_INT);

	attr4_field2_off = HOFFSET(struct attr4_struct, d);
	comp_type.insertMember(ATTR4_FIELDNAME2, attr4_field2_off, PredType::NATIVE_DOUBLE);

	attr4_field3_off = HOFFSET(struct attr4_struct, c);
	comp_type.insertMember(ATTR4_FIELDNAME3, attr4_field3_off, PredType::NATIVE_SCHAR);

	// Create dataspace for 1st attribute
	hsize_t		dims2[] = {ATTR4_DIM1,ATTR4_DIM2};
	DataSpace sid2(ATTR4_RANK, dims2);

	// Create complex attribute for the dataset
	Attribute attr = dataset.createAttribute(ATTR4_NAME, comp_type, sid2);

	// Try to create the same attribute again (should fail)
	try {
	    Attribute invalid_attr = dataset.createAttribute (ATTR4_NAME, comp_type, sid2);
	}
        catch (AttributeIException E) // catching invalid creating attribute
        {} // do nothing, exception expected

	// Write complex attribute data
	attr.write(comp_type, attr_data4);

    } // end try block
    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_compound_write() */

/****************************************************************
**
**  test_attr_compound_read(): Test basic H5A (attribute) code.
** 
****************************************************************/
static void 
test_attr_compound_read(void)
{
    hsize_t dims[ATTR_MAX_DIMS];	// Attribute dimensions
    size_t      size;   // Attribute datatype size as stored in file
    size_t      offset; // Attribute datatype field offset
    struct attr4_struct read_data4[ATTR4_DIM1][ATTR4_DIM2]; // Buffer for reading 4th attribute
    int     i,j;

    // Output message about test being performed
    MESSAGE(5, ("Testing Basic Attribute Functions\n"));

    try { 
	// Open file 
	H5File fid1(FILENAME, H5F_ACC_RDWR);

	// Open the dataset
	DataSet dataset = fid1.openDataSet(DSET1_NAME);

	// Verify the correct number of attributes
	int num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 1, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Open 1st attribute for the dataset 
	Attribute attr = dataset.openAttribute((unsigned)0);

	/* Verify Dataspace */

	// Get the dataspace of the attribute
	DataSpace space = attr.getSpace();

	// Get the rank of the dataspace and verify it
	int rank = space.getSimpleExtentNdims();
	verify_val(rank, ATTR4_RANK, "DataSpace::getSimpleExtentNdims", __LINE__, __FILE__);

	// Get the dims of the dataspace and verify them
	int ndims = space.getSimpleExtentDims(dims);
	if(dims[0]!=ATTR4_DIM1)
        verify_val((long)dims[0], (long)ATTR4_DIM1, "DataSpace::getSimpleExtentDims",__LINE__, __FILE__);
        verify_val((long)dims[1], (long)ATTR4_DIM2, "DataSpace::getSimpleExtentDims",__LINE__, __FILE__);

	// Get the class of the datatype that is used by attr
	H5T_class_t type_class = attr.getTypeClass();

	// Verify that the type is of compound datatype
	verify_val(type_class, H5T_COMPOUND, "Attribute::getTypeClass", __LINE__, __FILE__);

	// Get the compound datatype
	CompType datatype = attr.getCompType();

	// Verify the number of fields in the datatype, which must be 3
	int fields = datatype.getNmembers();
	verify_val(fields, 3, "CompType::getNmembers", __LINE__, __FILE__);

	// Verify that the fields have the same names as when the type 
	// was created
	for(i=0; i<fields; i++) 
	{
	    string fieldname = datatype.getMemberName(i);
	    if(!((fieldname == ATTR4_FIELDNAME1) || 
		(fieldname == ATTR4_FIELDNAME2) ||
		(fieldname == ATTR4_FIELDNAME3)))
            TestErrPrintf("%d:invalid field name for field #%d: %s\n",__LINE__,i,fieldname.c_str());
	} /* end for */

	offset = datatype.getMemberOffset(0);
	verify_val(offset, attr4_field1_off, "DataType::getMemberOffset", __LINE__, __FILE__);

	offset = datatype.getMemberOffset(1);
	verify_val(offset, attr4_field2_off, "DataType::getMemberOffset", __LINE__, __FILE__);

	offset = datatype.getMemberOffset(2);
	verify_val(offset, attr4_field3_off, "DataType::getMemberOffset", __LINE__, __FILE__);

	/* Verify each field's type, class & size */

	// Get and verify the type class of the first member
	type_class = datatype.getMemberClass(0);
	verify_val(type_class, H5T_INTEGER, "DataType::getMemberClass", __LINE__, __FILE__);
	// Get and verify the order of this member's type
	IntType i_type = datatype.getMemberIntType(0);
	H5T_order_t order = i_type.getOrder();
	verify_val(order, PredType::NATIVE_INT.getOrder(), "DataType::getOrder", __LINE__, __FILE__);

	// Get and verify the size of this member's type
	size = i_type.getSize();
	verify_val(size, PredType::NATIVE_INT.getSize(), "DataType::getSize", __LINE__, __FILE__);

	// Get and verify class, order, and size of the second member's type
	type_class = datatype.getMemberClass(1);
	verify_val(type_class, H5T_FLOAT, "DataType::getMemberClass", __LINE__, __FILE__);
	FloatType f_type = datatype.getMemberFloatType(1);
	order = f_type.getOrder();
	verify_val(order, PredType::NATIVE_DOUBLE.getOrder(), "DataType::getOrder", __LINE__, __FILE__);
	size = f_type.getSize();
	verify_val(size, PredType::NATIVE_DOUBLE.getSize(), "DataType::getSize", __LINE__, __FILE__);

	// Get and verify class, order, and size of the third member's type
	type_class = datatype.getMemberClass(2);
	verify_val(type_class, H5T_INTEGER, "DataType::getMemberClass", __LINE__, __FILE__);
	// Note: H5T_INTEGER is correct here!

	StrType s_type = datatype.getMemberStrType(2);
	order = s_type.getOrder();
	verify_val(order, PredType::NATIVE_SCHAR.getOrder(), "DataType::getOrder", __LINE__, __FILE__);
	size = s_type.getSize();
	verify_val(size, PredType::NATIVE_SCHAR.getSize(), "DataType::getSize", __LINE__, __FILE__);

	// Read attribute information
	attr.read(datatype, read_data4);

	// Verify values read in
	for(i=0; i<ATTR4_DIM1; i++)
	    for(j=0; j<ATTR4_DIM2; j++)
		if(HDmemcmp(&attr_data4[i][j],&read_data4[i][j],sizeof(struct attr4_struct))) {
		    TestErrPrintf("%d:attribute data different: attr_data4[%d][%d].i=%d, read_data4[%d][%d].i=%d\n",__LINE__,i,j,attr_data4[i][j].i,i,j,read_data4[i][j].i);
		    TestErrPrintf("%d:attribute data different: attr_data4[%d][%d].d=%f, read_data4[%d][%d].d=%f\n",__LINE__,i,j,attr_data4[i][j].d,i,j,read_data4[i][j].d);
		    TestErrPrintf("%d:attribute data different: attr_data4[%d][%d].c=%c, read_data4[%d][%d].c=%c\n",__LINE__,i,j,attr_data4[i][j].c,i,j,read_data4[i][j].c);
             } /* end if */

	// Verify name
	string attr_name = attr.getName();
	verify_val(attr_name, ATTR4_NAME, "Attribute::getName", __LINE__, __FILE__);
    } // end try block
    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_compound_read() */

/****************************************************************
**
**  test_attr_scalar_write(): Test scalar attribute writing functionality.
** 
****************************************************************/
static void 
test_attr_scalar_write(void)
{
    hsize_t		dims1[] = {SPACE1_DIM1, SPACE1_DIM2, SPACE1_DIM3};

	// Output message about test being performed
    MESSAGE(5, ("Testing Basic Scalar Attribute Writing Functions\n"));

    try { 
	// Create file
	H5File fid1(FILENAME, H5F_ACC_TRUNC);

	// Create dataspace for dataset
	hsize_t		dims1[] = {SPACE1_DIM1, SPACE1_DIM2, SPACE1_DIM3};
	DataSpace sid1(SPACE1_RANK, dims1);

	// Create a dataset
	DataSet dataset = fid1.createDataSet(DSET1_NAME, PredType::NATIVE_UCHAR,sid1);

	// Close dataset's dataspace
	sid1.close();

	// Create dataspace for attribute
	DataSpace att_space(ATTR5_RANK, NULL);

	// Create an attribute for the dataset
	Attribute ds_attr = dataset.createAttribute (ATTR5_NAME, PredType::NATIVE_FLOAT, att_space);

	// Try creating an attribute that already exists.  This should fail 
	// since two attributes cannot have the same name.  If an exception 
	// is not thrown for this action by createAttribute, then throw an 
	// invalid action exception.
	try {
	    Attribute invalid_attr = dataset.createAttribute (ATTR5_NAME, PredType::NATIVE_FLOAT, att_space); 

	    // continuation here, that means no exception has been thrown
	    throw InvalidActionException("H5File::createDataSet", "Library allowed overwrite of existing dataset");
	}
	catch (AttributeIException E) // catching invalid creating attribute
        {} // do nothing, exception expected

	// Write attribute information
	ds_attr.write (PredType::NATIVE_FLOAT, &attr_data5);
    } // end try block

    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_scalar_write() */

/****************************************************************
**
**  test_attr_scalar_read(): Test scalar attribute reading functionality.
** 
****************************************************************/
static void 
test_attr_scalar_read(void)
{
    // Output message about test being performed
    MESSAGE(5, ("Testing Basic Scalar Attribute Reading Functions\n"));

    try { 
	// Open file
	H5File fid1(FILENAME, H5F_ACC_RDWR);

	// Open the dataset
	DataSet dataset = fid1.openDataSet(DSET1_NAME);

	// Verify the correct number of attributes
	int num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 1, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Open an attribute for the dataset
	Attribute ds_attr=dataset.openAttribute(ATTR5_NAME);

	// Read attribute information
	float read_data2=0.0;  // Buffer for reading 1st attribute
	ds_attr.read(PredType::NATIVE_FLOAT,&read_data2);
	verify_val(read_data2, attr_data5, "Attribute::read", __LINE__, __FILE__);

	// Get the dataspace of the attribute
	DataSpace att_space = ds_attr.getSpace();

	// Make certain the dataspace is scalar
	H5S_class_t space_type = att_space.getSimpleExtentType();
	verify_val(space_type, H5S_SCALAR, "DataSpace::getSimpleExtentType", __LINE__, __FILE__);
    } // end try block

    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_scalar_read() */

/****************************************************************
**
**  test_attr_mult_write(): Test multiple attributes
** 
****************************************************************/
static void 
test_attr_mult_write(void)
{
    // Output message about test being performed
    MESSAGE(5, ("Testing Multiple Attribute Writing Functions\n"));

    try {
	// Create file
	H5File fid1 (FILENAME, H5F_ACC_TRUNC);

	// Create dataspace for dataset
	hsize_t	dims1[] = {SPACE1_DIM1, SPACE1_DIM2, SPACE1_DIM3};
	DataSpace ds_space (SPACE1_RANK, dims1);

	// Create a dataset
	DataSet dataset = fid1.createDataSet(DSET1_NAME, PredType::NATIVE_UCHAR, ds_space);

	// Create dataspace for 1st attribute
	hsize_t	dims2[] = {ATTR1_DIM1};
	DataSpace att_space (ATTR1_RANK, dims2);

	// Create 1st attribute for the dataset
	Attribute ds_attr = dataset.createAttribute (ATTR1_NAME, PredType::NATIVE_INT, att_space);

	// Write attribute information
	ds_attr.write (PredType::NATIVE_INT, attr_data1);

	// Create dataspace for 2nd attribute
	hsize_t	dims3[] = {ATTR2_DIM1,ATTR2_DIM2};
	DataSpace att2_space (ATTR2_RANK, dims3);

	// Create 2nd attribute for the dataset
	Attribute ds_attr2 = dataset.createAttribute (ATTR2_NAME, PredType::NATIVE_INT, att2_space);

	// Write 2nd attribute information
	ds_attr2.write (PredType::NATIVE_INT, attr_data2);

	// Create dataspace for 3rd attribute
	hsize_t	dims4[] = {ATTR3_DIM1,ATTR3_DIM2,ATTR3_DIM3};
	DataSpace att3_space (ATTR3_RANK, dims4);

	// Create 3rd attribute for the dataset
	Attribute ds_attr3 = dataset.createAttribute (ATTR3_NAME, PredType::NATIVE_DOUBLE, att3_space);

	// Try creating an attribute that already exists.  This should fail 
	// since two attributes cannot have the same name.  If an exception 
	// is not thrown for this action by createAttribute, then throw an 
	// invalid action exception.
	try {
	    Attribute invalid_attr = dataset.createAttribute (ATTR3_NAME, PredType::NATIVE_DOUBLE, att3_space); 

	    // continuation here, that means no exception has been thrown
	    throw InvalidActionException("DataSet::createAttribute", "Attempting to create a duplicate attribute");
	}
	catch (AttributeIException E) // catching invalid creating attribute
        {} // do nothing, exception expected

	// Write 3rd attribute information
	ds_attr3.write (PredType::NATIVE_DOUBLE, attr_data3);
    } // end try block

    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_mult_write() */

/****************************************************************
**
**  test_attr_mult_read(): Test reading multiple attributes.
** 
****************************************************************/
static void 
test_attr_mult_read(void)
{
    int     read_data1[ATTR1_DIM1]={0}; // Buffer for reading 1st attribute
    int     read_data2[ATTR2_DIM1][ATTR2_DIM2]={{0}}; // Buffer for reading 2nd attribute
    double  read_data3[ATTR3_DIM1][ATTR3_DIM2][ATTR3_DIM3]={{{0}}}; // Buffer for reading 3rd attribute
    int     i,j,k;

	// Output message about test being performed
    MESSAGE(5, ("Testing Multiple Attribute Reading Functions\n"));

    try { 
	// Open file 
	H5File fid1(FILENAME, H5F_ACC_RDWR);

	// Open the dataset
	DataSet dataset = fid1.openDataSet(DSET1_NAME);

	// Verify the correct number of attributes
	int num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 3, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Open 1st attribute for the dataset 
	Attribute attr = dataset.openAttribute((unsigned)0);

	/* Verify Dataspace */

	// Get the dataspace of the attribute
	DataSpace space = attr.getSpace();

	// Get the rank of the dataspace and verify it
	int rank = space.getSimpleExtentNdims();
	verify_val(rank, ATTR1_RANK, "DataSpace::getSimpleExtentNdims", __LINE__, __FILE__);

	// Get the dims of the dataspace and verify them
	hsize_t dims[ATTR_MAX_DIMS];    // Attribute dimensions
	int ndims = space.getSimpleExtentDims(dims);
	if(dims[0]!=ATTR1_DIM1)
	    TestErrPrintf("%d:attribute dimensions different: dims[0]=%d, should be %d\n",__LINE__,(int)dims[0],ATTR1_DIM1);

	/* Verify Datatype */

        // Get the class of the datatype that is used by attr
        H5T_class_t type_class = attr.getTypeClass();

        // Verify that the type is of integer datatype
        verify_val(type_class, H5T_INTEGER, "Attribute::getTypeClass", __LINE__, __FILE__);

    	// Get the integer datatype
        IntType i_type1 = attr.getIntType();

	// Get and verify the order of this type
	H5T_order_t order = i_type1.getOrder();
	verify_val(order, PredType::NATIVE_INT.getOrder(), "DataType::getOrder", __LINE__, __FILE__);

	// Get and verify the size of this type
	size_t size = i_type1.getSize();
	verify_val(size, PredType::NATIVE_INT.getSize(), "DataType::getSize", __LINE__, __FILE__);

	// Read attribute information
	attr.read(PredType::NATIVE_INT, read_data1);

	// Verify values read in
	for(i=0; i<ATTR1_DIM1; i++)
	    if(attr_data1[i]!=read_data1[i])
		TestErrPrintf("%d: attribute data different: attr_data1[%d]=%d,read_data1[%d]=%d\n",__LINE__,i,attr_data1[i],i,read_data1[i]);

	// Verify Name
	string attr_name = attr.getName();
	verify_val(attr_name, ATTR1_NAME, "DataType::getName", __LINE__, __FILE__);

	attr.close();
	space.close();

	// Open 2nd attribute for the dataset 
	attr = dataset.openAttribute((unsigned)1);

	/* Verify Dataspace */

	// Get the dataspace of the attribute
	space = attr.getSpace();

	// Get the rank of the dataspace and verify it
	rank = space.getSimpleExtentNdims();
	verify_val(rank, ATTR2_RANK, "DataSpace::getSimpleExtentNdims", __LINE__, __FILE__);

	// Get the dims of the dataspace and verify them
	ndims = space.getSimpleExtentDims(dims);
	if(dims[0]!=ATTR2_DIM1)
	    TestErrPrintf("%d:attribute dimensions different: dims[0]=%d, should be %d\n",__LINE__,(int)dims[0],ATTR2_DIM1);
	if(dims[1]!=ATTR2_DIM2)
	    TestErrPrintf("%d:attribute dimensions different: dims[1]=%d, should be %d\n",__LINE__,(int)dims[1],ATTR2_DIM2);

	/* Verify Datatype */

        // Get the class of the datatype that is used by attr
        type_class = attr.getTypeClass();

        // Verify that the type is of integer datatype
        verify_val(type_class, H5T_INTEGER, "Attribute::getTypeClass", __LINE__, __FILE__);

    	// Get the integer datatype
        IntType i_type2 = attr.getIntType();

	// Get and verify the order of this type
	order = i_type2.getOrder();
	verify_val(order, PredType::NATIVE_INT.getOrder(), "DataType::getOrder", __LINE__, __FILE__);

	// Get and verify the size of this type
	size = i_type2.getSize();
	verify_val(size, PredType::NATIVE_INT.getSize(), "DataType::getSize", __LINE__, __FILE__);

	// Read attribute information
	attr.read(PredType::NATIVE_INT, read_data2);
	//attr.read(i_type, read_data2);

	// Verify values read in
    for(i=0; i<ATTR2_DIM1; i++)
        for(j=0; j<ATTR2_DIM2; j++)
            if(attr_data2[i][j]!=read_data2[i][j])
                TestErrPrintf("%d: attribute data different: attr_data2[%d][%d]=%d, read_data2[%d][%d]=%d\n",__LINE__,i,j,attr_data2[i][j],i,j,read_data2[i][j]);

	// Verify Name
	attr_name = attr.getName();
	verify_val(attr_name, ATTR2_NAME, "DataType::getName", __LINE__, __FILE__);
	attr.close();
	space.close();

	// Open 3rd attribute for the dataset 
	attr = dataset.openAttribute((unsigned)2);

	/* Verify Dataspace */

	// Get the dataspace of the attribute
	space = attr.getSpace();

	// Get the rank of the dataspace and verify it
	rank = space.getSimpleExtentNdims();
	verify_val(rank, ATTR3_RANK, "DataSpace::getSimpleExtentNdims", __LINE__, __FILE__);

	// Get the dims of the dataspace and verify them
	ndims = space.getSimpleExtentDims(dims);
	verify_val((long)dims[0],(long)ATTR3_DIM1,"attribute dimensions",__FILE__,__LINE__);
	verify_val((long)dims[1],(long)ATTR3_DIM2,"attribute dimensions",__FILE__,__LINE__);
	verify_val((long)dims[2],(long)ATTR3_DIM3,"attribute dimensions",__FILE__,__LINE__);

	/* Verify Datatype */

        // Get the class of the datatype that is used by attr
        type_class = attr.getTypeClass();

        // Verify that the type is of compound datatype
        verify_val(type_class, H5T_FLOAT, "Attribute::getTypeClass", __LINE__, __FILE__);

    	// Get the double datatype
        FloatType f_type = attr.getFloatType();

	// Get and verify the order of this type
	order = f_type.getOrder();
	verify_val(order, PredType::NATIVE_DOUBLE.getOrder(), "DataType::getOrder", __LINE__, __FILE__);

	// Get and verify the size of this type
	size = f_type.getSize();
	verify_val(size, PredType::NATIVE_DOUBLE.getSize(), "DataType::getSize", __LINE__, __FILE__);

	// Read attribute information
	attr.read(PredType::NATIVE_DOUBLE, read_data3);

	// Verify values read in
	for(i=0; i<ATTR3_DIM1; i++)
	    for(j=0; j<ATTR3_DIM2; j++)
		for(k=0; k<ATTR3_DIM3; k++)
		    if(attr_data3[i][j][k]!=read_data3[i][j][k])
			TestErrPrintf("%d: attribute data different: attr_data3[%d][%d][%d]=%f, read_data3[%d][%d][%d]=%f\n",__LINE__,i,j,k,attr_data3[i][j][k],i,j,k,read_data3[i][j][k]);

	// Verify Name
	attr_name = attr.getName();
	verify_val(attr_name, ATTR3_NAME, "DataType::getName", __LINE__, __FILE__);
    } // end try block

    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_mult_read() */

/****************************************************************
**
**  test_attr_delete(): Test deleting attribute from different 
**			hdf5 objects.
** 
****************************************************************/
static void 
test_attr_delete(void)
{
    string  attr_name; // Buffer for attribute names

	// Output message about test being performed
    MESSAGE(5, ("Testing Removing Attribute Function\n"));

    try { 
	// Open file 
	H5File fid1(FILENAME, H5F_ACC_RDWR);

	// Open the dataset
	DataSet dataset = fid1.openDataSet(DSET1_NAME);

	// Verify the correct number of attributes
	int num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 3, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Try to delete bogus attribute, should fail.
	try {
	    dataset.removeAttr("Bogus");

	    // continuation here, that means no exception has been thrown
	    throw InvalidActionException("DataSet::removeAttr", "Attempting to remove non-existing attribute");
	}
	catch (AttributeIException E) // catching invalid removing attribute
        {} // do nothing, exception expected

	// Verify the correct number of attributes
	num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 3, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Delete middle (2nd) attribute
	dataset.removeAttr(ATTR2_NAME);

	// Verify the correct number of attributes
	num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 2, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Open 1st attribute for the dataset 
	Attribute attr = dataset.openAttribute((unsigned)0);

	// Verify Name
	attr_name = attr.getName();
	verify_val(attr_name, ATTR1_NAME, "Attribute::getName", __LINE__, __FILE__);

	// Close attribute
	attr.close();

	// Open last (formally 3rd) attribute for the dataset
	attr = dataset.openAttribute((unsigned)1);

	// Verify Name
	attr_name = attr.getName();
	verify_val(attr_name, ATTR3_NAME, "Attribute::getName", __LINE__, __FILE__);

	attr.close();

	// Delete first attribute
	dataset.removeAttr(ATTR1_NAME);

	// Verify the correct number of attributes
	num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 1, "H5Object::getNumAttrs", __LINE__, __FILE__);

	// Open last (formally 3rd) attribute for the dataset
	attr = dataset.openAttribute((unsigned)0);

	// Verify Name
	attr_name = attr.getName();
	verify_val(attr_name, ATTR3_NAME, "Attribute::getName", __LINE__, __FILE__);
	// Close attribute
	attr.close();

	// Delete first attribute
	dataset.removeAttr(ATTR3_NAME);

	// Verify the correct number of attributes
	num_attrs = dataset.getNumAttrs();
	verify_val(num_attrs, 0, "H5Object::getNumAttrs", __LINE__, __FILE__);
    } // end try block

    catch (Exception E) {
	issue_fail_msg(E.getCFuncName(), __LINE__, __FILE__, E.getCDetailMsg());
    }
}   /* test_attr_delete() */

/****************************************************************
**
**  test_attr(): Main attribute testing routine.
** 
****************************************************************/
void 
test_attr(void)
{
    // Output message about test being performed
    MESSAGE(5, ("Testing Attributes\n"));

    // These next two tests use the same file information
    test_attr_basic_write();	// Test basic H5A writing code
    test_attr_rename();		// Test renaming attribute
    test_attr_basic_read(); 	// Test basic H5A reading code

    // These next two tests use the same file information
    test_attr_compound_write();  // Test complex datatype H5A writing code
    test_attr_compound_read();   // Test complex datatype H5A reading code

    // These next two tests use the same file information
    test_attr_scalar_write();  // Test scalar dataspace H5A writing code
    test_attr_scalar_read();   // Test scalar dataspace H5A reading code

    // These next four tests use the same file information
    test_attr_mult_write(); 	// Test H5A writing code for multiple attributes
    test_attr_mult_read();  	// Test H5A reading code for multiple attributes
    test_attr_delete();     	// Test H5A code for deleting attributes

}   /* test_attr() */

/*-------------------------------------------------------------------------
 * Function:	cleanup_attr
 *
 * Purpose:	Cleanup temporary test files
 *
 * Return:	none
 *
 * Programmer:	Albert Cheng
 *              July 2, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void
cleanup_attr(void)
{
    remove(FILENAME.c_str());
}