/*
FILE:     CifDataBlock.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 CifDataBlock

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

int CifDataBlock::GetInsertIndex(const char *categoryName)
// ------------------------------------------------------------------
// GetInsertIndex(): In order for fast fetch, categories array are 
//                   ordered in alphaphic order. Get the position
//                   for insertion
// Return ItemValue: The position to insert 
// ------------------------------------------------------------------
{
  int uplimit, lowerlimit, mid, keycomp = -1;

  if (_categories.Length() ==0) return 0;
  uplimit = _categories.Length();
  lowerlimit = 0;
  mid = lowerlimit;
  while (uplimit > lowerlimit +1) {
    mid = (uplimit + lowerlimit) /2 + (uplimit +lowerlimit)%2;
    keycomp = CompareNoCase(categoryName, _categories[mid]._categoryName.Text());
    if (!keycomp)
      break;
    else {
      if (keycomp < 0)       uplimit = mid;
      else                   lowerlimit = mid;
    }
  }
  if (uplimit == lowerlimit +1 &&
      CompareNoCase(categoryName, _categories[mid]._categoryName.Text()) != 0) {
    keycomp = CompareNoCase(categoryName, _categories[lowerlimit]._categoryName.Text());
    if (keycomp == 0) 
      mid = lowerlimit;
    else if (keycomp <0) mid = lowerlimit;
    else      mid = uplimit;
  }
  return mid;
}

long CifDataBlock::GetItemFileOffset(const char *name)
// ------------------------------------------------------------------
// GetItemOffset(): Get file offset for a name, a name can be
//                  an item name or a category name.
// Return ItemValue: file offset
// ------------------------------------------------------------------
{
  int i, categoryIndex, itemIndex, ret, nrows, str_len, offset_index;
  CifString tmpname, *nameValue;
  CifItemValue *cifitemValue;

  // Checking if the given name is a category name or an item name
  str_len = strlen(name);
  if (name == NULL) return -1;
  for (i = 0; i< str_len; i++)
    if (name[i] == '.') break;

  if (i == str_len) {
    if (name[0] == '_') tmpname.CopyN(&name[1], i-1);
    else                tmpname.CopyN(name, i);
    categoryIndex = GetIndex(CIF_DDL_CATEGORY_CATEGORY);
    if (categoryIndex == -1) return -1;
    itemIndex = _categories[categoryIndex].GetIndex(CIF_DDL_ITEM_ID);
  }
  else {
    tmpname.Copy(name);
    categoryIndex = GetIndex(CIF_DDL_CATEGORY_ITEM);
    if (categoryIndex == -1) return -1;
    itemIndex = _categories[categoryIndex].GetIndex(CIF_DDL_ITEM_NAME);
  }

  // Find its offset
  offset_index = _categories[categoryIndex].GetIndex(CIF_DDL_ITEM_OFFSET);
  if (itemIndex == -1 || offset_index  == -1) return -1;
  nrows = _categories[categoryIndex].CountRows();
  for (i=0; i<nrows; i++) {
    ret = GetItemValue(&nameValue, categoryIndex, itemIndex, i);
    if (ret == CIF_DATA_IS_VALID && !Compare(*nameValue, tmpname)) {
      ret = GetItemValue(&cifitemValue, categoryIndex, offset_index, i);
      if (ret == CIF_DATA_IS_VALID)
	return cifitemValue->_data._intVal;
    }
  }
  return -1;
}
void CifDataBlock::SetDatablockName(const char *dataBlockName)
// ------------------------------------------------------------------
// SetDatablockName(): Set Datablock name
// ------------------------------------------------------------------
{
  _dataBlockName.Copy(dataBlockName);
}
void CifDataBlock::InsertRows(CifDataBlock *dic, CifCategory *newdata,
			       const int verify,  const int lineNo)
// ------------------------------------------------------------------
// InsertRows(): Insert rows from newdata to category tables
//               It makes sure all columns have the same number of rows
// Parameter: ReVarCifArray <CifDataItem> *newdata
// ------------------------------------------------------------------
{
  int insertpoint, insertpoint1, nrows, i, j, nvalues;
  char *insertmark = NULL;
  ReVarCifArray <CifString> *keys;
  //  CifString _datablock_id(CIF_DDL_ITEM_DATABLOCK_ID), _id(CIF_DDL_ITEM_ID);
  CifItemValue cifitemValue(CIF_STRING_VALUE, 1, TRUE);

  if (newdata == NULL || newdata->_items.Length() ==0) return;
  insertpoint = GetInsertIndex(newdata->_categoryName);
  if (dic != this && insertpoint >=0 && insertpoint < _categories.Length() &&
      !CompareNoCase(_categories[insertpoint]._categoryName,
		     newdata->_categoryName)) {
    newdata->CheckKeys(newdata, &_categories[insertpoint]._keys, this,
		       lineNo);
    newdata->CheckKeys(&_categories[insertpoint], &_categories[insertpoint]._keys, 
		       this,  lineNo);
  }
  if (newdata->_items.Length() ==0) return;

  if (insertpoint >=0 && insertpoint < _categories.Length() &&
      !CompareNoCase(_categories[insertpoint]._categoryName, newdata->_categoryName)) {

    // insertmark is the flags which make sure every columns gets inserted
    insertmark = (char *) 
      calloc((_categories[insertpoint]._items.Length()+1), sizeof(char));
    
    // Keep the number of _itemValues/rows before processing it for futuer usage
    // Number of rows/_itemValues get changed while inserting
    

    if (_categories[insertpoint]._items.Length() > 0)
      nrows = _categories[insertpoint].CountRows();
    else nrows = 0;

    for (i=0; i< newdata->_items.Length(); i++) {
      insertpoint1 = _categories[insertpoint].GetIndex(newdata->_items[i]._keyword);
      
      if (insertpoint1 != -1) {
	// column for this item exist
	insertmark[insertpoint1] = 1;
	nvalues = newdata->_items[i].CountItemValue();
	for (j=0; j< nvalues; j++) 
	  InsertItemValue(dic, &newdata->_items[i]._itemValues[j],
		      insertpoint, insertpoint1, nrows+j, verify, lineNo);
      }
      else  {
	// column for this item doesn't exist, add a new column, and put null to this
        // column make the column have the same row number as the other columns in
        // this table before insert the new itemValue.
	 _categories[insertpoint]._items.Add(newdata->_items[i]);
	 insertpoint1 = _categories[insertpoint]._items.Length()-1;
	 if (nrows > 0) {
	   for (j=0; j<nrows; j++) 
             InsertItemValue(dic, &cifitemValue, insertpoint, insertpoint1, 0, 
			     verify, lineNo);
	 }
	 insertmark = (char *) realloc(insertmark, 
            (_categories[insertpoint]._items.Length()+1) * sizeof(char));
	 insertmark[insertpoint1] = 1;
       }
     }
     // Check if all the _items for this category are inserted or not. 
     // null should be put to those item without _itemValues
     for (i=0; i<_categories[insertpoint]._items.Length(); i++) {
       if (insertmark[i] == 0) {
         nvalues = newdata->CountRows();
    
	 for (j=0; j< nvalues; j++)  
           InsertItemValue(dic, &cifitemValue, insertpoint, i, nrows+j, verify, lineNo);
       }	 
     }
     free(insertmark);
  }
  else {
    // New category to insert to category table
    newdata->_readInOrder = _categories.Length();

    if (dic != this) {
      keys = dic->BuildCategoryKeys(newdata->_categoryName);
      if (keys != NULL) {
	newdata->_keys.Copy(*keys);
	delete keys;
      }
      newdata->CheckKeys(newdata, &newdata->_keys, this, lineNo);
    }
    _categories.InsertNAt(insertpoint, newdata, 1);

#if !defined (linux)
    if (verify) {
      for (i=0; i<_categories[insertpoint]._items.Length(); i++) {
        for (j=0; j<_categories[insertpoint].CountRows();j++) 
          VerifyItemValue(dic, &_categories[insertpoint]._items[i]._itemValues[j], insertpoint,
			  i, lineNo);
	
      }
    }
#endif

  }
}


void CifDataBlock::InsertRowsFromFile(CifDataBlock *dic,
                                       const int verify,
                                       const int lineNo,
                                       const long offset,
                                       CifCategory *newdata,  
  				       const CifString *datablockName, 
  				       const CifString *categoryName, 
				       const CifString *itemName)
// ------------------------------------------------------------------
// InsertRowsFromFile(): Insert rows from newdata to category tables
//                       and also create extra columns to hold category name
//                       and item name
// Parameter: ReVarCifArray <CifDataItem> *newdata
//            const CifString *categoryName: category label from save_ label
//            const CifString *datablockName: datablock block name from data_
//            Put categoryName and itemName to temperary columns
//            in case the category and itemName are mandatory implicit
//            should be pick up from save_ labels
// ------------------------------------------------------------------
{
  CifItemValue *itemValue;
  int i, j, ret, nrows;

  // Checking if there's itemValue in the new table
  nrows = newdata->CountRows();
  for (i=0; i< nrows; i++) {
    for (j=0; j<newdata->_items.Length(); j++) {
      ret = newdata->GetItemValue(&itemValue, j, i);
      if (ret != CIF_DATA_IS_NULL  && ret != CIF_DATA_NOT_APPROPRIATE &&
	  ret < CIF_OUT_OF_RANGE_ERROR_CODE_START)
	break;
    }
    if (j == newdata->_items.Length()) {
      for (j=0; j<newdata->_items.Length(); j++) 
	newdata->_items[j]._itemValues.DeleteAt(i);
      i--;
      if (newdata->CountRows() < nrows) break;
    }
  }
  if (newdata == NULL || newdata->_items.Length() == 0 ||
      newdata->CountRows() == 0) return;

  // put extra columns for category_id and itemName in case they should be
  // pick up from save_ label
  if (itemName != NULL) {
    if (dic == this) 
      newdata->AddDataItem(dic, CIF_DDL_TEMP_ITEM_ITEM, *itemName);
    else
      newdata->AddImplicitItem(dic, this, CIF_DDL_TEMP_ITEM_ITEM, *itemName, lineNo);
  }
  if (categoryName != NULL) {
    if (dic == this)  
      newdata->AddDataItem(dic, CIF_DDL_TEMP_ITEM_CATEGORY_ID, *categoryName);
    else
      newdata->AddImplicitItem(dic, this, CIF_DDL_TEMP_ITEM_CATEGORY_ID, 
			       *categoryName, lineNo);
  }
  // Put extra column Datablock_id for those category does not have Datablock_id column
  if (datablockName != NULL) {
    for (i=0; i<newdata->_items.Length(); i++)
      if (!CompareNoCase(newdata->_categoryName.Text(), CIF_DDL_CATEGORY_BLOCK) &&
	  !CompareNoCase(newdata->_items[i]._keyword.Text(), CIF_DDL_ITEM_ID) ||
	  !CompareNoCase(newdata->_items[i]._keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID))
	break;
    if (i == newdata->_items.Length())
      newdata->AddDataItem(dic, CIF_DDL_ITEM_DATABLOCK_ID, *datablockName);
  }
  // Put extra column _fileOffset for those category and item table for the purpose
  // of browsing
  if (!CompareNoCase(newdata->_categoryName.Text(), CIF_DDL_CATEGORY_CATEGORY )  ||
      !CompareNoCase(newdata->_categoryName.Text(), CIF_DDL_CATEGORY_ITEM ))

    newdata->AddDataItem(dic, CIF_DDL_ITEM_OFFSET, offset);

  InsertRows(dic, newdata, verify, lineNo);
}

/*
void CifDataBlock::DeleteElement()
// ------------------------------------------------------------------
// DeleteElement(): Clear up the class
// ------------------------------------------------------------------
{
  _dataBlockName.DeleteElement();
  _dictionaryName.DeleteElement(); 
  _dictionaryVersion.DeleteElement(); 
  _fileName.DeleteElement(); 
  _nameAttributes.DeleteElement();
  _constructs.DeleteElement();
  _categories.DeleteElement();
  _errors.DeleteElement();
  _warnings.DeleteElement();
}
*/
void CifDataBlock::DeleteElement()
// ------------------------------------------------------------------
// DeleteElement(): Clear up the class
// ------------------------------------------------------------------
{
  _dataBlockName.Clear();
  _dictionaryIndex = -1;
  _dictionaryName.Clear(); 
  _dictionaryVersion.Clear(); 
  _fileName.Clear(); 
  _fileOffset = 0;
  _constructs.Clear();
  _categories.Clear();
  _errors.Clear();
  _warnings.Clear();
  _nameAttributes.DeleteElement();
}
/*
void CifDataBlock::Clear()
// ------------------------------------------------------------------
// Clear(): Clear up the class
// ------------------------------------------------------------------
{
  _dataBlockName.Clear();
  _dictionaryName.Clear(); 
  _dictionaryVersion.Clear(); 
  _fileName.Clear(); 
  _fileOffset = 0;
  _dictionaryIndex = -1;
  _constructs.Clear();
  _categories.Clear();
  _errors.Clear();
  _warnings.Clear();
  _nameAttributes.Clear();
}
*/

CifDataBlock::CifDataBlock(const CifDataBlock &old)
// ------------------------------------------------------------------
// Copy(): Copy funcion, copy its itemValue from another CifDataBlock
// ------------------------------------------------------------------
{
  //  _verbose = TRUE;
  Copy(old);
}
CifDataBlock &CifDataBlock::Copy(const CifDataBlock &old)
// ------------------------------------------------------------------
// Copy(): Copy funcion, copy its itemValue from another CifDataBlock
// ------------------------------------------------------------------
{
  unsigned i;
  DeleteElement();
  _verbose = old._verbose; 
  _nameAttributes = old._nameAttributes;
  _dictionaryIndex = old._dictionaryIndex;
  _dataBlockName.Copy(old._dataBlockName);
  _dictionaryName.Copy(old._dictionaryName);
  _dictionaryVersion.Copy(old._dictionaryVersion);
  _fileName.Copy(old._fileName);
  _fileOffset = old._fileOffset;
  for (i=0; i <old._categories.Length(); i++)
    _categories.Add(old._categories[i]);
  for (i=0; i <old._constructs.Length(); i++) 
    _constructs.Add(old._constructs[i]);
  for (i=0; i <old._errors.Length(); i++) 
    _errors.Add(old._errors[i]);
  for (i=0; i <old._warnings.Length(); i++) 
    _warnings.Add(old._warnings[i]);

  return *this;
}
void CifDataBlock::Print() const
// ------------------------------------------------------------------
// Print(): Dump the class, for debug purpose
// ------------------------------------------------------------------
{
  unsigned i;
  char message[CIF_MAXSTRLEN];

  PrintLine("Data Block");
  cout << "\t\t\tDatablock : " << _dataBlockName.Text() << endl;
  cout << "\t\t\tFile Name : " << _fileName.Text() << endl;
  cout << "\t\t\tDictionary: " << _dictionaryName.Text() << "(version: " <<
    _dictionaryVersion.Text() << " index: " << _dictionaryIndex << ')' 
      << endl;
  cout << "\t\t\tNumber of categories: " << _categories.Length();
  
  for (i=0; i <_categories.Length(); i++) {
    sprintf(message,"Category %d\0", i);
    PrintLine(message);
    _categories[i].Print();
  }
  if (_errors.Length() > 0) {
    sprintf(message,"_Errors %d\0", _errors.Length());
    PrintLine(message);
    for (i=0; i <_errors.Length(); i++) {
      cout << i << ' '; 
      _errors[i].Print();
      cout << endl;
    }
  }
  if (_warnings.Length() > 0) {
    sprintf(message,"Warnings %d\0", i);
    PrintLine(message);
    for (i=0; i <_warnings.Length(); i++) {
      cout << i << ' '; 
      _warnings[i].Print();
      cout << endl;
    }
  }
  cout << endl;
    
}  
void CifDataBlock::PrintDetails() const
// ------------------------------------------------------------------
// Print(): Dump the class, for debug purpose
// ------------------------------------------------------------------
{
  unsigned i;
  cout << _dataBlockName.Text() << ", " << _dictionaryName.Text() 
    << '(' << _dictionaryVersion.Text() << "), " << _fileName.Text() 
      << ", "  << _dictionaryIndex << ", "  << _fileOffset << endl;
  cout << "_Constructs:  " << _constructs.Length() << endl;
  for (i=0; i<_constructs.Length(); i++) {
    cout << i << ' ';
    _constructs[i].Print();
    cout << endl;
  }
  cout << endl;
  _nameAttributes.Print();
  for (i=0; i <_categories.Length(); i++) {
    cout << "    " << "categories: " << i <<endl; 
    _categories[i].PrintDetails();
    cout << endl;
  }
  for (i=0; i <_errors.Length(); i++) {
    cout << "    " << "Errors: " << i <<endl; 
    _errors[i].Print();
    cout << endl;
  }
  for (i=0; i <_warnings.Length(); i++) {
    cout << "    " << "Warnings: " << i <<endl; 
    _warnings[i].Print();
    cout << endl;
  }
  cout << endl;
}  
void CifDataBlock::PrintTable()  const
// ------------------------------------------------------------------
// PrintTable(): Print the class with table like layout, for debug purpose
// ------------------------------------------------------------------
{
  unsigned i;
  cout << _dataBlockName.Text() << ", " << _dictionaryName.Text() 
    << '(' << _dictionaryVersion.Text() << "), "  << _fileName.Text()
      << ", "  << _dictionaryIndex  << ", "  << _fileOffset << endl;
  cout << "Constructs:  " << _constructs.Length() << endl;
  for (i=0; i<_constructs.Length(); i++) {
    cout << i << ' ';
    _constructs[i].Print();
    cout << endl;
  }
  for (i=0; i <_errors.Length(); i++) {
    cout << "    " << "Errors: " << i <<endl; 
    _errors[i].Print();
    cout << endl;
  }
  for (i=0; i <_warnings.Length(); i++) {
    cout << "    " << "Warnings: " << i <<endl; 
    _warnings[i].Print();
    cout << endl;
  }
  cout << endl;
  _nameAttributes.Print();
  cout << endl;

  for (i=0; i <_categories.Length(); i++) {
    cout << "    " << "categories: " << i <<endl; 
    _categories[i].PrintTable();
    cout << endl;
  }
  cout << endl;
}  
void CifDataBlock::FreeExtra()
//---------------------------------------------------------------------------
//   Purpose:   FreeExtra() Free extra spaces
//--------------------------------------------------------------------------
{
  unsigned i;
  for (i =0; i< _categories.Length() ; i++)
    _categories[i].FreeExtra();
  _categories.FreeExtra();
  _constructs.FreeExtra();
  _errors.FreeExtra();
  _warnings.FreeExtra();
}

