/*
FILE:     CifCategory.C
*/
/*
VERSION:  4
*/
/*
DATE:     6/17/2002
*/
/*
  Comments and Questions to: sw-help@rcsb.rutgers.edu
*/
/*
COPYRIGHT 1999-2002 Rutgers - The State University of New Jersey

This software is provided WITHOUT WARRANTY OF MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE OR ANY OTHER WARRANTY, EXPRESS OR
IMPLIED.  RUTGERS MAKE NO REPRESENTATION OR WARRANTY THAT THE
SOFTWARE WILL NOT INFRINGE ANY PATENT, COPYRIGHT OR OTHER
PROPRIETARY RIGHT.

The user of this software shall indemnify, hold harmless and defend
Rutgers, its governors, trustees, officers, employees, students,
agents and the authors against any and all claims, suits,
losses, liabilities, damages, costs, fees, and expenses including
reasonable attorneys' fees resulting from or arising out of the
use of this software.  This indemnification shall include, but is
not limited to, any and all claims alleging products liability.
*/
/*
               PDB SOFTWARE LICENSE AGREEMENT

BY CLICKING THE ACCEPTANCE BUTTON OR INSTALLING OR USING 
THIS "SOFTWARE, THE INDIVIDUAL OR ENTITY LICENSING THE  
SOFTWARE ("LICENSEE") IS CONSENTING TO BE BOUND BY AND IS 
BECOMING A PARTY TO THIS AGREEMENT.  IF LICENSEE DOES NOT 
AGREE TO ALL OF THE TERMS OF THIS AGREEMENT
THE LICENSEE MUST NOT INSTALL OR USE THE SOFTWARE.

1. LICENSE AGREEMENT

This is a license between you ("Licensee") and the Protein Data Bank (PDB) 
at Rutgers, The State University of New Jersey (hereafter referred to 
as "RUTGERS").   The software is owned by RUTGERS and protected by 
copyright laws, and some elements are protected by laws governing 
trademarks, trade dress and trade secrets, and may be protected by 
patent laws. 

2. LICENSE GRANT

RUTGERS grants you, and you hereby accept, non-exclusive, royalty-free 
perpetual license to install, use, modify, prepare derivative works, 
incorporate into other computer software, and distribute in binary 
and source code format, or any derivative work thereof, together with 
any associated media, printed materials, and on-line or electronic 
documentation (if any) provided by RUTGERS (collectively, the "SOFTWARE"), 
subject to the following terms and conditions: (i) any distribution 
of the SOFTWARE shall bind the receiver to the terms and conditions 
of this Agreement; (ii) any distribution of the SOFTWARE in modified 
form shall clearly state that the SOFTWARE has been modified from 
the version originally obtained from RUTGERS.  

2. COPYRIGHT; RETENTION OF RIGHTS.  

The above license grant is conditioned on the following: (i) you must 
reproduce all copyright notices and other proprietary notices on any 
copies of the SOFTWARE and you must not remove such notices; (ii) in 
the event you compile the SOFTWARE, you will include the copyright 
notice with the binary in such a manner as to allow it to be easily 
viewable; (iii) if you incorporate the SOFTWARE into other code, you 
must provide notice that the code contains the SOFTWARE and include 
a copy of the copyright notices and other proprietary notices.  All 
copies of the SOFTWARE shall be subject to the terms of this Agreement.  

3. NO MAINTENANCE OR SUPPORT; TREATMENT OF ENHANCEMENTS 

RUTGERS is under no obligation whatsoever to: (i) provide maintenance 
or support for the SOFTWARE; or (ii) to notify you of bug fixes, patches, 
or upgrades to the features, functionality or performance of the 
SOFTWARE ("Enhancements") (if any), whether developed by RUTGERS 
or third parties.  If, in its sole discretion, RUTGERS makes an 
Enhancement available to you and RUTGERS does not separately enter 
into a written license agreement with you relating to such bug fix, 
patch or upgrade, then it shall be deemed incorporated into the SOFTWARE 
and subject to this Agreement. You are under no obligation whatsoever 
to provide any Enhancements to RUTGERS or the public that you may 
develop over time; however, if you choose to provide your Enhancements 
to RUTGERS, or if you choose to otherwise publish or distribute your 
Enhancements, in source code form without contemporaneously requiring 
end users or RUTGERS to enter into a separate written license agreement 
for such Enhancements, then you hereby grant RUTGERS a non-exclusive,
royalty-free perpetual license to install, use, modify, prepare
derivative works, incorporate into the SOFTWARE or other computer
software, distribute, and sublicense your Enhancements or derivative
works thereof, in binary and source code form.

4. FEES.  There is no license fee for the SOFTWARE.  If Licensee
wishes to receive the SOFTWARE on media, there may be a small charge
for the media and for shipping and handling.  Licensee is
responsible for any and all taxes.

5. TERMINATION.  Without prejudice to any other rights, Licensor
may terminate this Agreement if Licensee breaches any of its terms
and conditions.  Upon termination, Licensee shall destroy all
copies of the SOFTWARE.

6. PROPRIETARY RIGHTS.  Title, ownership rights, and intellectual
property rights in the Product shall remain with RUTGERS.  Licensee 
acknowledges such ownership and intellectual property rights and will 
not take any action to jeopardize, limit or interfere in any manner 
with RUTGERS' ownership of or rights with respect to the SOFTWARE.  
The SOFTWARE is protected by copyright and other intellectual 
property laws and by international treaties.  Title and related 
rights in the content accessed through the SOFTWARE is the property 
of the applicable content owner and is protected by applicable law.  
The license granted under this Agreement gives Licensee no rights to such
content.

7. DISCLAIMER OF WARRANTY.  THE SOFTWARE IS PROVIDED FREE OF 
CHARGE, AND, THEREFORE, ON AN "AS IS" BASIS, WITHOUT WARRANTY OF 
ANY KIND, INCLUDING WITHOUT LIMITATION THE WARRANTIES THAT IT 
IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 
OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND 
PERFORMANCE OF THE SOFTWARE IS BORNE BY LICENSEE.  SHOULD THE 
SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, THE LICENSEE AND NOT 
LICENSOR ASSUMES THE ENTIRE COST OF ANY SERVICE AND REPAIR.  
THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF 
THIS AGREEMENT.  NO USE OF THE PRODUCT IS AUTHORIZED HEREUNDER 
EXCEPT UNDER THIS DISCLAIMER.

8. LIMITATION OF LIABILITY.  TO THE MAXIMUM EXTENT PERMITTED BY
APPLICABLE LAW,  IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY 
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 
OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, INCLUDING, 
WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK 
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL 
OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF ADVISED OF THE
POSSIBILITY THEREOF. 
*/
/* **************************************************************** *

  PURPOSE:      This file contains the methods for class CifCategory

 * **************************************************************** */
#include "CifCategory.h"
#include "CifDataBlock.h"

// *********************************************************************
//      class CifCategory functions:
//       Copy(), Print(), DeleteElement(), 
//       GetIndex(), GetItemValue()
// *********************************************************************
 CifString *CifCategory::GetItemDataTypeCode(const int item_index) 
// ----------------------------------------------------------------------
//   GetItemKeyword() Returns the name of the item_index column, NULL if
//                  the item doesnot exist
// --------------------------------------------------------------------- 
{
  if (item_index < 0 || item_index >= _items.Length())
    return NULL;
  else {
    return _items[item_index].GetItemDataTypeCode();
  }
}
 CifString *CifCategory::GetItemDataTypeCode(const CifString item_name) 
// ----------------------------------------------------------------------
//   GetItemKeyword() Returns the name of the item_index column, NULL if
//                  the item doesnot exist
// --------------------------------------------------------------------- 
{
  int item_index;
  item_index = GetIndex(item_name);
  if (item_index < 0 || item_index >= _items.Length())
    return  NULL;
  else return _items[item_index].GetItemDataTypeCode();
}

