|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// Copyright 2002-2007 Adobe Systems Incorporated
|
|
Packit Service |
21b5d1 |
// All Rights Reserved.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
|
|
Packit Service |
21b5d1 |
// of the Adobe license agreement accompanying it.
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#include "XMP_Environment.h" // ! This must be the first include!
|
|
Packit Service |
21b5d1 |
#include "XMP_Version.h"
|
|
Packit Service |
21b5d1 |
#include "XMPCore_Impl.hpp"
|
|
Packit Service |
21b5d1 |
#include "XMPMeta.hpp" // *** For use of GetNamespacePrefix in FindSchemaNode.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#include "UnicodeInlines.incl_cpp"
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#include <algorithm>
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
using namespace std;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#if XMP_WinBuild
|
|
Packit Service |
21b5d1 |
#ifdef _MSC_VER
|
|
Packit Service |
21b5d1 |
#pragma warning ( disable : 4290 ) // C++ exception specification ignored except ... not __declspec(nothrow)
|
|
Packit Service |
21b5d1 |
#pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// *** Add debug codegen checks, e.g. that typical masking operations really work
|
|
Packit Service |
21b5d1 |
// *** Make option constants 0x...UL.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Internal code should be using #if with XMP_MacBuild, XMP_WinBuild, or XMP_UNIXBuild.
|
|
Packit Service |
21b5d1 |
// This is a sanity check in case of accidental use of *_ENV. Some clients use the poor
|
|
Packit Service |
21b5d1 |
// practice of defining the *_ENV macro with an empty value.
|
|
Packit Service |
21b5d1 |
#if defined ( MAC_ENV )
|
|
Packit Service |
21b5d1 |
#if ! MAC_ENV
|
|
Packit Service |
21b5d1 |
#error "MAC_ENV must be defined so that \"#if MAC_ENV\" is true"
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
#elif defined ( WIN_ENV )
|
|
Packit Service |
21b5d1 |
#if ! WIN_ENV
|
|
Packit Service |
21b5d1 |
#error "WIN_ENV must be defined so that \"#if WIN_ENV\" is true"
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
#elif defined ( UNIX_ENV )
|
|
Packit Service |
21b5d1 |
#if ! UNIX_ENV
|
|
Packit Service |
21b5d1 |
#error "UNIX_ENV must be defined so that \"#if UNIX_ENV\" is true"
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// Static Variables
|
|
Packit Service |
21b5d1 |
// ================
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Int32 sXMP_InitCount = 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_StringMap * sNamespaceURIToPrefixMap = 0;
|
|
Packit Service |
21b5d1 |
XMP_StringMap * sNamespacePrefixToURIMap = 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_AliasMap * sRegisteredAliasMap = 0; // Needed by XMPIterator.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_VarString * sOutputNS = 0;
|
|
Packit Service |
21b5d1 |
XMP_VarString * sOutputStr = 0;
|
|
Packit Service |
21b5d1 |
XMP_VarString * sExceptionMessage = 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Mutex sXMPCoreLock;
|
|
Packit Service |
21b5d1 |
int sLockCount = 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#if TraceXMPCalls
|
|
Packit Service |
21b5d1 |
FILE * xmpOut = stderr;
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void * voidVoidPtr = 0; // Used to backfill null output parameters.
|
|
Packit Service |
21b5d1 |
XMP_StringPtr voidStringPtr = 0;
|
|
Packit Service |
21b5d1 |
XMP_StringLen voidStringLen = 0;
|
|
Packit Service |
21b5d1 |
XMP_OptionBits voidOptionBits = 0;
|
|
Packit Service |
21b5d1 |
XMP_Uns8 voidByte = 0;
|
|
Packit Service |
21b5d1 |
bool voidBool = 0;
|
|
Packit Service |
21b5d1 |
XMP_Int32 voidInt32 = 0;
|
|
Packit Service |
21b5d1 |
XMP_Int64 voidInt64 = 0;
|
|
Packit Service |
21b5d1 |
double voidDouble = 0.0;
|
|
Packit Service |
21b5d1 |
XMP_DateTime voidDateTime;
|
|
Packit Service |
21b5d1 |
WXMP_Result void_wResult;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// Mutex Utilities
|
|
Packit Service |
21b5d1 |
// ===============
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// ! Note that the mutex need not be "recursive", allowing the same thread to acquire it multiple
|
|
Packit Service |
21b5d1 |
// ! times. There is a single XMP lock which is acquired in the wrapper classes. Internal calls
|
|
Packit Service |
21b5d1 |
// ! never go back out to the wrappers.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#if XMP_WinBuild
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
bool XMP_InitMutex ( XMP_Mutex * mutex ) {
|
|
Packit Service |
21b5d1 |
InitializeCriticalSection ( mutex );
|
|
Packit Service |
21b5d1 |
return true;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void XMP_TermMutex ( XMP_Mutex & mutex ) {
|
|
Packit Service |
21b5d1 |
DeleteCriticalSection ( &mutex );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void XMP_EnterCriticalRegion ( XMP_Mutex & mutex ) {
|
|
Packit Service |
21b5d1 |
EnterCriticalSection ( &mutex );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void XMP_ExitCriticalRegion ( XMP_Mutex & mutex ) {
|
|
Packit Service |
21b5d1 |
LeaveCriticalSection ( &mutex );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#else
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Use pthread for both Mac and generic UNIX.
|
|
Packit Service |
21b5d1 |
// ! Would be nice to specify PTHREAD_MUTEX_ERRORCHECK, but the POSIX documentation is useless.
|
|
Packit Service |
21b5d1 |
// ! Would be OK but overkill to specify PTHREAD_MUTEX_RECURSIVE.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
bool XMP_InitMutex ( XMP_Mutex * mutex ) {
|
|
Packit Service |
21b5d1 |
int err = pthread_mutex_init ( mutex, 0 );
|
|
Packit Service |
21b5d1 |
return (err == 0 );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void XMP_TermMutex ( XMP_Mutex & mutex ) {
|
|
Packit Service |
21b5d1 |
(void) pthread_mutex_destroy ( &mutex );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void XMP_EnterCriticalRegion ( XMP_Mutex & mutex ) {
|
|
Packit Service |
21b5d1 |
int err = pthread_mutex_lock ( &mutex );
|
|
Packit Service |
21b5d1 |
if ( err != 0 ) XMP_Throw ( "XMP_EnterCriticalRegion - pthread_mutex_lock failure", kXMPErr_ExternalFailure );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void XMP_ExitCriticalRegion ( XMP_Mutex & mutex ) {
|
|
Packit Service |
21b5d1 |
int err = pthread_mutex_unlock ( &mutex );
|
|
Packit Service |
21b5d1 |
if ( err != 0 ) XMP_Throw ( "XMP_ExitCriticalRegion - pthread_mutex_unlock failure", kXMPErr_ExternalFailure );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// Local Utilities
|
|
Packit Service |
21b5d1 |
// ===============
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// VerifyXPathRoot
|
|
Packit Service |
21b5d1 |
// ---------------
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Set up the first 2 components of the expanded XPath. Normalizes the various cases of using the
|
|
Packit Service |
21b5d1 |
// full schema URI and/or a qualified root property name. Returns true for normal processing. If
|
|
Packit Service |
21b5d1 |
// allowUnknownSchemaNS is true and the schema namespace is not registered, false is returned. If
|
|
Packit Service |
21b5d1 |
// allowUnknownSchemaNS is false and the schema namespace is not registered, an exception is thrown.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// *** Should someday check the full syntax.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static void
|
|
Packit Service |
21b5d1 |
VerifyXPathRoot ( XMP_StringPtr schemaURI,
|
|
Packit Service |
21b5d1 |
XMP_StringPtr propName,
|
|
Packit Service |
21b5d1 |
XMP_ExpandedXPath * expandedXPath )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
// Do some basic checks on the URI and name. Try to lookup the URI. See if the name is qualified.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (schemaURI != 0) && (propName != 0) && (*propName != 0) );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (expandedXPath != 0) && (expandedXPath->empty()) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( *schemaURI == 0 ) XMP_Throw ( "Schema namespace URI is required", kXMPErr_BadSchema );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (*propName == '?') || (*propName == '@') ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Top level name must not be a qualifier", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
for ( XMP_StringPtr ch = propName; *ch != 0; ++ch ) {
|
|
Packit Service |
21b5d1 |
if ( (*ch == '/') || (*ch == '[') ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Top level name must be simple", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_StringMapPos uriPos = sNamespaceURIToPrefixMap->find ( XMP_VarString ( schemaURI ) );
|
|
Packit Service |
21b5d1 |
if ( uriPos == sNamespaceURIToPrefixMap->end() ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Unregistered schema namespace URI", kXMPErr_BadSchema );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_StringPtr colonPos = propName;
|
|
Packit Service |
21b5d1 |
while ( (*colonPos != 0) && (*colonPos != ':') ) ++colonPos;
|
|
Packit Service |
21b5d1 |
VerifySimpleXMLName ( propName, colonPos ); // Verify the part before any colon.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Verify the various URI and prefix combinations. Initialize the expanded XPath.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( *colonPos == 0 ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// The propName is unqualified, use the schemaURI and associated prefix.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
|
|
Packit Service |
21b5d1 |
expandedXPath->push_back ( XPathStepInfo ( uriPos->second, 0 ) );
|
|
Packit Service |
21b5d1 |
(*expandedXPath)[kRootPropStep].step += propName;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// The propName is qualified. Make sure the prefix is legit. Use the associated URI and qualified name.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t prefixLen = colonPos - propName + 1; // ! Include the colon.
|
|
Packit Service |
21b5d1 |
VerifySimpleXMLName ( colonPos+1, colonPos+strlen(colonPos) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_VarString prefix ( propName, prefixLen );
|
|
Packit Service |
21b5d1 |
XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( prefix );
|
|
Packit Service |
21b5d1 |
if ( prefixPos == sNamespacePrefixToURIMap->end() ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Unknown schema namespace prefix", kXMPErr_BadSchema );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( prefix != uriPos->second ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Schema namespace URI and prefix mismatch", kXMPErr_BadSchema );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
|
|
Packit Service |
21b5d1 |
expandedXPath->push_back ( XPathStepInfo ( propName, 0 ) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // VerifyXPathRoot
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// VerifyQualName
|
|
Packit Service |
21b5d1 |
// --------------
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static void
|
|
Packit Service |
21b5d1 |
VerifyQualName ( XMP_StringPtr qualName, XMP_StringPtr nameEnd )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
if ( qualName >= nameEnd ) XMP_Throw ( "Empty qualified name", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_StringPtr colonPos = qualName;
|
|
Packit Service |
21b5d1 |
while ( (colonPos < nameEnd) && (*colonPos != ':') ) ++colonPos;
|
|
Packit Service |
21b5d1 |
if ( (colonPos == qualName) || (colonPos >= nameEnd) ) XMP_Throw ( "Ill-formed qualified name", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
VerifySimpleXMLName ( qualName, colonPos );
|
|
Packit Service |
21b5d1 |
VerifySimpleXMLName ( colonPos+1, nameEnd );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t prefixLen = colonPos - qualName + 1; // ! Include the colon.
|
|
Packit Service |
21b5d1 |
XMP_VarString prefix ( qualName, prefixLen );
|
|
Packit Service |
21b5d1 |
XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( prefix );
|
|
Packit Service |
21b5d1 |
if ( prefixPos == sNamespacePrefixToURIMap->end() ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Unknown namespace prefix for qualified name", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // VerifyQualName
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// FindIndexedItem
|
|
Packit Service |
21b5d1 |
// ---------------
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// [index] An element of an array.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Support the implicit creation of a new last item.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static XMP_Index
|
|
Packit Service |
21b5d1 |
FindIndexedItem ( XMP_Node * arrayNode, const XMP_VarString & indexStep, bool createNodes )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Index index = 0;
|
|
Packit Service |
21b5d1 |
size_t chLim = indexStep.size() - 1;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (chLim >= 2) && (indexStep[0] == '[') && (indexStep[chLim] == ']') );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( size_t chNum = 1; chNum != chLim; ++chNum ) {
|
|
Packit Service |
21b5d1 |
XMP_Assert ( ('0' <= indexStep[chNum]) && (indexStep[chNum] <= '9') );
|
|
Packit Service |
21b5d1 |
index = (index * 10) + (indexStep[chNum] - '0');
|
|
Packit Service |
21b5d1 |
if ( index < 0 ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Array index overflow", kXMPErr_BadXPath ); // ! Overflow, not truly negative.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
--index; // Change to a C-style, zero based index.
|
|
Packit Service |
21b5d1 |
if ( index < 0 ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (index == (XMP_Index)arrayNode->children.size()) && createNodes ) { // Append a new last+1 node.
|
|
Packit Service |
21b5d1 |
XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, kXMP_NewImplicitNode );
|
|
Packit Service |
21b5d1 |
arrayNode->children.push_back ( newItem );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// ! Don't throw here for a too large index. SetProperty will throw, GetProperty will not.
|
|
Packit Service |
21b5d1 |
if ( index >= (XMP_Index)arrayNode->children.size() ) index = -1;
|
|
Packit Service |
21b5d1 |
return index;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // FindIndexedItem
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// SplitNameAndValue
|
|
Packit Service |
21b5d1 |
// -----------------
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Split the name and value parts for field and qualifier selectors:
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// [qualName="value"] An element in an array of structs, chosen by a field value.
|
|
Packit Service |
21b5d1 |
// [?qualName="value"] An element in an array, chosen by a qualifier value.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// The value portion is a string quoted by ''' or '"'. The value may contain any character including
|
|
Packit Service |
21b5d1 |
// a doubled quoting character. The value may be empty.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static void
|
|
Packit Service |
21b5d1 |
SplitNameAndValue ( const XMP_VarString & selStep, XMP_VarString * nameStr, XMP_VarString * valueStr )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_StringPtr partBegin = selStep.c_str();
|
|
Packit Service |
21b5d1 |
XMP_StringPtr partEnd;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
const XMP_StringPtr valueEnd = partBegin + (selStep.size() - 2);
|
|
Packit Service |
21b5d1 |
const char quote = *valueEnd;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (*partBegin == '[') && (*(valueEnd+1) == ']') );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (selStep.size() >= 6) && ((quote == '"') || (quote == '\'')) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Extract the name part.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
++partBegin; // Skip the opening '['.
|
|
Packit Service |
21b5d1 |
if ( *partBegin == '?' ) ++partBegin;
|
|
Packit Service |
21b5d1 |
for ( partEnd = partBegin+1; *partEnd != '='; ++partEnd ) {};
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
nameStr->assign ( partBegin, (partEnd - partBegin) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Extract the value part, reducing doubled quotes.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( *(partEnd+1) == quote );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
partBegin = partEnd + 2;
|
|
Packit Service |
21b5d1 |
valueStr->erase();
|
|
Packit Service |
21b5d1 |
valueStr->reserve ( valueEnd - partBegin ); // Maximum length, don't optimize doubled quotes.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( partEnd = partBegin; partEnd < valueEnd; ++partEnd ) {
|
|
Packit Service |
21b5d1 |
if ( (*partEnd == quote) && (*(partEnd+1) == quote) ) {
|
|
Packit Service |
21b5d1 |
++partEnd;
|
|
Packit Service |
21b5d1 |
valueStr->append ( partBegin, (partEnd - partBegin) );
|
|
Packit Service |
21b5d1 |
partBegin = partEnd+1; // ! Loop will increment partEnd again.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
valueStr->append ( partBegin, (partEnd - partBegin) ); // ! The loop does not add the last part.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // SplitNameAndValue
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// LookupQualSelector
|
|
Packit Service |
21b5d1 |
// ------------------
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// [?qualName="value"] An element in an array, chosen by a qualifier value.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Note that we don't create implicit nodes for qualifier selectors, so no CreateNodes parameter.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static XMP_Index
|
|
Packit Service |
21b5d1 |
LookupQualSelector ( XMP_Node * arrayNode, const XMP_VarString & qualName, XMP_VarString & qualValue )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Index index;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( qualName == "xml:lang" ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// *** Should check that the value is legit RFC 1766/3066.
|
|
Packit Service |
21b5d1 |
NormalizeLangValue ( &qualValue );
|
|
Packit Service |
21b5d1 |
index = LookupLangItem ( arrayNode, qualValue ) ;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Index itemLim;
|
|
Packit Service |
21b5d1 |
for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
const XMP_Node * currItem = arrayNode->children[index];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currItem->parent == arrayNode );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t q, qualLim;
|
|
Packit Service |
21b5d1 |
for ( q = 0, qualLim = currItem->qualifiers.size(); q != qualLim; ++q ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * currQual = currItem->qualifiers[q];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currQual->parent == currItem );
|
|
Packit Service |
21b5d1 |
if ( currQual->name != qualName ) continue;
|
|
Packit Service |
21b5d1 |
if ( currQual->value == qualValue ) break; // Exit qual loop.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( q != qualLim ) break; // Exit child loop, found an item with a matching qualifier.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( index == itemLim ) index = -1;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
return index;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // LookupQualSelector
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// FollowXPathStep
|
|
Packit Service |
21b5d1 |
// ---------------
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// After processing by ExpandXPath, a step can be of these forms:
|
|
Packit Service |
21b5d1 |
// qualName A top level property or struct field.
|
|
Packit Service |
21b5d1 |
// [index] An element of an array.
|
|
Packit Service |
21b5d1 |
// [last()] The last element of an array.
|
|
Packit Service |
21b5d1 |
// [qualName="value"] An element in an array of structs, chosen by a field value.
|
|
Packit Service |
21b5d1 |
// [?qualName="value"] An element in an array, chosen by a qualifier value.
|
|
Packit Service |
21b5d1 |
// ?qualName A general qualifier.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Find the appropriate child node, resolving aliases, and optionally creating nodes.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static XMP_Node *
|
|
Packit Service |
21b5d1 |
FollowXPathStep ( XMP_Node * parentNode,
|
|
Packit Service |
21b5d1 |
const XMP_ExpandedXPath & fullPath,
|
|
Packit Service |
21b5d1 |
size_t stepNum,
|
|
Packit Service |
21b5d1 |
bool createNodes,
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos * ptrPos,
|
|
Packit Service |
21b5d1 |
bool aliasedArrayItem = false )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Node * nextNode = 0;
|
|
Packit Service |
21b5d1 |
const XPathStepInfo & nextStep = fullPath[stepNum];
|
|
Packit Service |
21b5d1 |
XMP_Index index = 0;
|
|
Packit Service |
21b5d1 |
XMP_OptionBits stepKind = nextStep.options & kXMP_StepKindMask;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (kXMP_StructFieldStep <= stepKind) && (stepKind <= kXMP_FieldSelectorStep) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( stepKind == kXMP_StructFieldStep ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
nextNode = FindChildNode ( parentNode, nextStep.step.c_str(), createNodes, ptrPos );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else if ( stepKind == kXMP_QualifierStep ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_StringPtr qualStep = nextStep.step.c_str();
|
|
Packit Service |
21b5d1 |
XMP_Assert ( *qualStep == '?' );
|
|
Packit Service |
21b5d1 |
++qualStep;
|
|
Packit Service |
21b5d1 |
nextNode = FindQualifierNode ( parentNode, qualStep, createNodes, ptrPos );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// This is an array indexing step. First get the index, then get the node.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( ! (parentNode->options & kXMP_PropValueIsArray) ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Indexing applied to non-array", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( stepKind == kXMP_ArrayIndexStep ) {
|
|
Packit Service |
21b5d1 |
index = FindIndexedItem ( parentNode, nextStep.step, createNodes );
|
|
Packit Service |
21b5d1 |
} else if ( stepKind == kXMP_ArrayLastStep ) {
|
|
Packit Service |
21b5d1 |
index = parentNode->children.size() - 1;
|
|
Packit Service |
21b5d1 |
} else if ( stepKind == kXMP_FieldSelectorStep ) {
|
|
Packit Service |
21b5d1 |
XMP_VarString fieldName, fieldValue;
|
|
Packit Service |
21b5d1 |
SplitNameAndValue ( nextStep.step, &fieldName, &fieldValue );
|
|
Packit Service |
21b5d1 |
index = LookupFieldSelector ( parentNode, fieldName.c_str(), fieldValue.c_str() );
|
|
Packit Service |
21b5d1 |
} else if ( stepKind == kXMP_QualSelectorStep ) {
|
|
Packit Service |
21b5d1 |
XMP_VarString qualName, qualValue;
|
|
Packit Service |
21b5d1 |
SplitNameAndValue ( nextStep.step, &qualName, &qualValue );
|
|
Packit Service |
21b5d1 |
index = LookupQualSelector ( parentNode, qualName, qualValue );
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Unknown array indexing step in FollowXPathStep", kXMPErr_InternalFailure );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (0 <= index) && (index <= (XMP_Index)parentNode->children.size()) ) nextNode = parentNode->children[index];
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (index == -1) && createNodes && aliasedArrayItem && (stepKind == kXMP_QualSelectorStep) ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// An ugly special case without an obvious better place to be. We have an alias to the
|
|
Packit Service |
21b5d1 |
// x-default item of an alt-text array. A simple reference via SetProperty must create
|
|
Packit Service |
21b5d1 |
// the x-default item if it does not yet exist.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( parentNode->options & kXMP_PropArrayIsAltText );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (stepNum == 2) && (nextStep.step == "[?xml:lang=\"x-default\"]") );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
nextNode = new XMP_Node ( parentNode, kXMP_ArrayItemName,
|
|
Packit Service |
21b5d1 |
(kXMP_PropHasQualifiers | kXMP_PropHasLang | kXMP_NewImplicitNode) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node * langQual = new XMP_Node ( nextNode, "xml:lang", "x-default", kXMP_PropIsQualifier );
|
|
Packit Service |
21b5d1 |
nextNode->qualifiers.push_back ( langQual );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( parentNode->children.empty() ) {
|
|
Packit Service |
21b5d1 |
parentNode->children.push_back ( nextNode );
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
parentNode->children.insert ( parentNode->children.begin(), nextNode );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
index = 0; // ! C-style index! The x-default item is always first.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (nextNode != 0) && (ptrPos != 0) ) *ptrPos = parentNode->children.begin() + index;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (nextNode != 0) && (nextNode->options & kXMP_NewImplicitNode) ) {
|
|
Packit Service |
21b5d1 |
nextNode->options |= (nextStep.options & kXMP_PropArrayFormMask);
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (ptrPos == 0) || (nextNode == 0) || (nextNode == **ptrPos) );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (nextNode != 0) || (! createNodes) );
|
|
Packit Service |
21b5d1 |
return nextNode;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // FollowXPathStep
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// CheckImplicitStruct
|
|
Packit Service |
21b5d1 |
// -------------------
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static inline void
|
|
Packit Service |
21b5d1 |
CheckImplicitStruct ( XMP_Node * node,
|
|
Packit Service |
21b5d1 |
const XMP_ExpandedXPath & expandedXPath,
|
|
Packit Service |
21b5d1 |
size_t stepNum,
|
|
Packit Service |
21b5d1 |
size_t stepLim )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (stepNum < stepLim) &&
|
|
Packit Service |
21b5d1 |
((node->options & kXMP_PropCompositeMask) == 0) &&
|
|
Packit Service |
21b5d1 |
(GetStepKind ( expandedXPath[stepNum].options ) == kXMP_StructFieldStep) ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
node->options |= kXMP_PropValueIsStruct;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // CheckImplicitStruct
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// DeleteSubtree
|
|
Packit Service |
21b5d1 |
// -------------
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// *** Might be useful elsewhere?
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static void
|
|
Packit Service |
21b5d1 |
DeleteSubtree ( XMP_NodePtrPos rootNodePos )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Node * rootNode = *rootNodePos;
|
|
Packit Service |
21b5d1 |
XMP_Node * rootParent = rootNode->parent;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( ! (rootNode->options & kXMP_PropIsQualifier) ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
rootParent->children.erase ( rootNodePos );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
rootParent->qualifiers.erase ( rootNodePos );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( rootParent->options & kXMP_PropHasQualifiers);
|
|
Packit Service |
21b5d1 |
if ( rootParent->qualifiers.empty() ) rootParent->options ^= kXMP_PropHasQualifiers;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( rootNode->name == "xml:lang" ) {
|
|
Packit Service |
21b5d1 |
XMP_Assert ( rootParent->options & kXMP_PropHasLang);
|
|
Packit Service |
21b5d1 |
rootParent->options ^= kXMP_PropHasLang;
|
|
Packit Service |
21b5d1 |
} else if ( rootNode->name == "rdf:type" ) {
|
|
Packit Service |
21b5d1 |
XMP_Assert ( rootParent->options & kXMP_PropHasType);
|
|
Packit Service |
21b5d1 |
rootParent->options ^= kXMP_PropHasType;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
delete rootNode;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // DeleteSubtree
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// VerifySetOptions
|
|
Packit Service |
21b5d1 |
// ================
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Normalize and verify the option flags for SetProperty and similar functions. The allowed options
|
|
Packit Service |
21b5d1 |
// here are just those that apply to the property, that would be kept in the XMP_Node. Others that
|
|
Packit Service |
21b5d1 |
// affect the selection of the node or other processing must be removed by now. These are:
|
|
Packit Service |
21b5d1 |
// kXMP_InsertBeforeItem
|
|
Packit Service |
21b5d1 |
// kXMP_InsertAfterItem
|
|
Packit Service |
21b5d1 |
// kXMP_KeepQualifiers
|
|
Packit Service |
21b5d1 |
// kXMPUtil_AllowCommas
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
enum {
|
|
Packit Service |
21b5d1 |
kXMP_AllSetOptionsMask = (kXMP_PropValueIsURI |
|
|
Packit Service |
21b5d1 |
kXMP_PropValueIsStruct |
|
|
Packit Service |
21b5d1 |
kXMP_PropValueIsArray |
|
|
Packit Service |
21b5d1 |
kXMP_PropArrayIsOrdered |
|
|
Packit Service |
21b5d1 |
kXMP_PropArrayIsAlternate |
|
|
Packit Service |
21b5d1 |
kXMP_PropArrayIsAltText |
|
|
Packit Service |
21b5d1 |
kXMP_DeleteExisting)
|
|
Packit Service |
21b5d1 |
};
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_OptionBits
|
|
Packit Service |
21b5d1 |
VerifySetOptions ( XMP_OptionBits options, XMP_StringPtr propValue )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( options & kXMP_PropArrayIsAltText ) options |= kXMP_PropArrayIsAlternate;
|
|
Packit Service |
21b5d1 |
if ( options & kXMP_PropArrayIsAlternate ) options |= kXMP_PropArrayIsOrdered;
|
|
Packit Service |
21b5d1 |
if ( options & kXMP_PropArrayIsOrdered ) options |= kXMP_PropValueIsArray;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( options & ~kXMP_AllSetOptionsMask ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Unrecognized option flags", kXMPErr_BadOptions );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (options & kXMP_PropValueIsStruct) && (options & kXMP_PropValueIsArray) ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "IsStruct and IsArray options are mutually exclusive", kXMPErr_BadOptions );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (options & kXMP_PropValueOptionsMask) && (options & kXMP_PropCompositeMask) ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Structs and arrays can't have \"value\" options", kXMPErr_BadOptions );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (propValue != 0) && (options & kXMP_PropCompositeMask) ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Structs and arrays can't have string values", kXMPErr_BadOptions );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
return options;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // VerifySetOptions
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// ComposeXPath
|
|
Packit Service |
21b5d1 |
// ============
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Compose the canonical string form of an expanded XPath expression.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
extern void
|
|
Packit Service |
21b5d1 |
ComposeXPath ( const XMP_ExpandedXPath & expandedXPath,
|
|
Packit Service |
21b5d1 |
XMP_VarString * stringXPath )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
*stringXPath = expandedXPath[kRootPropStep].step;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( size_t index = kRootPropStep+1; index < expandedXPath.size(); ++index ) {
|
|
Packit Service |
21b5d1 |
const XPathStepInfo & currStep = expandedXPath[index];
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
switch ( currStep.options & kXMP_StepKindMask ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
case kXMP_StructFieldStep :
|
|
Packit Service |
21b5d1 |
case kXMP_QualifierStep :
|
|
Packit Service |
21b5d1 |
*stringXPath += '/';
|
|
Packit Service |
21b5d1 |
*stringXPath += currStep.step;
|
|
Packit Service |
21b5d1 |
break;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
case kXMP_ArrayIndexStep :
|
|
Packit Service |
21b5d1 |
case kXMP_ArrayLastStep :
|
|
Packit Service |
21b5d1 |
case kXMP_QualSelectorStep :
|
|
Packit Service |
21b5d1 |
case kXMP_FieldSelectorStep :
|
|
Packit Service |
21b5d1 |
*stringXPath += currStep.step;
|
|
Packit Service |
21b5d1 |
break;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
default:
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Unexpected", kXMPErr_InternalFailure );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // ComposeXPath
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// ExpandXPath
|
|
Packit Service |
21b5d1 |
// ===========
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Split an XPath expression apart at the conceptual steps, adding the root namespace prefix to the
|
|
Packit Service |
21b5d1 |
// first property component. The schema URI is put in the first (0th) slot in the expanded XPath.
|
|
Packit Service |
21b5d1 |
// Check if the top level component is an alias, but don't resolve it.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// In the most verbose case steps are separated by '/', and each step can be of these forms:
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// qualName A top level property or struct field.
|
|
Packit Service |
21b5d1 |
// *[index] An element of an array.
|
|
Packit Service |
21b5d1 |
// *[last()] The last element of an array.
|
|
Packit Service |
21b5d1 |
// *[fieldName="value"] An element in an array of structs, chosen by a field value.
|
|
Packit Service |
21b5d1 |
// *[@xml:lang="value"] An element in an alt-text array, chosen by the xml:lang qualifier.
|
|
Packit Service |
21b5d1 |
// *[?qualName="value"] An element in an array, chosen by a qualifier value.
|
|
Packit Service |
21b5d1 |
// @xml:lang An xml:lang qualifier.
|
|
Packit Service |
21b5d1 |
// ?qualName A general qualifier.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// The logic is complicated though by shorthand for arrays, the separating '/' and leading '*'
|
|
Packit Service |
21b5d1 |
// are optional. These are all equivalent: array/*[2] array/[2] array*[2] array[2]
|
|
Packit Service |
21b5d1 |
// All of these are broken into the 2 steps "array" and "[2]".
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// The value portion in the array selector forms is a string quoted by ''' or '"'. The value
|
|
Packit Service |
21b5d1 |
// may contain any character including a doubled quoting character. The value may be empty.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// The syntax isn't checked, but an XML name begins with a letter or '_', and contains letters,
|
|
Packit Service |
21b5d1 |
// digits, '.', '-', '_', and a bunch of special non-ASCII Unicode characters. An XML qualified
|
|
Packit Service |
21b5d1 |
// name is a pair of names separated by a colon.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void
|
|
Packit Service |
21b5d1 |
ExpandXPath ( XMP_StringPtr schemaNS,
|
|
Packit Service |
21b5d1 |
XMP_StringPtr propPath,
|
|
Packit Service |
21b5d1 |
XMP_ExpandedXPath * expandedXPath )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (schemaNS != 0) && (propPath != 0) && (*propPath != 0) && (expandedXPath != 0) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_StringPtr stepBegin, stepEnd;
|
|
Packit Service |
21b5d1 |
XMP_StringPtr qualName, nameEnd;
|
|
Packit Service |
21b5d1 |
XMP_VarString currStep;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
qualName = nameEnd = NULL;
|
|
Packit Service |
21b5d1 |
size_t resCount = 2; // Guess at the number of steps. At least 2, plus 1 for each '/' or '['.
|
|
Packit Service |
21b5d1 |
for ( stepEnd = propPath; *stepEnd != 0; ++stepEnd ) {
|
|
Packit Service |
21b5d1 |
if ( (*stepEnd == '/') || (*stepEnd == '[') ) ++resCount;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
expandedXPath->clear();
|
|
Packit Service |
21b5d1 |
expandedXPath->reserve ( resCount );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -------------------------------------------------------------------------------------------
|
|
Packit Service |
21b5d1 |
// Pull out the first component and do some special processing on it: add the schema namespace
|
|
Packit Service |
21b5d1 |
// prefix and see if it is an alias. The start must be a qualName.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
stepBegin = propPath;
|
|
Packit Service |
21b5d1 |
stepEnd = stepBegin;
|
|
Packit Service |
21b5d1 |
while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
|
|
Packit Service |
21b5d1 |
if ( stepEnd == stepBegin ) XMP_Throw ( "Empty initial XPath step", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
currStep.assign ( stepBegin, (stepEnd - stepBegin) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
VerifyXPathRoot ( schemaNS, currStep.c_str(), expandedXPath );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_OptionBits stepFlags = kXMP_StructFieldStep;
|
|
Packit Service |
21b5d1 |
if ( sRegisteredAliasMap->find ( (*expandedXPath)[kRootPropStep].step ) != sRegisteredAliasMap->end() ) {
|
|
Packit Service |
21b5d1 |
stepFlags |= kXMP_StepIsAlias;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
(*expandedXPath)[kRootPropStep].options |= stepFlags;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// -----------------------------------------------------
|
|
Packit Service |
21b5d1 |
// Now continue to process the rest of the XPath string.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
while ( *stepEnd != 0 ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
stepBegin = stepEnd;
|
|
Packit Service |
21b5d1 |
if ( *stepBegin == '/' ) ++stepBegin;
|
|
Packit Service |
21b5d1 |
if ( *stepBegin == '*' ) {
|
|
Packit Service |
21b5d1 |
++stepBegin;
|
|
Packit Service |
21b5d1 |
if ( *stepBegin != '[' ) XMP_Throw ( "Missing '[' after '*'", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
stepEnd = stepBegin;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( *stepBegin != '[' ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// A struct field or qualifier.
|
|
Packit Service |
21b5d1 |
qualName = stepBegin;
|
|
Packit Service |
21b5d1 |
while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
|
|
Packit Service |
21b5d1 |
nameEnd = stepEnd;
|
|
Packit Service |
21b5d1 |
stepFlags = kXMP_StructFieldStep; // ! Touch up later, also changing '@' to '?'.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// One of the array forms.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
++stepEnd; // Look at the character after the leading '['.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( ('0' <= *stepEnd) && (*stepEnd <= '9') ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// A numeric (decimal integer) array index.
|
|
Packit Service |
21b5d1 |
while ( (*stepEnd != 0) && ('0' <= *stepEnd) && (*stepEnd <= '9') ) ++stepEnd;
|
|
Packit Service |
21b5d1 |
if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for integer array index", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
stepFlags = kXMP_ArrayIndexStep;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Could be "[last()]" or one of the selector forms. Find the ']' or '='.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
while ( (*stepEnd != 0) && (*stepEnd != ']') && (*stepEnd != '=') ) ++stepEnd;
|
|
Packit Service |
21b5d1 |
if ( *stepEnd == 0 ) XMP_Throw ( "Missing ']' or '=' for array index", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( *stepEnd == ']' ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( strncmp ( "[last()", stepBegin, (stepEnd - stepBegin) ) != 0 ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Invalid non-numeric array index", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
stepFlags = kXMP_ArrayLastStep;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
qualName = stepBegin+1;
|
|
Packit Service |
21b5d1 |
nameEnd = stepEnd;
|
|
Packit Service |
21b5d1 |
++stepEnd; // Absorb the '=', remember the quote.
|
|
Packit Service |
21b5d1 |
const char quote = *stepEnd;
|
|
Packit Service |
21b5d1 |
if ( (quote != '\'') && (quote != '"') ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Invalid quote in array selector", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
++stepEnd; // Absorb the leading quote.
|
|
Packit Service |
21b5d1 |
while ( *stepEnd != 0 ) {
|
|
Packit Service |
21b5d1 |
if ( *stepEnd == quote ) {
|
|
Packit Service |
21b5d1 |
if ( *(stepEnd+1) != quote ) break;
|
|
Packit Service |
21b5d1 |
++stepEnd;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
++stepEnd;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( *stepEnd == 0 ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "No terminating quote for array selector", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
++stepEnd; // Absorb the trailing quote.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
stepFlags = kXMP_FieldSelectorStep; // ! Touch up later, also changing '@' to '?'.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for array index", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
++stepEnd;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( stepEnd == stepBegin ) XMP_Throw ( "Empty XPath step", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
currStep.assign ( stepBegin, (stepEnd - stepBegin) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( GetStepKind ( stepFlags ) == kXMP_StructFieldStep ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( currStep[0] == '@' ) {
|
|
Packit Service |
21b5d1 |
currStep[0] = '?';
|
|
Packit Service |
21b5d1 |
if ( currStep != "?xml:lang" ) XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( currStep[0] == '?' ) {
|
|
Packit Service |
21b5d1 |
++qualName;
|
|
Packit Service |
21b5d1 |
stepFlags = kXMP_QualifierStep;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
VerifyQualName ( qualName, nameEnd );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else if ( GetStepKind ( stepFlags ) == kXMP_FieldSelectorStep ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( currStep[1] == '@' ) {
|
|
Packit Service |
21b5d1 |
currStep[1] = '?';
|
|
Packit Service |
21b5d1 |
if ( strncmp ( currStep.c_str(), "[?xml:lang=", 11 ) != 0 ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( currStep[1] == '?' ) {
|
|
Packit Service |
21b5d1 |
++qualName;
|
|
Packit Service |
21b5d1 |
stepFlags = kXMP_QualSelectorStep;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
VerifyQualName ( qualName, nameEnd );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
expandedXPath->push_back ( XPathStepInfo ( currStep, stepFlags ) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // ExpandXPath
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// FindSchemaNode
|
|
Packit Service |
21b5d1 |
// ==============
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Find or create a schema node. Returns a pointer to the node, and optionally an iterator for the
|
|
Packit Service |
21b5d1 |
// node's position in the top level vector of schema nodes. The iterator is unchanged if no schema
|
|
Packit Service |
21b5d1 |
// node (null) is returned.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node *
|
|
Packit Service |
21b5d1 |
FindSchemaNode ( XMP_Node * xmpTree,
|
|
Packit Service |
21b5d1 |
XMP_StringPtr nsURI,
|
|
Packit Service |
21b5d1 |
bool createNodes,
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos * ptrPos /* = 0 */ )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Node * schemaNode = 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( xmpTree->parent == 0 );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( size_t schemaNum = 0, schemaLim = xmpTree->children.size(); schemaNum != schemaLim; ++schemaNum ) {
|
|
Packit Service |
21b5d1 |
XMP_Node * currSchema = xmpTree->children[schemaNum];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currSchema->parent == xmpTree );
|
|
Packit Service |
21b5d1 |
if ( currSchema->name == nsURI ) {
|
|
Packit Service |
21b5d1 |
schemaNode = currSchema;
|
|
Packit Service |
21b5d1 |
if ( ptrPos != 0 ) *ptrPos = xmpTree->children.begin() + schemaNum;
|
|
Packit Service |
21b5d1 |
break;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (schemaNode == 0) && createNodes ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
schemaNode = new XMP_Node ( xmpTree, nsURI, (kXMP_SchemaNode | kXMP_NewImplicitNode) );
|
|
Packit Service |
21b5d1 |
XMP_StringPtr prefixPtr;
|
|
Packit Service |
21b5d1 |
XMP_StringLen prefixLen;
|
|
Packit Service |
21b5d1 |
bool found = XMPMeta::GetNamespacePrefix ( nsURI, &prefixPtr, &prefixLen ); // *** Use map directly?
|
|
Packit Service |
21b5d1 |
XMP_Assert ( found );
|
|
Packit Service |
21b5d1 |
UNUSED(found);
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
schemaNode->value.assign ( prefixPtr, prefixLen );
|
|
Packit Service |
21b5d1 |
xmpTree->children.push_back ( schemaNode );
|
|
Packit Service |
21b5d1 |
if ( ptrPos != 0 ) *ptrPos = xmpTree->children.end() - 1;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
#if 0 // *** XMP_DebugBuild
|
|
Packit Service |
21b5d1 |
schemaNode->_valuePtr = schemaNode->value.c_str();
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (ptrPos == 0) || (schemaNode == 0) || (schemaNode == **ptrPos) );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (schemaNode != 0) || (! createNodes) );
|
|
Packit Service |
21b5d1 |
return schemaNode;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // FindSchemaNode
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// FindChildNode
|
|
Packit Service |
21b5d1 |
// =============
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Find or create a child node under a given parent node. Returns a pointer to the child node, and
|
|
Packit Service |
21b5d1 |
// optionally an iterator for the node's position in the parent's vector of children. The iterator
|
|
Packit Service |
21b5d1 |
// is unchanged if no child node (null) is returned.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node *
|
|
Packit Service |
21b5d1 |
FindChildNode ( XMP_Node * parent,
|
|
Packit Service |
21b5d1 |
XMP_StringPtr childName,
|
|
Packit Service |
21b5d1 |
bool createNodes,
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos * ptrPos /* = 0 */ )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Node * childNode = 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( ! (parent->options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) {
|
|
Packit Service |
21b5d1 |
if ( ! (parent->options & kXMP_NewImplicitNode) ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Named children only allowed for schemas and structs", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( parent->options & kXMP_PropValueIsArray ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Named children not allowed for arrays", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( ! createNodes ) { // *** Should be assert? If !createNodes, why is the parent a new implicit node?
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Parent is new implicit node, but createNodes is false", kXMPErr_InternalFailure );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
parent->options |= kXMP_PropValueIsStruct;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( size_t childNum = 0, childLim = parent->children.size(); childNum != childLim; ++childNum ) {
|
|
Packit Service |
21b5d1 |
XMP_Node * currChild = parent->children[childNum];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currChild->parent == parent );
|
|
Packit Service |
21b5d1 |
if ( currChild->name == childName ) {
|
|
Packit Service |
21b5d1 |
childNode = currChild;
|
|
Packit Service |
21b5d1 |
if ( ptrPos != 0 ) *ptrPos = parent->children.begin() + childNum;
|
|
Packit Service |
21b5d1 |
break;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (childNode == 0) && createNodes ) {
|
|
Packit Service |
21b5d1 |
childNode = new XMP_Node ( parent, childName, kXMP_NewImplicitNode );
|
|
Packit Service |
21b5d1 |
parent->children.push_back ( childNode );
|
|
Packit Service |
21b5d1 |
if ( ptrPos != 0 ) *ptrPos = parent->children.end() - 1;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (ptrPos == 0) || (childNode == 0) || (childNode == **ptrPos) );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (childNode != 0) || (! createNodes) );
|
|
Packit Service |
21b5d1 |
return childNode;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // FindChildNode
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// FindQualifierNode
|
|
Packit Service |
21b5d1 |
// =================
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Find or create a qualifier node under a given parent node. Returns a pointer to the qualifier node,
|
|
Packit Service |
21b5d1 |
// and optionally an iterator for the node's position in the parent's vector of qualifiers. The iterator
|
|
Packit Service |
21b5d1 |
// is unchanged if no qualifier node (null) is returned.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// ! On entry, the qualName parameter must not have the leading '?' from the XPath step.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node *
|
|
Packit Service |
21b5d1 |
FindQualifierNode ( XMP_Node * parent,
|
|
Packit Service |
21b5d1 |
XMP_StringPtr qualName,
|
|
Packit Service |
21b5d1 |
bool createNodes,
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos * ptrPos /* = 0 */ ) // *** Require ptrPos internally & remove checks?
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Node * qualNode = 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( *qualName != '?' );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( size_t qualNum = 0, qualLim = parent->qualifiers.size(); qualNum != qualLim; ++qualNum ) {
|
|
Packit Service |
21b5d1 |
XMP_Node * currQual = parent->qualifiers[qualNum];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currQual->parent == parent );
|
|
Packit Service |
21b5d1 |
if ( currQual->name == qualName ) {
|
|
Packit Service |
21b5d1 |
qualNode = currQual;
|
|
Packit Service |
21b5d1 |
if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.begin() + qualNum;
|
|
Packit Service |
21b5d1 |
break;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (qualNode == 0) && createNodes ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
qualNode = new XMP_Node ( parent, qualName, (kXMP_PropIsQualifier | kXMP_NewImplicitNode) );
|
|
Packit Service |
21b5d1 |
parent->options |= kXMP_PropHasQualifiers;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
const bool isLang = XMP_LitMatch ( qualName, "xml:lang" );
|
|
Packit Service |
21b5d1 |
const bool isType = XMP_LitMatch ( qualName, "rdf:type" );
|
|
Packit Service |
21b5d1 |
const bool isSpecial = isLang | isType;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( isLang ) {
|
|
Packit Service |
21b5d1 |
parent->options |= kXMP_PropHasLang;
|
|
Packit Service |
21b5d1 |
} else if ( isType ) {
|
|
Packit Service |
21b5d1 |
parent->options |= kXMP_PropHasType;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( parent->qualifiers.empty() || (! isSpecial) ) {
|
|
Packit Service |
21b5d1 |
parent->qualifiers.push_back ( qualNode );
|
|
Packit Service |
21b5d1 |
if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.end() - 1;
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos insertPos = parent->qualifiers.begin(); // ! Lang goes first, type after.
|
|
Packit Service |
21b5d1 |
if ( isType && (parent->options & kXMP_PropHasLang) ) ++insertPos; // *** Does insert at end() work?
|
|
Packit Service |
21b5d1 |
insertPos = parent->qualifiers.insert ( insertPos, qualNode );
|
|
Packit Service |
21b5d1 |
if ( ptrPos != 0 ) *ptrPos = insertPos;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (ptrPos == 0) || (qualNode == 0) || (qualNode == **ptrPos) );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (qualNode != 0) || (! createNodes) );
|
|
Packit Service |
21b5d1 |
return qualNode;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // FindQualifierNode
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// LookupFieldSelector
|
|
Packit Service |
21b5d1 |
// ===================
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// [fieldName="value"] An element in an array of structs, chosen by a field value.
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Note that we don't create implicit nodes for field selectors, so no CreateNodes parameter.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Index
|
|
Packit Service |
21b5d1 |
LookupFieldSelector ( const XMP_Node * arrayNode, XMP_StringPtr fieldName, XMP_StringPtr fieldValue )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Index index, itemLim;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
const XMP_Node * currItem = arrayNode->children[index];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currItem->parent == arrayNode );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( ! (currItem->options & kXMP_PropValueIsStruct) ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Field selector must be used on array of struct", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t f, fieldLim;
|
|
Packit Service |
21b5d1 |
for ( f = 0, fieldLim = currItem->children.size(); f != fieldLim; ++f ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * currField = currItem->children[f];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currField->parent == currItem );
|
|
Packit Service |
21b5d1 |
if ( currField->name != fieldName ) continue;
|
|
Packit Service |
21b5d1 |
if ( currField->value == fieldValue ) break; // Exit qual loop.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( f != fieldLim ) break; // Exit child loop, found an item with a matching qualifier.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( index == itemLim ) index = -1;
|
|
Packit Service |
21b5d1 |
return index;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // LookupFieldSelector
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// LookupLangItem
|
|
Packit Service |
21b5d1 |
// ==============
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// ! Assumes that the language value is already normalized.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Index
|
|
Packit Service |
21b5d1 |
LookupLangItem ( const XMP_Node * arrayNode, XMP_VarString & lang )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) { // *** Check for alt-text?
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "Language item must be used on array", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Index index = 0;
|
|
Packit Service |
21b5d1 |
XMP_Index itemLim = arrayNode->children.size();
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( ; index != itemLim; ++index ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * currItem = arrayNode->children[index];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( currItem->parent == arrayNode );
|
|
Packit Service |
21b5d1 |
if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) continue;
|
|
Packit Service |
21b5d1 |
if ( currItem->qualifiers[0]->value == lang ) break;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( index == itemLim ) index = -1;
|
|
Packit Service |
21b5d1 |
return index;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // LookupLangItem
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// FindNode
|
|
Packit Service |
21b5d1 |
// ========
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Follow an expanded path expression to find or create a node. Returns a pointer to the node, and
|
|
Packit Service |
21b5d1 |
// optionally an iterator for the node's position in the parent's vector of children or qualifiers.
|
|
Packit Service |
21b5d1 |
// The iterator is unchanged if no child node (null) is returned.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node *
|
|
Packit Service |
21b5d1 |
FindNode ( XMP_Node * xmpTree,
|
|
Packit Service |
21b5d1 |
const XMP_ExpandedXPath & expandedXPath,
|
|
Packit Service |
21b5d1 |
bool createNodes,
|
|
Packit Service |
21b5d1 |
XMP_OptionBits leafOptions /* = 0 */,
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos * ptrPos /* = 0 */ )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Node * currNode = 0;
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos currPos;
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos newSubPos; // Root of implicitly created subtree. Valid only if leaf is new.
|
|
Packit Service |
21b5d1 |
bool leafIsNew = false;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (leafOptions == 0) || createNodes );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( expandedXPath.empty() ) XMP_Throw ( "Empty XPath", kXMPErr_BadXPath );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t stepNum = 1; // By default start calling FollowXPathStep for the top level property step.
|
|
Packit Service |
21b5d1 |
size_t stepLim = expandedXPath.size();
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// The start of processing deals with the schema node and top level alias. If the top level step
|
|
Packit Service |
21b5d1 |
// is not an alias, lookup the expanded path's schema URI. Otherwise, lookup the expanded path
|
|
Packit Service |
21b5d1 |
// for the actual. While tempting, don't substitute the actual's path into the local one, don't
|
|
Packit Service |
21b5d1 |
// risk messing with the caller's use of that. Also don't call FindNode recursively, we need to
|
|
Packit Service |
21b5d1 |
// keep track of the root of the implicitly created subtree as we move down the path.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( ! (expandedXPath[kRootPropStep].options & kXMP_StepIsAlias) ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
currNode = FindSchemaNode ( xmpTree, expandedXPath[kSchemaStep].step.c_str(), createNodes, &currPos );
|
|
Packit Service |
21b5d1 |
if ( currNode == 0 ) return 0;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( currNode->options & kXMP_NewImplicitNode ) {
|
|
Packit Service |
21b5d1 |
currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
|
|
Packit Service |
21b5d1 |
if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
|
|
Packit Service |
21b5d1 |
leafIsNew = true; // If any parent is new, the leaf will be new also.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
stepNum = 2; // ! Continue processing the original path at the second level step.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_AliasMapPos aliasPos = sRegisteredAliasMap->find ( expandedXPath[kRootPropStep].step );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( aliasPos != sRegisteredAliasMap->end() );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
currNode = FindSchemaNode ( xmpTree, aliasPos->second[kSchemaStep].step.c_str(), createNodes, &currPos );
|
|
Packit Service |
21b5d1 |
if ( currNode == 0 ) goto EXIT;
|
|
Packit Service |
21b5d1 |
if ( currNode->options & kXMP_NewImplicitNode ) {
|
|
Packit Service |
21b5d1 |
currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
|
|
Packit Service |
21b5d1 |
if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
|
|
Packit Service |
21b5d1 |
leafIsNew = true; // If any parent is new, the leaf will be new also.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
currNode = FollowXPathStep ( currNode, aliasPos->second, 1, createNodes, &currPos );
|
|
Packit Service |
21b5d1 |
if ( currNode == 0 ) goto EXIT;
|
|
Packit Service |
21b5d1 |
if ( currNode->options & kXMP_NewImplicitNode ) {
|
|
Packit Service |
21b5d1 |
currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
|
|
Packit Service |
21b5d1 |
CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim );
|
|
Packit Service |
21b5d1 |
if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
|
|
Packit Service |
21b5d1 |
leafIsNew = true; // If any parent is new, the leaf will be new also.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_OptionBits arrayForm = aliasPos->second[kRootPropStep].options & kXMP_PropArrayFormMask;
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (arrayForm == 0) || (arrayForm & kXMP_PropValueIsArray) );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (arrayForm == 0) ? (aliasPos->second.size() == 2) : (aliasPos->second.size() == 3) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( arrayForm != 0 ) {
|
|
Packit Service |
21b5d1 |
currNode = FollowXPathStep ( currNode, aliasPos->second, 2, createNodes, &currPos, true );
|
|
Packit Service |
21b5d1 |
if ( currNode == 0 ) goto EXIT;
|
|
Packit Service |
21b5d1 |
if ( currNode->options & kXMP_NewImplicitNode ) {
|
|
Packit Service |
21b5d1 |
currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
|
|
Packit Service |
21b5d1 |
CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim );
|
|
Packit Service |
21b5d1 |
if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
|
|
Packit Service |
21b5d1 |
leafIsNew = true; // If any parent is new, the leaf will be new also.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Now follow the remaining steps of the original XPath.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// *** ??? Change all the num/lim loops back to num
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
try {
|
|
Packit Service |
21b5d1 |
for ( ; stepNum < stepLim; ++stepNum ) {
|
|
Packit Service |
21b5d1 |
currNode = FollowXPathStep ( currNode, expandedXPath, stepNum, createNodes, &currPos );
|
|
Packit Service |
21b5d1 |
if ( currNode == 0 ) goto EXIT;
|
|
Packit Service |
21b5d1 |
if ( currNode->options & kXMP_NewImplicitNode ) {
|
|
Packit Service |
21b5d1 |
currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit.
|
|
Packit Service |
21b5d1 |
CheckImplicitStruct ( currNode, expandedXPath, stepNum+1, stepLim );
|
|
Packit Service |
21b5d1 |
if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node.
|
|
Packit Service |
21b5d1 |
leafIsNew = true; // If any parent is new, the leaf will be new also.
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
} catch ( ... ) {
|
|
Packit Service |
21b5d1 |
if ( leafIsNew ) DeleteSubtree ( newSubPos );
|
|
Packit Service |
21b5d1 |
throw;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Done. Delete the implicitly created subtree if the eventual node was not found.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
EXIT:
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (currNode == 0) || (currNode == *currPos) );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (currNode != 0) || (! createNodes) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( leafIsNew ) {
|
|
Packit Service |
21b5d1 |
if ( currNode != 0 ) {
|
|
Packit Service |
21b5d1 |
currNode->options |= leafOptions;
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
DeleteSubtree ( newSubPos );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (currNode != 0) && (ptrPos != 0) ) *ptrPos = currPos;
|
|
Packit Service |
21b5d1 |
return currNode;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // FindNode
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// CloneOffspring
|
|
Packit Service |
21b5d1 |
// ==============
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void
|
|
Packit Service |
21b5d1 |
CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
size_t qualCount = origParent->qualifiers.size();
|
|
Packit Service |
21b5d1 |
size_t childCount = origParent->children.size();
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( qualCount > 0 ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
cloneParent->qualifiers.reserve ( qualCount );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( size_t qualNum = 0, qualLim = qualCount; qualNum != qualLim; ++qualNum ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * origQual = origParent->qualifiers[qualNum];
|
|
Packit Service |
21b5d1 |
XMP_Node * cloneQual = new XMP_Node ( cloneParent, origQual->name, origQual->value, origQual->options );
|
|
Packit Service |
21b5d1 |
CloneOffspring ( origQual, cloneQual );
|
|
Packit Service |
21b5d1 |
cloneParent->qualifiers.push_back ( cloneQual );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( childCount > 0 ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
cloneParent->children.reserve ( childCount );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( size_t childNum = 0, childLim = childCount; childNum != childLim; ++childNum ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * origChild = origParent->children[childNum];
|
|
Packit Service |
21b5d1 |
XMP_Node * cloneChild = new XMP_Node ( cloneParent, origChild->name, origChild->value, origChild->options );
|
|
Packit Service |
21b5d1 |
CloneOffspring ( origChild, cloneChild );
|
|
Packit Service |
21b5d1 |
cloneParent->children.push_back ( cloneChild );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // CloneOffspring
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// CloneSubtree
|
|
Packit Service |
21b5d1 |
// ============
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node *
|
|
Packit Service |
21b5d1 |
CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
#if XMP_DebugBuild
|
|
Packit Service |
21b5d1 |
if ( cloneParent->parent == 0 ) {
|
|
Packit Service |
21b5d1 |
XMP_Assert ( origRoot->options & kXMP_SchemaNode );
|
|
Packit Service |
21b5d1 |
XMP_Assert ( FindConstSchema ( cloneParent, origRoot->name.c_str() ) == 0 );
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
XMP_Assert ( ! (origRoot->options & kXMP_SchemaNode) );
|
|
Packit Service |
21b5d1 |
if ( cloneParent->options & kXMP_PropValueIsStruct ) { // Might be an array.
|
|
Packit Service |
21b5d1 |
XMP_Assert ( FindConstChild ( cloneParent, origRoot->name.c_str() ) == 0 );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
#endif
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node * cloneRoot = new XMP_Node ( cloneParent, origRoot->name, origRoot->value, origRoot->options );
|
|
Packit Service |
21b5d1 |
CloneOffspring ( origRoot, cloneRoot ) ;
|
|
Packit Service |
21b5d1 |
cloneParent->children.push_back ( cloneRoot );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
return cloneRoot;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // CloneSubtree
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// CompareSubtrees
|
|
Packit Service |
21b5d1 |
// ===============
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Compare 2 subtrees for semantic equality. The comparison includes value, qualifiers, and form.
|
|
Packit Service |
21b5d1 |
// Schemas, top level properties, struct fields, and qualifiers are allowed to have differing order,
|
|
Packit Service |
21b5d1 |
// the appropriate right node is found from the left node's name. Alt-text arrays are allowed to be
|
|
Packit Service |
21b5d1 |
// in differing language order, other arrays are compared in order.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// *** Might someday consider sorting unordered arrays.
|
|
Packit Service |
21b5d1 |
// *** Should expose this through XMPUtils.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
bool
|
|
Packit Service |
21b5d1 |
CompareSubtrees ( const XMP_Node & leftNode, const XMP_Node & rightNode )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
// Don't compare the names here, we want to allow the outermost roots to have different names.
|
|
Packit Service |
21b5d1 |
if ( (leftNode.value != rightNode.value) ||
|
|
Packit Service |
21b5d1 |
(leftNode.options != rightNode.options) ||
|
|
Packit Service |
21b5d1 |
(leftNode.children.size() != rightNode.children.size()) ||
|
|
Packit Service |
21b5d1 |
(leftNode.qualifiers.size() != rightNode.qualifiers.size()) ) return false;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Compare the qualifiers, allowing them to be out of order.
|
|
Packit Service |
21b5d1 |
for ( size_t qualNum = 0, qualLim = leftNode.qualifiers.size(); qualNum != qualLim; ++qualNum ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * leftQual = leftNode.qualifiers[qualNum];
|
|
Packit Service |
21b5d1 |
const XMP_Node * rightQual = FindConstQualifier ( &rightNode, leftQual->name.c_str() );
|
|
Packit Service |
21b5d1 |
if ( (rightQual == 0) || (! CompareSubtrees ( *leftQual, *rightQual )) ) return false;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (leftNode.parent == 0) || (leftNode.options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// The parent node is a tree root, a schema, or a struct.
|
|
Packit Service |
21b5d1 |
for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * leftChild = leftNode.children[childNum];
|
|
Packit Service |
21b5d1 |
const XMP_Node * rightChild = FindConstChild ( &rightNode, leftChild->name.c_str() );
|
|
Packit Service |
21b5d1 |
if ( (rightChild == 0) || (! CompareSubtrees ( *leftChild, *rightChild )) ) return false;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else if ( leftNode.options & kXMP_PropArrayIsAltText ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// The parent node is an alt-text array.
|
|
Packit Service |
21b5d1 |
for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * leftChild = leftNode.children[childNum];
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (! leftChild->qualifiers.empty()) && (leftChild->qualifiers[0]->name == "xml:lang") );
|
|
Packit Service |
21b5d1 |
XMP_Index rightIndex = LookupLangItem ( &rightNode, leftChild->qualifiers[0]->value );
|
|
Packit Service |
21b5d1 |
if ( rightIndex == -1 ) return false;
|
|
Packit Service |
21b5d1 |
const XMP_Node * rightChild = rightNode.children[rightIndex];
|
|
Packit Service |
21b5d1 |
if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} else {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// The parent must be simple or some other (not alt-text) kind of array.
|
|
Packit Service |
21b5d1 |
XMP_Assert ( (! (leftNode.options & kXMP_PropCompositeMask)) || (leftNode.options & kXMP_PropValueIsArray) );
|
|
Packit Service |
21b5d1 |
for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) {
|
|
Packit Service |
21b5d1 |
const XMP_Node * leftChild = leftNode.children[childNum];
|
|
Packit Service |
21b5d1 |
const XMP_Node * rightChild = rightNode.children[childNum];
|
|
Packit Service |
21b5d1 |
if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
return true;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // CompareSubtrees
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// DeleteEmptySchema
|
|
Packit Service |
21b5d1 |
// =================
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void
|
|
Packit Service |
21b5d1 |
DeleteEmptySchema ( XMP_Node * schemaNode )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( XMP_NodeIsSchema ( schemaNode->options ) && schemaNode->children.empty() ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_Node * xmpTree = schemaNode->parent;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t schemaNum = 0;
|
|
Packit Service |
21b5d1 |
size_t schemaLim = xmpTree->children.size();
|
|
Packit Service |
21b5d1 |
while ( (schemaNum < schemaLim) && (xmpTree->children[schemaNum] != schemaNode) ) ++schemaNum;
|
|
Packit Service |
21b5d1 |
XMP_Assert ( schemaNum < schemaLim );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
XMP_NodePtrPos schemaPos = xmpTree->children.begin() + schemaNum;
|
|
Packit Service |
21b5d1 |
XMP_Assert ( *schemaPos == schemaNode );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
xmpTree->children.erase ( schemaPos );
|
|
Packit Service |
21b5d1 |
delete schemaNode;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // DeleteEmptySchema
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// NormalizeLangValue
|
|
Packit Service |
21b5d1 |
// ==================
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Normalize an xml:lang value so that comparisons are effectively case insensitive as required by
|
|
Packit Service |
21b5d1 |
// RFC 3066 (which superceeds RFC 1766). The normalization rules:
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// - The primary subtag is lower case, the suggested practice of ISO 639.
|
|
Packit Service |
21b5d1 |
// - All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166.
|
|
Packit Service |
21b5d1 |
// - All other subtags are lower case.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void
|
|
Packit Service |
21b5d1 |
NormalizeLangValue ( XMP_VarString * value )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
char * tagStart;
|
|
Packit Service |
21b5d1 |
char * tagEnd;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Find and process the primary subtag.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
tagStart = (char*) value->c_str();
|
|
Packit Service |
21b5d1 |
for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
|
|
Packit Service |
21b5d1 |
if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Find and process the secondary subtag.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
tagStart = tagEnd;
|
|
Packit Service |
21b5d1 |
if ( *tagStart == '-' ) ++tagStart;
|
|
Packit Service |
21b5d1 |
for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
|
|
Packit Service |
21b5d1 |
if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
if ( tagEnd == tagStart+2 ) {
|
|
Packit Service |
21b5d1 |
if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20;
|
|
Packit Service |
21b5d1 |
++tagStart;
|
|
Packit Service |
21b5d1 |
if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// Find and process the remaining subtags.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
while ( true ) {
|
|
Packit Service |
21b5d1 |
tagStart = tagEnd;
|
|
Packit Service |
21b5d1 |
if ( *tagStart == '-' ) ++tagStart;
|
|
Packit Service |
21b5d1 |
if ( *tagStart == 0 ) break;
|
|
Packit Service |
21b5d1 |
for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) {
|
|
Packit Service |
21b5d1 |
if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // NormalizeLangValue
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// NormalizeLangArray
|
|
Packit Service |
21b5d1 |
// ==================
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Make sure the x-default item is first. Touch up "single value" arrays that have a default plus
|
|
Packit Service |
21b5d1 |
// one real language. This case should have the same value for both items. Older Adobe apps were
|
|
Packit Service |
21b5d1 |
// hardwired to only use the 'x-default' item, so we copy that value to the other item.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void
|
|
Packit Service |
21b5d1 |
NormalizeLangArray ( XMP_Node * array )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Assert ( XMP_ArrayIsAltText(array->options) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t itemNum;
|
|
Packit Service |
21b5d1 |
size_t itemLim = array->children.size();
|
|
Packit Service |
21b5d1 |
bool hasDefault = false;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( itemNum = 0; itemNum < itemLim; ++itemNum ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( array->children[itemNum]->qualifiers.empty() ||
|
|
Packit Service |
21b5d1 |
(array->children[itemNum]->qualifiers[0]->name != "xml:lang") ) {
|
|
Packit Service |
21b5d1 |
XMP_Throw ( "AltText array items must have an xml:lang qualifier", kXMPErr_BadXMP );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( array->children[itemNum]->qualifiers[0]->value == "x-default" ) {
|
|
Packit Service |
21b5d1 |
hasDefault = true;
|
|
Packit Service |
21b5d1 |
break;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( hasDefault ) {
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( itemNum != 0 ) {
|
|
Packit Service |
21b5d1 |
XMP_Node * temp = array->children[0];
|
|
Packit Service |
21b5d1 |
array->children[0] = array->children[itemNum];
|
|
Packit Service |
21b5d1 |
array->children[itemNum] = temp;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// 09-Oct-07, ahu: disabled to avoid unexpected behaviour
|
|
Packit Service |
21b5d1 |
// if ( itemLim == 2 ) array->children[1]->value = array->children[0]->value;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // NormalizeLangArray
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// DetectAltText
|
|
Packit Service |
21b5d1 |
// =============
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// See if an array is an alt-text array. If so, make sure the x-default item is first.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void
|
|
Packit Service |
21b5d1 |
DetectAltText ( XMP_Node * xmpParent )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
XMP_Assert ( XMP_ArrayIsAlternate(xmpParent->options) );
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
size_t itemNum, itemLim;
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
for ( itemNum = 0, itemLim = xmpParent->children.size(); itemNum < itemLim; ++itemNum ) {
|
|
Packit Service |
21b5d1 |
XMP_OptionBits currOptions = xmpParent->children[itemNum]->options;
|
|
Packit Service |
21b5d1 |
if ( (currOptions & kXMP_PropCompositeMask) || (! (currOptions & kXMP_PropHasLang)) ) break;
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
if ( (itemLim != 0) && (itemNum == itemLim) ) {
|
|
Packit Service |
21b5d1 |
xmpParent->options |= kXMP_PropArrayIsAltText;
|
|
Packit Service |
21b5d1 |
NormalizeLangArray ( xmpParent );
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
} // DetectAltText
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|
|
Packit Service |
21b5d1 |
// SortNamedNodes
|
|
Packit Service |
21b5d1 |
// ==============
|
|
Packit Service |
21b5d1 |
//
|
|
Packit Service |
21b5d1 |
// Sort the pointers in an XMP_NodeOffspring vector by name.
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
static inline bool Compare ( const XMP_Node * left, const XMP_Node * right )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
return (left->name < right->name);
|
|
Packit Service |
21b5d1 |
}
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
void
|
|
Packit Service |
21b5d1 |
SortNamedNodes ( XMP_NodeOffspring & nodeVector )
|
|
Packit Service |
21b5d1 |
{
|
|
Packit Service |
21b5d1 |
sort ( nodeVector.begin(), nodeVector.end(), Compare );
|
|
Packit Service |
21b5d1 |
} // SortNamedNodes
|
|
Packit Service |
21b5d1 |
|
|
Packit Service |
21b5d1 |
// =================================================================================================
|