int CifDataBlock::GetIndex(const char *category)
// ----------------------------------------------------------------------
//   GetIndex() Finds the index of the keyword in _items array, 
//              -1 if not found
// --------------------------------------------------------------------- 
{
  int index;

  index = GetInsertIndex(category);
  if (index < 0 || index >= _categories.Length() ||
      CompareNoCase(category, _categories[index]._categoryName.Text())!=0)
    return -1;
  else return index;
}
CifCategoryIndex *CifDataBlock::GetTableIndex(const char *name)
// ----------------------------------------------------------------------
//   GetTableIndex() Get a table index of an item name
//             NULL or the pointer of the table index
// --------------------------------------------------------------------- 
{
  unsigned i, j;
  CifCategoryIndex *tableIndex;

  for (i=0; i<_categories.Length(); i++) 
    for (j=0; j<_categories[i]._items.Length(); j++) 
      if (!CompareNoCase(name, _categories[i]._items[j]._itemAttribute._name.Text()) ||
	  _categories[i]._items[j]._alias != NULL &&
	  !CompareNoCase(name, _categories[i]._items[j]._alias->GetAliasName()->Text())) {
	tableIndex = new CifCategoryIndex;
	tableIndex->SetCategoryIndex(i);
	tableIndex->SetItemIndex(j);
	return tableIndex;
      }
  return NULL;
}
CifCategoryIndex *CifDataBlock::GetTableIndex(const char *category, 
					      const char *item)
// ----------------------------------------------------------------------
//   GetIndex() Finds the index of the keyword in _items array, 
//              NULL if not found
// --------------------------------------------------------------------- 
{
  int index1, index2;
  CifCategoryIndex *link_list;
  
  index1 = GetIndex(category);
  if (index1 == -1)
    return NULL;
  else  {
    index2 = _categories[index1].GetIndex(item);
    if (index2 == -1) return NULL;
    else {
      link_list = new CifCategoryIndex(index1, index2);
      return link_list;
    }
  }
}

void CifDataBlock:: Merge(CifDataBlock *dic, CifDataBlock *block)
// ----------------------------------------------------------------------
//   Merge(): Merge two Datablocks, Datablocks have to have to based on the 
//            same dictionary
//            Function Merge inserts rows of block
// --------------------------------------------------------------------- 
{
  int i;
  
  if (block == NULL) 
    return;
  else if (_dictionaryIndex != block->GetDataBlockDictionaryIndex()) 
    return;

  for (i = 0; i < block->_categories.Length(); i++)  
    InsertRows(dic, &block->_categories[i], FALSE, -1);
/*
  for (i = 0; i < block->aliases.Length(); i++)
    aliases.Add(block->aliases[i]);
*/
  for (i = 0; i < block->_nameAttributes._list.Length(); i++) {
    _nameAttributes._list.Add(block->_nameAttributes._list[i]);
    _nameAttributes._list[_nameAttributes._list.Length()-1]._constructIndex +=
      _constructs.Length(); 
  }
  for (i = 0; i < block->_constructs.Length(); i++)
    _constructs.Add(block->_constructs[i]);
  for (i = 0; i < block->_errors.Length(); i++)
    _errors.Add(block->_errors[i]);
  for (i = 0; i < block->_warnings.Length(); i++)
    _warnings.Add(block->_warnings[i]);
}

void CifDataBlock:: ReadDatablock(CifFileIo *ciffile, 
				   CifDataBlock *global_information,
				   CifDataBlock *dic,const int verify)
// ----------------------------------------------------------------------
//   ReadDatablock() 
//   Parameters: CifFileIo *ciffile : the file to read from          
//               CifDataBlock *global_information
//               global_information should be always copied to all Datablocks
//   Return ItemValue: SUCCEED or FAILURE
// --------------------------------------------------------------------- 
{
  int type, i, messageLineNo = 5000;
  CifCategory newdata;
  CifString      categoryName, itemName;

  CifFileIo *newfile;

  // Copy Datablock label
  if (ciffile->GetTokenType() == CIF_KEYWORD_DATA && 
      !strcmp(_dataBlockName.Text(),"")) {
    SetDatablockName(&ciffile->GetTokenValue()->Text()[5]);
    type = ciffile->GetNextCifToken(this, dic);
  }
  type = ciffile->GetTokenType();

  if (_verbose) {
    cout << "Be patient, processing file \'" << _fileName.Text() 
      << "\' datablock \'" << _dataBlockName.Text() << '\'' << endl;
    cout.flush();
  }
  // Insert Global to CifDataBlock
  if (global_information != NULL) {
    for (i=0; i<global_information->_categories.Length(); i++) 
      InsertRows(dic, &global_information->_categories[i], verify, 
		 ciffile->GetCurrLineNo());
  }
  while (ciffile->GetTokenType() != EOF &&
	 (/* type == CIF_KEYWORD_ALIAS ||*/ type == CIF_DEFINITION_CATEGORY ||
	  type == CIF_DEFINITION_ITEM ||  type == CIF_KEYWORD_INCLUDE ||
	  type == CIF_KEYWORD_LOOP || type == CIF_ITEM_NAME)) {
    /*
    if (type == CIF_KEYWORD_ALIAS) 
      BuildAliasBlock(ciffile);
    
    else */
    if (type == CIF_KEYWORD_INCLUDE) {
      // keyword include_ treate it just like read in another file
      
      ciffile->GetNextCifToken(this, dic);
      newfile = new CifFileIo;
      if (newfile->OpenFile(ciffile->GetTokenValue()->Text(), 'r') ==  FILE_OPERATION_SUCCEED ) 
	ReadDatablock(newfile, global_information, dic, verify);
      delete newfile;
      type = ciffile->GetNextCifToken(this, dic);
    }
    else {
      
      if (type == CIF_DEFINITION_CATEGORY || type == CIF_DEFINITION_ITEM) {
	
	// Get categoryName and itemName from save_ label
	for (i=0; i< ciffile->GetTokenValue()->Length(); i++)  
	  if (ciffile->GetTokenValue()->Text()[i] == CIF_DATA_PREFIX_CHARACTER) break;
	if (type == CIF_DEFINITION_CATEGORY)  
	  categoryName.Copy(&ciffile->GetTokenValue()->Text()[i+1]);
	else 
	  itemName.Copy(&ciffile->GetTokenValue()->Text()[i+1]);
	ciffile->GetNextCifToken(this, dic);
      }
      
      do {
	if ( ciffile->GetTokenType() == CIF_KEYWORD_LOOP || 
	    ciffile->GetTokenType() == CIF_ITEM_NAME)  {
	  if (ciffile->GetTokenType() == CIF_KEYWORD_LOOP) 
	    ciffile->GetCifLoopedValue(dic, this, &newdata);

	  else 
	    ciffile->GetCifNonLoopedValue(dic, this, &newdata);

	  if (newdata._items.Length() != 0) {
	    if (type == CIF_DEFINITION_CATEGORY)  
	      InsertRowsFromFile(dic, verify, ciffile->GetCurrLineNo(), 
				 ciffile->GetSaveOffset(), &newdata, 
				 &_dataBlockName, &categoryName);
	    else if (type == CIF_DEFINITION_ITEM)  
	      InsertRowsFromFile(dic, verify,  ciffile->GetCurrLineNo(), 
				 ciffile->GetSaveOffset(), &newdata,
				 &_dataBlockName, 
				 &categoryName, &itemName);
	    else 
	      InsertRowsFromFile(dic, verify, ciffile->GetCurrLineNo(), 
				 ciffile->GetSaveOffset(), &newdata, &_dataBlockName);
	    newdata.DeleteElement();
	  }
	  if (_verbose && ciffile->GetCurrLineNo() > messageLineNo) {
	    cout << ciffile->GetCurrLineNo() << " lines are processed!" << endl;
	    cout.flush();
	    messageLineNo += 5000;
	  }
	}
	else
	  ciffile->SetSyntaxError(this,
				  "Item name or reserved word \'loop_\' is expected");
      } while (ciffile->GetTokenType() == CIF_ITEM_NAME  ||
	       ciffile->GetTokenType() == CIF_KEYWORD_LOOP) ;
    }
    type = ciffile->GetTokenType();
    if (type == CIF_KEYWORD_SAVE_FALSE) // End of a save_ block
      type = ciffile->GetNextCifToken(this, dic);
  }
    RefineDataBlock(dic, verify);
  FreeExtra();
}

void CifDataBlock:: PermanentWrite(FILE *fp)
{
  int i;
  
  _dictionaryName.PermanentWrite(fp);
  _dictionaryVersion.PermanentWrite(fp);
  _dataBlockName.PermanentWrite(fp);
  _fileName.PermanentWrite(fp);
  fprintf(fp, "%ld\n", _fileOffset);
  _nameAttributes.PermanentWrite(fp);

  fprintf(fp, "%d\n", _categories.Length());
  for (i =0; i<_categories.Length(); i++) 
    _categories[i].PermanentWrite(fp);
  
  fprintf(fp, "%d\n", _errors.Length());
  for (i =0; i<_errors.Length(); i++) 
    _errors[i].PermanentWrite(fp);
  
  fprintf(fp, "%d\n", _warnings.Length());
  for (i =0; i<_warnings.Length(); i++) 
    _warnings[i].PermanentWrite(fp);
  
  
}
void CifDataBlock:: PermanentRead(FILE *fp)
{
  int i, tmplength;
  CifString error;
  CifCategory category;

  _dictionaryName.PermanentRead(fp);
  _dictionaryVersion.PermanentRead(fp);
  _dataBlockName.PermanentRead(fp);
  _fileName.PermanentRead(fp);
  fscanf(fp, "%ld ", &_fileOffset);

  if (_verbose) {
    cout << "Please be patient, processing file \'" << _fileName.Text() 
      << "\' datablock \'" << _dataBlockName.Text() << '\'' << endl;
    cout.flush();
  }

  _nameAttributes.PermanentRead(fp);

  fscanf(fp, "%d\n", &tmplength);
  if (tmplength > 0) _categories.ChgGrowByInc(tmplength);
  for (i =0; i < tmplength; i++)  {
    category.DeleteElement();
    category.PermanentRead(fp);
    _categories.Add(category);
  }
  fscanf(fp, "%d\n", &tmplength);
  for (i =0; i< tmplength; i++)  {
    error.PermanentRead(fp);
    _errors.Add(error);
  }
  
  fscanf(fp, "%d\n", &tmplength);
  for (i =0; i< tmplength; i++)  {
    error.PermanentRead(fp);
    _warnings.Add(error);
  }
  BuildItemTypeListConstruct();
}
void CifDataBlock:: WriteDataBlock(CifFileIo *ciffile,
				    const int writeComment,
				    const int esdFlag,
				    const CifString *dictionary,
				    const CifString *version,
				    CifDataBlock *dic)
// ----------------------------------------------------------------------
//   WriteDataBlock() 
//   Parameters: CifFileIo *ciffile : the file to read from          
//               CifDataBlock *global_information
//               global_information should be always copied to all Datablocks
//   Return ItemValue: SUCCEED or FAILURE
// --------------------------------------------------------------------- 
{
  int i, j;
  int *readin_list, readin_order;

  if (_verbose) {
    cout << "Please be patient, writing datablock "<< _dataBlockName.Text() << endl;
    cout.flush();
  }
  ciffile->FileIo::PrintF("%s%s\n", CIF_RESERVED_KEYWORD_DATA, 
			   _dataBlockName.Text());

  /* 
   * Readin_order is the buffer to keep the order of the readin order of the file
   * So, when we write out a CIF file, we can write it out as the same order as
   * it was written in
   */

  
  readin_list = (int *) calloc(_categories.Length(), sizeof(int));
  for (i=0; i<_categories.Length(); i++) 
    readin_list[i] = -1;
  for (i=0; i<_categories.Length(); i++) {
    readin_order = 0;
    for (j=0; j <_categories.Length(); j++) 
      if (_categories[i]._readInOrder > _categories[j]._readInOrder)
	readin_order ++;
    if (readin_list[readin_order] == -1)
      readin_list[readin_order] = i;
  }

  for (j=0; j<_categories.Length(); j++) {
    if (readin_list[j] == -1) 
      continue;
    else {
      i = readin_list[j];
      if (dictionary == NULL ||
	  CompareNoCase(_dictionaryName.Text(), dictionary->Text()) ||
	  !CompareNoCase(_dictionaryName.Text(), dictionary->Text()) &&
	  (version == NULL && strcmp(_dictionaryVersion.Text(),"") ||
	   version != NULL && 
	   CompareNoCase(_dictionaryVersion.Text(), version->Text())))
	_categories[i].WriteCategory(ciffile, dic, this, 
					 writeComment, 
					 esdFlag, dictionary, version);
      else
	_categories[i].WriteCategory(ciffile, dic, this,
					 writeComment, 
					 esdFlag, NULL, NULL);
    }
  }
  free(readin_list);
}


ReVarCifArray <CifString> *CifDataBlock::GetParentsName(CifString name)
// ----------------------------------------------------------------------
//   GetParentsName: Get parent name from item_linkage
//                  "_item_linkage"
//   Parameters: int categoryIndex, int itemIndex
// --------------------------------------------------------------------- 
{
  int  i, row_no, ret;
  ReVarCifArray <CifString> *parents, *grandParents;
  int linkage_index, parent_index, child_index;
  CifItemValue *parent, *child;
  CifString tmpname;
  linkage_index = GetIndex(CIF_DDL_CATEGORY_LINKAGE);
  if (linkage_index <0) return NULL;
  parent_index = _categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_PARENT);
  if (parent_index <0) return NULL;
  child_index = _categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_CHILD);
  if (child_index < 0) return NULL;

  for (i = 0; i<name.Length(); i++)
    if (name.Text()[i] == '.') break;
  if (!strcmp(name.Text(),CIF_DDL_ITEM_DATABLOCK_ID) ||
      i != name.Length() && !strcmp(&name.Text()[i+1],CIF_DDL_ITEM_DATABLOCK_ID )) {
    JoinCategoryItemName(CIF_DDL_CATEGORY_BLOCK , CIF_DDL_ITEM_ID , &tmpname);
    parents = new ReVarCifArray <CifString>;
    parents->Add(tmpname);
    return parents;
  }
  parents = NULL;
  row_no = 0;
  tmpname.Copy(name);
  tmpname.ToLower();
  do {
    // Get child_name and parent_name from "_item_linkage" table
      ret = GetItemValue(&child, linkage_index, child_index, row_no);

    if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL &&
	ret != CIF_DATA_NOT_APPROPRIATE) {
      if (child->_flags._case &&  !Compare(name, child->_buf) ||
	  !child->_flags._case && !CompareNoCase(tmpname, child->_buf)) {

	ret = GetItemValue(&parent, linkage_index , parent_index, row_no);
	if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL &&
	    ret != CIF_DATA_NOT_APPROPRIATE) {
	  if (parents == NULL)
	    parents = new ReVarCifArray <CifString>;
	  parents->Add(parent->_buf);
	  grandParents = GetParentsName(parent->_buf);
	  if (grandParents != NULL) {
	    for (i=0; i<grandParents->Length(); i++)
	      parents->Add((*grandParents)[i]);
	    delete grandParents;
	  }
	}
      }
    }
    row_no ++;
  }  while (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START);
  return parents;
}