CifString *CifCategory::GetItemPrimitiveCode(const int item_index) 
// ----------------------------------------------------------------------
//   GetItemKeyword() Returns the name of the item_index column, NULL if
//                  the item doesnot exist
// --------------------------------------------------------------------- 
{
  if (item_index < 0 || item_index >= _items.Length())
    return NULL;
  else {
    return _items[item_index].GetItemPrimitiveCode();
  }
}
CifString *CifCategory::GetItemPrimitiveCode(const CifString item_name) 
// ----------------------------------------------------------------------
//   GetItemKeyword() Returns the name of the item_index column, NULL if
//                  the item doesnot exist
// --------------------------------------------------------------------- 
{
  int item_index;
  item_index = GetIndex(item_name);
  if (item_index < 0 || item_index >= _items.Length())
    return  NULL;
  else return _items[item_index].GetItemPrimitiveCode();
}



void CifCategory::DeleteElement()
// ----------------------------------------------------------------------
//   DeleteElement(): Print the structure CifCategory
// --------------------------------------------------------------------- 
{
  _items.Clear();
  _keys.Clear();
}
CifCategory::CifCategory(const CifCategory &olditem)
{
  unsigned i;
  _readInOrder = olditem._readInOrder;
  _categoryName = olditem._categoryName;
  for (i=0; i< olditem._items.Length(); i++)
    _items.Add(olditem._items[i]);
  for (i=0; i< olditem._keys.Length(); i++)
    _keys.Add(olditem._keys[i]);
}

CifCategory &CifCategory::Copy(const CifCategory &olditem)
// ----------------------------------------------------------------------
//   Copy(): Copies whole structure CifCategory from olditem
// --------------------------------------------------------------------- 
{
  unsigned i;
  DeleteElement();
  _readInOrder = olditem._readInOrder;
  _categoryName = olditem._categoryName;
  for (i=0; i< olditem._items.Length(); i++)
    _items.Add(olditem._items[i]);
  for (i=0; i< olditem._keys.Length(); i++)
    _keys.Add(olditem._keys[i]);
  return *this;
}

void CifCategory::PrintTable() const
// ----------------------------------------------------------------------
//   PrintTable(): Print table like _itemValues of CifCategory
// --------------------------------------------------------------------- 
{
  unsigned i;
  cout << "\t" << "_items: " << _categoryName.Text()  << ' ' << _items.Length() << ' ' 
    << CountRows()<<  endl;
  cout << "\t" << "keys: " << _keys.Length() ;
  for (i = 0; i < _keys.Length(); i++) 
    _keys[i].Print();
  cout << endl;
  for (i = 0; i < _items.Length(); i++) 
    _items[i].PrintTable();
  cout << endl;
  cout << "---------------------------------------------------------------------------------------------" << endl;
  for (unsigned j=0; j<CountRows(); j++) {
    cout << j ;
    for (i = 0; i < _items.Length(); i++)  {
      _items[i]._itemValues[j].PrintTable();
    }
    cout << endl;
  }
    
}
void CifCategory::Print() const
// ----------------------------------------------------------------------
//   Print(): Print the structure CifCategory
// --------------------------------------------------------------------- 
{
  unsigned i;
  char message[CIF_MAXSTRLEN];

  cout << "\t\t\tCategory: " <<_categoryName.Text() << endl;
  cout << "\t\t\t" << "Category keys: ";
  for (i = 0; i < _keys.Length(); i++) 
    _keys[i].Print();
  cout << endl;
  cout << "\t\t\t" << "Number of Items: " << _items.Length() << endl;
  for (i = 0; i < _items.Length(); i++) {
    sprintf(message,"Item %d\0", i);
    PrintLine(message);
    _items[i].Print();
  }
}
void CifCategory::PrintDetails() const
// ----------------------------------------------------------------------
//   Print(): Print the structure CifCategory
// --------------------------------------------------------------------- 
{
  unsigned i;
  cout << "\t" << "_items: " << _categoryName.Text() << ' ' << _items.Length() << endl;
  cout << "\t" << "keys: "<< _keys.Length();
  for (i = 0; i < _keys.Length(); i++) 
    _keys[i].Print();
  cout << endl;
  for (i = 0; i < _items.Length(); i++) {
    cout << "\t" << i << ": ";
    _items[i].PrintDetails();
  }
}
void CifCategory::SetCategoryName(const char *newname) 
{
  int str_len, i;
  if (newname == NULL) return;
  str_len = strlen(newname);
  for (i=0; i< str_len; i++)  
    if (newname[i] == CIF_DATA_JOIN_CHARACTER) break;
  if (i != str_len && newname[0] == '_')
    _categoryName.CopyN(&newname[1], i-1);
  else if (i != str_len)
    _categoryName.CopyN(newname, i);
  else if (newname[0] == '_')
    _categoryName.Copy(&newname[1]);
  else
    _categoryName.Copy(newname);
}
int CifCategory::CountRows() const
// ----------------------------------------------------------------------
//   CountRows(): Get number of rows of the table
// --------------------------------------------------------------------- 
{
  int nrows = 0;
  for (unsigned i = 0; i < _items.Length(); i++) 
    if (_items[i]._itemValues.Length() > nrows)
      nrows = _items[i]._itemValues.Length();
  return nrows;
}

int CifCategory::GetIndex(const char *keyword)
// ----------------------------------------------------------------------
//   GetIndex() Finds the index of the keyword in _items array, 
//              -1 if not found
// --------------------------------------------------------------------- 
{
  int i, str_len;
  for (i=0; i<_items.Length(); i++) 
    if (!CompareNoCase(keyword, _items[i]._keyword.Text())) break;
  if (i == _items.Length()) {
    for (str_len=0; str_len< strlen(keyword); str_len++)
      if (keyword[str_len] == '[') break;
    if (str_len == strlen(keyword))
      return -1;
    else {
      for (i=0; i<_items.Length(); i++) 
	if (_items[i]._itemAttribute._structureNumValue > 1 &&
	    str_len == _items[i]._keyword.Length() &&
	    !CompareNoCase(keyword, _items[i]._keyword.Text(), str_len)) break;
      if (i == _items.Length()) return -1;
    }
  }
  return i;
}


void /*CifCategory* */CifCategory::
  JoinConditionCalculation(CifJoin *join)
{
  int i, j, ret;
  CifTree *root;

  i = 0;
  root = join->BuildConditionTree(&i);
  for (i=0; i< CountRows(); i++) {
    ret = root->ConditionTravserval(this, i, join);
    if (ret != SUCCEED) {
/*
      if (first == TRUE) {
	join_table = new CifCategory;
	item = new CifDataItem;
	for (j=0; j < _items.Length(); j++) {
	  item->_itemAttribute._dataType = _items[j]._itemAttribute._dataType;
	  item->keyword.Copy(_items[j]._keyword);
	  item->SetCaseSensitivity(_items[j]._itemAttribute._flags._case);
	  join_table->_items.Add(*item);
	}
	delete item;
	first = FALSE;
      }
*/
      for (j=0; j < _items.Length(); j++)
	_items[j].DeleteItemValue(i);
      i--;
      if (i >= CountRows()-1) break;
    }
    /*
      if (ret != SUCCEED) 
      for (j=0; j < _items.Length(); j++)
      _items[j].DeleteItemValue(i);
      */
  }
  if (root) delete root;
}
int CifCategory::GetItemValue(CifString **itemValue, const CifString item_label, 
				 const int row_index)
