// file: $isip/class/mmedia/XMLParser/xp_04.cc
// version: $Id: xp_04.cc 10284 2005-10-27 20:52:14Z wholland $

#if defined(HAVE_EXPAT)

// ISIP include files
//
#include "XMLParser.h"

// method: markUnhandledTokens
//
// arguments: none
//
// return: a boolean indicating status
//
// this method checks a vector of strings containing
// the names of tokens which are allowed for a given
// application, valid_values_d. All tokens whose values
// are not in this vector are set to type UNHANDLED*. 
//
// note that this does not directly apply to cdata type
// token. whether a cdata token is handled depends
// on whether the tags it's nested in are handled.
// as long as a CDATA token is not nested in unhandled tags,
// it is considered handled.
//
boolean XMLParser::markUnhandledTokens() {
  
  // loop over all tokens in the token_vector_d
  //
  for(long i=0; i < token_vector_d.length(); i++) {
    
    // if a previous token exists, get its type
    //
    XMLToken::TYPE previous_token_type = XMLToken::NULL_TAG;
    if(i - 1 > 0) {
      previous_token_type = token_vector_d(i-1)
	.getType();
    }
    
    // also, all CDATA tags contained within unhandled tags are considered
    // unhandled
    //
    if(token_vector_d(i).isA(XMLToken::CDATA) &&
       previous_token_type == XMLToken::UNHANDLED) {
      
      // set the type of the token to UNHANDLED
      //
      token_vector_d(i).setType(XMLToken::UNHANDLED);	
    }
    // if the value of the token is not present in the valid
    // values list, then it is unhandled
    //
    else if(!valid_values_d.contains
	    (token_vector_d(i).getPointerToValue()) &&
	    !token_vector_d(i).isA(XMLToken::CDATA)) {
      
      // set the type of the token to UNHANDLED
      //
      token_vector_d(i).setType(XMLToken::UNHANDLED);
    }
    
  } // end looping over token vector
  
  // indicate success
  //
  return true;
}

// method: removeUnhandledTokens
//
// arguments: none
//
// return: a boolean indicating status
//
// this method removes all unhandled tokens and recomputes
// the depths of all tokens stored in the parser.
//
boolean XMLParser::removeUnhandledTokens() {
  
  // make sure that cdata content of the unhandled
  // tokens is removed as well
  //
  markUnhandledTokens();
  
  // a vector to store the modified xml document,
  // which will only contain handled tags
  //
  Vector<XMLToken> handled_vector;
  
  // loop over all tokens contained in the token vector
  //
  for(long i = 0; i < token_vector_d.length(); i++) {
    
    // if the token is not an unhandled one, copy it to the new
    // vector of handled tokens
    //
    if(token_vector_d(i).getType() != XMLToken::UNHANDLED) {
      handled_vector.concat(token_vector_d(i));
    }
  }
  
  // reset the depths of the tokens
  //
  setTokenDepths(handled_vector);
  
  // set the new vector of handled tokens as the token_vector_d
  //
  setTokenVector(handled_vector);
  
  // indicate success
  //
  return true;
}

// method: setTokenDepths
//
// arguments: none
//
// return: a boolean indicating status
//
// this method calculates and sets the depths of all xml
// tokens within the token_vertex_vector_d
//
boolean XMLParser::setTokenDepths() {
  
  // call the master method
  //
  XMLParser::setTokenDepths(getTokenVector());
  
  // build a table of debugging information
  //
  if(debug_level_d > Integral::DETAILED) {
    debugTokenDepths(getTokenVector());
  }
  
  // indicate success
  //
  return true;
}

// method: setTokenDepths
//
// arguments:
//   Vector<XMLToken> token_vector: (input) a vector of XMLTokens
//     representing an XML document.
//
// returns: a boolean indicating status
//
// this method loops over all tokens in the vector. When it
// encounters a start tag, the depth of that start tag and
// of all following tags is increased by one. When it
// encounters an end tag, the depth of that end tag and of all
// following tags is decreased by one. Unhandled tags, combined
// start and end tags, and CDATA tags do not affect the depth.
//
const boolean XMLParser::setTokenDepths(Vector<XMLToken>& token_vector_a) {
  
  // declare a variable to store the current depth in the document
  //
  long token_depth = 0;
  
  // loop over all tokens in the vector
  //
  for(long i=0; i < token_vector_a.length(); i++) {
    
    // start tags increase the depth
    //
    if(token_vector_a(i).isA(XMLToken::START_TAG)) {
      token_vector_a(i).setDepth(++token_depth);
    }
    // end tags decrease the depth
    //
    else if (token_vector_a(i).isA(XMLToken::END_TAG)) {
      token_vector_a(i).setDepth(--token_depth);
    }
    // if this token is of type CDATA, UNHANDLED*, or
    // START_AND_END_TAG
    //
    else {
      token_vector_a(i).setDepth(token_depth);
    }
  }
  
  // indicate success
  //
  return true;
}

// method: debugTokenDepths
//
// arguments:
//   Vector<XMLToken> token_vector: (input) a vector of xml
//     tokens (any xml document)
//
// return: a boolean indicating status
//
// this method outputs the token vector, alongside the token
// indices and token depths
//
const boolean XMLParser::debugTokenDepths(Vector<XMLToken>& token_vector_a) {
  
  // declare a variable to store the output depth of the token
  // (this is almost, but not quite the same as the conceptual
  // depth of the token)
  //
  long token_depth = 0;
  
  // strings to store debugging information
  //
  SysString column1;
  SysString column2;
  SysString column3;
  SysString header(L"XMLParser: parsed token information:\n");
  header.concat(L"|input |depth | xml form of input");
  SysString output(header);
  
  for(long j = 0; j < token_vector_a.length(); j++) {
    token_depth = token_vector_a(j).getDepth();
    column1.assign(j, L"%-6d");
    column2.assign(token_depth, L"%-6d");
    
    if(token_vector_a(j).isA(XMLToken::START_TAG)) { 
      
      // adjust the depth for proper display (visual depth is different
      // from logical depth)
      //
      token_depth--;
    }
    column3.assign(getIndent(token_depth));
    column3.concat(token_vector_a(j).toXML());
    
    output.concat(L"\n|");
    output.concat(column1);
    output.concat(L"|");
    output.concat(column2);
    output.concat(L"|");
    output.concat(column3);
  }
  
  // output the table
  //
  Console::put(output);
  
  // indicate success
  //
  return true;
}

#endif