ReVarCifArray <CifString> *CifDataBlock::GetChildrenName(CifString name)
// ----------------------------------------------------------------------
//   GetChildrenName: Get children name from item_linkage
//                  "_item_linkage"
//   Parameters: int categoryIndex, int itemIndex
// --------------------------------------------------------------------- 
{
  int  i, row_no, ret;
  ReVarCifArray <CifString> *children, *grandChildren;
  int linkage_index, parent_index, child_index;
  CifItemValue *parent, *child;
  CifString tmpname;

  linkage_index = GetIndex(CIF_DDL_CATEGORY_LINKAGE);
  if (linkage_index <0) return NULL;
  parent_index = _categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_PARENT);
  if (parent_index <0) return NULL;
  child_index = _categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_CHILD);
  if (child_index < 0) return NULL;

  children = NULL;
  for (i = 0; i<name.Length(); i++)
    if (name.Text()[i] == '.') break;
  if (!strcmp(name.Text(),CIF_DDL_ITEM_DATABLOCK_ID) ||
      i != name.Length() && !strcmp(&name.Text()[i+1],CIF_DDL_ITEM_DATABLOCK_ID )) {
    JoinCategoryItemName(CIF_DDL_CATEGORY_BLOCK , CIF_DDL_ITEM_ID , &tmpname);
    children = new ReVarCifArray <CifString>;
    children->Add(tmpname);
    return children;
  }
  row_no = 0;
  tmpname.Copy(name);
  tmpname.ToLower();

  do {
    // Get child_name and parent_name from "_item_linkage" table
      ret = GetItemValue(&parent, linkage_index, parent_index, row_no);

    if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL &&
	ret != CIF_DATA_NOT_APPROPRIATE) {
      if (parent->_flags._case &&  !Compare(name, parent->_buf) ||
	  !parent->_flags._case && !CompareNoCase(tmpname, parent->_buf)) {
	ret = GetItemValue(&child, linkage_index , child_index, row_no);
	if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL &&
	    ret != CIF_DATA_NOT_APPROPRIATE) {
	  if (children == NULL)
	    children = new ReVarCifArray <CifString>;
	  children->Add(child->_buf);
	  grandChildren = GetChildrenName(child->_buf);
	  if (grandChildren != NULL) {
	    for (i=0; i<grandChildren->Length(); i++)
	      children->Add((*grandChildren)[i]);
	    delete grandChildren;
	  }
	}
      }
    }
    row_no ++;
  }  while (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START);
  return children;
}

void CifDataBlock::AddLinkParentsAndChildren(CifDataBlock *dic,
					      int categoryIndex, int itemIndex)
// ----------------------------------------------------------------------
//   AddLinkParentsAndChildren(): Build _linkParent and _linkedChildren from table 
//                  "_item_linkage"
//   Parameters: int categoryIndex, int itemIndex
// --------------------------------------------------------------------- 
{
  int k, ret_p, ret_c, row_no, linkage_index, parent_index, child_index;
  CifCategoryIndex *parent_table_index, *child_table_index;
  //  CifString newcategory,  newitem;
  CifString *parent, *child;
  //CifString parent_category, parent_item;

  linkage_index = dic->GetIndex(CIF_DDL_CATEGORY_LINKAGE);
  if (linkage_index <0) return;
  parent_index = dic->_categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_PARENT);
  if (parent_index <0) return;
  child_index = dic->_categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_CHILD);
  if (child_index < 0) return;

  row_no = 0;
  do {
    // Get child_name and parent_name from "_item_linkage" table
    ret_c = dic->GetItemValue(&child, linkage_index , child_index, row_no);
    ret_p = dic->GetItemValue(&parent, linkage_index, parent_index, row_no);

    // Build parent

    if (ret_c < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret_p != CIF_DATA_IS_NULL &&
	ret_c != CIF_DATA_NOT_APPROPRIATE 
	&& !Compare(*child,_categories[categoryIndex]._items[itemIndex]._itemAttribute._name)) {
      parent_table_index = GetTableIndex(parent->Text());
      if (parent_table_index == NULL)
	parent_table_index = new CifCategoryIndex(-1, -1, parent);
      else
	parent_table_index->SetItemName(*parent);

      for (k=0; k<_categories[categoryIndex]._items[itemIndex]._linkedParents.Length(); k++) 
	if (!CompareNoCase(_categories[categoryIndex]._items[itemIndex].
			   _linkedParents[k].GetItemName()->Text(),
			   parent->Text()))
	  break;
	
      if (k == _categories[categoryIndex]._items[itemIndex]._linkedParents.Length()) 
	_categories[categoryIndex]._items[itemIndex].
	  _linkedParents.Add(*parent_table_index);
      else
	_categories[categoryIndex]._items[itemIndex].
	  _linkedParents[k].Copy(*parent_table_index);
      delete parent_table_index;
    }
    // Build child
    if (ret_p < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret_p != CIF_DATA_IS_NULL &&
	ret_p != CIF_DATA_NOT_APPROPRIATE 
	&& !Compare(*parent,_categories[categoryIndex]._items[itemIndex]._itemAttribute._name)) {
      child_table_index = GetTableIndex(child->Text());
      if (child_table_index == NULL)
	child_table_index = new CifCategoryIndex(-1, -1, child);
      else
	child_table_index->SetItemName(*child);

      for (k=0; k<_categories[categoryIndex]._items[itemIndex]._linkedChildren.Length(); k++) 
	if (!CompareNoCase(_categories[categoryIndex]._items[itemIndex].
			   _linkedChildren[k].GetItemName()->Text(),
			   child->Text()))
	  break;
	
      if (k == _categories[categoryIndex]._items[itemIndex]._linkedChildren.Length()) 
	_categories[categoryIndex]._items[itemIndex]. _linkedChildren.Add(*child_table_index);
      else
	_categories[categoryIndex]._items[itemIndex]._linkedChildren[k].Copy(*child_table_index);
      delete child_table_index;
    }
    row_no ++;
  } while (ret_c < CIF_OUT_OF_RANGE_ERROR_CODE_START &&
	   ret_p < CIF_OUT_OF_RANGE_ERROR_CODE_START);

}
  
void CifDataBlock::BuildNameAttributes()
// ----------------------------------------------------------------------
//   BuildNameAttributes(): Build attributes for all the item names
// --------------------------------------------------------------------- 
{
  int i, j, k, nvalues, nrows, categoryIndex, itemIndex, ret;
  int insertpoint, insertpoint1;
  ReVarCifArray <CifString> *parents, *children;
  CifNameAttribute name_attribute;
  CifString *nameItemValue;
  
  if (_verbose) {
    cout << "Building item name attributes " << endl;
    cout.flush();
  }

  categoryIndex = GetIndex(CIF_DDL_CATEGORY_ITEM);
  if (categoryIndex == -1) return;
  itemIndex = _categories[categoryIndex].GetIndex(CIF_DDL_ITEM_NAME);
  int offset_index = _categories[categoryIndex].GetIndex(CIF_DDL_ITEM_OFFSET);
  if (itemIndex == -1) return;

  _nameAttributes.DeleteElement();
  nvalues = _categories[categoryIndex].CountRows();

  for (i=0; i<nvalues; i++) {
    ret =  GetItemValue(&nameItemValue, categoryIndex, itemIndex, i);
    if (ret == CIF_DATA_IS_VALID) {
      nrows = _nameAttributes._list.Length();
      for (j=0; j<nrows; j++) 
	if (!Compare(_nameAttributes._list[j]._name.Text(), 
		     *nameItemValue)) break;
      if (j == nrows) {
	name_attribute.DeleteElement();
	name_attribute._name.Copy(nameItemValue->Text());
	name_attribute._name.ToLower();
	insertpoint = _nameAttributes.GetInsertIndex(name_attribute._name);
	_nameAttributes._list.InsertNAt(insertpoint, &name_attribute, 1);
	_nameAttributes._list[insertpoint]._offset = 
	  _categories[categoryIndex]._items[offset_index]._itemValues[i]._data._intVal;

	parents = GetParentsName(*nameItemValue);
	GetNameItemInfo(*nameItemValue, parents, insertpoint);
	GetNameStructureInfo(*nameItemValue, parents, insertpoint);
	GetNameItemDefaultValue(*nameItemValue, insertpoint);
	GetNameItemAliasName(*nameItemValue, insertpoint);
	if (parents != NULL) delete parents;
	children = GetChildrenName(*nameItemValue);
	if (children != NULL) {
	  name_attribute = _nameAttributes._list[insertpoint];
	  if (name_attribute._defaultValue!= NULL) {
	    delete name_attribute._defaultValue;
	    name_attribute._defaultValue = NULL;
	  }
	  if (name_attribute._aliasNames != NULL) {
	    delete name_attribute._aliasNames;
	    name_attribute._aliasNames = NULL;
	  }
	  for (j=0; j < children->Length(); j++) {
	    for (k=0; k< _nameAttributes._list.Length(); k++) 
	      if (!Compare(_nameAttributes._list[k]._name.Text(), 
			   (*children)[j])) break;
	    if (k == _nameAttributes._list.Length()) {
	      insertpoint1 = _nameAttributes.GetInsertIndex((*children)[j]);
	      name_attribute._name.Copy((*children)[j]);
	      _nameAttributes._list.InsertNAt(insertpoint1, &name_attribute, 1);
	      GetNameItemAliasName((*children)[j], insertpoint1);
	      GetNameItemDefaultValue((*children)[j], insertpoint1);
	    }
	  }
	  delete children;

	}
      }
    }
  }
}

void CifDataBlock::BuildParentsChildrenLinkList(CifDataBlock *dic)
// ----------------------------------------------------------------------
//   BuildParentsChildrenLinkList(): Build _linkParent and _linkedChildren 
//                  from table "_item_linkage" for all categories and _items
// --------------------------------------------------------------------- 
{
  int i, j, k, block_index, id_index;
  CifCategoryIndex table_index;
  CifString block_id;

  for ( i=0; i < _categories.Length(); i++) 
    for (j=0; j< _categories[i]._items.Length(); j++) 
      AddLinkParentsAndChildren(dic, i, j);

  block_index = id_index = -1;
  block_index = GetIndex(CIF_DDL_CATEGORY_BLOCK);
  if (block_index != -1) 
    id_index = _categories[block_index].GetIndex(CIF_DDL_ITEM_ID);
  JoinCategoryItemName(CIF_DDL_CATEGORY_BLOCK,CIF_DDL_ITEM_ID, &block_id); 

  // Add parent_children relationship for datablock_id
  for (i=0; i < _categories.Length(); i++) 
    for (j=0; j< _categories[i]._items.Length(); j++) {
      if (!strcmp(_categories[i]._items[j]._keyword.Text(), 
		  CIF_DDL_ITEM_DATABLOCK_ID)) {
	for (k=0; k<_categories[i]._items[j]._linkedParents.Length(); k++) {
	  if (!CompareNoCase(_categories[i]._items[j]._linkedParents[k].
			     GetItemName()->Text(), block_id)) 
	    break;
	}
	table_index.Copy(block_index, id_index, &block_id);
	if (k == _categories[i]._items[j]._linkedParents.Length()) 
	  _categories[i]._items[j]._linkedParents.Add(table_index);
	else
	  _categories[i]._items[j]._linkedParents[k].Copy(table_index);
	
	if (block_index != -1 && id_index != -1) {
	  for (k=0; k<_categories[block_index]._items[id_index]._linkedChildren.Length(); k++) 
	    if (!CompareNoCase(_categories[block_index]._items[id_index].
			       _linkedChildren[k].GetItemName()->Text(), 
			       _categories[i]._items[j]._itemAttribute._name.Text())) 
	      break;
	  
	  table_index.Copy(i, j, &_categories[i]._items[j]._itemAttribute._name);
	  if (k == _categories[block_index]._items[id_index]._linkedChildren.Length()) 
	    _categories[block_index]._items[id_index]._linkedChildren.Add(table_index);
	  else
	    _categories[block_index]._items[id_index]._linkedChildren[k].Copy(table_index);
	}
      }
  }
}

void CifDataBlock::BuildDependentItems(CifDataBlock *dic)
// ----------------------------------------------------------------------
//   BuildParentsChildrenLinkList(): Build _linkParent and _linkedChildren 
//                  from table "_item_linkage" for all categories and _items
// --------------------------------------------------------------------- 
{
  int i, j, categoryIndex, name_index, dependentIndex, ret, row_no, nvalues;
  CifString  *nameValue;
  CifCategoryIndex *table_index;

  categoryIndex = dic->GetIndex(CIF_DDL_CATEGORY_ITEM_DEPENDENT);
  if(categoryIndex < 0) return;
  name_index = dic->_categories[categoryIndex].GetIndex(CIF_DDL_ITEM_NAME);
  if (name_index <0) return;
  dependentIndex = dic->_categories[categoryIndex].GetIndex(CIF_DDL_ITEM_DEPENDENT_NAME);
  if (dependentIndex < 0) return;

  for ( i=0; i < _categories.Length(); i++) {
    for (j=0; j< _categories[i]._items.Length(); j++) {
      nvalues = dic->CountRows(categoryIndex);
      for (row_no = 0; row_no < nvalues; row_no ++) {
	ret = dic->GetItemValue(&nameValue, categoryIndex, name_index, row_no);
	if (ret == CIF_DATA_IS_VALID &&
	    !Compare(*nameValue, _categories[i]._items[j]._itemAttribute._name)) {
	  ret = dic->GetItemValue(&nameValue, categoryIndex, dependentIndex, row_no);
	  if (ret == CIF_DATA_IS_VALID) 
	    table_index = GetTableIndex(*nameValue);
	  if (table_index == NULL) table_index = new CifCategoryIndex;
	  table_index->SetItemName(*nameValue);
	  _categories[i]._items[j]._dependentItems.Add(*table_index);
	  delete table_index ;
	  table_index = NULL;
	}
      }
    }
  }
}

void CifDataBlock::BuildRelatedItems(CifDataBlock *dic)
// ----------------------------------------------------------------------
//   BuildParentsChildrenLinkList(): Build _linkParent and _linkedChildren 
//                  from table "_item_linkage" for all _categories and _items
// --------------------------------------------------------------------- 
{
  int i, j, k, l, relatedIndex, nameIndex, categoryIndex;
  int ret, row_no, nvalues, functionCodeIndex;
  CifString *name, *functionCode, *relatedName;
  CifString message;
  CifRelatedItem relatedItem;
  CifCategoryIndex *tableIndex;

  categoryIndex = dic->GetIndex(CIF_DDL_CATEGORY_ITEM_RELATED);
  if (categoryIndex < 0) return ;
  nameIndex = dic->_categories[categoryIndex].GetIndex(CIF_DDL_ITEM_NAME);
  if (nameIndex < 0) return;
  relatedIndex = dic->_categories[categoryIndex].GetIndex(CIF_DDL_ITEM_RELATED_NAME);
  if (relatedIndex < 0) return;
  functionCodeIndex = dic->_categories[categoryIndex].GetIndex(CIF_DDL_ITEM_FUNCTION_CODE);
  if (functionCodeIndex < 0) return;

  nvalues = dic->CountRows(categoryIndex);
  for ( i=0; i < _categories.Length(); i++) {
    for (j=0; j< _categories[i]._items.Length(); j++) {
      for (row_no = 0; row_no < nvalues; row_no ++) {
	ret = dic->GetItemValue(&name, categoryIndex, nameIndex, row_no);
	if (ret == CIF_DATA_IS_VALID &&
	    !CompareNoCase(*name,_categories[i]._items[j]._itemAttribute._name)) {
	  ret = dic->GetItemValue(&relatedName, categoryIndex, relatedIndex, row_no);
	  if (ret == CIF_DATA_IS_VALID) {
	    for (k=0; k<_categories[i]._items[j]._relatedItems.Length(); k++) 
	      if (!CompareNoCase(_categories[i]._items[j]._relatedItems[k].GetItemName()->Text(),
				 relatedName->Text())) break;
	    if (k == _categories[i]._items[j]._relatedItems.Length()) {
	      ret = dic->GetItemValue(&functionCode, categoryIndex, functionCodeIndex, row_no);
	      if (ret == CIF_DATA_IS_VALID) 
		relatedItem.Copy(functionCode, relatedName);
	      _categories[i]._items[j]._relatedItems.Add(relatedItem);
	    }
	  }
	}
      }

      if (_categories[i]._items[j]._itemAttribute._dataType == CIF_INTEGER_VALUE ||
	  _categories[i]._items[j]._itemAttribute._dataType == CIF_DOUBLE_VALUE) {
	for (k=0; k<_categories[i]._items[j]._relatedItems.Length(); k++) 
	  if (!CompareNoCase(_categories[i]._items[j]._relatedItems[k].GetFunctionCode()->Text(), 
			     CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_ESD)) {
	    _categories[i].AddEsdDataItem(dic, this, _categories[i]._items[j].
					 _relatedItems[k].GetItemName(), j);
	    break;
	}
	if (k == _categories[i]._items[j]._relatedItems.Length()) {
	  for (k=0; k<_categories[i]._items[j].CountItemValue(); k++) {
	    for (l=0; l< _categories[i]._items[j]._itemValues[k]._buf.Length(); l++)
	      if (_categories[i]._items[j]._itemValues[k]._buf[l] == '(') break;
	    if (l != _categories[i]._items[j]._itemValues[k]._buf.Length()) {
	      message.Clear();
	      message += "Value \'";
	      message += _categories[i]._items[j]._itemValues[k]._buf.Text();
	      message += "\' of \'";
	      message += _categories[i]._items[j]._itemAttribute._name.Text();
	      message += "\' does not have \'";
	      message += CIF_RELATED_ITEM_FUNCTION_ASSOCIATED_ESD;
	      message += "\' related item";
	      ErrorMessage(CIF_WARNING, -1, message);
	    }
	  }
	}
      }
    }
  }
  for ( i=0; i < _categories.Length(); i++) {
    for (j=0; j< _categories[i]._items.Length(); j++) {
      for (k=0; k< _categories[i]._items[j]._relatedItems.Length(); k++) {
	tableIndex = GetTableIndex(_categories[i]._items[j]._relatedItems[k].
				   GetItemName()->Text());
	if (tableIndex != NULL) {
	  if (tableIndex->GetCategoryIndex() == i) {
	    _categories[i]._items[j]._relatedItems[k].
	      SetCategoryIndex(tableIndex->GetCategoryIndex());
	    _categories[i]._items[j]._relatedItems[k].
	      SetItemIndex(tableIndex->GetItemIndex());
	  }
	}
      }
    }
  }

}
int CifDataBlock::ItemValueExist(const CifItemValue *itemValue, 
			      const char *categoryName, 
			      const char *itemName)
{
  int categoryIndex, itemIndex;

  if (itemValue == NULL || itemValue->_status == CIF_DATA_IS_NULL || 
      itemValue->_status == CIF_DATA_NOT_APPROPRIATE)
    return TRUE;
  categoryIndex = GetIndex(categoryName);
  if (categoryIndex == -1) return FALSE;
  else {
    itemIndex = _categories[categoryIndex].GetIndex(itemName);
    if (itemIndex == -1) return FALSE;
    else return ItemValueExist(itemValue, categoryIndex, itemIndex);
  }
}
int CifDataBlock::ItemValueExist(const CifItemValue *itemValue, const int to_category_no, 
			      const int to_item_no)
{
  CifItemValue *to_itemValue;
  unsigned i;

  if (itemValue == NULL || itemValue->_status == CIF_DATA_IS_NULL || 
      itemValue->_status == CIF_DATA_NOT_APPROPRIATE)
    return TRUE;
  else if (to_category_no < 0 || to_category_no >= _categories.Length()) 
    return FALSE;
  else if (itemValue->_dataType != _categories[to_category_no]._items[to_item_no]._itemAttribute._dataType)
    return FALSE;
   
  int ret, nvalues = _categories[to_category_no]._items[to_item_no].CountItemValue();
  for (i=0; i< nvalues;i++) { 
    ret = GetItemValue(&to_itemValue, to_category_no, to_item_no , i);
    if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL
	  && ret != CIF_DATA_NOT_APPROPRIATE) {
      if (itemValue->_dataType == CIF_INTEGER_VALUE ) {
	if (itemValue->_data._intVal == to_itemValue->_data._intVal) return TRUE;
      }
      else if (itemValue->_dataType == CIF_DOUBLE_VALUE) {
	if (itemValue->_data._dblVal == to_itemValue->_data._dblVal) return TRUE;
      }
      else {
	if (!_categories[to_category_no]._items[to_item_no]._itemAttribute._flags._case 
	    && !CompareNoCase(itemValue->_buf, to_itemValue->_buf) )
	  return TRUE;
	else if (_categories[to_category_no]._items[to_item_no]._itemAttribute._flags._case 
		 && !Compare(itemValue->_buf, to_itemValue->_buf))
	  return TRUE;
      }
      
    }
  }
  return FALSE;
}