// ----------------------------------------------------------------------
//   GetItemValue() Gets itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int item_index;
  int order , error, *indexes, nindexes;
  CifString tmplabel;

  item_index = GetIndex(item_label);

  if (item_index == -1)
     return CIF_KEYWORD_NOT_EXIST  ;
  else {
    if (_items[item_index]._itemAttribute._structureNumValue <= 1) 
      return _items[item_index].GetItemValue(row_index, itemValue);
    else {
      GetDataItemIndexes(item_label, &indexes, &nindexes, &tmplabel);
      if (nindexes != 0) {
	error = _items[item_index].GetItemValueOrder(indexes, nindexes, &order);
	delete indexes;
	if (error == FALSE )
	  return _items[item_index].GetItemValue(row_index, itemValue, order);
	else  {
	  _items[item_index].GetItemValue(row_index, itemValue, 0);
	  return error;
	}
      }
      else return CIF_KEYWORD_NOT_EXIST;
    }
  }
}
int CifCategory::GetItemValue(CifItemValue **itemValue, 
				 const CifString item_label,
				 CifJoin *join, int order, int *row_index)
{
  int i, item_index, ret, meet_number = 0;
  CifTree *root;

  i = 0;
  root = join->BuildConditionTree(&i);
  meet_number = 0;
  *row_index = -1;
  item_index = GetIndex(item_label);
  for (i=CountRows() -1; i>=0; i--) {
    ret = root->ConditionTravserval(this, i, join);
    if (ret == SUCCEED) {
      meet_number ++;
      if (meet_number == order) {
	*row_index = i;
	return _items[item_index]._itemValues[i].GetItemValue(itemValue);
      }
    }
  }
  if (root) delete root;
  return CIF_VALUE_NOT_EXIST;
}

int CifCategory::GetItemValue(CifString **itemValue, 
				 const CifString item_label,
				 CifJoin *join, int order, int *row_index)
{
  int i, item_index, ret, meet_number = 0;
  CifTree *root;

  i = 0;
  root = join->BuildConditionTree(&i);
  meet_number = 0;
  *row_index = -1;
  item_index = GetIndex(item_label);
  for (i=CountRows() -1; i>=0; i--) {
    ret = root->ConditionTravserval(this, i, join);
    if (ret == SUCCEED) {
      meet_number ++;
      if (meet_number == order) {
	*row_index = i;
	if (root) delete root;
	return _items[item_index]._itemValues[i].GetItemValue(itemValue);
      }
    }
  }
  if (root) delete root;
  return CIF_VALUE_NOT_EXIST;
}

int CifCategory::UpdateItemValue(const CifItemValue *itemValue, const CifString item_label, 
				    const int row_index)
// ----------------------------------------------------------------------
//   UpdateItemValue() Updates itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int item_index;
  int order , error, *indexes, nindexes;
  CifString tmplabel;

  item_index = GetIndex(item_label);


  if (item_index == -1)
    return CIF_KEYWORD_NOT_EXIST  ;
  else {
    if (_items[item_index]._itemAttribute._structureNumValue <= 1)
      return _items[item_index].UpdateItemValue(row_index, itemValue);
    else {
      if (item_label.Length() != _items[item_index]._keyword.Length()) {
	GetDataItemIndexes(item_label, &indexes, &nindexes, &tmplabel);
	if (nindexes != 0) {
	  error = _items[item_index].GetItemValueOrder(indexes, nindexes, &order);
	  delete indexes;
	  if (error == FALSE)
	    return _items[item_index].UpdateItemValue(row_index, itemValue, order);
	  else {
	    _items[item_index].UpdateItemValue(row_index, itemValue, 0);
	    return error;
	  }
	}
	else return CIF_KEYWORD_NOT_EXIST;
      }
      else return _items[item_index].UpdateItemValue(row_index, itemValue, -1);
    }
  }
}

int CifCategory::JoinComparsion(const int row_no, 
				       ReVarCifArray <CifString> *join_tables,
				       CifJoinCondition *condition)
 
{
  int i, j, ret, left_item_no, right_item_no, case_sensitive;
  ret = FALSE;

  for (j=0; j< join_tables->Length(); j++) 
    if (!Compare((*join_tables)[j], condition->left_category))
      break;
  if (j != join_tables->Length()) {
    for (left_item_no =0; left_item_no < _items.Length(); left_item_no ++) {
      for (i=0; i<_items[left_item_no]._keyword.Length(); i++)
	if (_items[left_item_no]._keyword.Text()[i] == CIF_DATA_JOIN_CHARACTER) break;
      if (!CompareNoCase(condition->left_category, _categoryName) &&
	  !CompareNoCase(condition->left_item, _items[left_item_no]._keyword) ||
	  !CompareNoCase(condition->left_category.Text(),
		   (char *) &_items[left_item_no]._keyword.Text()[1], i-1) &&
	  !CompareNoCase(condition->left_item.Text(),
		   (char *) &_items[left_item_no]._keyword.Text()[i+1])) break;
    }
    if (left_item_no != _items.Length()) {
      case_sensitive = _items[left_item_no]._itemAttribute._flags._case;
      if (condition->cond_operator > CIF_WITH_OPERAND ||
	  condition->operand != NULL) {
	ret = condition->CondCompare(&_items[left_item_no]._itemValues[row_no], 
				     condition->operand, case_sensitive); 
      }
      else if (condition->right_category != NULL && condition->right_item != NULL) {
	for (j=0; j< join_tables->Length(); j++) 
	  if (!Compare((*join_tables)[j], *condition->right_category))
	    break;
	if (j != join_tables->Length()) {
	  for (right_item_no =0; right_item_no < _items.Length(); right_item_no ++) {
	    for (i=0; i<_items[right_item_no]._keyword.Length(); i++)
	      if (_items[right_item_no]._keyword.Text()[i] == CIF_DATA_JOIN_CHARACTER) break;
	    if (!CompareNoCase(*condition->right_category, _categoryName) &&
		!Compare(*condition->right_item, _items[right_item_no]._keyword) ||
		!Compare(condition->right_category->Text(),
			 (char *) &_items[right_item_no]._keyword.Text()[1], i-1) &&
		!Compare(condition->right_item->Text(),
			 (char *) &_items[right_item_no]._keyword.Text()[i+1])) break;
	  }
	  if (right_item_no != _items.Length())
	    ret = condition->CondCompare(&_items[left_item_no]._itemValues[row_no],
					 &_items[right_item_no]._itemValues[row_no], 
					 case_sensitive); 
	}
      }
    }
  }
  return ret;
}

