// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2014 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it.
// =================================================================================================
#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
#include "public/include/XMP_Const.h"
#include "XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h"
#include "source/Endian.h"
#include "source/ExpatAdapter.hpp"
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include <algorithm>
namespace IFF_RIFF {
static const char * tagNames[ iXMLMetadata::kLastEntry ] = {
"TAPE", //kTape, // std::string
"TAKE", //kTake, // XMP_Uns64
"SCENE", //kScene, // std::string
"NOTE", //kNote, // std::string
"PROJECT", //kProject, // std::string
"NO_GOOD", //kNoGood, // bool( true/false )
"FILE_SAMPLE_RATE", //kFileSampleRate, // XMP_Uns64
"AUDIO_BIT_DEPTH", //kAudioBitDepth, // XMP_Uns64
"CIRCLED", //kCircled, // bool( true/false )
"BWF_DESCRIPTION", //kBWFDescription, // std::string( 256 )
"BWF_ORIGINATOR", //kBWFOriginator, // std::string( 32 )
"BWF_ORIGINATOR_REFERENCE", //kBWFOriginatorReference, // std::string( 32 )
"BWF_ORIGINATION_DATE", //kBWFOriginationDate, // std::string( 10 )
"BWF_ORIGINATION_TIME", //kBWFOriginationTime, // std::string( 8 )
"BWF_TIME_REFERENCE_LOW", //kBWFTimeReferenceLow, // XMP_Uns32
"BWF_TIME_REFERENCE_HIGH", //kBWFTimeReferenceHigh, // XMP_Uns32
"BWF_VERSION", //kBWFVersion, // XMP_Uns16
"BWF_UMID", //kBWFUMID, // std::string[64]
"BWF_CODING_HISTORY", //kBWFHistory, // std::string
"TIMECODE_FLAG", //kTimeCodeFlag, // std::string[DF/NDF]
"TIMECODE_RATE", //kTimeCodeRate, // std::string
"TIMESTAMP_SAMPLE_RATE", //kTimeStampSampleRate, // XMP_Uns64
"TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO", //kTimeStampSampleSinceMidnightLow, // XMP_Uns32
"TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI", //kTimeStampSampleSinceMidnightHi, // XMP_Uns32
};
static const char * rootTagName = "BWFXML";
static const char * speedTagName = "SPEED";
static const char * bextTagName = "BEXT";
iXMLMetadata::iXMLMetadata()
: mExpatAdapter( NULL )
, mRootNode( NULL )
, mErrorCallback( NULL )
, mExtraSpaceSize( 1024 ) {}
iXMLMetadata::~iXMLMetadata() {
if ( mExpatAdapter ) {
mRootNode = NULL;
}
delete mExpatAdapter;
delete mRootNode;
mExpatAdapter = NULL;
}
#define MAX_SZ ((size_t)~((size_t)0))
void iXMLMetadata::parse( const XMP_Uns8 * chunkData, XMP_Uns64 size ) {
if ( chunkData == NULL || size == 0 ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: iXML chunk is not well formed" );
NotifyClient( kXMPErrSev_Recoverable, error );
return;
}
mExpatAdapter = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
if ( mExpatAdapter == 0 ) XMP_Throw ( "iXMLMetadata: Can't create Expat adapter", kXMPErr_NoMemory );
mExpatAdapter->SetErrorCallback( mErrorCallback );
try {
XMP_Uns64 parsedSize = 0;
while ( parsedSize < size ) {
XMP_Uns64 currentSize = std::min<XMP_Uns64>( size - parsedSize, MAX_SZ );
mExpatAdapter->ParseBuffer( chunkData + parsedSize, (size_t) currentSize, false );
parsedSize += currentSize;
}
mExpatAdapter->ParseBuffer( 0, 0, true );
} catch( ... ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: iXML chunk is not well formed" );
NotifyClient( kXMPErrSev_Recoverable, error );
return;
}
// read the particular nodes we are interested in and store in the map
// Get the root node
// Get the root node of the XML tree.
XML_Node & xmlTree = mExpatAdapter->tree;
for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) {
if ( xmlTree.content[i]->kind == kElemNode ) {
mRootNode = xmlTree.content[i];
break;
}
}
if ( mRootNode == NULL ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: No Root Element present in iXML chunk" );
NotifyClient( kXMPErrSev_Recoverable, error );
return;
}
XMP_StringPtr rootLocalName = mRootNode->name.c_str() + mRootNode->nsPrefixLen;
if ( ! XMP_LitMatch ( rootLocalName, rootTagName ) ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: Unexpected Root Element present in iXML chunk" );
NotifyClient( kXMPErrSev_Recoverable, error );
return;
}
XMP_StringPtr ns = mRootNode->ns.c_str();
XML_NodePtr currentNode( NULL );
ParseAndSetProperties();
resetChanges();
}
XMP_Uns64 iXMLMetadata::serialize( XMP_Uns8** buffer ) {
*buffer = NULL;
if ( mRootNode == NULL ) {
mRootNode = new XML_Node( NULL, rootTagName, kElemNode );
if ( mRootNode == NULL ) {
XMP_Error error( kXMPErr_NoMemory, "iXML Metadata reconciliation failure: Can't create Root Node" );
NotifyClient( kXMPErrSev_OperationFatal, error );
return 0;
}
}
// Create SPEED and bext node if required
XML_Node * node = mRootNode->GetNamedElement( "", speedTagName );
if ( node == NULL ) {
node = new XML_Node( mRootNode, speedTagName, kElemNode );
if ( node == NULL ) {
XMP_Error error( kXMPErr_NoMemory, "iXML Metadata reconciliation failure: Can't create Speed Node" );
NotifyClient( kXMPErrSev_OperationFatal, error );
return 0;
}
mRootNode->content.push_back( node );
}
node = mRootNode->GetNamedElement( "", bextTagName );
if ( node == NULL ) {
node = new XML_Node( mRootNode, bextTagName, kElemNode );
if ( node == NULL ) {
XMP_Error error( kXMPErr_NoMemory, "iXML Metadata reconciliation failure: Can't create Bext Node" );
NotifyClient( kXMPErrSev_OperationFatal, error );
return 0;
}
mRootNode->content.push_back( node );
}
UpdateProperties();
// get the SPEED and bext node and remove them if empty
if ( node->content.size() == 0 )
RemoveXMLNode( mRootNode, bextTagName );
node = mRootNode->GetNamedElement( "", speedTagName );
if ( node->content.size() == 0 )
RemoveXMLNode( mRootNode, speedTagName );
node = NULL;
std::string strBuffer;
mRootNode->Serialize( &strBuffer );
// move the contents of the string into the new bigger buffer
size_t newSize = strBuffer.size() + mExtraSpaceSize;
XMP_Uns8 * newBuffer = new XMP_Uns8[ newSize ];
memset( newBuffer, 0x20, newSize );
memcpy( newBuffer, "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n", 39 );
memcpy( newBuffer + 39, strBuffer.c_str(), strBuffer.size() );
*buffer = newBuffer;
return newSize;
}
bool iXMLMetadata::isEmptyValue( XMP_Uns32 id, ValueObject& valueObj ) {
bool ret = true;
switch( id )
{
case kTape:
case kScene:
case kNote:
case kProject:
case kBWFDescription:
case kBWFOriginator:
case kBWFOriginatorReference:
case kBWFOriginationDate:
case kBWFOriginationTime:
case kBWFHistory:
case kBWFUMID:
case kTimeCodeFlag:
case kTimeCodeRate:
{
TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(&valueObj);
ret = ( strObj == NULL || ( strObj != NULL && strObj->getValue().empty() ) );
}
break;
case kTake:
case kFileSampleRate:
case kAudioBitDepth:
case kBWFTimeReferenceLow:
case kBWFTimeReferenceHigh:
case kBWFVersion:
case kTimeStampSampleRate:
case kTimeStampSampleSinceMidnightLow:
case kTimeStampSampleSinceMidnightHigh:
ret = false;
break;
case kNoGood:
case kCircled:
ret = false;
break;
default:
ret = true;
}
return ret;
}
void iXMLMetadata::ParseAndSetStringProperty( XML_Node * parentNode, XMP_Uns32 id ) {
std::string nodeValue = ParseStringValue( parentNode, id );
if ( nodeValue.size() > 0 ) {
this->setValue< std::string >( id, nodeValue );
}
}
void iXMLMetadata::SetErrorCallback( GenericErrorCallback * errorCallback ) {
mErrorCallback = errorCallback;
}
void iXMLMetadata::NotifyClient( XMP_ErrorSeverity severity, XMP_Error & error ) {
XMPFileHandler::NotifyClient( mErrorCallback, severity, error );
}
static XMP_Uns64 ConvertStringToUns64( const std::string & strValue ) {
int count;
char nextCh;
XMP_Uns64 result;
count = sscanf ( strValue.c_str(), "%llu%c", &result, &nextCh );
if ( count != 1 ) XMP_Throw ( "Invalid integer string", kXMPErr_BadParam );
return result;
}
void iXMLMetadata::ParseAndSetIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ) {
std::string strValue = ParseStringValue( parentNode, id );
if ( strValue.size() > 0 ) {
XMP_Uns64 uValue;
try {
uValue = ConvertStringToUns64( strValue );
} catch( ... ) {
// some nodes like tape can be non integer also. Treat it as warning
XMP_Error error( kXMPErr_BadFileFormat, "iXML Metadata reconciliation failure: node is supposed to have integer value" );
NotifyClient( kXMPErrSev_Recoverable, error );
return;
}
this->setValue< XMP_Uns64 >( id, uValue );
}
}
void iXMLMetadata::ParseAndSetBoolProperty( XML_Node * parentNode, XMP_Uns32 id ) {
std::string strValue = ParseStringValue( parentNode, id );
if ( strValue.size() > 0 ) {
if ( strValue.compare( "TRUE" ) == 0 )
this->setValue< bool >( id, true );
else if ( strValue.compare( "FALSE" ) == 0 )
this->setValue< bool >( id, false );
else {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: invalid boolean value present" );
NotifyClient( kXMPErrSev_Recoverable, error );
}
}
}
void iXMLMetadata::ParseAndSetProperties() {
// top level properties
ParseAndSetStringProperty( mRootNode, kTape );
ParseAndSetIntegerProperty( mRootNode, kTake );
ParseAndSetStringProperty( mRootNode, kScene );
ParseAndSetStringProperty( mRootNode, kNote );
ParseAndSetStringProperty( mRootNode, kProject );
ParseAndSetBoolProperty( mRootNode, kNoGood );
ParseAndSetBoolProperty( mRootNode, kCircled );
// speed node children
XML_Node * speedNode = mRootNode->GetNamedElement( "", speedTagName );
if ( speedNode ) {
ParseAndSetIntegerProperty( speedNode, kFileSampleRate );
ParseAndSetIntegerProperty( speedNode, kAudioBitDepth );
ParseAndSetStringProperty( speedNode, kTimeCodeFlag );
ParseAndSetStringProperty( speedNode, kTimeCodeRate );
ParseAndSetIntegerProperty( speedNode, kTimeStampSampleRate );
ParseAndSetIntegerProperty( speedNode, kTimeStampSampleSinceMidnightLow );
ParseAndSetIntegerProperty( speedNode, kTimeStampSampleSinceMidnightHigh );
}
// bext node children
XML_Node * bextNode = mRootNode->GetNamedElement( "", bextTagName );
if ( bextNode ) {
ParseAndSetStringProperty( bextNode, kBWFDescription );
ParseAndSetStringProperty( bextNode, kBWFOriginator );
ParseAndSetStringProperty( bextNode, kBWFOriginatorReference );
ParseAndSetStringProperty( bextNode, kBWFOriginationDate );
ParseAndSetStringProperty( bextNode, kBWFOriginationTime );
ParseAndSetIntegerProperty( bextNode, kBWFTimeReferenceLow );
ParseAndSetIntegerProperty( bextNode, kBWFTimeReferenceHigh );
ParseAndSetIntegerProperty( bextNode, kBWFVersion );
ParseAndSetStringProperty( bextNode, kBWFHistory );
ParseAndSetStringProperty( bextNode, kBWFUMID );
}
}
void iXMLMetadata::UpdateStringProperty( XML_Node * parentNode, XMP_Uns32 id ) {
if ( valueExists( id ) ) {
std::string value;
try {
value = this->getValue< std::string >( id );
} catch ( ... ) {
XMP_Error e1( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the value to be of string type" );
NotifyClient( kXMPErrSev_Recoverable, e1 );
return;
}
UpdateXMLNode( parentNode, tagNames[ id ], value );
} else {
RemoveXMLNode( parentNode, tagNames[ id ] );
}
}
void iXMLMetadata::UpdateXMLNode( XML_Node * parentNode, const char * localName, const std::string & value ) {
XML_Node * node = parentNode->GetNamedElement( "", localName );
if ( node == NULL ) {
node = new XML_Node( parentNode, localName, kElemNode );
if ( node == NULL ) {
XMP_Error error( kXMPErr_NoMemory, "Unable to create new objects" );
NotifyClient( kXMPErrSev_OperationFatal, error );
return;
}
parentNode->content.push_back( node );
}
if ( node->IsLeafContentNode() == false ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node was supposed to be a leaf node" );
NotifyClient( kXMPErrSev_Recoverable, error );
node->RemoveContent();
}
node->SetLeafContentValue( value.c_str() );
}
void iXMLMetadata::RemoveXMLNode( XML_Node * parentNode, const char * localName ) {
XML_Node * node = parentNode->GetNamedElement( "", localName );
if ( node ) {
// find the position
XML_NodeVector::iterator it = std::find( parentNode->content.begin(), parentNode->content.end(), node );
XMP_Assert( it != parentNode->content.end() );
parentNode->content.erase( it );
delete node;
}
}
#if XMP_WinBuild
#define snprintf _snprintf
#endif
static std::string ConvertUns64ToString( XMP_Uns64 uValue ) {
char buffer[64];
snprintf( buffer, sizeof( buffer ), "%llu", uValue );
std::string str( buffer );
return str;
}
void iXMLMetadata::UpdateIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ) {
if ( valueExists( id ) ) {
XMP_Uns64 uValue;
try {
uValue = this->getValue< XMP_Uns64 >( id );
} catch ( ... ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the value to be of XMP_Uns64 type" );
NotifyClient( kXMPErrSev_Recoverable, error );
return;
}
std::string strValue = ConvertUns64ToString( uValue );
UpdateXMLNode( parentNode, tagNames[ id ], strValue );
} else {
RemoveXMLNode( parentNode, tagNames[ id ] );
}
}
void iXMLMetadata::UpdateBoolProperty( XML_Node * parentNode, XMP_Uns32 id ) {
if ( valueExists( id ) ) {
bool value;
try {
value = this->getValue< bool >( id );
} catch ( ... ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the value to be of bool type" );
NotifyClient( kXMPErrSev_Recoverable, error );
return;
}
std::string strValue;
if ( value ) strValue = "TRUE";
else strValue = "FALSE";
UpdateXMLNode( parentNode, tagNames[ id ], strValue );
} else {
RemoveXMLNode( parentNode, tagNames[ id ] );
}
}
void iXMLMetadata::UpdateProperties() {
// top level properties
UpdateStringProperty( mRootNode, kTape );
UpdateIntegerProperty( mRootNode, kTake );
UpdateStringProperty( mRootNode, kScene );
UpdateStringProperty( mRootNode, kNote );
UpdateStringProperty( mRootNode, kProject );
UpdateBoolProperty( mRootNode, kNoGood );
UpdateBoolProperty( mRootNode, kCircled );
// speed node children
XML_Node * speedNode = mRootNode->GetNamedElement( "", speedTagName );
if ( speedNode ) {
UpdateIntegerProperty( speedNode, kFileSampleRate );
UpdateIntegerProperty( speedNode, kAudioBitDepth );
UpdateStringProperty( speedNode, kTimeCodeFlag );
UpdateStringProperty( speedNode, kTimeCodeRate );
UpdateIntegerProperty( speedNode, kTimeStampSampleRate );
UpdateIntegerProperty( speedNode, kTimeStampSampleSinceMidnightLow );
UpdateIntegerProperty( speedNode, kTimeStampSampleSinceMidnightHigh );
}
// bext node children
XML_Node * bextNode = mRootNode->GetNamedElement( "", bextTagName );
if ( bextNode ) {
UpdateStringProperty( bextNode, kBWFDescription );
UpdateStringProperty( bextNode, kBWFOriginator );
UpdateStringProperty( bextNode, kBWFOriginatorReference );
UpdateStringProperty( bextNode, kBWFOriginationDate );
UpdateStringProperty( bextNode, kBWFOriginationTime );
UpdateIntegerProperty( bextNode, kBWFTimeReferenceLow );
UpdateIntegerProperty( bextNode, kBWFTimeReferenceHigh );
UpdateIntegerProperty( bextNode, kBWFVersion );
UpdateStringProperty( bextNode, kBWFHistory );
UpdateStringProperty( bextNode, kBWFUMID );
}
}
bool iXMLMetadata::valueValid( XMP_Uns32 id, ValueObject * valueObj ) {
switch( id ) {
case kTape:
return validateStringSize( valueObj );
break;
case kTake:
return validateInt( valueObj );
break;
case kScene:
return validateStringSize( valueObj );
break;
case kNote:
return validateStringSize( valueObj );
break;
case kProject:
return validateStringSize( valueObj );
break;
case kNoGood:
return validateBool( valueObj );
break;
case kFileSampleRate:
return validateInt( valueObj );
break;
case kAudioBitDepth:
return validateInt( valueObj );
break;
case kCircled:
return validateBool( valueObj );
break;
case kBWFDescription:
case kBWFOriginator:
case kBWFOriginatorReference:
// string length can be anything but while setting it will be trimmed.
return validateStringSize( valueObj );
break;
case kBWFOriginationDate:
return validateDate( valueObj );
break;
case kBWFOriginationTime:
return validateTime( valueObj );
break;
case kBWFTimeReferenceLow:
case kBWFTimeReferenceHigh:
return validateInt( valueObj, 0, Max_XMP_Uns32 );
break;
case kBWFVersion:
return validateInt( valueObj, 0, Max_XMP_Uns16 );
break;
case kBWFUMID:
return validateUMID( valueObj );
break;
case kBWFHistory:
return validateStringSize( valueObj );
break;
case kTimeCodeFlag:
return validateTimeCodeFlag( valueObj );
break;
case kTimeCodeRate:
return validateRational( valueObj );
break;
case kTimeStampSampleRate:
return validateInt( valueObj );
break;
case kTimeStampSampleSinceMidnightHigh:
case kTimeStampSampleSinceMidnightLow:
return validateInt( valueObj, 0, Max_XMP_Uns32 );
break;
default:
return false;
break;
}
}
void iXMLMetadata::valueModify( XMP_Uns32 id, ValueObject * value ) {
switch( id ) {
case kBWFDescription:
shortenString( value, 256 );
break;
case kBWFOriginator:
shortenString( value, 32 );
break;
case kBWFOriginatorReference:
shortenString( value, 32 );
break;
case kBWFUMID:
shortenString( value, 128 );
break;
default:
// do nothing
break;
}
}
bool iXMLMetadata::validateStringSize( ValueObject * value, size_t minSize /*= 1*/, size_t maxSize /*= std::string::npos */ ) {
TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(value);
if ( strObj ) {
const std::string * strPtr = &strObj->getValue();
size_t sizeOfString = strPtr->size();
if ( sizeOfString < minSize ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: length of string is less than expected" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
} else if ( sizeOfString > maxSize ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: length of string is more than expected" );
NotifyClient( kXMPErrSev_Recoverable, error);
return false;
} else {
return true;
}
} else { // object is not of string type
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected string value" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
return false;
}
bool iXMLMetadata::validateInt( ValueObject * value, XMP_Uns64 minValue /*= 0*/, XMP_Uns64 maxValue /*= Max_XMP_Uns64*/ ) {
TValueObject< XMP_Uns64 > * valuePtr = dynamic_cast< TValueObject< XMP_Uns64 > * > ( value );
if ( valuePtr ) {
XMP_Uns64 uValue = valuePtr->getValue();
if ( uValue < minValue ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node integer value is less than allowed" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
} else if ( uValue > maxValue ) {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node integer value is more than allowed" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
} else {
return true;
}
} else {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected XMP_Uns64 value" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
}
bool iXMLMetadata::validateBool( ValueObject * value ) {
// just check typecasts is possible or not
TValueObject< bool > * boolValuePtr = dynamic_cast< TValueObject< bool > * > ( value );
if ( boolValuePtr ) {
return true;
} else {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected bool value" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
}
void iXMLMetadata::shortenString( ValueObject * value, size_t lengthOfString ) {
TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value );
if ( strObj ) {
const std::string * strPtr = &strObj->getValue();
size_t sizeOfString = strPtr->size();
if ( sizeOfString > lengthOfString ) {
std::string newString;
newString.append( *strPtr, 0, lengthOfString );
strObj->setValue( newString );
}
}
}
static bool charIsNumber( const char & ch ) {
if ( ch >= '0' && ch <= '9' )
return true;
else
return false;
}
bool iXMLMetadata::validateDate( ValueObject * value ) {
bool stringOK = validateStringSize( value, 10, 10 );
if ( stringOK ) {
TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value );
const std::string * strPtr = &strObj->getValue();
// check 0,1,2,3,5,6,8,9 elements are integer
for ( size_t i = 0; i < 10; i++ ) {
if ( i == 4 || i == 7 )
continue;
stringOK = charIsNumber( strPtr->operator[]( i ) );
if ( !stringOK ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a number character" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
}
return stringOK;
}
return false;
}
bool iXMLMetadata::validateTime( ValueObject * value ) {
bool stringOK = validateStringSize( value, 8, 8 );
if ( stringOK ) {
TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value );
const std::string * strPtr = &strObj->getValue();
// check 0,1,3,4,6,7 elements are integer
for ( size_t i = 0; i < 8; i++ ) {
if ( i == 2 || i == 5 )
continue;
stringOK = charIsNumber( strPtr->operator[]( i ) );
if ( !stringOK ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a number character" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
}
return stringOK;
}
return false;
}
static bool charIsHexDigit( const char & ch ) {
if ( charIsNumber( ch ) ) {
return true;
} else {
if ( ch >= 'A' && ch <= 'F' )
return true;
else if ( ch >= 'a' && ch <= 'f' )
return true;
else
return false;
}
}
bool iXMLMetadata::validateUMID( ValueObject * value ) {
bool stringOK = validateStringSize( value );
if ( stringOK ) {
// max size to consider is 128
TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value );
const std::string * strPtr = &strObj->getValue();
size_t effectiveLength = strPtr->size();
if ( effectiveLength > 128 )
effectiveLength = 128;
// first check length needs to even
if ( effectiveLength % 2 == 0 ) {
for ( size_t i = 0; i < effectiveLength; i++ ) {
stringOK = charIsHexDigit( strPtr->operator[]( i ) );
if ( !stringOK ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a hex character" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
}
return stringOK;
} else {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the hex string length to be even" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
}
return false;
}
std::string iXMLMetadata::ParseStringValue( XML_Node * parentNode, XMP_Uns32 id ) {
std::string nodeValue;
XML_Node * node = parentNode->GetNamedElement( "", tagNames[ id ] );
if ( node ) {
if ( node->IsLeafContentNode() && node->content.size() != 0 ) {
size_t lengthOfValue = node->content[0]->value.size();
if ( lengthOfValue > 0 ) {
nodeValue = node->content[0]->value;
}
} else {
XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node was supposed to be a leaf node" );
NotifyClient( kXMPErrSev_Recoverable, error );
}
}
return nodeValue;
}
bool iXMLMetadata::validateTimeCodeFlag( ValueObject * value ) {
bool returnValue = validateStringSize( value, 2, 3 );
if ( returnValue ) {
TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(value);
if ( strObj ) {
const std::string * strPtr = &strObj->getValue();
if ( strPtr->compare( "DF" ) == 0 )
return true;
else if ( strPtr->compare( "NDF" ) == 0 )
return true;
}
}
return false;
}
bool iXMLMetadata::validateRational( ValueObject * value ) {
bool returnValue = validateStringSize( value, 3 );
if ( returnValue ) {
TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(value);
if ( strObj ) {
const std::string * strPtr = &strObj->getValue();
size_t posOfSlash = strPtr->find( "/" );
if ( posOfSlash == std::string::npos || posOfSlash == strPtr->size() - 1 || posOfSlash == 0 ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: node value was supposed to be in a fractional format" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
for ( size_t i = 0; i < strPtr->size(); i++ ) {
if ( i == posOfSlash )
continue;
returnValue = charIsNumber( strPtr->operator[]( i ) );
if ( ! returnValue ) {
XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a number character" );
NotifyClient( kXMPErrSev_Recoverable, error );
return false;
}
}
return returnValue;
}
}
return false;
}
}