void CifDataBlock::GetDDLItemInfo(CifDataBlock *dic, const int verify)
{
  int i, j, k;
  ReVarCifArray <CifString> *keys;

  for (i=0; i < _categories.Length(); i++)  {

    keys = dic->BuildCategoryKeys(_categories[i]._categoryName);

    if (keys != NULL) {
      _categories[i]._keys = *keys;
      delete keys; keys=NULL;
    }
  }

  DeleteDuplicateRows();

  for (i=0; i < _categories.Length(); i++)  
    for (j=0; j< _categories[i]._items.Length(); j++)  
      dic->GetItemInfo(&_categories[i]._items[j]);

#if !defined (linux)
  if (verify)
    for (i=0; i < _categories.Length(); i++) 
      for (j=0; j< _categories[i]._items.Length(); j++)  
	for (k=0; k< _categories[i]._items[j]._itemValues.Length(); k++)  
	  VerifyItemValue(dic, &_categories[i]._items[j]._itemValues[k], i, j, -1);
#endif

}

CifString *CifDataBlock:: GetItemNameFromAlias(CifString *aliasName, CifString *itemName)
{
  int index;
  CifString *name;

  if (aliasName == NULL) return itemName;
  index = _nameAttributes.GetIndex(aliasName->Text());
  if (index != -1) {
    if (itemName != NULL) name = itemName;
    else  name = new CifString;
    name->Clear();
    name->Copy(_nameAttributes._list[index]._name);
    return name;
  }
  else return itemName;
		       
}
void CifDataBlock::GetItemStructureInfo(CifDataItem *item, CifString categoryName)
{
  //  CifJoin structure_join, structure_list_join;
  int structure_index, structure_list_index;
  CifItemValue *cifitemValue, *code_itemValue;
  CifString itemName, nameValue;

  int ret, nvalues, row_no, i, nameIndex, organization_index;
  int structurelist_code_index, structure_code_index, dimension_index, index_index;
  if (!CompareNoCase(CIF_DDL_TEMP_ITEM_CATEGORY_ID, item->_keyword.Text()) ||
      !CompareNoCase(CIF_DDL_TEMP_ITEM_ITEM, item->_keyword.Text())) {
    GetIdOrItemKeyword(categoryName,
		       item->_keyword.Text(),  &nameValue);
    JoinCategoryItemName(categoryName, nameValue, &itemName);
    itemName.ToLower();
  }
  else  if (CompareNoCase(item->_keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID  ))
    itemName.Copy(item->_itemAttribute._name);
  else JoinCategoryItemName(CIF_DDL_CATEGORY_BLOCK, CIF_DDL_ITEM_ID, &itemName);    

  structure_index = GetIndex(CIF_DDL_CATEGORY_ITEM_STRUCTURE);
  if (structure_index == -1) return;
  nameIndex = _categories[structure_index].GetIndex(CIF_DDL_ITEM_NAME);
  organization_index = _categories[structure_index].GetIndex(CIF_DDL_ITEM_ORGANIZATION);
  structure_code_index = _categories[structure_index].GetIndex(CIF_DDL_ITEM_CODE);
  if (nameIndex == -1) return;
  structure_list_index = GetIndex(CIF_DDL_CATEGORY_ITEM_STRUCTURE_LIST);
  if (structure_list_index == -1) return;
  structurelist_code_index = _categories[structure_list_index].GetIndex(CIF_DDL_ITEM_CODE);
  dimension_index = _categories[structure_list_index].GetIndex(CIF_DDL_ITEM_DIMENSION);
  index_index = _categories[structure_list_index].GetIndex(CIF_DDL_ITEM_INDEX);
      
  if (structure_index != -1) {
    nvalues = _categories[structure_index].CountRows();
    for (row_no = 0; row_no < nvalues; row_no ++) {
      ret = GetItemValue(&cifitemValue,structure_index,  nameIndex, row_no);
      if (ret == CIF_DATA_IS_VALID &&  !CompareNoCase(cifitemValue->_buf, itemName)) {
	ret = GetItemValue(&cifitemValue, structure_index, organization_index, row_no);
	if (ret == CIF_DATA_IS_VALID && 
	    !CompareNoCase(cifitemValue->_buf.Text(), "rowwise"))
	  item->_itemAttribute._structureOrganization = CIF_STRUCTURE_ROW_WISE;
	else if (!CompareNoCase(cifitemValue->_buf.Text(), "columnwise"))
	  item->_itemAttribute._structureOrganization = CIF_STRUCTURE_COLUMN_WISE;
	ret = GetItemValue(&code_itemValue, structure_index, structure_code_index, row_no);
	break;
      }
    }
  }
  if (row_no == nvalues) return;

  if (structure_list_index != -1 && ret == CIF_DATA_IS_VALID) {
    nvalues = _categories[structure_list_index].CountRows();
    for (row_no = 0; row_no < nvalues; row_no ++) {
      ret = GetItemValue(&cifitemValue, structure_list_index, structurelist_code_index, row_no);
      if (ret == CIF_DATA_IS_VALID &&  
	  (code_itemValue->_flags._case &&  !CompareNoCase(cifitemValue->_buf, code_itemValue->_buf) ||
	   !code_itemValue->_flags._case && 
	   !CompareNoCase(cifitemValue->_buf, code_itemValue->_buf) )  ) {
	ret = GetItemValue(&cifitemValue, structure_list_index, dimension_index, row_no);
	if (ret == CIF_DATA_IS_VALID) 
	  item->_itemAttribute._structureDimension = atoi(cifitemValue->_buf.Text());

	ret = GetItemValue(&cifitemValue, structure_list_index, index_index, row_no);
	if (ret == CIF_DATA_IS_VALID) 
	  item->_itemAttribute._structureIndex = atoi(cifitemValue->_buf.Text());

	if (item->_itemAttribute._structureOrganization == CIF_STRUCTURE_COLUMN_WISE ||
	    item->_itemAttribute._structureOrganization == CIF_STRUCTURE_ROW_WISE) {
	  item->_itemAttribute._structureNumValue =  1;
	  for ( i=0; i<structure_index; i++)
	    item->_itemAttribute._structureNumValue *= item->_itemAttribute._structureDimension;
	}
	else {
	  item->_itemAttribute._structureNumValue =  item->_itemAttribute._structureDimension +1;;
	  for (i=1; i<structure_index; i++)
	    item->_itemAttribute._structureNumValue *= item->_itemAttribute._structureDimension;
	  item->_itemAttribute._structureNumValue /= 2;
	}
	break;
      }
    }
  }
}

void CifDataBlock::GetNameStructureInfo(CifString itemName,
					 ReVarCifArray <CifString> *parents,
					 int insertpoint)
{
  int parent, nvalues, row_no, i, ret;
  int structure_index, nameIndex, organization_index, code_index;
  int structurelist_index, dimension_index, index_index;
  int num_itemValues;
  CifString *nameValue;
  CifItemValue *cifitemValue, *code_itemValue;

  structure_index = GetIndex(CIF_DDL_CATEGORY_ITEM_STRUCTURE);
  if (structure_index == -1) return;
  nameIndex = _categories[structure_index].GetIndex(CIF_DDL_ITEM_NAME);
  organization_index = _categories[structure_index].GetIndex(CIF_DDL_ITEM_ORGANIZATION);
  code_index = _categories[structure_index].GetIndex(CIF_DDL_ITEM_CODE);
  if (nameIndex == -1 || organization_index == -1 || code_index == -1) 
    return;

  nvalues = _categories[structure_index].CountRows();
  for (row_no = 0; row_no < nvalues; row_no ++) {
    ret = GetItemValue(&nameValue, structure_index, nameIndex, row_no);
    if (ret == CIF_DATA_IS_VALID) {
      parent = -1;
      if (CompareNoCase(*nameValue, itemName)) {
	if (parents != NULL) {
	  for (parent=0; parent < parents->Length(); parent++) 
	    if (!CompareNoCase((*parents)[parent], *nameValue)) break;
	  
	  if (parent == parents->Length()) parent = -1;
	}
      }
      if (!CompareNoCase(*nameValue, itemName) || parent != -1) {
	ret = GetItemValue(&cifitemValue, structure_index, organization_index, row_no);
	if (ret == CIF_DATA_IS_VALID) {
	  if (!CompareNoCase(cifitemValue->_buf.Text(), "columnwise")) 
	    _nameAttributes._list[insertpoint]._structureOrganization 
	      = CIF_STRUCTURE_COLUMN_WISE;
	  else 
	    _nameAttributes._list[insertpoint]._structureOrganization 
	       = CIF_STRUCTURE_ROW_WISE;

	}
	ret = GetItemValue(&code_itemValue, structure_index, code_index, row_no);
	break;
      }
    }
  }
  structurelist_index = GetIndex(CIF_DDL_CATEGORY_ITEM_STRUCTURE_LIST);
  code_index = _categories[structurelist_index].GetIndex(CIF_DDL_ITEM_CODE);
  dimension_index = _categories[structurelist_index].GetIndex(CIF_DDL_ITEM_DIMENSION);
  index_index = _categories[structurelist_index].GetIndex(CIF_DDL_ITEM_INDEX);
  if (ret == CIF_DATA_IS_VALID) {
    nvalues = _categories[structurelist_index].CountRows();
    for (row_no = 0; row_no < nvalues; row_no ++) {
      ret = GetItemValue(&cifitemValue, structurelist_index, code_index, row_no);
      if (ret == CIF_DATA_IS_VALID &&  
	  (code_itemValue->_flags._case &&  !CompareNoCase(cifitemValue->_buf, code_itemValue->_buf) ||
	   !code_itemValue->_flags._case &&
	   !CompareNoCase(cifitemValue->_buf, code_itemValue->_buf))) {
	ret = GetItemValue(&cifitemValue, structurelist_index, dimension_index, row_no);
	if (ret == CIF_DATA_IS_VALID) 
	  _nameAttributes._list[insertpoint]._structureDimension = cifitemValue->_data._intVal;

	ret = GetItemValue(&cifitemValue, structurelist_index, index_index, row_no);
	if (ret == CIF_DATA_IS_VALID) 
	  _nameAttributes._list[insertpoint]._structureIndex = cifitemValue->_data._intVal;

	num_itemValues =  1;
	for ( i=0; i<_nameAttributes._list[insertpoint]._structureIndex; i++)
	  num_itemValues *= _nameAttributes._list[insertpoint]._structureDimension;
	_nameAttributes._list[insertpoint]._structureNumValue  = num_itemValues;
	break;
      }
    }
  }
    
}
void CifDataBlock::GetNameItemDefaultValue(CifString itemName,
					    int insertpoint)
{
  int nvalues, row_no, ret;
  int item_default_index, nameIndex, value_index;
  CifString *nameValue;
  CifItemValue *cifitemValue;

  item_default_index = GetIndex(CIF_DDL_CATEGORY_ITEM_DEFAULT);
  if (item_default_index == -1) return;
  nameIndex = _categories[item_default_index].GetIndex(CIF_DDL_ITEM_NAME);
  value_index = _categories[item_default_index].GetIndex(CIF_DDL_ITEM_VALUE);
  if (nameIndex == -1 || value_index == -1)
    return;

  nvalues = _categories[item_default_index].CountRows();
  for (row_no = 0; row_no < nvalues; row_no ++) {
    ret = GetItemValue(&nameValue, item_default_index, nameIndex, row_no);
    if (ret == CIF_DATA_IS_VALID) {
      if (!CompareNoCase(*nameValue, itemName)) {
	ret = GetItemValue(&cifitemValue, item_default_index, value_index, row_no);
	if (ret == CIF_DATA_IS_VALID) {
	  _nameAttributes._list[insertpoint]._defaultValue = new CifItemValue;
	  _nameAttributes._list[insertpoint]._defaultValue->Copy(*cifitemValue);
	}
	break;
      }
    }
  }
}
void CifDataBlock::GetNameItemAliasName(CifString itemName, int insertpoint)
{
  int nvalues, row_no, ret;
  int item_alias_index, nameIndex, alias_index, dictionary_index, version_index;
  CifString *nameValue, *itemValue;
  CifAlias alias;

  item_alias_index = GetIndex(CIF_DDL_CATEGORY_ITEM_ALIASES);
  if (item_alias_index == -1) return;
  nameIndex = _categories[item_alias_index].GetIndex(CIF_DDL_ITEM_NAME);
  alias_index = _categories[item_alias_index].GetIndex(CIF_DDL_ITEM_ALIAS_NAME);
  dictionary_index = _categories[item_alias_index].GetIndex(CIF_DDL_ITEM_DICTIONARY);
  version_index = _categories[item_alias_index].GetIndex(CIF_DDL_ITEM_VERSION);
  if (nameIndex == -1 || alias_index == -1)
    return;

  nvalues = _categories[item_alias_index].CountRows();
  for (row_no = 0; row_no < nvalues; row_no ++) {
    ret = GetItemValue(&nameValue, item_alias_index, nameIndex, row_no);
    if (ret == CIF_DATA_IS_VALID) {
      if (!CompareNoCase(*nameValue, itemName)) {
	ret = GetItemValue(&itemValue, item_alias_index, alias_index, row_no);
	if (ret == CIF_DATA_IS_VALID) {
	  alias.DeleteElement();
	  alias._aliasName.Copy(*itemValue);
	  ret = GetItemValue(&itemValue, item_alias_index, dictionary_index, row_no);
	  if (ret == CIF_DATA_IS_VALID)
	    alias._dictionary.Copy(*itemValue);
	  ret = GetItemValue(&itemValue, item_alias_index, version_index, row_no);
	  if (ret == CIF_DATA_IS_VALID)
	    alias._version.Copy(*itemValue);
	  if (_nameAttributes._list[insertpoint]._aliasNames == NULL)
	    _nameAttributes._list[insertpoint]._aliasNames = new ReVarCifArray <CifAlias>;
	  _nameAttributes._list[insertpoint]._aliasNames->Add(alias);
	}
      }
    }
  }
}
void CifDataBlock::GetNameItemInfo(CifString itemName,
				    ReVarCifArray <CifString> *parents, 
				    int insertpoint)
{
  int parent, nvalues, row_no, ret;
  int type_index, nameIndex, code_index;
  int typelist_index, primitive_index;
  CifString *nameItemValue;
  CifItemValue *code_itemValue, *itemValue;
  CifString message;

  type_index = GetIndex(CIF_DDL_CATEGORY_ITEM_TYPE);
  if (type_index == -1) return;
  nameIndex = _categories[type_index].GetIndex(CIF_DDL_ITEM_NAME);
  code_index = _categories[type_index].GetIndex(CIF_DDL_ITEM_CODE);
  
  if (nameIndex == -1 || code_index == -1) 
    return;
  nvalues = _categories[type_index].CountRows();
  for (row_no = 0; row_no < nvalues; row_no ++) {
    ret = GetItemValue(&nameItemValue, type_index, nameIndex, row_no);
    if (ret == CIF_DATA_IS_VALID) {
      parent = -1;
      if (CompareNoCase(*nameItemValue, itemName)) {
	if (parents != NULL) {
	  for (parent=0; parent < parents->Length(); parent++) 
	    if (!CompareNoCase((*parents)[parent], *nameItemValue) ) break;
	  
	  if (parent == parents->Length()) parent = -1;
	}
      }
      if (!CompareNoCase(*nameItemValue, itemName) || parent != -1) {
	ret = GetItemValue(&code_itemValue, type_index, code_index, row_no);
	if (ret == CIF_DATA_IS_VALID) {
	  _nameAttributes._list[insertpoint]._dataTypeCode.Copy(code_itemValue->_buf);
	  if (!CompareNoCase(code_itemValue->_buf.Text(), CIF_INTEGER_TERM) ||
	      !CompareNoCase(code_itemValue->_buf.Text(), CIF_INT_TERM))
	    _nameAttributes._list[insertpoint]._dataType = CIF_INTEGER_VALUE;
	  else if (!CompareNoCase(code_itemValue->_buf.Text(), CIF_REAL_TERM) ||
		   !CompareNoCase(code_itemValue->_buf.Text(), CIF_FLOAT_TERM) ||
		   !CompareNoCase(code_itemValue->_buf.Text(), CIF_DOUBLE_TERM))
	    _nameAttributes._list[insertpoint]._dataType = CIF_DOUBLE_VALUE;
	  else
	    _nameAttributes._list[insertpoint]._dataType = CIF_STRING_VALUE;
	}
	else
	  _nameAttributes._list[insertpoint]._dataType = CIF_STRING_VALUE;
	break;
      }
    }
  }
  if (row_no == nvalues) return;
  typelist_index = GetIndex(CIF_DDL_CATEGORY_ITEM_TYPE_LIST);
  if (typelist_index == -1) return;
  code_index = _categories[typelist_index].GetIndex(CIF_DDL_ITEM_CODE);
  primitive_index = _categories[typelist_index].GetIndex(CIF_DDL_ITEM_PRIMITIVE_CODE);
  if (code_index == -1 || primitive_index == -1) return;

  if (ret == CIF_DATA_IS_VALID) {
    nvalues = _categories[typelist_index].CountRows();
    for (row_no = 0; row_no < nvalues; row_no ++) {
      ret = GetItemValue(&itemValue, typelist_index, code_index, row_no);
      if (ret == CIF_DATA_IS_VALID) {
	if (itemValue->_flags._case &&  !CompareNoCase(itemValue->_buf, code_itemValue->_buf) ||
	    !itemValue->_flags._case &&
	    !CompareNoCase(itemValue->_buf, code_itemValue->_buf)) {
	  _nameAttributes._list[insertpoint]._constructIndex = row_no;

	  ret = GetItemValue(&itemValue, typelist_index, primitive_index, row_no);
	  if (ret == CIF_DATA_IS_VALID) {
	    _nameAttributes._list[insertpoint]._primitiveCode.Copy(itemValue->_buf);
	    if (!CompareNoCase(itemValue->_buf.Text(),CIF_DDL_NON_CASE_SENSITIVE_TERM)) 
	      _nameAttributes._list[insertpoint]._flags._case = FALSE;
	    else 
	      _nameAttributes._list[insertpoint]._flags._case = TRUE;
	    return;
	  }
	}
      }
    }
  }
  if (_nameAttributes._list[insertpoint]._constructIndex == -1) {
    message.Clear();
    message += "Value \'";
    message += code_itemValue->_buf.Text();
    message += "\' of ";
    message += CIF_DDL_CATEGORY_ITEM_TYPE;
    message += '.';
    message += CIF_DDL_ITEM_CODE;
    message += " for item \'";
    message += itemName.Text();
    message += "\' is not recognized";
    ErrorMessage(CIF_ERROR, -1, message);
  }
    
}