void CifCategory::CheckKeys(CifCategory *table, 
				   const ReVarCifArray <CifString> *key_list,
				   CifDataBlock *datablock,
				   const int lineNo)
{
  
  int i, j, k, l, n, nrows1, nrows2;
  int *index1, *index2, *index3, *index4;
  CifString message;

  if (key_list == NULL || key_list->Length() == 0) return;
  if (_items.Length() == 0 || table->_items.Length() ==0) return;


  index1 = new int[key_list->Length()];
  index2 = new int[key_list->Length()];
  for (j=0; j<key_list->Length(); j++) {
    for (k=0; k< _items.Length(); k++) 
      if (!CompareNoCase(_items[k]._keyword, (*key_list)[j]))
	break;
    if (k == _items.Length()) {
      message.Clear();
      message += "Category key \'_";
      message += _categoryName.Text();
      message += '.';
      message +=  (*key_list)[j].Text();
      message += "\' is missing";
      datablock->ErrorMessage(CIF_ERROR, lineNo, message);
      delete[] index1;
      delete[] index2;
      return;
    }
    else index1[j] = k;
    if (table == this)  index2[j] = index2[j];
      
  }

  if (table != this) {
    for (j=0; j<key_list->Length(); j++) {
      for (k=0; k< table->_items.Length(); k++) 
	if (!CompareNoCase(table->_items[k]._keyword, (*key_list)[j]))
	  break;
      if (k == table->_items.Length()) {
	message.Clear();
	message += "Category key value of item \'_";
	message += _categoryName.Text();
	message += '.';
	message +=  (*key_list)[j].Text();
	message += "\' is missing";
	datablock->ErrorMessage(CIF_ERROR, lineNo, message);
	delete [] index1;
	delete [] index2;
      }
      else index2[j] = k;
    }
  }

  index3 = new int[_items.Length()];
  if (table != this) {
    for (k=0; k< _items.Length(); k++) {
      for (j=0; j< table->_items.Length(); j++)
	if (!CompareNoCase(table->_items[j]._keyword, _items[k]._keyword)) break;
      if (j == table->_items.Length()) index3[k] = -1;
      else index3[k] = j;
    }
  }
  else {
    for (k=0; k< _items.Length(); k++) 
      index3[k] = k;
  }
  index4 = new int[_items.Length()];
  for (k=0; k< _items.Length(); k++) {
    if (key_list->Length()== table->_items.Length() ||
	CompareNoCase(_items[k]._keyword.Text(),
		      CIF_DDL_ITEM_DATABLOCK_ID ) &&
	CompareNoCase(_items[k]._keyword.Text(),
		      CIF_DDL_ITEM_OFFSET ) &&
	CompareNoCase(_items[k]._keyword.Text(),
		      CIF_DDL_ITEM_ID ) ) 
      index4[k] =1;
    else index4[k] = 0;
  }
  
  
  nrows1 = CountRows();

  /* Check key columns does not have value null */
  for (j=0; j<key_list->Length(); j++) 
    if (index1[j] != -1) {
      k = index1[j];
      for (i=0; i<nrows1; i++) {
	if (_items[k]._itemValues[i]._status == CIF_DATA_IS_NULL) {
	  message.Clear();
	  message += "Category key value of item \'_";
	  message += _categoryName.Text();
	  message += '.';
	  message +=  (*key_list)[j].Text();
	  message += "\' is missing";
	  datablock->ErrorMessage(CIF_ERROR, lineNo, message);
	}
	  
      }
  }

  /* Delete Duplication Rows */

  /* Checking between table and the current table */
  for (i=0; i< nrows1; i++) {
    if (table != this)
      nrows2 = table->CountRows();
    else 
      nrows2 = i;
    for (l=0; l<nrows2; l++) {
      for (j=0; j<key_list->Length(); j++) {
	k = index1[j];
	if (k == -1) break;
	if (this == table) 
	  n = k;
	else {
	  n = index2[j];
	  if (n == -1) break;
	}
	if (i>=_items[k]._itemValues.Length()) break;
	if (_items[k]._itemAttribute._flags._case &&
	    Compare(_items[k]._itemValues[i]._buf, table->_items[n]._itemValues[l]._buf) ||
	    !_items[k]._itemAttribute._flags._case &&
	    CompareNoCase(_items[k]._itemValues[i]._buf, 
			  table->_items[n]._itemValues[l]._buf))
	  break;
      }
      if (j == key_list->Length()) {
	// This part is giving warning to the rows are exactly the same with 
	// some other rows. Because we do not want to give warning for everything,
	// such as item.description, item._name for some save blocks (item._name might
	// defined at some place, and item.description is defined at the other place)
	// however, we do not want not to warn the user when everything columns are
	// keys and they are duplicate
	for (k=0; k<_items.Length(); k++) {
	  n = index3[k];
	  if (n != -1 &&
	      _items[k]._itemValues[i]._status == CIF_DATA_IS_VALID &&
	      table->_items[n]._itemValues[l]._status == CIF_DATA_IS_VALID &&
	      CompareNoCase(_items[k]._itemValues[i]._buf,
			    table->_items[n]._itemValues[l]._buf)) break;
	}
	if (k == _items.Length()) {
	  for (k=0; k<key_list->Length(); k++) {
	    message.Clear();
	    message += "Duplicate key values \'";
	    message += _items[index1[k]]._itemValues[i]._buf.Text();
	    message += "\' of item \'";
	    message +=  (*key_list)[k].Text();
	    message += "\' are detected";
	    datablock->ErrorMessage(CIF_WARNING, lineNo, message);
	  }
	}
	for (k=0; k<_items.Length(); k++) {
	  n = index3[k];
	  if (n != -1) {
	    if (index4[k] &&
		CompareNoCase(_items[k]._itemValues[i]._buf,
			      table->_items[n]._itemValues[l]._buf) &&
		_items[k]._itemValues[i]._status == CIF_DATA_IS_VALID &&
		table->_items[n]._itemValues[l]._status == CIF_DATA_IS_VALID ) {
	      message.Clear();
	      message += "Duplicate key values \'";
	      message += _items[k]._itemValues[i]._buf;
	      message += "\' of item \'";
	      message +=  _items[k]._itemAttribute._name.Text();
	      message += "\' are detected";
	      datablock->ErrorMessage(CIF_WARNING, lineNo, message);
	    }
	    if (_items[k]._itemValues[i]._status == CIF_DATA_IS_VALID &&
		table->_items[n]._itemValues[l]._status != CIF_DATA_IS_VALID) 
	      table->_items[n]._itemValues[l].Copy(_items[k]._itemValues[i]);{
	      _items[k]._itemValues.DeleteAt(i);
	    }
	  }
	}

      }
      if (this == table && l >= table->CountRows()-1) break;
    }
    if (i >= CountRows()-1) break;
  }
  delete [] index1;
  delete [] index2;
  delete [] index3;
  delete [] index4;
}
void CifCategory::AddImplicitItem(CifDataBlock *dic, 
					 CifDataBlock *datablock,
					 const char *tmpkeyword, 
					 const CifString newitemValue,
					 const int lineNo)
