00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifdef _MSC_VER
00021 #include "stdafx.h"
00022 #else
00023 #include "config.h"
00024 #endif
00025 #include "CallStack.h"
00026
00027 #include "DataDictionary.h"
00028 #include "Message.h"
00029 #include <fstream>
00030 #include <memory>
00031
00032 #ifdef HAVE_LIBXML
00033 #include "LIBXML_DOMDocument.h"
00034 #elif _MSC_VER
00035 #include "MSXML_DOMDocument.h"
00036 #else
00037 #include "LIBXML_DOMDocument.h"
00038 #endif
00039
00040 #ifdef _MSC_VER
00041 #define RESET_AUTO_PTR(OLD, NEW) OLD = NEW;
00042 #else
00043 #define RESET_AUTO_PTR(OLD, NEW) OLD.reset( NEW.release() );
00044 #endif
00045
00046 namespace FIX
00047 {
00048 DataDictionary::DataDictionary()
00049 : m_hasVersion( false ), m_checkFieldsOutOfOrder( true ),
00050 m_checkFieldsHaveValues( true ), m_checkUserDefinedFields( true ),
00051 m_orderedFieldsArray(0) {}
00052
00053 DataDictionary::DataDictionary( std::istream& stream )
00054 throw( ConfigError )
00055 : m_hasVersion( false ), m_checkFieldsOutOfOrder( true ),
00056 m_checkFieldsHaveValues( true ), m_checkUserDefinedFields( true ),
00057 m_orderedFieldsArray(0)
00058 {
00059 readFromStream( stream );
00060 }
00061
00062 DataDictionary::DataDictionary( const std::string& url )
00063 throw( ConfigError )
00064 : m_hasVersion( false ), m_checkFieldsOutOfOrder( true ),
00065 m_checkFieldsHaveValues( true ), m_checkUserDefinedFields( true ),
00066 m_orderedFieldsArray(0)
00067 {
00068 readFromURL( url );
00069 }
00070
00071 DataDictionary::DataDictionary( const DataDictionary& copy )
00072 {
00073 *this = copy;
00074 }
00075
00076 DataDictionary::~DataDictionary()
00077 {
00078 FieldToGroup::iterator i;
00079 for ( i = m_groups.begin(); i != m_groups.end(); ++i )
00080 delete i->second.second;
00081 if( m_orderedFieldsArray )
00082 delete [] m_orderedFieldsArray;
00083 }
00084
00085 DataDictionary& DataDictionary::operator=( const DataDictionary& rhs )
00086 { QF_STACK_PUSH(DataDictionary::operator=)
00087
00088 m_hasVersion = rhs.m_hasVersion;
00089 m_checkFieldsOutOfOrder = rhs.m_checkFieldsOutOfOrder;
00090 m_checkFieldsHaveValues = rhs.m_checkFieldsHaveValues;
00091 m_checkUserDefinedFields = rhs.m_checkUserDefinedFields;
00092 m_beginString = rhs.m_beginString;
00093 m_messageFields = rhs.m_messageFields;
00094 m_requiredFields = rhs.m_requiredFields;
00095 m_messages = rhs.m_messages;
00096 m_fields = rhs.m_fields;
00097 m_orderedFields = rhs.m_orderedFields;
00098 m_orderedFieldsArray = 0;
00099 m_headerFields = rhs.m_headerFields;
00100 m_trailerFields = rhs.m_trailerFields;
00101 m_fieldTypes = rhs.m_fieldTypes;
00102 m_fieldValues = rhs.m_fieldValues;
00103 m_fieldNames = rhs.m_fieldNames;
00104 m_names = rhs.m_names;
00105 m_valueNames = rhs.m_valueNames;
00106
00107 FieldToGroup::const_iterator i = rhs.m_groups.begin();
00108 for ( ; i != rhs.m_groups.end(); ++i )
00109 {
00110 addGroup( i->first.first, i->first.second,
00111 i->second.first, *i->second.second );
00112 }
00113 return *this;
00114
00115 QF_STACK_POP
00116 }
00117
00118 void DataDictionary::validate( const Message& message )
00119 throw( std::exception )
00120 { QF_STACK_PUSH(DataDictionary::validate)
00121
00122 BeginString beginString;
00123 MsgType msgType;
00124 message.getHeader().getField( beginString );
00125 message.getHeader().getField( msgType );
00126
00127 std::string ddBeginString = getVersion();
00128 if ( m_hasVersion && m_beginString != beginString )
00129 throw UnsupportedVersion();
00130
00131 int field = 0;
00132 if ( m_checkFieldsOutOfOrder && !message.hasValidStructure(field) )
00133 throw TagOutOfOrder(field);
00134
00135 if ( m_hasVersion )
00136 {
00137 checkMsgType( msgType );
00138 checkHasRequired( message.getHeader(), message, message.getTrailer(), msgType );
00139 }
00140
00141 iterate( message.getHeader(), msgType );
00142 iterate( message.getTrailer(), msgType );
00143 iterate( message, msgType );
00144
00145 QF_STACK_POP
00146 }
00147
00148 void DataDictionary::iterate( const FieldMap& map, const MsgType& msgType )
00149 { QF_STACK_PUSH(DataDictionary::iterate)
00150
00151 int lastField = 0;
00152
00153 FieldMap::iterator i;
00154 for ( i = map.begin(); i != map.end(); ++i )
00155 {
00156 const FieldBase& field = i->second;
00157 if( i != map.begin() && (field.getField() == lastField) )
00158 throw RepeatedTag( lastField );
00159 checkHasValue( field );
00160
00161 if ( m_hasVersion )
00162 {
00163 checkValidFormat( field );
00164 checkValue( field );
00165 }
00166
00167 if ( m_beginString.getValue().length() && shouldCheckTag(field) )
00168 {
00169 checkValidTagNumber( field );
00170 if ( !Message::isHeaderField( field, this )
00171 && !Message::isTrailerField( field, this ) )
00172 {
00173 checkIsInMessage( field, msgType );
00174 checkGroupCount( field, map, msgType );
00175 }
00176 }
00177 lastField = field.getField();
00178 }
00179
00180 QF_STACK_POP
00181 }
00182
00183 void DataDictionary::readFromURL( const std::string& url )
00184 throw( ConfigError )
00185 { QF_STACK_PUSH(DataDictionary::readFromURL)
00186
00187 #ifdef HAVE_LIBXML
00188 DOMDocumentPtr pDoc = DOMDocumentPtr(new LIBXML_DOMDocument());
00189 #elif _MSC_VER
00190 DOMDocumentPtr pDoc = DOMDocumentPtr(new MSXML_DOMDocument());
00191 #else
00192 DOMDocumentPtr pDoc = DOMDocumentPtr(new LIBXML_DOMDocument());
00193 #endif
00194
00195 if(!pDoc->load(url))
00196 throw ConfigError(url + ": Could not parse data dictionary file");
00197
00198 try
00199 {
00200 readFromDocument( pDoc );
00201 }
00202 catch( ConfigError& e )
00203 {
00204 throw ConfigError( url + ": " + e.what() );
00205 }
00206
00207 QF_STACK_POP
00208 }
00209
00210 void DataDictionary::readFromStream( std::istream& stream )
00211 throw( ConfigError )
00212 { QF_STACK_PUSH(DataDictionary::readFromStream)
00213
00214 #ifdef HAVE_LIBXML
00215 DOMDocumentPtr pDoc = DOMDocumentPtr(new LIBXML_DOMDocument());
00216 #elif _MSC_VER
00217 DOMDocumentPtr pDoc = DOMDocumentPtr(new MSXML_DOMDocument());
00218 #else
00219 DOMDocumentPtr pDoc = DOMDocumentPtr(new LIBXML_DOMDocument());
00220 #endif
00221
00222 if(!pDoc->load(stream))
00223 throw ConfigError("Could not parse data dictionary stream");
00224
00225 readFromDocument( pDoc );
00226
00227 QF_STACK_POP
00228 }
00229
00230 void DataDictionary::readFromDocument( DOMDocumentPtr pDoc )
00231 throw( ConfigError )
00232 { QF_STACK_PUSH(DataDictionary::readFromDocument)
00233
00234
00235 DOMNodePtr pFixNode = pDoc->getNode("/fix");
00236 if(!pFixNode.get())
00237 throw ConfigError("Could not parse data dictionary file"
00238 ", or no <fix> node found at root");
00239 DOMAttributesPtr attrs = pFixNode->getAttributes();
00240 std::string major;
00241 if(!attrs->get("major", major))
00242 throw ConfigError("major attribute not found on <fix>");
00243 std::string minor;
00244 if(!attrs->get("minor", minor))
00245 throw ConfigError("minor attribute not found on <fix>");
00246 setVersion("FIX." + major + "." + minor);
00247
00248
00249 DOMNodePtr pFieldsNode = pDoc->getNode("/fix/fields");
00250 if(!pFieldsNode.get())
00251 throw ConfigError("<fields> section not found in data dictionary");
00252
00253 DOMNodePtr pFieldNode = pFieldsNode->getFirstChildNode();
00254 if(!pFieldNode.get()) throw ConfigError("No fields defined");
00255
00256 while(pFieldNode.get())
00257 {
00258 if(pFieldNode->getName() == "field")
00259 {
00260 DOMAttributesPtr attrs = pFieldNode->getAttributes();
00261 std::string name;
00262 if(!attrs->get("name", name))
00263 throw ConfigError("<field> does not have a name attribute");
00264 std::string number;
00265 if(!attrs->get("number", number))
00266 throw ConfigError("<field> " + name + " does not have a number attribute");
00267 int num = atol(number.c_str());
00268 std::string type;
00269 if(!attrs->get("type", type))
00270 throw ConfigError("<field> " + name + " does not have a type attribute");
00271 addField(num);
00272 addFieldType(num, XMLTypeToType(type));
00273 addFieldName(num, name);
00274
00275 DOMNodePtr pFieldValueNode = pFieldNode->getFirstChildNode();
00276 while(pFieldValueNode.get())
00277 {
00278 if(pFieldValueNode->getName() == "value")
00279 {
00280 DOMAttributesPtr attrs = pFieldValueNode->getAttributes();
00281 std::string enumeration;
00282 if(!attrs->get("enum", enumeration))
00283 throw ConfigError("<value> does not have enum attribute in field " + name);
00284 addFieldValue(num, enumeration);
00285 std::string description;
00286 if(attrs->get("description", description))
00287 addValueName(num, enumeration, description);
00288 }
00289 RESET_AUTO_PTR(pFieldValueNode, pFieldValueNode->getNextSiblingNode());
00290 }
00291 }
00292 RESET_AUTO_PTR(pFieldNode, pFieldNode->getNextSiblingNode());
00293 }
00294
00295
00296 DOMNodePtr pHeaderNode = pDoc->getNode("/fix/header");
00297 if(!pHeaderNode.get())
00298 throw ConfigError("<header> section not found in data dictionary");
00299
00300 DOMNodePtr pHeaderFieldNode = pHeaderNode->getFirstChildNode();
00301 if(!pHeaderFieldNode.get()) throw ConfigError("No header fields defined");
00302
00303 while(pHeaderFieldNode.get())
00304 {
00305 if(pHeaderFieldNode->getName() == "field" || pHeaderFieldNode->getName() == "group" )
00306 {
00307 DOMAttributesPtr attrs = pHeaderFieldNode->getAttributes();
00308 std::string name;
00309 if(!attrs->get("name", name))
00310 throw ConfigError("<field> does not have a name attribute");
00311 std::string required = "false";
00312 attrs->get("required", required);
00313 addHeaderField(lookupXMLFieldNumber(pDoc.get(), name), required == "true");
00314 }
00315 if(pHeaderFieldNode->getName() == "group")
00316 {
00317 DOMAttributesPtr attrs = pHeaderFieldNode->getAttributes();
00318 std::string required;
00319 attrs->get("required", required);
00320 bool isRequired = (required == "Y" || required == "y");
00321 addXMLGroup(pDoc.get(), pHeaderFieldNode.get(), "_header_", *this, isRequired);
00322 }
00323
00324 RESET_AUTO_PTR(pHeaderFieldNode, pHeaderFieldNode->getNextSiblingNode());
00325 }
00326
00327
00328 DOMNodePtr pTrailerNode = pDoc->getNode("/fix/trailer");
00329 if(!pTrailerNode.get())
00330 throw ConfigError("<trailer> section not found in data dictionary");
00331
00332 DOMNodePtr pTrailerFieldNode = pTrailerNode->getFirstChildNode();
00333 if(!pTrailerFieldNode.get()) throw ConfigError("No trailer fields defined");
00334
00335 while(pTrailerFieldNode.get())
00336 {
00337 if(pTrailerFieldNode->getName() == "field" || pTrailerFieldNode->getName() == "group" )
00338 {
00339 DOMAttributesPtr attrs = pTrailerFieldNode->getAttributes();
00340 std::string name;
00341 if(!attrs->get("name", name))
00342 throw ConfigError("<field> does not have a name attribute");
00343 std::string required = "false";
00344 attrs->get("required", required);
00345 addTrailerField(lookupXMLFieldNumber(pDoc.get(), name), required == "true");
00346 }
00347 if(pTrailerFieldNode->getName() == "group")
00348 {
00349 DOMAttributesPtr attrs = pTrailerFieldNode->getAttributes();
00350 std::string required;
00351 attrs->get("required", required);
00352 bool isRequired = (required == "Y" || required == "y");
00353 addXMLGroup(pDoc.get(), pTrailerFieldNode.get(), "_trailer_", *this, isRequired);
00354 }
00355
00356 RESET_AUTO_PTR(pTrailerFieldNode, pTrailerFieldNode->getNextSiblingNode());
00357 }
00358
00359
00360 DOMNodePtr pMessagesNode = pDoc->getNode("/fix/messages");
00361 if(!pMessagesNode.get())
00362 throw ConfigError("<messages> section not found in data dictionary");
00363
00364 DOMNodePtr pMessageNode = pMessagesNode->getFirstChildNode();
00365 if(!pMessageNode.get()) throw ConfigError("No messages defined");
00366
00367 while(pMessageNode.get())
00368 {
00369 if(pMessageNode->getName() == "message")
00370 {
00371 DOMAttributesPtr attrs = pMessageNode->getAttributes();
00372 std::string msgtype;
00373 if(!attrs->get("msgtype", msgtype))
00374 throw ConfigError("<field> does not have a name attribute");
00375 addMsgType(msgtype);
00376
00377 std::string name;
00378 if(attrs->get("name", name))
00379 addValueName( 35, msgtype, name );
00380
00381 DOMNodePtr pMessageFieldNode = pMessageNode->getFirstChildNode();
00382 if( !pMessageFieldNode.get() )
00383 throw ConfigError("<message> contains no fields");
00384 while( pMessageFieldNode.get() )
00385 {
00386 if(pMessageFieldNode->getName() == "field"
00387 || pMessageFieldNode->getName() == "group")
00388 {
00389 DOMAttributesPtr attrs = pMessageFieldNode->getAttributes();
00390 std::string name;
00391 if(!attrs->get("name", name))
00392 throw ConfigError("<field> does not have a name attribute");
00393 int num = lookupXMLFieldNumber(pDoc.get(), name);
00394 addMsgField(msgtype, num);
00395
00396 std::string required;
00397 if(attrs->get("required", required)
00398 && (required == "Y" || required == "y"))
00399 {
00400 addRequiredField(msgtype, num);
00401 }
00402 }
00403 else if(pMessageFieldNode->getName() == "component")
00404 {
00405 DOMAttributesPtr attrs = pMessageFieldNode->getAttributes();
00406 std::string required;
00407 attrs->get("required", required);
00408 bool isRequired = (required == "Y" || required == "y");
00409 addXMLComponentFields(pDoc.get(), pMessageFieldNode.get(),
00410 msgtype, *this, isRequired);
00411 }
00412 if(pMessageFieldNode->getName() == "group")
00413 {
00414 DOMAttributesPtr attrs = pMessageFieldNode->getAttributes();
00415 std::string required;
00416 attrs->get("required", required);
00417 bool isRequired = (required == "Y" || required == "y");
00418 addXMLGroup(pDoc.get(), pMessageFieldNode.get(), msgtype, *this, isRequired);
00419 }
00420 RESET_AUTO_PTR(pMessageFieldNode,
00421 pMessageFieldNode->getNextSiblingNode());
00422 }
00423 }
00424 RESET_AUTO_PTR(pMessageNode, pMessageNode->getNextSiblingNode());
00425 }
00426
00427 QF_STACK_POP
00428 }
00429
00430 int* DataDictionary::getOrderedFields() const
00431 { QF_STACK_PUSH(DataDictionary::getOrderedFields)
00432
00433 if( m_orderedFieldsArray ) return m_orderedFieldsArray;
00434 m_orderedFieldsArray = new int[m_orderedFields.size() + 1];
00435
00436 int* i = m_orderedFieldsArray;
00437 OrderedFields::const_iterator iter;
00438 for( iter = m_orderedFields.begin(); iter != m_orderedFields.end(); *(i++) = *(iter++) ) {}
00439 *i = 0;
00440 return m_orderedFieldsArray;
00441
00442 QF_STACK_POP
00443 }
00444
00445 int DataDictionary::lookupXMLFieldNumber( DOMDocument* pDoc, DOMNode* pNode ) const
00446 { QF_STACK_PUSH(DataDictionary::lookupXMLFieldNumber)
00447
00448 DOMAttributesPtr attrs = pNode->getAttributes();
00449 std::string name;
00450 if(!attrs->get("name", name))
00451 throw ConfigError("No name given to field");
00452 return lookupXMLFieldNumber( pDoc, name );
00453
00454 QF_STACK_POP
00455 }
00456
00457 int DataDictionary::lookupXMLFieldNumber
00458 ( DOMDocument* pDoc, const std::string& name ) const
00459 { QF_STACK_PUSH(DataDictionary::lookupXMLFieldNumber)
00460
00461 NameToField::const_iterator i = m_names.find(name);
00462 if( i == m_names.end() )
00463 throw ConfigError("Field " + name + " not defined in fields section");
00464 return i->second;
00465
00466 QF_STACK_POP
00467 }
00468
00469 int DataDictionary::addXMLComponentFields( DOMDocument* pDoc, DOMNode* pNode,
00470 const std::string& msgtype,
00471 DataDictionary& DD,
00472 bool componentRequired )
00473 { QF_STACK_PUSH(DataDictionary::addXMLComponentFields)
00474
00475 int firstField = 0;
00476
00477 DOMAttributesPtr attrs = pNode->getAttributes();
00478 std::string name;
00479 if(!attrs->get("name", name))
00480 throw ConfigError("No name given to component");
00481
00482 DOMNodePtr pComponentNode =
00483 pDoc->getNode("/fix/components/component[@name='" + name + "']");
00484 if(pComponentNode.get() == 0)
00485 throw ConfigError("Component not found");
00486
00487 DOMNodePtr pComponentFieldNode = pComponentNode->getFirstChildNode();
00488 while(pComponentFieldNode.get())
00489 {
00490 if(pComponentFieldNode->getName() == "field"
00491 || pComponentFieldNode->getName() == "group")
00492 {
00493 DOMAttributesPtr attrs = pComponentFieldNode->getAttributes();
00494 std::string name;
00495 if(!attrs->get("name", name))
00496 throw ConfigError("No name given to field");
00497 int field = lookupXMLFieldNumber(pDoc, name);
00498 if( firstField == 0 ) firstField = field;
00499
00500 std::string required;
00501 if(attrs->get("required", required)
00502 && (required == "Y" || required =="y")
00503 && componentRequired)
00504 {
00505 addRequiredField(msgtype, field);
00506 }
00507
00508 DD.addField(field);
00509 DD.addMsgField(msgtype, field);
00510 }
00511 if(pComponentFieldNode->getName() == "group")
00512 {
00513 DOMAttributesPtr attrs = pComponentFieldNode->getAttributes();
00514 std::string required;
00515 attrs->get("required", required);
00516 bool isRequired = (required == "Y" || required == "y");
00517 addXMLGroup(pDoc, pComponentFieldNode.get(), msgtype, DD, isRequired);
00518 }
00519 RESET_AUTO_PTR(pComponentFieldNode,
00520 pComponentFieldNode->getNextSiblingNode());
00521 }
00522 return firstField;
00523
00524 QF_STACK_POP
00525 }
00526
00527 void DataDictionary::addXMLGroup( DOMDocument* pDoc, DOMNode* pNode,
00528 const std::string& msgtype,
00529 DataDictionary& DD, bool groupRequired )
00530 { QF_STACK_PUSH(DataDictionary::addXMLGroup)
00531
00532 DOMAttributesPtr attrs = pNode->getAttributes();
00533 std::string name;
00534 if(!attrs->get("name", name))
00535 throw ConfigError("No name given to group");
00536 int group = lookupXMLFieldNumber( pDoc, name );
00537 int delim = 0;
00538 int field = 0;
00539 DataDictionary groupDD;
00540 DOMNodePtr node = pNode->getFirstChildNode();
00541 while(node.get())
00542 {
00543 if( node->getName() == "field" )
00544 {
00545 field = lookupXMLFieldNumber( pDoc, node.get() );
00546 groupDD.addField( field );
00547
00548 DOMAttributesPtr attrs = node->getAttributes();
00549 std::string required;
00550 if( attrs->get("required", required)
00551 && ( required == "Y" || required =="y" )
00552 && groupRequired )
00553 {
00554 groupDD.addRequiredField(msgtype, field);
00555 }
00556 }
00557 else if( node->getName() == "component" )
00558 {
00559 field = addXMLComponentFields( pDoc, node.get(), msgtype, groupDD, false );
00560 }
00561 else if( node->getName() == "group" )
00562 {
00563 field = lookupXMLFieldNumber( pDoc, node.get() );
00564 groupDD.addField( field );
00565 DOMAttributesPtr attrs = node->getAttributes();
00566 std::string required;
00567 if( attrs->get("required", required )
00568 && ( required == "Y" || required =="y" )
00569 && groupRequired)
00570 {
00571 groupDD.addRequiredField(msgtype, field);
00572 }
00573 bool isRequired = false;
00574 if( attrs->get("required", required) )
00575 isRequired = (required == "Y" || required == "y");
00576 addXMLGroup( pDoc, node.get(), msgtype, groupDD, isRequired );
00577 }
00578 if( delim == 0 ) delim = field;
00579 RESET_AUTO_PTR(node, node->getNextSiblingNode());
00580 }
00581
00582 if( delim ) DD.addGroup( msgtype, group, delim, groupDD );
00583
00584 QF_STACK_POP
00585 }
00586
00587 TYPE::Type DataDictionary::XMLTypeToType( const std::string& type ) const
00588 { QF_STACK_PUSH(DataDictionary::XMLTypeToType)
00589
00590 if ( m_beginString < "FIX.4.2" && type == "CHAR" )
00591 return TYPE::String;
00592
00593 if ( type == "STRING" ) return TYPE::String;
00594 if ( type == "CHAR" ) return TYPE::Char;
00595 if ( type == "PRICE" ) return TYPE::Price;
00596 if ( type == "INT" ) return TYPE::Int;
00597 if ( type == "AMT" ) return TYPE::Amt;
00598 if ( type == "QTY" ) return TYPE::Qty;
00599 if ( type == "CURRENCY" ) return TYPE::Currency;
00600 if ( type == "MULTIPLEVALUESTRING" ) return TYPE::MultipleValueString;
00601 if ( type == "EXCHANGE" ) return TYPE::Exchange;
00602 if ( type == "UTCTIMESTAMP" ) return TYPE::UtcTimeStamp;
00603 if ( type == "BOOLEAN" ) return TYPE::Boolean;
00604 if ( type == "LOCALMKTDATE" ) return TYPE::LocalMktDate;
00605 if ( type == "DATA" ) return TYPE::Data;
00606 if ( type == "FLOAT" ) return TYPE::Float;
00607 if ( type == "PRICEOFFSET" ) return TYPE::PriceOffset;
00608 if ( type == "MONTHYEAR" ) return TYPE::MonthYear;
00609 if ( type == "DAYOFMONTH" ) return TYPE::DayOfMonth;
00610 if ( type == "UTCDATE" ) return TYPE::UtcDate;
00611 if ( type == "UTCDATEONLY" ) return TYPE::UtcDateOnly;
00612 if ( type == "UTCTIMEONLY" ) return TYPE::UtcTimeOnly;
00613 if ( type == "NUMINGROUP" ) return TYPE::NumInGroup;
00614 if ( type == "PERCENTAGE" ) return TYPE::Percentage;
00615 if ( type == "SEQNUM" ) return TYPE::SeqNum;
00616 if ( type == "LENGTH" ) return TYPE::Length;
00617 if ( type == "COUNTRY" ) return TYPE::Country;
00618 if ( type == "TIME" ) return TYPE::UtcTimeStamp;
00619 return TYPE::Unknown;
00620
00621 QF_STACK_POP
00622 }
00623 }