CifItemValue *CifDataBlock::GetItemDefaultValue(const CifString name)
{

  int value_index;

  value_index = _nameAttributes.GetIndex(name);
  if (value_index != -1) 
    return _nameAttributes._list[value_index]._defaultValue;
  else return NULL;
}  
CifString *CifDataBlock::GetItemAliasName(const int categoryIndex, 
					const int itemIndex, 
					const CifString *dictionary, 
					const CifString *version) 
{
  if (categoryIndex < 0 || categoryIndex >= _categories.Length())
    return  (CifString *) NULL;
  else return _categories[categoryIndex].GetItemAliasName(itemIndex, dictionary, version);
}
void CifDataBlock::GetItemInfo(CifDataItem *item)
{
  int value_index, i, item_typelist_index, code_index;

  value_index = _nameAttributes.GetIndex(item->_itemAttribute._name);
  if (value_index != -1) {
    item->_itemAttribute = _nameAttributes._list[value_index];
    if (item->_alias != NULL) {
      if (item->_itemAttribute._aliasNames != NULL) {
	for (i=0; i<item->_itemAttribute._aliasNames->Length(); i++)
	  if (!CompareNoCase(item->_alias->GetAliasName()->Text(), 
			     (*item->_itemAttribute._aliasNames)[i].GetAliasName()->Text()))
	    break;
	if (i != item->_itemAttribute._aliasNames->Length()) {
	  item->_alias->SetDictionary((*item->_itemAttribute._aliasNames)[i].GetDictionary());
	  item->_alias->SetDictionaryVersion((*item->_itemAttribute._aliasNames)[i].GetDictionaryVersion());
	}
      }
    }
  }
  else if (!CompareNoCase(item->_keyword.Text(), CIF_DDL_ITEM_OFFSET))  {
    item->_itemAttribute._dataType = CIF_INTEGER_VALUE;
    for (i=0; i<_nameAttributes._list.Length(); i++) {
      if (_nameAttributes._list[i]._dataType == item->_itemAttribute._dataType) {
	item->_itemAttribute._constructIndex = _nameAttributes._list[i]._constructIndex;
	break;
      }
    }
  }
  else if (!CompareNoCase(item->_keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID) ||
	   !CompareNoCase(item->_keyword.Text(), CIF_DDL_TEMP_ITEM_CATEGORY_ID) ||
	   !CompareNoCase(item->_keyword.Text(), CIF_DDL_TEMP_ITEM_ITEM)) {
    item->_itemAttribute._flags._case = FALSE;
    item->_itemAttribute._dataType = CIF_STRING_VALUE;
    item_typelist_index = GetIndex(CIF_DDL_CATEGORY_ITEM_TYPE_LIST);
    if (item_typelist_index == -1) return;
    code_index = _categories[item_typelist_index].GetIndex(CIF_DDL_ITEM_CODE);
    if (code_index == -1) return;
    for (i=0; i< _categories[item_typelist_index].CountRows(); i++) {
      if (!CompareNoCase(item->_keyword.Text(), CIF_DDL_ITEM_DATABLOCK_ID) &&
	  !CompareNoCase(_categories[item_typelist_index]._items[code_index]._itemValues[i]._buf.Text(),
			 CIF_DDL_CODE_TERM)) {
	item->_itemAttribute._constructIndex = i;
	break;
      }
      else if (!CompareNoCase(item->_keyword.Text(), CIF_DDL_TEMP_ITEM_CATEGORY_ID ) &&
	       !CompareNoCase(_categories[item_typelist_index]._items[code_index]._itemValues[i]._buf.Text(),
		       CIF_DDL_ID_NAME_TERM )) {
	item->_itemAttribute._constructIndex = i;
	break;
      }
      else if (!CompareNoCase(item->_keyword.Text(),  CIF_DDL_TEMP_ITEM_ITEM ) &&
	       !CompareNoCase(_categories[item_typelist_index]._items[code_index]._itemValues[i]._buf.Text(),
		       CIF_DDL_NAME_TERM)) {
	item->_itemAttribute._constructIndex = i;
	break;
      }
    }
  }

}

void CifDataBlock::CheckParentsChildrenRelation()
// ----------------------------------------------------------------------
//   CheckParentsChildrenLinkList(): Check if there any _itemValues exists in 
//          children _items but not in parents _items.
//          if there is, set status of that itemValue to be some error code
// --------------------------------------------------------------------- 
{
  int i, j, k, l, ret, parent_category, parent_item, ret_save, nvalues;
  CifItemValue *childItemValue;
  CifString message;

  if (_verbose) {
    cout << "Checking parents and children relationship" << endl;
    cout.flush();
  }
  for (i=0; i < _categories.Length(); i++) {
    for (j=0; j< _categories[i]._items.Length(); j++) {
      if (_categories[i]._items[j]._linkedParents.Length() > 0) {
	nvalues = _categories[i]._items[j].CountItemValue();
	for (k=0; k < nvalues; k++) {
	  ret_save = FALSE;
	  ret = GetItemValue(&childItemValue, i, j, k);
	  // if ret is not IS_VALID, NULL, nor NOT APPROPRIATE means 
	  // it is not valid already no need to do more checking
	  if (ret == CIF_DATA_IS_VALID)  {
	    for (l=0; l< _categories[i]._items[j]._linkedParents.Length(); l++) {
	      parent_category = _categories[i]._items[j]._linkedParents[l].GetCategoryIndex();
	      parent_item     = _categories[i]._items[j]._linkedParents[l].GetItemIndex();
	      if (parent_category != -1 && parent_item != -1) {
		ret = ItemValueExist(childItemValue, parent_category, parent_item);
		if (ret == FALSE) {
		  message.Clear();
		  message += "Missing the parent item \'";
		  message += _categories[parent_category]._items[parent_item]._itemAttribute._name.Text();
		  message += "\' of item \'";
		  message += _categories[i]._items[j]._itemAttribute._name.Text(),
		  message += "\' with value \'";
		  message += childItemValue->_buf.Text();
		  message += "\'";
		  ErrorMessage(CIF_WARNING, -1, message);
		  _categories[i]._items[j]._itemValues[k].SetDataStatus
		    (CIF_VALUE_NOT_EXIST_IN_ONE_LINK_PARENT);
		}
		else ret_save = CIF_DATA_IS_VALID;
	      }
	      if (ret_save == FALSE && l != 0 && _categories[i]._items[j]._itemValues[k]._status 
		== CIF_VALUE_NOT_EXIST_IN_ONE_LINK_PARENT ) {
		message.Clear();
		message += "Value \'";
		message += childItemValue->_buf.Text();
		message += "\' of item \'";
		message += _categories[i]._items[j]._itemAttribute._name.Text(),
		message += "\' does not exist in any parent item";
		ErrorMessage(CIF_ERROR, -1, message);
		_categories[i]._items[j]._itemValues[k].SetDataStatus
		  (CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS);
	      }
	    }
	  }
	}
      }
    }
  }
}
int CifDataBlock::GetIdOrItemKeyword(const CifString categoryName,
				      const char *tmpkeyword, 
				      CifString *itemName)
// ----------------------------------------------------------------------
//   GetIdOrItemKeyword(): Get the keyword corresponding to _category.id
//                         or _item._name from "_item_linkage" table
//   Parameters: CifString categoryName, categoryName of this table
//               CifString keyword: Look for _category.id or _item._name
//               CifString *categoryName its corresponding categoryName
//               CifString *itemName its corresponding itemName
// --------------------------------------------------------------------- 
{
  int ret, row_no, join_pos, linkage_index, parent_index, child_index;
  CifItemValue *childItemValue, *parentItemValue;

  ret = CIF_DATA_IS_VALID;
  if (!strcmp(categoryName.Text(), CIF_DDL_CATEGORY_CATEGORY) &&
      !strcmp(tmpkeyword, CIF_DDL_TEMP_ITEM_CATEGORY_ID)) {
    itemName->Copy(CIF_DDL_ITEM_ID);
  }
  else if (!strcmp(categoryName.Text(), CIF_DDL_CATEGORY_ITEM) &&
	   !strcmp(tmpkeyword,CIF_DDL_TEMP_ITEM_ITEM)) {
    itemName->Copy(CIF_DDL_ITEM_NAME);
  }
  else {
    // Get item 'id' or 'name' from _item_linkage table by trace
    // its parent until get _category.id or _item._name
    row_no = 0;
    linkage_index = GetIndex(CIF_DDL_CATEGORY_LINKAGE);
    if (linkage_index == -1) return CIF_CATEGORY_NOT_EXIST;
    parent_index = _categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_PARENT);
    if (parent_index == -1) return CIF_KEYWORD_NOT_EXIST;
    child_index = _categories[linkage_index].GetIndex(CIF_DDL_ITEM_LINKAGE_CHILD);
    if (child_index == -1) return CIF_KEYWORD_NOT_EXIST;
    do {
      ret = GetItemValue(&childItemValue, linkage_index, child_index, row_no);
      if (ret == CIF_DATA_IS_VALID) {
	for (join_pos=0; join_pos< childItemValue->_buf.Length(); join_pos++)
	  if (childItemValue->_buf.Text()[join_pos] == CIF_DATA_JOIN_CHARACTER) 
	    break;
	if (join_pos == childItemValue->_buf.Length())
	  join_pos = -1;
	
	if (!CompareNoCase(&childItemValue->_buf.Text()[1],categoryName.Text(),
		     join_pos-1) &&
	    !CompareNoCase(&childItemValue->_buf.Text()[1], categoryName.Text(),
		     categoryName.Length())) {
	  ret = GetItemValue(&parentItemValue, linkage_index, parent_index, row_no);
	  if (ret == CIF_DATA_IS_VALID) {
	    if (!CompareNoCase(CIF_DDL_TEMP_ITEM_CATEGORY_ID, tmpkeyword) &&
		!CompareNoCase(parentItemValue->_buf.Text(), CIF_DDL_NAME_CATEGORY_ID) ||
		!CompareNoCase(CIF_DDL_TEMP_ITEM_ITEM, tmpkeyword) &&
		!CompareNoCase(parentItemValue->_buf.Text(), CIF_DDL_NAME_ITEM_NAME)) {
	      itemName->Copy(&childItemValue->_buf.Text()[join_pos+1]);
	      break;
	    }
	  }
	}
      }
      row_no++;
    } while (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START);
  }
  return ret;
}

int CifDataBlock::GetMandatoryCode(CifString item_full_name,
				    CifItemValue **mandatory_itemValue)
// ----------------------------------------------------------------------
//   GetMandatoryCode(): Get the itemValue of mandatory_code
//   Parameters: CifString categoryName, categoryName of this table
//               CifString keyword: Look for _category.id or _item._name
//               CifItemValue **category_itemValue  
//               CifItemValue **mandatory_itemValue 
// --------------------------------------------------------------------- 
{
  int row_no, ret, nvalues;
  int table_index;
  CifItemValue *nameValue;
  int nameIndex, mandatory_index;

  table_index = GetIndex(CIF_DDL_CATEGORY_ITEM);
  if (table_index != -1) {
    nameIndex = _categories[table_index].GetIndex(CIF_DDL_ITEM_NAME);
    mandatory_index = _categories[table_index].GetIndex(CIF_DDL_ITEM_MANDATORY_CODE);
    nvalues = _categories[table_index].CountRows();
    for (row_no =0; row_no < nvalues; row_no++) {
      ret = GetItemValue(&nameValue, table_index, nameIndex, row_no);
      if (ret == CIF_DATA_IS_VALID && 
	  !CompareNoCase(nameValue->_buf, item_full_name)) {
	ret = GetItemValue(mandatory_itemValue, table_index, mandatory_index, row_no);
	if (ret == CIF_DATA_IS_VALID) return ret;
      }
    }
  }
  return CIF_VALUE_NOT_EXIST;

}