//-----------------------------------------------------------------------
// AddImplicitDataItem(): Add an extra column to ReVarCifArray <CifDataItem>
//                ReVarCifArray <CifDataItem> *newdata
//                CifString _categoryName: category for the extra column
//                CifString keyword : item for the extra column
//                    keyword for this extra column will be 
//                          '_' + category_name + '.' + keyword
//                CifString newitemValue to be the _itemValues of the extra column
//-----------------------------------------------------------------------
{
  int i, j, ret, nrows;
  CifString item_name, item_full_name;
  CifItemValue *mandatory_VALUE;
  CifDataItem adddataitem;
  CifItemValue stritemValue(CIF_STRING_VALUE, 1, FALSE);
  CifString message;

  // Get the keyword corresponding _category.id or _item._name for this
  // particular category
  ret = dic->GetIdOrItemKeyword(_categoryName, tmpkeyword, &item_name);
  if (ret > CIF_OUT_OF_RANGE_ERROR_CODE_START) return;
  JoinCategoryItemName(_categoryName, item_name, &item_full_name);
  item_full_name.ToLower();
  ret = dic->GetMandatoryCode(item_full_name, &mandatory_VALUE);
  
  if (ret == CIF_DATA_IS_VALID) {
    // mandatory_code itemValue is not "no"
    if (CompareNoCase(mandatory_VALUE->_buf.Text(), CIF_NO_TERM)) {
      for (i =0; i< _items.Length(); i++) 
	if (!CompareNoCase(item_name, _items[i]._keyword)) break;
      
      if (!CompareNoCase(mandatory_VALUE->_buf.Text(), CIF_IMPLICIT_TERM)) {
	// All the itemValue should be extract from the save_ label
	if (i == _items.Length()) {
	  adddataitem.SetKeyword(_categoryName.Text(), item_name,"");
	  nrows = CountRows();
	  stritemValue.UpdateItemValue(CIF_STRING_VALUE, CIF_DATA_IS_VALID, newitemValue.Text());
	  for (i=0; i< nrows; i++)  
	    ret = adddataitem.AppendItemValue(&stritemValue);
	  dic->GetItemInfo(&adddataitem);
	  _items.Add(adddataitem);
	  adddataitem.DeleteElement();
	}
	// Some _itemValues do not have to be extracted from the save_label
	else {
	  nrows = _items[i].CountItemValue();
	  for (j=0; j< nrows ; j++) {
	    ret = _items[i].GetDataStatus(j);
	    if (ret == CIF_DATA_IS_NULL) 
	      _items[i]._itemValues[j].UpdateItemValue(CIF_STRING_VALUE, CIF_DATA_IS_VALID,
					     newitemValue.Text());
	    
	    else if (ret != CIF_DATA_IS_VALID) {
	      message.Clear();
	      message += "Value for implicit mandatory item \'";
	      message += item_full_name.Text();
	      message += "\' specified as n.a.";
	      datablock->ErrorMessage(CIF_WARNING, lineNo, message);
	    }
	    else if (nrows == 1 && CompareNoCase(newitemValue, _items[i]._itemValues[j]._buf)) {
	    }
	  }
	}
      }
      // mandatory_code itemValue is "yes", check if the itemValue is there
      // if not, give error messages
      else if (!CompareNoCase(mandatory_VALUE->_buf.Text(), CIF_YES_TERM)) {
	if (i == _items.Length()) {
	  message.Clear();
	  message += "Value for mandatory item \'";
	  message += item_full_name.Text();
	  message += "\' is missing";
	  datablock->ErrorMessage(CIF_ERROR, lineNo, message);
	}
	else {
	  nrows = _items[i].CountItemValue();
	  for (j=0; j< nrows ; j++) {
	    ret = _items[i].GetDataStatus(j);
	    if (ret != CIF_DATA_IS_VALID && ret != CIF_DATA_NOT_APPROPRIATE) {
	      message.Clear();
	      message += "Value for mandatory item \'";
	      message += item_full_name.Text();
	      message += "\' is missing";
	      datablock->ErrorMessage(CIF_ERROR, lineNo, message);
	    }
	    else if (ret == CIF_DATA_NOT_APPROPRIATE) {
	      message.Clear();
	      message += "Value for mandatory item \'";
	      message += item_full_name.Text();
	      message += "\' specified as n.a.";
	      datablock->ErrorMessage(CIF_WARNING, lineNo, message);
	    }
	  }
	}
      }
      else {
	// mandatory_code itemValue is neither "implicit", nor "yes", nor "no"
	// do not know what to do, only give error message
	message.Clear();
	message += "Mandatory value \'";
	message += mandatory_VALUE->_buf.Text();
	message += "\' of data item \'";
	message += item_full_name.Text();
	message += "\' is not recognized";
	datablock->ErrorMessage(CIF_WARNING, lineNo, message);
	return;
      }
    }
  }
}


void CifCategory::AddDataItem(CifDataBlock *dic, const char * tmpkeyword, 
				     const CifString newitemValue)
//-----------------------------------------------------------------------
// AddDataItem(): Add an extra column to ReVarCifArray <CifDataItem>
//                ReVarCifArray <CifDataItem> *newdata
//                CifString _categoryName: category for the extra column
//                CifString keyword : item for the extra column
//                    keyword for this extra column will be 
//                          '_' + _categoryName + '.' + keyword
//                CifString newitemValue to be the _itemValues of the extra column
//-----------------------------------------------------------------------
{
  int i, nrows;
  CifDataItem adddataitem;
  CifItemValue stritemValue(CIF_STRING_VALUE, 1, FALSE);

  for (i=0; i<_items.Length(); i++) 
    if (!CompareNoCase(_items[i]._keyword.Text(), tmpkeyword)) break;
  if (i != _items.Length()) return;

  adddataitem.SetKeyword(_categoryName.Text(), tmpkeyword, "");
  dic->GetItemInfo(&adddataitem);
  nrows = CountRows();
  stritemValue.UpdateItemValue(CIF_STRING_VALUE, CIF_DATA_IS_VALID, newitemValue.Text());
  for (i=0; i< nrows; i++)  
    adddataitem.AppendItemValue(&stritemValue);
  _items.Add(adddataitem);
  adddataitem.DeleteElement();
}


void CifCategory::AddDataItem(CifDataBlock *dic, const char * tmpkeyword, 
				     const long offset)
//-----------------------------------------------------------------------
// AddDataItem(): Add an extra column to ReVarCifArray <CifDataItem>
//                ReVarCifArray <CifDataItem> *newdata
//                CifString category_name: category for the extra column
//                CifString keyword : item for the extra column
//                    keyword for this extra column will be 
//                          '_' + _categoryName + '.' + keyword
//                CifString newitemValue to be the _itemValues of the extra column
//-----------------------------------------------------------------------
{
  int i, nrows;
  CifDataItem adddataitem;
  CifItemValue dblitemValue(1);

  for (i=0; i<_items.Length(); i++) 
    if (!CompareNoCase(_items[i]._keyword.Text(), tmpkeyword)) break;
  if (i != _items.Length()) return;

  adddataitem.SetKeyword(_categoryName.Text(), tmpkeyword,"");
  dic->GetItemInfo(&adddataitem);
  nrows = CountRows();
  dblitemValue.UpdateItemValue(offset);
  for (i=0; i< nrows; i++)  
    adddataitem.AppendItemValue(&dblitemValue);
  _items.Add(adddataitem);
  adddataitem.DeleteElement();
}

CifDataItem *CifCategory::AddDataItem(CifDataBlock *dic, 
					       CifDataBlock *datablock,
					       const char *itemKeyword)
{
  int i, nvalues, index;
  CifItemValue itemValue;
  CifDataItem adddataitem;
  CifString aliasName, token;
  CifString message;

  index = dic->_nameAttributes.GetIndex(itemKeyword);
  for (i=0; i<strlen(itemKeyword); i++)
    if (itemKeyword[i] == CIF_DATA_JOIN_CHARACTER ) break;
  if (index == -1 && i == strlen(itemKeyword)) {
    aliasName.Clear();
    JoinCategoryItemName(_categoryName, itemKeyword, &token);
    if (!CompareNoCase(_categoryName.Text(),CIF_DDL_CATEGORY_BLOCK) &&
	!CompareNoCase(itemKeyword,CIF_DDL_ITEM_ID) ||
	CompareNoCase(_categoryName.Text(), CIF_DDL_CATEGORY_BLOCK) &&
	!CompareNoCase(itemKeyword, CIF_DDL_ITEM_DATABLOCK_ID))
      index = 1;
    else
      index = dic->_nameAttributes.GetIndex(token);
  }
  else { // itemKeyword is an alias 
    if (i == strlen(itemKeyword))
      aliasName.Copy(itemKeyword);
    else aliasName.Clear();
    token.Copy(dic->_nameAttributes._list[index]._name);
    index = dic->_nameAttributes.GetIndex(token);
  }

  if (index == -1) {
    message.Clear();
    message += "Add new column error: Item name \'";
    message += itemKeyword;
    message += "\' does not exist in dictionary \'";
    message += dic->_dataBlockName;
    message += "\'";
    datablock->ErrorMessage(CIF_ERROR, -1, message.Text());
    return NULL;
  }
  for (i=0; i< _items.Length(); i++) 
    if (!CompareNoCase(_items[i]._itemAttribute._name, token)) break;
    
  if (i != _items.Length()) {
    message.Clear();
    message += "Add new column error: Item name \'";
    message += itemKeyword;
    message += "\' exists in the current category \'";
    message += _categoryName;
    message += "\'";
    datablock->ErrorMessage(CIF_ERROR, -1, message.Text());
    
    return NULL;
  }

  itemValue.UpdateItemValue(CIF_STRING_VALUE, CIF_DATA_IS_NULL, NULL);

		       
  adddataitem.DeleteElement();
  adddataitem.SetKeyword(token, aliasName);

  dic->GetItemInfo(&adddataitem);
  nvalues = CountRows();
  for (i=0; i< nvalues; i++) 
    adddataitem._itemValues.Add(itemValue);
  _items.Add(adddataitem);
  return &_items[_items.Length()-1];

}
void CifCategory::AddEsdDataItem(CifDataBlock *dic, CifDataBlock *datablock,
					const CifString *itemName, 
					const int columnNo)

//-----------------------------------------------------------------------
// AddDataItem(): Add an extra column to ReVarCifArray <CifDataItem>
//                ReVarCifArray <CifDataItem> *newdata
//                CifString _categoryName: category for the extra column
//                CifString keyword : item for the extra column
//                    keyword for this extra column will be 
//                          '_' + _categoryName + '.' + keyword
//                CifString newitemValue to be the _itemValues of the extra column
//-----------------------------------------------------------------------
{
  int i, j, k, nrows, addedFlag;
  CifDataItem *adddataitem;
  CifItemValue nullValue;
  CifString tmpValue, message;

  if (columnNo < 0 || columnNo >= _items.Length() ||
      (_items[columnNo]._itemAttribute._dataType != CIF_INTEGER_VALUE && 
       _items[columnNo]._itemAttribute._dataType != CIF_DOUBLE_VALUE))
    return;
  nrows = CountRows();

  addedFlag = FALSE;
  for (i=0; i<_items.Length(); i++) 
    if (!CompareNoCase(_items[i]._itemAttribute._name, *itemName)) break;
  if (i != _items.Length()) 
    adddataitem = &_items[i];

  else {
    addedFlag = TRUE;
    adddataitem = new CifDataItem;
    adddataitem->SetKeyword(itemName->Text(),"");
    dic->GetItemInfo(adddataitem);
    nullValue.UpdateItemValue(adddataitem->_itemAttribute._dataType,
			      CIF_DATA_IS_NULL, (char *) NULL);
    for (i=0; i<nrows; i++)
      adddataitem->_itemValues.Add(nullValue);
/*
    _items.InsertNAt(columnNo+1, adddataitem);
    adddataitem = &_items[columnNo+1];
*/
  }

  for (i=0; i<nrows; i++) {
    for (j=0; j<_items[columnNo]._itemValues[i]._buf.Length(); j++)
      if (_items[columnNo]._itemValues[i]._buf[j] == '(') break;
    tmpValue.Clear();
    if (j != _items[columnNo]._itemValues[i]._buf.Length() &&
	isdigit(_items[columnNo]._itemValues[i]._buf[j+1])) {
      tmpValue.Copy(&_items[columnNo]._itemValues[i]._buf.Text()[j+1]);
      for (k=0; k<tmpValue.Length(); k++)
	if (tmpValue[k] == ')') {
	  tmpValue.DeleteAt(k, tmpValue.Length() -k);
	  break;
	}
      tmpValue.RemoveBlanks();
    }
    if (adddataitem->_itemValues[i]._status != CIF_DATA_IS_VALID) {
      if (strcmp(tmpValue.Text(), ""))
	adddataitem->_itemValues[i].UpdateItemValue(adddataitem->_itemAttribute._dataType,
						    CIF_DATA_IS_VALID, tmpValue.Text());
    }
    else if (strcmp(tmpValue.Text(), "")){
      message.Clear();
      message += "Duplicate Values \'";
      message += adddataitem->_itemValues[i]._buf.Text();
      message += "\' and \'";
      message += tmpValue.Text();
      message += "\' of item \'";
      message += adddataitem->_itemAttribute._name.Text();
      message += "\'";
      datablock->ErrorMessage(CIF_WARNING, -1, message);
    }
  }
  if (addedFlag == TRUE)  {
    for (i=0; i<nrows; i++) 
      if (adddataitem->_itemValues[i]._status == CIF_DATA_IS_VALID) break;
    if (i != nrows) 
      _items.InsertNAt(columnNo+1, adddataitem);
  }
  if (adddataitem) delete adddataitem;
}


void CifCategory::PermanentWrite(FILE *fp)
{
  unsigned i;

  fprintf(fp, "%d", _readInOrder);
  _categoryName.PermanentWrite(fp);

  fprintf(fp, "%d\n", _keys.Length());
  for (i =0; i<_keys.Length(); i++) {
    fprintf(fp, "%d %s\n", _keys[i].Length(), _keys[i].Text());
  }
  fprintf(fp, "%d\n", _items.Length());
  for (i =0; i<_items.Length(); i++)
    _items[i].PermanentWrite(fp);
}


void CifCategory::PermanentRead(FILE *fp)
{
  int tmplength, i;
  CifDataItem item;
  CifString key;

  fscanf(fp, "%d ", &_readInOrder);
  _categoryName.PermanentRead(fp);
  
  fscanf(fp, "%d ", &tmplength);
  if (tmplength > 0) {
    _keys.GrowBy(tmplength);
    for (i =0; i<tmplength; i++) {
      key.PermanentRead(fp);
      _keys.Add(key);
    }
  }
  fscanf(fp, "%d ", &tmplength);
  if (tmplength > 0) {
    _items.GrowBy(tmplength);
    for (i =0; i<tmplength; i++) {
      item.DeleteElement();
      item.PermanentRead(fp);
      _items.Add(item);
    }
  }
}


void CifCategory:: GetItemAttribute(const CifString match_itemValue,
					   const int matchedItemIndex,
					   const int valueItemIndex,
					   ReVarCifArray <CifString> *array)
{
  int i, nvalues;
  array->Clear();
  if (matchedItemIndex < 0 || matchedItemIndex >= _items.Length() ||
      valueItemIndex < 0 || valueItemIndex >=_items.Length())
    return;
  nvalues = CountRows();
  for ( i=0; i< nvalues; i++) {
    if (!_items[matchedItemIndex]._itemAttribute._flags._case &&
	!CompareNoCase(_items[matchedItemIndex]._itemValues[i]._buf, match_itemValue) ||
	_items[matchedItemIndex]._itemAttribute._flags._case &&
	!Compare(_items[matchedItemIndex]._itemValues[i]._buf, match_itemValue) )
      array->Add(_items[valueItemIndex]._itemValues[i]._buf);
  }
}
void CifCategory:: WriteComment(CifFileIo *ciffile,
				       CifDataBlock *dic,
				       const CifString *dictionary,
				       const CifString *version,
				       const int esdFlag)
{
  CifString _id(CIF_DDL_ITEM_ID);
  CifString _name(CIF_DDL_ITEM_NAME);
  CifString _description(CIF_DDL_ITEM_DESCRIPTION);
  CifString _case(CIF_DDL_ITEM_CASE);

  CifString _item_description(CIF_DDL_CATEGORY_ITEM_DESCRIPTION);
  CifString _category(CIF_DDL_CATEGORY_CATEGORY);
  CifString _item_examples(CIF_DDL_CATEGORY_ITEM_EXAMPLES);
  int cat_cat, cat_id_col, cat_des_col, i, j;
  int des_cat, des_name_col, des_des_col ;
  int ex_cat, ex_name_col, ex_ex_col;
  CifString comment, title;
  ReVarCifArray <CifString> comment_table;
  CifString *aliasName;
  
  cat_cat = dic->GetIndex(_category);
  if (cat_cat != -1) {
    cat_id_col = dic->_categories[cat_cat].GetIndex(_id);
    cat_des_col = dic->_categories[cat_cat].GetIndex(_description);
  }
  
  des_cat = dic->GetIndex(_item_description);
  if (des_cat != -1) {
    des_name_col = dic->_categories[des_cat].GetIndex(_name);
    des_des_col = dic->_categories[des_cat].GetIndex(_description);
  }

  ex_cat = dic->GetIndex(_item_examples);
  if (ex_cat != -1) {
    ex_name_col = dic->_categories[ex_cat].GetIndex(_name);
    ex_ex_col = dic->_categories[ex_cat].GetIndex(_case);
  }


  comment.Copy("Category: ");
  comment += _categoryName;
  ciffile->PrintSectionLine();
  ciffile->PrintComment(comment, 1);
  if (cat_cat != -1) {
    dic->_categories[cat_cat].
      GetItemAttribute(_categoryName, cat_id_col, cat_des_col, &comment_table);
    if (comment.Length() != 0) {
      title.Copy("Category description: ");
      ciffile->PrintComment(title, &comment_table);
    }
  }
  
  for (i=0; i<_items.Length(); i++) {
    aliasName = NULL;
    if (strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID)&&
	strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_OFFSET)) {
      
      if (dictionary == NULL && esdFlag) {
	ciffile->PrintComment("\n");
	ciffile->PrintComment(_items[i]._keyword, 1);
      }
      else {
	for (j=0; j< _items[i]._relatedItems.Length(); j++) 
	  if (_items[i]._relatedItems[j].GetItemIndex() != -1 &&
	      !CompareNoCase(_items[i]._relatedItems[j].GetFunctionCode()->Text(),
			     CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_VALUE))
	    break;
	if (j != _items[i]._relatedItems.Length()) continue;
	if (dictionary != NULL) 
	  aliasName = _items[i].GetItemAliasName(dictionary, version);

	if (dictionary == NULL) {
	  ciffile->PrintComment("\n");
	  ciffile->PrintComment(_items[i]._keyword, 1);
	}
	else if (aliasName == NULL) 
	  continue;
	else {
	  ciffile->PrintComment("\n"); 
	  ciffile->PrintComment(*aliasName, 1);
	}
	
      }
      if (des_cat != -1) {
	dic->_categories[des_cat].
	  GetItemAttribute(_items[i]._itemAttribute._name,
			   des_name_col,des_des_col, &comment_table);
	if (comment.Length() != 0) {
	  title.Copy("Item description: ");
	  ciffile->PrintComment(title, &comment_table);
	}
      }
      if (ex_cat != -1) {
	dic->_categories[ex_cat].
	  GetItemAttribute(_items[i]._itemAttribute._name,
			   ex_name_col, ex_ex_col, &comment_table);
	if (comment.Length() != 0) {
	  title.Copy("Item examples: ");
	  ciffile->PrintComment(title, &comment_table);
	}
      }
    }
  }
  ciffile->PrintComment("\n");
}