void CifDataBlock::DeleteDuplicateRows()
{
  int i, j, k, l, m, nrows;
  CifString message;

  if (_verbose) {
    cout << "Deleting duplicate rows " << endl;
    cout.flush();
  }
  for (i=0; i<_categories.Length(); i++) {
    if (_categories[i]._keys.Length() != 0) {
      nrows = _categories[i].CountRows();
      for (j=0; j < nrows -1; j++) {
	for (k=j+1; k<nrows; k++) {

	  // Find the first difference in keys in the current row pair and bail.
	  // Completing following block means a missing key or a duplicate key.
	  for (m=0; m < _categories[i]._keys.Length(); m++) {
	    for (l=0; l<_categories[i]._items.Length(); l++)  {
	      if (_categories[i]._items[l]._keyword == _categories[i]._keys[m] &&
		  CompareNoCase(_categories[i]._items[l]._itemValues[j]._buf,
		  _categories[i]._items[l]._itemValues[k]._buf) )
		break;
	    }
	    if (l != _categories[i]._items.Length()) break;
	  }

	  // Duplicate keys in this row pair

	  if (m == _categories[i]._keys.Length()) {

	    for (l=0; l<_categories[i]._items.Length(); l++) {
#if 0
	      if (_categories[i]._items.Length() != _categories[i]._keys.Length())
		for (m=0; m < _categories[i]._keys.Length(); m++) 
		  if (_categories[i]._items[l]._keyword == _categories[i]._keys[m]) break;

	      if (m == _categories[i]._keys.Length()) {
		message.Clear();
		message += "Missing key item in category \'";
		message +=  _categories[i]._categoryName.Text();
		message += "\' keyword \'";
		message += _categories[i]._items[l]._keyword.Text();
		message += "\'";
		ErrorMessage(CIF_ERROR, -1, message);
	      }
#endif

	      if (_categories[i]._items.Length() == _categories[i]._keys.Length()) {
		if (CompareNoCase(_categories[i]._items[l]._keyword.Text(),
			   CIF_DDL_ITEM_DATABLOCK_ID ) &&
		    CompareNoCase(_categories[i]._items[l]._keyword.Text(),
			   CIF_DDL_ITEM_OFFSET ) &&
		    CompareNoCase(_categories[i]._items[l]._keyword.Text(),
			   CIF_DDL_ITEM_ID ) &&
		    CompareNoCase(_categories[i]._items[l]._itemValues[j]._buf.Text(),
			   _categories[i]._items[l]._itemValues[k]._buf.Text()) &&
		    _categories[i]._items[l]._itemValues[j]._status == CIF_DATA_IS_VALID &&
		    _categories[i]._items[l]._itemValues[k]._status == CIF_DATA_IS_VALID) {
		  message.Clear();
		  message += "Key item \'";
		  message += _categories[i]._items[l]._itemAttribute._name.Text();
		  message += "\' has duplicated value \'";
		  message += _categories[i]._items[l]._itemValues[k]._buf.Text();
		  message += "\'";
		  ErrorMessage(CIF_ERROR, -1, message);
		}
	      }
	      if (_categories[i]._items[l]._itemValues[j]._status != CIF_DATA_IS_VALID &&
		  _categories[i]._items[l]._itemValues[k]._status == CIF_DATA_IS_VALID) 
		_categories[i]._items[l]._itemValues[j].Copy(_categories[i]._items[l]._itemValues[k]);
	      _categories[i]._items[l]._itemValues.DeleteAt(k, 1);
	    }
	    k--;
	  }
	  if (k >=  _categories[i].CountRows()-1) break;
	}
	if (j >=  _categories[i].CountRows()-1) break;
      }
    }
  }
}


void CifDataBlock::  BuildItemTypeListConstruct()
{
  int nvalues, row_no, ret, typelist_index, construct_index, i;
  CifString buf;
  CifItemValue *cifitemValue;  
  long tmpnumber1;

  if (_verbose) {
    cout << "Building item type list construct " << endl;
    cout.flush();
  }

  typelist_index = GetIndex(CIF_DDL_CATEGORY_ITEM_TYPE_LIST);
  if (typelist_index == -1) return;
  construct_index = _categories[typelist_index].GetIndex(CIF_DDL_ITEM_CONSTRUCT);
  if (construct_index == -1) return;

  nvalues = _categories[typelist_index].CountRows();
  for (row_no = 0; row_no < nvalues; row_no ++) {
    ret = GetItemValue(&cifitemValue, typelist_index, construct_index, row_no);
    if (ret == CIF_DATA_IS_VALID) {
      buf.Copy(cifitemValue->_buf);
      for (i=0; i<buf.Length()-1; i++)
	if (buf[i] == '\\') {
	  if (buf[i+1] == 'n') {
	    buf[i] = '\n';
	    buf.DeleteAt(i+1, 1);
	  }
	  else if (buf[i+1] == 't') {
	    buf[i] = '\t';
	    buf.DeleteAt(i+1, 1);
	  }
	  else if (isdigit(buf[i+1])) {
	    tmpnumber1 = strtol(&buf[i+1], NULL, 8);
	    buf[i] = (char) tmpnumber1;
	    while (isdigit(buf[i+1]) )
	      buf.DeleteAt(i+1,1);
	  }
	}
      _constructs.Add(buf);
    }
  }
}

void CifDataBlock::CheckIdAndItemColumns(CifDataBlock *dic)
// ----------------------------------------------------------------------
//   CheckIdAndItemColumns(): Check the column which corresponding to
//           "_category.id" and "_item._name" if they are mandatory implicit
//            and missing copy the itemValue from temperary buffer, otherwise
//            give warning message or delete the temperary buffer
// --------------------------------------------------------------------- 
{
  int i, j, k, m, ret, nvalues;
  CifItemValue *itemValue, *mandatoryValue;
  CifString itemName, itemFullName, message;


  if (_verbose) {
    cout << "Item name, Id, block_id is being checked " << endl;
    cout.flush();
  }

  // Delete datablock id for table data_block 
  i = GetIndex(CIF_DDL_CATEGORY_BLOCK);
  if (i != -1) {
    for (j =0; j<_categories[i]._items.Length(); j++) 
      if (!CompareNoCase(CIF_DDL_ITEM_DATABLOCK_ID, 
			 _categories[i]._items[j]._keyword.Text())) break;
    if (j != _categories[i]._items.Length()) 
      _categories[i]._items.DeleteAt(j, 1);
  }

  for (i=0; i< _categories.Length(); i++) {
    // Get the mandatory_code itemValue for current category
    for (j =0; j<_categories[i]._items.Length(); j++) {
      if (!strcmp(CIF_DDL_TEMP_ITEM_CATEGORY_ID, _categories[i]._items[j]._keyword.Text()) ||
	  !strcmp(CIF_DDL_TEMP_ITEM_ITEM, _categories[i]._items[j]._keyword.Text())) {
	// Get the keyword to corresponding _category.id or _item._name for this
   // particular category
	ret = dic->GetIdOrItemKeyword(_categories[i]._categoryName,
				      _categories[i]._items[j]._keyword.Text(), 
				      &itemName);
	if (ret > CIF_OUT_OF_RANGE_ERROR_CODE_START) {
	  // Can not find the name's parent is '_category.id' or '_item.item' 
  	  // from item_linkage table => the temperary buffer to hold implicit 
     // are not necessary anymore
	  _categories[i]._items.DeleteAt(j, 1);
	  j--;
	  continue;
	}
        JoinCategoryItemName(_categories[i]._categoryName, itemName, &itemFullName);
        itemFullName.ToLower();

        ret = dic->GetMandatoryCode(itemFullName,&mandatoryValue);

        if (ret == CIF_DATA_IS_VALID) {

          // mandatory_code itemValue is "no", delete the temperary buffer
	  if (!CompareNoCase(mandatoryValue->_buf.Text(), CIF_NO_TERM)) {
	    _categories[i]._items.DeleteAt(j, 1);
	    j--;
	  }
	  else {
            // mandatory_code itemValue is "implicit", copy itemValue from temperary buffer
	    for (k =0; k<_categories[i]._items.Length(); k++) 
	      if (!CompareNoCase(itemName, _categories[i]._items[k]._keyword)) break;
	      
	    if (!CompareNoCase(mandatoryValue->_buf.Text(), CIF_IMPLICIT_TERM)) {
              // All the itemValue should be extract from the save_ label
	      if (k == _categories[i]._items.Length()) {
		_categories[i]._items[j].SetKeyword(_categories[i]._categoryName.Text(),
                                                  itemName, "");
                dic->GetItemInfo(&_categories[i]._items[j]);
              }
	      
	      
              // Some _itemValues do not have to be extract from the save_label
 	      else {
                nvalues = _categories[i]._items[j].CountItemValue();
		for (m=0; m< nvalues ; m++) {
		  ret = GetItemValue(&itemValue, i, k, m);
 	          if (ret == CIF_DATA_IS_NULL) {
		    ret = GetItemValue(&itemValue, i, j, m);
	            _categories[i].UpdateItemValue(itemValue, k, m);
		    
		  }
	          else if (ret == CIF_DATA_NOT_APPROPRIATE) {
		    message.Clear();
		    message += "Value for implicit mandatory item \'";
		    message += itemFullName.Text();
		    message += "\' specified as n.a.";
		    ErrorMessage(CIF_ERROR, -1, message);
		  }
	        }
	        _categories[i]._items.DeleteAt(j, 1);
	        j--;
	      }
	    }
	    // mandatory_code itemValue is "yes", check if the itemValue is there
	    // if not, give error messages
	    else if (!CompareNoCase(mandatoryValue->_buf.Text(), CIF_YES_TERM)) {
		    _categories[i]._items.DeleteAt(j, 1);
		
	     if (k == _categories[i]._items.Length()) {
	       message += "Value for mandatory item \'";
	       message += itemFullName.Text();
	       message += "\' is missing";
	       ErrorMessage(CIF_ERROR, -1, message);
	     }
             nvalues = _categories[i]._items[k].CountItemValue();
	     for (m=0; m< nvalues ; m++) {
	       ret = GetItemValue(&itemValue, i, k, m);
	       if (ret != CIF_DATA_IS_VALID && ret != CIF_DATA_NOT_APPROPRIATE) {
		 message += "Value for mandatory item \'";
		 message += itemFullName.Text();
		 message += "\' is missing at row";
		 message += m;
		 ErrorMessage(CIF_ERROR, -1, message);
	       }
	       else if (ret == CIF_DATA_NOT_APPROPRIATE) {
		 message += "Value for mandatory item \'";
		 message += itemFullName.Text();
		 message += "\' specified as an n.a. at row";
		 message += m;
		 ErrorMessage(CIF_ERROR, -1, message);
	       }
     
	     }
	    j --;
	  }
	  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 code \'";
	    message += mandatoryValue->_buf.Text();
	    message += "\' for item \'";
	    message += itemFullName.Text() ;
	    message += "\' is not recognized";
	    ErrorMessage(CIF_ERROR, -1, message);
	    break;
	  }
	}
       }
      }
    }
  }
}

int CifDataBlock::InsertItemValue(CifDataBlock *dic, CifItemValue *itemValue, 
				   const int categoryIndex, 
				   const int itemIndex, const int rowIndex, 
				   const int verify, int lineNo)
// ----------------------------------------------------------------------
//   InsertItemValue() Gets itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int ret;
  CifItemValue *defaultValue;

  if (categoryIndex < 0 || categoryIndex >= _categories.Length())
    return CIF_CATEGORY_NOT_EXIST;
  if (itemIndex < 0 || itemIndex >= _categories[categoryIndex]._items.Length())
    return CIF_KEYWORD_NOT_EXIST;

#if !defined (linux)
  if (verify != CIF_NO_VERIFICATION ) 
    ret = VerifyItemValue(dic, itemValue, categoryIndex, itemIndex, lineNo);
#endif

  if (lineNo == -1) {
    if (itemValue->_status == CIF_DATA_IS_NULL || itemValue->_status == CIF_DATA_NOT_APPROPRIATE) {
      defaultValue = GetItemDefaultValue(categoryIndex, itemIndex);
      if (defaultValue != NULL)  
	itemValue->UpdateItemValue(itemValue->_dataType, defaultValue->_status,
				   defaultValue->_buf.Text());
    }
  }


  ret = _categories[categoryIndex].InsertItemValue(itemValue, itemIndex, rowIndex);
  if (itemValue->_status != CIF_DATA_IS_VALID && itemValue->_status != CIF_DATA_IS_NULL &&
      itemValue->_status != CIF_DATA_NOT_APPROPRIATE) return itemValue->_status;
  else return ret;
}

int CifDataBlock::InsertItemValue(CifDataBlock *dic, 
			       CifItemValue *itemValue, 
			       const CifString categoryName, 
			       const CifString itemKeyword, const int rowIndex,
			       const int verify, int lineNo)
// ----------------------------------------------------------------------
//   InsertItemValue() Gets itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int categoryIndex, itemIndex;
  categoryIndex = GetIndex(categoryName);
  if (categoryIndex == -1)
    return CIF_CATEGORY_NOT_EXIST ;
  itemIndex = _categories[categoryIndex].GetIndex(itemKeyword);
  if (itemIndex == -1)
    return CIF_KEYWORD_NOT_EXIST ;

  return InsertItemValue(dic, itemValue, categoryIndex, itemIndex, rowIndex, 
			 verify, lineNo);
}

int CifDataBlock::CheckChildrenItemValueBeforeDelete(const int categoryIndex, 
						  const int itemIndex, 
						  const int rowIndex, 
						  const int verify)
{
  int i, j, ret, ret_save, nvalues, child_category, child_item, case_sensitive;
  CifItemValue *olditemValue, *childItemValue;

  // Check if it is the last one with the itemValue to be deleted. If it is 
  // check the deleted itemValue exist in its children or not, if it does, set
  // status flag of children's _itemValues to be CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS;

  ret_save = CIF_DATA_IS_VALID;
  ret = GetItemValue(&olditemValue, categoryIndex, itemIndex, rowIndex);
  if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL &&
      ret != CIF_DATA_NOT_APPROPRIATE) {
    case_sensitive = _categories[categoryIndex]._items[itemIndex]._itemAttribute._flags._case;
    if (!ItemValueExist(olditemValue, categoryIndex, itemIndex)) {
      for (i=0; i< _categories[categoryIndex]._items[itemIndex]._linkedChildren.Length(); i++) {
        child_category = 
	  _categories[categoryIndex]._items[itemIndex]._linkedChildren[i].GetCategoryIndex();
        child_item = 
	  _categories[categoryIndex]._items[itemIndex]._linkedChildren[i].GetItemIndex();
        nvalues =	_categories[child_category]._items[child_item].CountItemValue();

        for (j=0; j< nvalues; j++) {
	  ret = GetItemValue(&childItemValue, child_category, child_item, j);
	  if (ret == CIF_DATA_IS_VALID) {
	    if (ret_save == CIF_DATA_IS_VALID) ret_save = CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS ;
            if (!case_sensitive && !CompareNoCase(childItemValue->_buf, olditemValue->_buf) ||
                case_sensitive && !Compare(childItemValue->_buf, olditemValue->_buf))  {
	      if (verify == CIF_VERIFICATION_SET_FLAG ) 
                _categories[child_category]._items[child_item]._itemValues[j]._status = 
	            CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS;
              else
                DeleteItemValue(child_category, child_item, j, verify);
            }
	  }
        }
      }
    }
  }
  return ret_save;
}

int CifDataBlock::DeleteItemValue(const int categoryIndex, 
			       const int itemIndex, const int rowIndex,
			       const int verify)
// ----------------------------------------------------------------------
//   DeleteItemValue() Gets itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int ret_save, ret;
  if (categoryIndex < 0 || categoryIndex >= _categories.Length())
    return CIF_CATEGORY_NOT_EXIST;
  if (itemIndex < 0 || itemIndex >= _categories[categoryIndex]._items.Length())
    return CIF_KEYWORD_NOT_EXIST;

  ret_save = CIF_DATA_IS_VALID;


  if (verify != CIF_NO_VERIFICATION ) {
    // Check if it is the last one with the itemValue to be deleted. If it is,
    // check the deleted itemValue exist in its children or not, if it does, set
    // status flag of children's _itemValues to be CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS;
    ret_save = CheckChildrenItemValueBeforeDelete(categoryIndex, itemIndex, rowIndex,
               verify);
  }
  ret = _categories[categoryIndex].DeleteItemValue(itemIndex, rowIndex);
  if (ret_save != CIF_DATA_IS_VALID) return ret_save;
  else return ret;
}

int CifDataBlock::DeleteItemValue(const CifString categoryName, 
			       const CifString itemKeyword, const int rowIndex,
			       const int verify)
// ----------------------------------------------------------------------
//   DeleteItemValue() Gets itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int categoryIndex,  itemIndex;
  
  categoryIndex = GetIndex(categoryName);
  if (categoryIndex == -1)
    return CIF_CATEGORY_NOT_EXIST ;
  itemIndex = _categories[categoryIndex].GetIndex(itemKeyword);
  if (itemIndex == -1)
    return CIF_KEYWORD_NOT_EXIST ;

  return DeleteItemValue(categoryIndex, itemIndex, rowIndex, verify);
}

int CifDataBlock::UpdateItemValue(CifDataBlock *dic, CifItemValue *itemValue, 
			       const int categoryIndex, 
			       const int itemIndex, const int rowIndex,
			       const int verify)