void CifCategory::WriteCategory(CifFileIo *ciffile, 
					    CifDataBlock *dic,
					    CifDataBlock *datablock,
					    const int write_comment,
					    const int esdFlag,
					    const CifString *dictionary,
					    const CifString *version)
{
  CifString message, *aliasName;
  int i;

  for (i=0; i<_items.Length(); i++) {
    aliasName = NULL;
    if (strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID)&&
	strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_OFFSET)) {
      
      if (dictionary == NULL)
	break;
      else {
	if (dictionary != NULL) 
	  aliasName = _items[i].GetItemAliasName(dictionary, version);
	if (aliasName == NULL) 
	  continue;
	else break;
      }
    }
  }

  if (i == _items.Length()) {
    message.Clear();
    message += "No alias name found in category \'";
    message += _categoryName.Text();
    message += "\' for items";
    for (i=0; i<_items.Length(); i++) {
      message += " \'";
      message += _items[i]._keyword.Text();
      if (i != _items.Length()) message += "\', ";
      else                      message += "\'";
    }
    datablock->ErrorMessage(CIF_WARNING, -1, message.Text());
    return;
  }
  if (write_comment && dic != NULL)
    WriteComment(ciffile, dic, dictionary, version, esdFlag);
  if (CountRows() <=1) 
    WriteNonLoopedCategory(ciffile, datablock, dictionary, version, esdFlag);
  // Loopable keywords
  else 
    WriteLoopedCategory(ciffile, datablock, dictionary, version, esdFlag);
  ciffile->PrintF("#\n");
}

void CifCategory:: WriteNonLoopedCategory(CifFileIo *ciffile,
						      CifDataBlock *datablock,
						      const CifString *dictionary,
						      const CifString *version,
						      const int esdFlag)
{
  int i, j, ret, ret1, multipleline;
  CifItemValue *tmpdata, *tmpdata1;
  CifString *aliasName, message;

  aliasName = NULL;
  for (i=0; i<_items.Length(); i++) {
    if (strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID) &&
	strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_OFFSET)) {
      if (dictionary == NULL && esdFlag) 
	ciffile->PrintF("%k\t  ", _items[i]._itemAttribute._name.Text());
      else {
	ret1 = CIF_DATA_IS_NULL;
	for (j=0; j<_items[i]._relatedItems.Length(); j++) {
	  if (_items[i]._relatedItems[j].GetItemIndex() != -1 &&
	      !CompareNoCase(_items[i]._relatedItems[j].GetFunctionCode()->Text(),
			     CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_ESD)) {
	    ret1 = _items[_items[i]._relatedItems[j].GetItemIndex()].GetItemValue(0, &tmpdata1);
	    break;
	  }
	}
	if (dictionary != NULL)
	  aliasName = _items[i].GetItemAliasName(dictionary, version);
	if (aliasName == NULL) {
	  for (j=0; j< _items[i]._relatedItems.Length(); j++) 
	    if (_items[i]._relatedItems[j].GetItemIndex() != -1 &&
		!CompareNoCase(_items[i]._relatedItems[j].GetFunctionCode()->Text(),
			       CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_VALUE))
	      break;
	  if (j != _items[i]._relatedItems.Length()) continue;
	  else if (j == _items[i]._relatedItems.Length()) {
	    if (dictionary != NULL) {
	      message.Clear();
	      message += "No alias name found for item \'";
	      message += _items[i]._itemAttribute._name;
	      message += "\'";
	      datablock->ErrorMessage(CIF_WARNING, -1, message.Text());
	      continue;
	    }
	    else
	      ciffile->PrintF("%k\t  ", _items[i]._itemAttribute._name.Text());
	  }
	}
	else 
	  ciffile->PrintF("%k\t  ", aliasName->Text());
	
      }

      ret = _items[i].GetItemValue(0, &tmpdata);

      
      if (ret == CIF_DATA_IS_NULL || ret >= CIF_OUT_OF_RANGE_ERROR_CODE_START )
	ciffile->PrintF(" %c", CIF_DATA_NULL_CHARACTER );
      else if (ret == CIF_DATA_NOT_APPROPRIATE)
	ciffile->PrintF(" %c", CIF_DATA_NOT_APPROPRIATE_CHARACTER);
      else  if (ret == CIF_DATA_IS_VALID) {
	if (_items[i]._itemAttribute._dataType == CIF_INTEGER_VALUE)
	  ciffile->PrintF(" %d", tmpdata->_data._intVal);
	else if (_items[i]._itemAttribute._dataType == CIF_DOUBLE_VALUE)  
	  ciffile->PrintF(" %lf", tmpdata->_data._dblVal);
	else {
	  multipleline = FALSE;
	  ciffile->PrintStrval(tmpdata->_buf, &multipleline);
	}
      }
      else 
	ciffile->PrintF(" %c", CIF_DATA_NULL_CHARACTER );
      if ( ret1 == CIF_DATA_IS_VALID && (tmpdata1->_dataType == CIF_INTEGER_VALUE ||
					 tmpdata1->_dataType == CIF_DOUBLE_VALUE )) {
	ciffile->PrintF("%c", '(');
	if (tmpdata1->_dataType == CIF_INTEGER_VALUE)
	  ciffile->PrintF("%d", tmpdata1->_data._intVal);
	else
	  ciffile->PrintF("%lf", tmpdata1->_data._dblVal);
	ciffile->PrintF("%c", ')');
      }
      if (ret != CIF_DATA_IS_VALID ||
	  _items[i]._itemAttribute._dataType == CIF_INTEGER_VALUE ||
	  _items[i]._itemAttribute._dataType == CIF_DOUBLE_VALUE ||
	  multipleline != TRUE) 
	ciffile->PutChar('\n');
    }
  }
}
void CifCategory:: WriteLoopedCategory(CifFileIo *ciffile,
						    CifDataBlock *datablock,
						   const CifString *dictionary,
						   const CifString *version,
						   const int esdFlag)
{
  int i, j, k, ret, ret1, multipleline, nvalues;
  CifItemValue *tmpdata, *tmpdata1;
  CifString *aliasName, message;
  aliasName = NULL;

  ciffile->PrintF("%k", "loop_\n");
  nvalues = 0;
  for (i=0; i< _items.Length(); i++) {
    if (strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID) &&
	strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_OFFSET)) {
      if (dictionary == NULL && esdFlag)
	ciffile->PrintF("%k\n", _items[i]._itemAttribute._name.Text());
      else {
	if (dictionary != NULL)
	  aliasName = _items[i].GetItemAliasName(dictionary, version);
	if (aliasName == NULL){
	  for (j=0; j< _items[i]._relatedItems.Length(); j++) 
	    if (_items[i]._relatedItems[j].GetItemIndex() != -1 &&
		!CompareNoCase(_items[i]._relatedItems[j].GetFunctionCode()->Text(),
			       CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_VALUE))
	      break;
	  if (j != _items[i]._relatedItems.Length()) continue;
	  else if (j == _items[i]._relatedItems.Length()) {
	    if (dictionary != NULL) {
	      message.Clear();
	      message += "No alias name found for item \'";
	      message += _items[i]._itemAttribute._name;
	      message += "\'";
	      datablock->ErrorMessage(CIF_WARNING, -1, message.Text());
	      continue;
	    }
	    else
	      ciffile->PrintF("%k\n", _items[i]._itemAttribute._name.Text());
	  }
	}
	else
	  ciffile->PrintF("%k\n", aliasName->Text());
      }
      if (nvalues < _items[i].CountItemValue())
	nvalues = _items[i].CountItemValue();
    }
  }
  
  for (k=0; k< nvalues; k++) {
    multipleline = FALSE;
    for (i=0; i< _items.Length(); i++) {
      if (strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID) &&
	strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_OFFSET)) {

	if (!esdFlag || dictionary ) {
	  if (dictionary != NULL)
	    aliasName = _items[i].GetItemAliasName(dictionary, version);
	  if (aliasName == NULL){
	    for (j=0; j< _items[i]._relatedItems.Length(); j++) 
	      if (_items[i]._relatedItems[j].GetItemIndex() != -1 &&
		  !CompareNoCase(_items[i]._relatedItems[j].GetFunctionCode()->Text(),
				 CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_VALUE))
		break;
	    if (j != _items[i]._relatedItems.Length()) continue;
	  }
	  ret1 = CIF_DATA_IS_NULL;
	  for (j=0; j<_items[i]._relatedItems.Length(); j++) {
	    if (_items[i]._relatedItems[j].GetItemIndex() != -1 &&
		!CompareNoCase(_items[i]._relatedItems[j].GetFunctionCode()->Text(),
			       CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_ESD)) {
	      ret1 = _items[_items[i]._relatedItems[j].GetItemIndex()].GetItemValue(k, &tmpdata1);
	      break;
	    }
	  }
	}
	if (k < _items[i].CountItemValue()) 
	  ret = _items[i].GetItemValue(k, &tmpdata);

	else ret = CIF_DATA_IS_NULL;
	if (ret != CIF_DATA_IS_VALID) multipleline = FALSE;
	if (ret == CIF_DATA_IS_NULL)
	  ciffile->PrintF("  %c", CIF_DATA_NULL_CHARACTER);
	else if (ret == CIF_DATA_NOT_APPROPRIATE)
	  ciffile->PrintF("  %c", CIF_DATA_NOT_APPROPRIATE_CHARACTER);
	else {
	  if (ret == CIF_DATA_IS_VALID) {
	    if (_items[i]._itemAttribute._dataType == CIF_INTEGER_VALUE) 
	      ciffile->PrintF("%d", tmpdata->_data._intVal);
	    else if (_items[i]._itemAttribute._dataType == CIF_DOUBLE_VALUE) 
	      ciffile->PrintF("%lf", tmpdata->_data._dblVal);
	    else 
	      ciffile->PrintStrval(tmpdata->_buf, &multipleline);
	  }
	  else
	    ciffile->PrintF("  %c", CIF_DATA_NULL_CHARACTER);
	}
	
	if ( ret1 == CIF_DATA_IS_VALID && (tmpdata1->_dataType == CIF_INTEGER_VALUE ||
					   tmpdata1->_dataType == CIF_DOUBLE_VALUE )) {
	  ciffile->PrintF("%c", '(');
	  if (tmpdata1->_dataType == CIF_INTEGER_VALUE)
	    ciffile->PrintF("%d", tmpdata1->_data._intVal);
	  else
	    ciffile->PrintF("%lf", tmpdata1->_data._dblVal);
	  ciffile->PrintF("%c", ')');
	}
	if (ret == CIF_DATA_IS_NULL || ret == CIF_DATA_NOT_APPROPRIATE||
	    tmpdata->_dataType == CIF_INTEGER_VALUE ||
	    tmpdata->_dataType == CIF_DOUBLE_VALUE) ciffile->PrintF("  ");
      }
    }
    if (!multipleline) ciffile->PrintF("\n");
  }
}

int CifCategory:: CheckDictionaryVersion(CifDataItem *tmpitem,
						CifDataBlock *datablock,
						const CifString *dicDatablockName,
						const int currLineNo)
{
  int i;
  CifString message;

  if (tmpitem->_alias != NULL) {
    for (i=0; i< _items.Length(); i++)
      if (strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID) &&
	  strcmp(_items[i]._keyword.Text(), CIF_DDL_ITEM_OFFSET)) {
	if (_items[i]._alias == NULL ||
	    !strcmp(tmpitem->_alias->GetDictionary()->Text(),"") ||
	    !strcmp(tmpitem->_alias->GetDictionaryVersion()->Text(),"") ||
	    CompareNoCase(tmpitem->_alias->GetDictionary()->Text(),
			  _items[i]._alias->GetDictionary()->Text()) ||
	    CompareNoCase(tmpitem->_alias->GetDictionaryVersion()->Text(),
			  _items[i]._alias->GetDictionaryVersion()->Text()))
	  break;
      }
  }
  else 
    for (i=0; i< _items.Length(); i++)
      if (_items[i]._alias != NULL) break;

  if (i != _items.Length()) {
    message.Clear();
    message += "Item name \'";
    if (tmpitem->_alias != NULL) 
      message += tmpitem->_alias->GetAliasName()->Text();
    else
      message += tmpitem->_itemAttribute._name.Text();
    message += "\' exists in different dictionary \'";
    if (tmpitem->_alias != NULL) {
      message += tmpitem->_alias->GetDictionary()->Text();
      message += "\'(";
      message += tmpitem->_alias->GetDictionaryVersion()->Text();
      message += ") ";
    }
    else {
      message += dicDatablockName->Text();
      message += "\'";
    }
    message += " from item \'";
    if (_items[i]._alias == NULL) 
      message += _items[i]._itemAttribute._name.Text();
    else
      message += _items[i]._alias->GetAliasName()->Text();
    message += "\' dictionary \'";
    if (_items[i]._alias != NULL) {
      message += _items[i]._alias->GetDictionary()->Text();
      message += "\'(";
      message += _items[i]._alias->GetDictionaryVersion()->Text();
      message += ")";
    }
    else {
      message += dicDatablockName->Text();
      message += "\'";
    }
    datablock->ErrorMessage(CIF_WARNING, currLineNo, message.Text());
    return FALSE;
  }
  return TRUE;
}