// ----------------------------------------------------------------------
//   UpdateItemValue() Gets itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int ret, ret_save, case_sensitive;
  CifItemValue *defaultValue;

  if (categoryIndex < 0 || categoryIndex >= _categories.Length())
    return CIF_CATEGORY_NOT_EXIST;
  if (itemIndex < 0 || itemIndex >= _categories[categoryIndex]._items.Length())
    return CIF_KEYWORD_NOT_EXIST;
  
  ret_save = CIF_DATA_IS_VALID;
  if (verify != CIF_NO_VERIFICATION ) {
    int i, j, nvalues, child_category, child_item;
    CifItemValue *olditemValue, *childItemValue;
    if (itemValue->_status == CIF_DATA_IS_NULL || itemValue->_status == CIF_DATA_NOT_APPROPRIATE) {
      defaultValue = GetItemDefaultValue(categoryIndex, itemIndex);
      if (defaultValue != NULL)  
	itemValue->UpdateItemValue(itemValue->_dataType, defaultValue->_status,
			   defaultValue->_buf.Text());
    }
    
#if !defined (linux)
    ret = VerifyItemValue(dic, itemValue, categoryIndex, itemIndex, FALSE);
#endif
    
    // Check children _itemValues, if children have the same old itemValue 
    // takes action according to verify
	ret = GetItemValue(&olditemValue, categoryIndex, itemIndex, rowIndex);
    
	// Check if it is the last one with the itemValue to be changed. If it is, 
	// check the old itemValue exist in its children or not, if it does, take actions
	// depends on the verification flag
	ret_save = CheckChildrenItemValueBeforeDelete(categoryIndex, itemIndex, rowIndex, 
						  verify);
    
    if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL &&
	ret != CIF_DATA_NOT_APPROPRIATE) {
      case_sensitive = _categories[categoryIndex]._items[itemIndex]._itemAttribute._flags._case;
      for (i=0; i< _categories[categoryIndex]._items[itemIndex]._linkedChildren.Length(); i++) {
	child_category = 
	  _categories[categoryIndex]._items[itemIndex]._linkedChildren[i].GetCategoryIndex();
	child_item = 
	  _categories[categoryIndex]._items[itemIndex]._linkedChildren[i].GetItemIndex();
	nvalues =	_categories[child_category]._items[child_item].CountItemValue();
	
	for (j=0; j< nvalues; j++) {
	  ret = GetItemValue(&childItemValue, child_category, child_item, j);
	  if (ret < CIF_OUT_OF_RANGE_ERROR_CODE_START && ret != CIF_DATA_IS_NULL &&
	      ret != CIF_DATA_NOT_APPROPRIATE) {
	    if ((!case_sensitive && !CompareNoCase(childItemValue->_buf, olditemValue->_buf)
		 || case_sensitive && !Compare(childItemValue->_buf, olditemValue->_buf)) &&
		!ItemValueExist(olditemValue, categoryIndex, itemIndex)) {
	      if (verify == CIF_VERIFICATION_CASCADING)
		UpdateItemValue(dic, itemValue, child_category, child_item, j, verify);
	      else 
		_categories[child_category]._items[child_item]._itemValues[j]._status =
		  CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS;
	    } 
	  }  
	}
      }
    }
  }
  ret = _categories[categoryIndex].UpdateItemValue(itemValue, itemIndex, rowIndex);

  if (ret_save != CIF_DATA_IS_VALID) return ret_save;
  else return ret;
}

int CifDataBlock::UpdateItemValue(CifDataBlock *dic, CifItemValue *itemValue, 
			       const CifString categoryName, 
			       const CifString itemKeyword, const int rowIndex,
			       const int verify)
// ----------------------------------------------------------------------
//   UpdateItemValue() Gets itemValue from _itemValues array
// --------------------------------------------------------------------- 
{
  int categoryIndex, itemIndex;

  categoryIndex = GetIndex(categoryName);
  if (categoryIndex == -1)
    return CIF_CATEGORY_NOT_EXIST ;
  itemIndex = _categories[categoryIndex].GetIndex(itemKeyword);
  if (itemIndex == -1)
    return CIF_KEYWORD_NOT_EXIST ;

  return UpdateItemValue(dic, itemValue, categoryIndex, itemIndex,
		     rowIndex, verify);

}


CifCategory *CifDataBlock::Join(CifJoin *join)
{
  CifCategory *newtable;/*, *join_table; */

  newtable = JoinTable(join);
  if (newtable != NULL) {
    newtable->JoinConditionCalculation(join);
    return newtable;
  }
  else return NULL;
}

CifCategory *CifDataBlock::JoinTable(CifJoin *join)
{
  CifCategory *newtable;
  CifDataItem  *item;
  int i, j, k, l, m, n, category_no, nvalues, noldcols, nrows;

  newtable = NULL;
  for (i=0; i<join->join_tables.Length(); i++) {
    category_no = GetIndex(join->join_tables[i]);
    if (category_no == -1) {
      if (newtable != NULL) delete newtable;
      return NULL;
    }
    if (category_no != -1) {
      if (newtable == NULL) 
	newtable = new CifCategory;
      if (i != 0)
	newtable->_categoryName += CIF_DATA_JOIN_CHAR ;
      newtable->_categoryName += _categories[category_no]._categoryName;

      noldcols = newtable->_items.Length();
      if (newtable->_items.Length() != 0)
	nvalues = newtable->CountRows();
      else nvalues = 1;

      item = new CifDataItem;
      for (j=0; j<_categories[category_no]._items.Length(); j++) {
	if (join->selection_item.Length()>0 && join->selection_category.Length()>0) {
	  l = join->conditions.Length();
	  for (k=0; k <join->selection_category.Length(); k++)
	    if (k < join->selection_item.Length() && 
		!CompareNoCase(join->selection_category[k],
			 _categories[category_no]._categoryName) &&
		!CompareNoCase(join->selection_item[k],
			 _categories[category_no]._items[j]._keyword))
	      break;
	  if (k == join->selection_category.Length()) {
	    for (l =0; l<join->conditions.Length(); l++) {
	      if (!CompareNoCase(join->conditions[l].left_category,
				 _categories[category_no]._categoryName) &&
		  !CompareNoCase(join->conditions[l].left_item,
				 _categories[category_no]._items[j]._keyword) ||
		  join->conditions[l].right_category != NULL && 
		  join->conditions[l].right_item != NULL &&
		  !CompareNoCase(*join->conditions[l].right_category,
			   _categories[category_no]._categoryName) &&
		  !CompareNoCase(*join->conditions[l].right_item,
			   _categories[category_no]._items[j]._keyword) ) break;
	    }
	  }
	}
	if (join->selection_item.Length()==0 || join->selection_category.Length() ==0 ||
	    k != join->selection_category.Length() ||
	    l != join->conditions.Length()) {

	  item->_itemAttribute._dataType = _categories[category_no]._items[j]._itemAttribute._dataType;
	  JoinCategoryItemName(_categories[category_no]._categoryName,
			       _categories[category_no]._items[j]._keyword, 
			       &item->_keyword);
	  item->SetCaseSensitivity(_categories[category_no]._items[j]._itemAttribute._flags._case);
	  newtable->_items.Add(*item);
	}
      }
      delete item;

      for (j=0; j< newtable->_items.Length(); j++) {
	nrows = _categories[category_no].CountRows();
	for (k=0; k< nrows ; k++) {
	  for (m=0; m< nvalues ; m++) {
	    if (j >= noldcols)  {
	      for (l=0; l<newtable->_items[j]._keyword.Length(); l++)
		if (newtable->_items[j]._keyword.Text()[l] == CIF_DATA_JOIN_CHARACTER) break;
	      for (n=0; n<_categories[category_no]._items.Length(); n++) 
		if (!CompareNoCase(_categories[category_no]._categoryName.Text(),
			     (char *) &newtable->_items[j]._keyword.Text()[1], l-1) &&
		    !CompareNoCase(_categories[category_no]._items[n]._keyword.Text(),
			     (char *) &newtable->_items[j]._keyword.Text()[l+1])) break;
	      if (n != _categories[category_no]._items.Length())
		newtable->_items[j].AppendItemValue(&_categories[category_no].
					       _items[n]._itemValues[k]);
	    }
	    else if (k != 0 ) 
	      newtable->_items[j].AppendItemValue(&newtable->_items[j]._itemValues[m]);
	  }
	}
      }
    }
  }
  return newtable;
}

ReVarCifArray <CifString> *CifDataBlock:: BuildCategoryKeys(CifString categoryName)
{
  ReVarCifArray <CifString> *keys;
  int i, nrows, ret, keyIndex;
  CifString tmpcategory(categoryName), *itemValue, name, key;
  CifItemValue  *matchedValue;
  CifString idField(CIF_DDL_ITEM_ID), categoryKey(CIF_DDL_CATEGORY_CATEGORY_KEY);
  CifString nameField(CIF_DDL_ITEM_NAME); 
  CifCategory *keyTable;
  CifString _datablock(CIF_DDL_CATEGORY_BLOCK);
  CifString _datablock_id(CIF_DDL_ITEM_DATABLOCK_ID);

  tmpcategory.ToLower();
  keys = NULL;

  matchedValue = new CifItemValue(FALSE, tmpcategory);
  keyTable = GetItemAttribute(matchedValue, idField, categoryKey, nameField);
  if (keyTable != NULL) {
    keys = new ReVarCifArray<CifString>;
    nrows = keyTable->CountRows();
    JoinCategoryItemName(CIF_DDL_CATEGORY_CATEGORY_KEY, CIF_DDL_ITEM_NAME,
			 &name);
    keyIndex = keyTable->GetIndex(name);
    for (i=0; i< nrows; i++) {
      ret = keyTable->GetItemValue(&itemValue, keyIndex, i);
      if (ret == CIF_DATA_IS_VALID) {
	key.Copy(&itemValue->Text()[categoryName.Length()+2]);
	keys->Add(key);
      }
    }
    delete keyTable;
  }
  if (CompareNoCase(categoryName, _datablock)) {
    if (keys == NULL) keys = new ReVarCifArray<CifString>;
    key.Copy(_datablock_id);
    keys->Add(key);
  }
  if (matchedValue) delete(matchedValue);
  return keys;
}

ReVarCifArray <CifItemValue> *CifDataBlock::GetColumnItemValues(const int categoryIndex,
							      const int itemIndex)
{
  ReVarCifArray <CifItemValue> *columnValues;
  columnValues = NULL;

  if (categoryIndex >= 0 && categoryIndex < _categories.Length()) {
    columnValues = _categories[categoryIndex].GetColumnItemValues(itemIndex);
  }
  return columnValues;
}
ReVarCifArray <CifItemValue> *CifDataBlock::GetColumnItemValues(const CifString categoryName,
							      const CifString itemKeyword)
{
  ReVarCifArray <CifItemValue> *columnValues;
  int categoryIndex, itemIndex;

  columnValues = NULL;

  categoryIndex = GetIndex(categoryName);
  if (categoryIndex != -1) {
    itemIndex = _categories[categoryIndex].GetIndex(itemKeyword);
    columnValues = GetColumnItemValues(categoryIndex, itemIndex);
  }
  return columnValues;
}

CifCategory *CifDataBlock::GetItemAttribute(const CifItemValue *match_itemValue,
					    const CifString matchedItemKeyword,
					    const CifString categoryName,
					    const CifString itemKeyword)
{
  CifJoin join;
  CifCategory *newtable;

  join.DeleteElement();
  join.AddSelection(categoryName, itemKeyword);
  join.AddJoinTable(categoryName);
  join.AddJoinCondition(CIF_JOP_AND, categoryName, matchedItemKeyword, 
			CIF_OP_EQUAL, match_itemValue);
  newtable = Join(&join);
  return newtable;
}

void CifDataBlock::RefineDataBlock(CifDataBlock *dic, const int verify)
{
  CifString *title;
  title = dic->GetDictionaryTitle();
  if (title == NULL) {
    _dictionaryName.Copy(dic->_dataBlockName);
    _dictionaryName.RemoveBlanks();
  }
  else
    _dictionaryName.Copy(title);
  title = dic->GetDictionaryVersion();
  if (title != NULL) {
    _dictionaryVersion.Copy(title);
    _dictionaryVersion.RemoveBlanks();
  }
  else _dictionaryVersion.Clear();

  if (dic == this)
    CheckIdAndItemColumns(dic);

  BuildItemTypeListConstruct();
  BuildNameAttributes();
  // DDL
  if (dic == this) {
    if (_verbose) {
      cout << "Getting item structure, item case-sensitive " << endl;
      cout.flush();
    }
    GetDDLItemInfo(dic, verify);
  }
  if (verify) {
    if (_verbose) {
      cout << "Building parents and children list" << endl;
      cout.flush();
    }
    BuildParentsChildrenLinkList(dic);

    if (_verbose) {
      cout << "Building item_dependent list" << endl;
      cout.flush();
    }
    BuildDependentItems(dic);
    

  }
  if (verify) CheckDataBlock(dic, TRUE);

  // Add esd values after replace default value with null values
  if (_verbose) {
    cout << "Building item_related list" << endl;
    cout.flush();
  }
  BuildRelatedItems(dic);

}

void CifDataBlock::CheckDataBlock(CifDataBlock *dic, const int fromFile)
{

  int i, j, k;
  CifItemValue *defaultValue;

  CheckParentsChildrenRelation();

  if (_verbose) {
    cout << "Replacing null values with default values" << endl;
    cout.flush();
  }
  for (i=0; i< _categories.Length(); i++) {
    for (j=0; j<_categories[i]._items.Length(); j++) {
      defaultValue = dic->GetItemDefaultValue(_categories[i]._items[j]._itemAttribute._name);
      for (k=0; k<_categories[i].CountRows(); k++) {
	if (defaultValue != NULL && 
	    (_categories[i]._items[j]._itemValues[k]._status  == CIF_DATA_NOT_APPROPRIATE
	      || _categories[i]._items[j]._itemValues[k]._status == CIF_DATA_IS_NULL)) 
	  _categories[i]._items[j]._itemValues[k].
	    UpdateItemValue(_categories[i]._items[j]._itemAttribute._dataType,
			    defaultValue->_status,
			    defaultValue->_buf.Text());
      }
    }
  }

  // Remove blanks from _item_enumeration.value
  int enumeration_index = dic->GetIndex(CIF_DDL_CATEGORY_ITEM_ENUMERATION);

  if (enumeration_index >= 0 && enumeration_index < _categories.Length()) {

    int value_index = _categories[enumeration_index].GetIndex(CIF_DDL_ITEM_VALUE);
    if (value_index > 0) {
      
      int nvalues = CountRows(enumeration_index);
      for (i=0; i<nvalues; i++) {
	_categories[enumeration_index]._items[value_index]._itemValues[i]._buf.RemoveBlanks();
	if (_categories[enumeration_index]._items[value_index]._itemValues[i]._status == CIF_DATA_IS_VALID &&
	    _categories[enumeration_index]._items[value_index]._itemAttribute._dataType == CIF_STRING_VALUE &&
	    _categories[enumeration_index]._items[value_index]._itemValues[i]._data._strVal != NULL)
	  _categories[enumeration_index]._items[value_index]._itemValues[i]._data._strVal->RemoveBlanks();
      }
    }
  }
  // Check key values
  if (!fromFile) 
    for (i=0; i< _categories.Length(); i++) 
      _categories[i].CheckKeys(&_categories[i], &_categories[i]._keys, this,  -1);
      
}

#if !defined (linux)
int CifDataBlock::VerifyItemValue(CifDataBlock *dic, CifItemValue *itemValue, 
				   const int categoryIndex, const int itemIndex, 
				   const int lineNo)
{
  int ret = 1;
  ReVarCifArray <CifString> *parents;

  if (itemValue->_status == CIF_DATA_IS_NULL || itemValue->_status == CIF_DATA_NOT_APPROPRIATE)
    return SUCCEED;
  if (lineNo == -1 || dic != this) {
    ret = ItemValueConstructCheck(dic, itemValue, categoryIndex, itemIndex, lineNo);
    if (ret == FALSE) {
      itemValue->_flags._expression = FALSE;
      itemValue->_status =  CIF_VALUE_DOES_OBEY_REGULAR_EXPRESSION;
      return ret;
    }
  }

  if (ret == SUCCEED && lineNo == -1)  {
    ret = ItemValueInParents(itemValue, categoryIndex, itemIndex);
    if (!ret) {
      itemValue->_status =  CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS ;
      itemValue->_flags._valueInParent = FALSE;
    }
  }

  if (ret == SUCCEED) {
    parents = dic->GetParentsName(_categories[categoryIndex]._items[itemIndex].
				  _itemAttribute._name);
      
    if (_categories[categoryIndex]._items[itemIndex]._itemAttribute._dataType == CIF_STRING_VALUE)  {
      ret = ItemValueInEnumerationList(dic, parents, itemValue, categoryIndex, itemIndex, lineNo);
      if (!ret) {
	itemValue->_status = CIF_VALUE_NOT_IN_ENUMERATION_LIST   ;
	itemValue->_flags._range = FALSE;
      }
    }
    else if (itemValue->_dataType == CIF_INTEGER_VALUE) {
      ret = ItemValueInItemRange(dic, parents, (double) itemValue->_data._intVal, categoryIndex, itemIndex, lineNo);
      if (!ret) {
	itemValue->_status = CIF_VALUE_OUT_OF_ITEM_RANGE ;
	itemValue->_flags._range = FALSE;
      }
    }
    else {
      ret = ItemValueInItemRange(dic, parents, itemValue->_data._dblVal, categoryIndex, itemIndex, lineNo);
      if (!ret) {
	itemValue->_status = CIF_VALUE_OUT_OF_ITEM_RANGE ;
	itemValue->_flags._range = FALSE;
      }
    }
    if (parents != NULL) delete parents;
  }

  // Check if the itemValue is in the parent
  return ret;

}
#endif

int CifDataBlock::ItemValueInParents(const CifItemValue *itemValue,
				      const int categoryIndex, const int itemIndex)
{
  int parent_category, parent_item, i, ret;
  int not_in_one_parent, not_in_all_parents;
  CifString message;

  if (categoryIndex < 0 || categoryIndex >= _categories.Length()) 
    return CIF_CATEGORY_NOT_EXIST;
  else if (itemIndex < 0 || itemIndex >= _categories[categoryIndex]._items.Length())
    return CIF_KEYWORD_NOT_EXIST;

  not_in_all_parents = TRUE;
  not_in_one_parent = FALSE;
  for (i=0; i<_categories[categoryIndex]._items[itemIndex]._linkedParents.Length(); i++) {
    parent_category = _categories[categoryIndex]._items[itemIndex]._linkedParents[i].GetCategoryIndex();
    parent_item     = _categories[categoryIndex]._items[itemIndex]._linkedParents[i].GetItemIndex();
    if (parent_category != -1 && parent_item != -1) {
      ret = ItemValueExist(itemValue, parent_category, parent_item);
      if (ret == FALSE) {
	message += "Missing the parent item \'";
	message += _categories[parent_category]._items[parent_item]._itemAttribute._name.Text();
	message += "\' of item \'";
	message += _categories[categoryIndex]._items[itemIndex]._itemAttribute._name.Text(),
	message += "\' with value \'";
	message += itemValue->_buf.Text();
	message += "\'";
	ErrorMessage(CIF_WARNING, -1, message);
      }
      if (ret == TRUE) not_in_all_parents = FALSE;
      else             not_in_one_parent = TRUE;
    }
  }
  if (not_in_all_parents && 
      _categories[categoryIndex]._items[itemIndex]._linkedParents.Length() != 0) {
    message.Clear();
    message += "Value \'";
    message += itemValue->_buf.Text();
    message += "\' of item \'";
    message += _categories[categoryIndex]._items[itemIndex]._itemAttribute._name.Text(),
    message += "\' does not exist in any parent item";
    ErrorMessage(CIF_ERROR, -1, message);
    return CIF_VALUE_NOT_EXIST_IN_LINK_PARENTS;
  }
  else if (not_in_one_parent)
    return CIF_VALUE_NOT_EXIST_IN_ONE_LINK_PARENT;
  else return TRUE;

}
int CifDataBlock::ItemValueInEnumerationList(CifDataBlock *dic, 
					      ReVarCifArray <CifString> *parents,
					      const CifItemValue *inItemValue,
					      const int categoryIndex, 
					      const int itemIndex, 
					      const int lineNo)
{
  int i,parent = -1, nvalues, enumeration_index, ret, value_index, nameIndex, found;
  CifItemValue *cifitemValue;
  CifString *nameValue, itemValue;
  CifString message;

  if (categoryIndex < 0 || categoryIndex >= _categories.Length()) 
    return FALSE;
  else if (itemIndex < 0 || itemIndex >= _categories[categoryIndex]._items.Length())
    return  FALSE;

  enumeration_index = dic->GetIndex(CIF_DDL_CATEGORY_ITEM_ENUMERATION);

  if (enumeration_index < 0 || enumeration_index >= dic->_categories.Length()) 
    return TRUE;
  nameIndex = dic->_categories[enumeration_index].GetIndex(CIF_DDL_ITEM_NAME);
  value_index = dic->_categories[enumeration_index].GetIndex(CIF_DDL_ITEM_VALUE);
  if (nameIndex < 0 || value_index < 0) return TRUE;

  itemValue.Copy(inItemValue->_buf);
  itemValue.RemoveBlanks();
  nvalues = dic->CountRows(enumeration_index);
  found = FALSE;
  for (i=0; i<nvalues; i++) {
    ret = dic->GetItemValue(&nameValue, enumeration_index, nameIndex, i);
    if (ret == CIF_DATA_IS_VALID) {
      if (parents != NULL) {
	for (parent=0; parent < parents->Length(); parent++) 
	  if (!CompareNoCase((*parents)[parent], *nameValue) ) break;
	
	if (parent == parents->Length()) parent = -1;
      }
      else parent = -1;

      if (!Compare(*nameValue, _categories[categoryIndex]._items[itemIndex]._itemAttribute._name) 
	  || parent != -1){
	found = TRUE;
	ret = dic->GetItemValue(&cifitemValue, enumeration_index, value_index, i);

	if (ret == CIF_DATA_IS_VALID) {
	  if (!Compare(cifitemValue->_buf, itemValue) ||
	      !_categories[categoryIndex]._items[itemIndex]._itemAttribute._flags._case
	      && !CompareNoCase(cifitemValue->_buf, itemValue) )
	    return TRUE;
	}
      }
    }
  }
  if (found) {
    message.Clear();
    message += "Value \'";
    message += inItemValue->_buf.Text();
    message += "\' of item \'";
    message += _categories[categoryIndex]._items[itemIndex]._itemAttribute._name.Text();
    message += "\' is not in enumeration list";
    ErrorMessage(CIF_ERROR, lineNo, message);
    return FALSE;
  }
  else       return TRUE;
}

int CifDataBlock::ItemValueInItemRange(CifDataBlock *dic,
					ReVarCifArray <CifString> *parents,
					const double itemValue,
					const int categoryIndex, 
					const int itemIndex,
					const int lineNo)
{
  int nvalues, i, parent, range_index, maximum_index, minimum_index, nameIndex;
  int found, maxRet, minRet, ret;
  double maxRange, minRange;
  CifItemValue *minItemValue, *maxItemValue;
  CifString *nameItemValue;
  CifString message;
  
  if (categoryIndex < 0 || categoryIndex >= _categories.Length()) 
    return  FALSE;
  else if (itemIndex < 0 || itemIndex >= _categories[categoryIndex]._items.Length())
    return   FALSE;

  range_index = dic->GetIndex(CIF_DDL_CATEGORY_ITEM_RANGE);

  if (range_index < 0 || range_index >= dic->_categories.Length()) 
    return   TRUE;
  nameIndex = dic->_categories[range_index].GetIndex(CIF_DDL_ITEM_NAME);
  maximum_index = dic->_categories[range_index].GetIndex(CIF_DDL_ITEM_MAXIMUM);
  minimum_index = dic->_categories[range_index].GetIndex(CIF_DDL_ITEM_MINIMUM);
  if (nameIndex < 0 || maximum_index < 0 || minimum_index < 0) 
    return  TRUE;

  found = FALSE;
  nvalues = dic->CountRows(range_index);
  maxRange = CIF_NONSENSE_DOUBLE;
  minRange = -CIF_NONSENSE_DOUBLE;
  for (i=0; i<nvalues; i++) {
    ret = dic->GetItemValue(&nameItemValue, range_index, nameIndex, i);
    if (ret == CIF_DATA_IS_VALID) {
      if (parents != NULL) {
	for (parent=0; parent < parents->Length(); parent++) 
	  if (!CompareNoCase((*parents)[parent], *nameItemValue) ) break;
	
	if (parent == parents->Length()) parent = -1;
      }
      else parent = -1;

      if (!Compare(*nameItemValue, _categories[categoryIndex]._items[itemIndex]._itemAttribute._name) 
	  || parent != -1) {
	found = TRUE;
	maxRange = CIF_NONSENSE_DOUBLE;
	minRange = -CIF_NONSENSE_DOUBLE;
	maxRet = dic->GetItemValue(&maxItemValue, range_index, maximum_index, i);
	minRet = dic->GetItemValue(&minItemValue, range_index, minimum_index, i);
	if (maxRet == CIF_DATA_IS_VALID)
	  maxRange = atof(maxItemValue->_buf.Text());
	if (minRet == CIF_DATA_IS_VALID)
	  minRange = atof(minItemValue->_buf.Text());
	// Equal
	if (maxRet == CIF_DATA_IS_VALID && minRet == CIF_DATA_IS_VALID &&
	    minRange == maxRange && itemValue == minRange)
	  return TRUE;
	else if (maxRet == CIF_DATA_IS_VALID && itemValue < maxRange &&
		 (minRet == CIF_DATA_IS_VALID && itemValue > minRange ||
		  minRet != CIF_DATA_IS_VALID) ||
		 maxRet != CIF_DATA_IS_VALID &&
		 minRet == CIF_DATA_IS_VALID && itemValue > minRange)
	  return TRUE;
      }
    }
  }
  if (found) {
    message.Clear();
    message += "Value \'";
    message += itemValue;
    message += "\' of item \'";
    message += _categories[categoryIndex]._items[itemIndex]._itemAttribute._name.Text();
    message += "\' is out of item range";
    ErrorMessage(CIF_ERROR, lineNo, message);
    return FALSE;
  }
  else return TRUE;
}

#if !defined (linux)
int CifDataBlock::ItemValueConstructCheck(CifDataBlock *dic,
					   CifItemValue *itemValue, 
					   const int categoryIndex, const int itemIndex, 
					   const int lineNo)
{

#define NS 1
  regex_t preg;
  regmatch_t pmatch[NS];
  int icmp, len; //new regex from regex-v2
  int rowno, ret;
  CifString message;

  if (categoryIndex < 0 || categoryIndex >= _categories.Length())
    return CIF_CATEGORY_NOT_EXIST;
  if (itemIndex < 0 || itemIndex >= _categories[categoryIndex]._items.Length())
    return CIF_KEYWORD_NOT_EXIST;

  rowno = _categories[categoryIndex]._items[itemIndex]._itemAttribute._constructIndex;

  if ((rowno < 0 || rowno >= dic->_constructs.Length())) {
    message.Clear();
    message += CIF_DDL_CATEGORY_ITEM_TYPE;
    message += '.';
    message += CIF_DDL_ITEM_CODE;
    message += " ";
    message += " for item \'";
    message += _categories[categoryIndex]._items[itemIndex]._itemAttribute._name.Text();
    if (_categories[categoryIndex]._items[itemIndex]._alias != NULL) {
      message += "\' aliasname \'";
      message += _categories[categoryIndex]._items[itemIndex]._alias->GetAliasName()->Text();
    }
    message += "\' is not recognized";
    ErrorMessage(CIF_ERROR, lineNo, message);
    return SUCCEED;
  }

  icmp = regcomp(&preg,dic->_constructs[rowno].Text(),REG_EXTENDED);
    
  if (icmp == 0) {
    ret = regexec(&preg,itemValue->_buf.Text(),NS,pmatch,0);
    if (ret != 0) {
      message.Clear();
      message += "Value \'";
      message += itemValue->_buf.Text();
      message += "\' of item \'";
      message += _categories[categoryIndex]._items[itemIndex]._itemAttribute._name.Text();
      message += "\' does not match regular expression ";
      message += dic->_constructs[rowno].Text();
      message += " ";
      message += ret;
      message += " \'";
      message +=  itemValue->_buf.Text()[ret];
      message += '\'';
	ErrorMessage(CIF_ERROR, lineNo, message);
      return FAILURE;
    }
    else {
      len = pmatch[0].rm_eo - pmatch[0].rm_so;
      if (len != itemValue->_buf.Length()) {
	message.Clear();
	message += "Value \'";
	message += itemValue->_buf.Text();
	message += "\' of item \'";
	message += _categories[categoryIndex]._items[itemIndex]._itemAttribute._name.Text();
	message += "\' does not match regular expression ";
	message += dic->_constructs[rowno].Text();
	message += " ";
	message += len;
	message += " \'";
	message +=  itemValue->_buf.Text()[len];
	message += '\'';
	ErrorMessage(CIF_ERROR, lineNo, message);
	return FAILURE;
      }
    }
    regfree(&preg);
  }
  else  {
    message.Clear();
    message += "Regular expression compilation error \'";
    message += "\' category item_type_list row ";
    message += rowno;
    dic->ErrorMessage(CIF_ERROR, lineNo, message);
    return FAILURE;
  }
  return SUCCEED;
}
#endif

CifString *CifDataBlock:: GetDictionaryVersion()
{
  int dic_index, version_index;

  dic_index = GetIndex(CIF_DDL_CATEGORY_DICTIONARY);  
  if (dic_index == -1 || _categories[dic_index].CountRows() <1) return (CifString *) NULL;
 
  version_index = _categories[dic_index].GetIndex(CIF_DDL_ITEM_VERSION); 
  if (version_index == -1) return (CifString *) NULL;
  else return &_categories[dic_index]._items[version_index]._itemValues[0]._buf;
}

CifString *CifDataBlock:: GetDictionaryTitle()
{
  int dic_index, title_index;

  dic_index = GetIndex(CIF_DDL_CATEGORY_DICTIONARY);  
  if (dic_index == -1 || _categories[dic_index].CountRows() <1) return (CifString *) NULL;
 
  title_index = _categories[dic_index].GetIndex(CIF_DDL_ITEM_TITLE); 
  if (title_index == -1) return (CifString *) NULL;
  else return &_categories[dic_index]._items[title_index]._itemValues[0]._buf;
}


CifCategory *CifDataBlock::AddCategory(CifDataBlock *dic, 
						    const char *categoryName)
{
  CifCategory *category;
  CifItemValue itemValue;
  ReVarCifArray <CifString> *keys;

  int categoryIndex, idIndex;
  CifString message;

  if (GetIndex(categoryName) != -1) {
    message.Clear();
    message += "Category name \'";
    message += categoryName;
    message += "\' already exists in the current datablock \'";
    message += _dataBlockName;
    message += "\'";
    ErrorMessage(CIF_ERROR, -1,  message.Text());
	    
    return (CifCategory *) NULL;
  }
  categoryIndex = dic->GetIndex(CIF_DDL_CATEGORY_CATEGORY);

  if (categoryIndex != -1)
    idIndex = dic->_categories[categoryIndex].GetIndex(CIF_DDL_ITEM_ID);
  if (categoryIndex == -1 || idIndex == -1) return NULL;

  itemValue.UpdateItemValue(CIF_STRING_VALUE, CIF_DATA_IS_VALID, categoryName); 
  if (dic->ItemValueExist(&itemValue, categoryIndex, idIndex)) {
    category = new CifCategory;
    category->SetCategoryName(categoryName);
    category->_readInOrder = _categories.Length()+1;
    if (!CompareNoCase(categoryName, CIF_DDL_CATEGORY_BLOCK ))
      category->AddDataItem(dic, this, CIF_DDL_ITEM_ID);
    else
      category->AddDataItem(dic, this, CIF_DDL_ITEM_DATABLOCK_ID);
    keys = dic->BuildCategoryKeys(categoryName);

    
    if (keys != NULL) {
      category->_keys = *keys;
      delete keys;
    }
    _categories.Add(*category);
    delete category;
    return &_categories[_categories.Length()-1];
  }
  else {
    message.Clear();
    message += "Category name \'";
    message += categoryName;
    message += "\' does not exist in dictionary \'";
    message += dic->_dataBlockName;
    message += "\'";
    ErrorMessage(CIF_ERROR, -1,  message.Text());
	    
    return (CifCategory *) NULL;
  }

}
int CifDataBlock::AddRow(const int categoryIndex)
// ----------------------------------------------------------------------
//   AddRow() Gets itemValue from itemValues array
// --------------------------------------------------------------------- 
{
  int i;
  if (categoryIndex < 0 || categoryIndex >= _categories.Length())
    return -1;
  
  CifItemValue *itemValue;

  for (i=0; i< _categories[categoryIndex]._items.Length(); i++) {
    if (!CompareNoCase(_categories[categoryIndex]._categoryName.Text(),
		       CIF_DDL_CATEGORY_BLOCK)	&&
	!CompareNoCase(_categories[categoryIndex]._items[i]._keyword.Text(),
		       CIF_DDL_ITEM_ID) ||
	CompareNoCase(_categories[categoryIndex]._categoryName.Text(),
		      CIF_DDL_CATEGORY_BLOCK)	&&
	!CompareNoCase(_categories[categoryIndex]._items[i]._keyword.Text(),
		       CIF_DDL_ITEM_DATABLOCK_ID))
      itemValue = new CifItemValue(_categories[categoryIndex]._items[i]._itemAttribute._dataType,
				   _categories[categoryIndex]._items[i]._itemAttribute._flags._case,
				   CIF_DATA_IS_VALID,
				   _dataBlockName.Text());
    else
      itemValue = new CifItemValue(_categories[categoryIndex]._items[i]._itemAttribute._dataType,
				   _categories[categoryIndex]._items[i]._itemAttribute._structureNumValue,
				   _categories[categoryIndex]._items[i]._itemAttribute._flags._case);

    _categories[categoryIndex]._items[i]._itemValues.Add(*itemValue);
    delete itemValue;
  }
  return _categories[categoryIndex]._items.Length()-1;
}


void CifDataBlock::ErrorMessage(const int errorFlag, 
				 const int lineNo, const char *message)
{
  CifString error;
  if (errorFlag == CIF_ERROR)
    error.Copy("ERROR: ");
  else if (errorFlag == CIF_WARNING)
    error.Copy("Warning: ");
  
  if (strcmp(_fileName.Text(),"")) {
    if (lineNo != -1) {
      error +=  "File[";
      error += _fileName;
      error += "(line ";
      error += lineNo;
      error += ")";
      error += "] ";
    }
    else {
      error += "File(" ;
      error += _fileName;
      error += ") ";
    }
  }
  
  error += "Datablock(" ;
  error += _dataBlockName;
  error += ") " ;
  if (message != NULL) 
    error += '"';
  error += message;
  error += '"';

  if (errorFlag == CIF_ERROR)
    _errors.Add(error);
  else if (errorFlag == CIF_WARNING)
    _warnings.Add(error);

}


CifDataItem *CifDataBlock::AddDataItem(CifDataBlock *dic,
					  const int categoryIndex, 
					  const char *itemName)
// ----------------------------------------------------------------------
//   AddItemValue() Gets itemValue from itemValues array
// --------------------------------------------------------------------- 
{
  CifDataItem *item;
  if (categoryIndex < 0 || categoryIndex >= _categories.Length())
    return NULL;
  item = _categories[categoryIndex].AddDataItem(dic, this, itemName);
  BuildParentsChildrenLinkList(dic);
  BuildDependentItems(dic);
  BuildRelatedItems(dic);
  return item;
}

