summaryrefslogtreecommitdiff
path: root/gpr/source/lib/xmp_core/XMP_LibUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gpr/source/lib/xmp_core/XMP_LibUtils.cpp')
-rw-r--r--gpr/source/lib/xmp_core/XMP_LibUtils.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/gpr/source/lib/xmp_core/XMP_LibUtils.cpp b/gpr/source/lib/xmp_core/XMP_LibUtils.cpp
new file mode 100644
index 0000000..507294e
--- /dev/null
+++ b/gpr/source/lib/xmp_core/XMP_LibUtils.cpp
@@ -0,0 +1,705 @@
+// =================================================================================================
+// Copyright 2009 Adobe Systems Incorporated
+// All Rights Reserved.
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h"
+
+#include "XMP_LibUtils.hpp"
+
+#include "UnicodeInlines.incl_cpp"
+
+#include <cstdio>
+#include <cstring>
+
+// =================================================================================================
+
+#ifndef TraceThreadLocks
+ #define TraceThreadLocks 0
+#endif
+
+// -------------------------------------------------------------------------------------------------
+
+extern "C" bool Initialize_LibUtils()
+{
+ return true;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+extern "C" void Terminate_LibUtils(){
+ // Nothing to do.
+}
+
+// =================================================================================================
+// Error notifications
+// =================================================================================================
+
+bool GenericErrorCallback::CheckLimitAndSeverity ( XMP_ErrorSeverity severity ) const
+{
+
+ if ( this->limit == 0 ) return true; // Always notify if the limit is zero.
+ if ( severity < this->topSeverity ) return false; // Don't notify, don't count.
+
+ if ( severity > this->topSeverity ) {
+ this->topSeverity = severity;
+ this->notifications = 0;
+ }
+
+ this->notifications += 1;
+ return (this->notifications <= this->limit);
+
+} // GenericErrorCallback::CheckLimitAndSeverity
+
+// =================================================================================================
+
+void GenericErrorCallback::NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath /*= 0 */ ) const
+{
+ bool notifyClient = CanNotify() && !error.IsNotified();
+ bool returnAndRecover (severity == kXMPErrSev_Recoverable);
+
+ if ( notifyClient ) {
+ error.SetNotified();
+ notifyClient = CheckLimitAndSeverity ( severity );
+ if ( notifyClient ) {
+ returnAndRecover &= ClientCallbackWrapper( filePath, severity, error.GetID(), error.GetErrMsg() );
+ }
+ }
+
+ if ( ! returnAndRecover ) XMP_Error_Throw ( error );
+
+}
+
+// =================================================================================================
+// Thread synchronization locks
+// =================================================================================================
+
+XMP_ReadWriteLock::XMP_ReadWriteLock() : beingWritten(false)
+{
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ this->lockCount = 0;
+ // Atomic counter must be 32 or 64 bits and naturally aligned.
+ size_t counterSize = sizeof ( XMP_AtomicCounter );
+ size_t counterOffset = XMP_OffsetOf ( XMP_ReadWriteLock, lockCount );
+ XMP_Assert ( (counterSize == 4) || (counterSize == 8) ); // Counter must be 32 or 64 bits.
+ XMP_Assert ( (counterOffset & (counterSize-1)) == 0 ); // Counter must be naturally aligned.
+ #endif
+ XMP_BasicRWLock_Initialize ( this->lock );
+ #if TraceThreadLocks
+ fprintf ( stderr, "Created lock %.8X\n", this );
+ #endif
+}
+
+// ---------------------------------------------------------------------------------------------
+
+XMP_ReadWriteLock::~XMP_ReadWriteLock()
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Deleting lock %.8X\n", this );
+ #endif
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount == 0 );
+ #endif
+ XMP_BasicRWLock_Terminate ( this->lock );
+}
+
+// ---------------------------------------------------------------------------------------------
+
+void XMP_ReadWriteLock::Acquire ( bool forWriting )
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Acquiring lock %.8X for %s, count %d%s\n",
+ this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+
+ if ( forWriting ) {
+ XMP_BasicRWLock_AcquireForWrite ( this->lock );
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount == 0 );
+ #endif
+ } else {
+ XMP_BasicRWLock_AcquireForRead ( this->lock );
+ XMP_Assert ( ! this->beingWritten );
+ }
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_AtomicIncrement ( this->lockCount );
+ #endif
+ this->beingWritten = forWriting;
+
+ #if TraceThreadLocks
+ fprintf ( stderr, "Acquired lock %.8X for %s, count %d%s\n",
+ this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+}
+
+// ---------------------------------------------------------------------------------------------
+
+void XMP_ReadWriteLock::Release()
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Releasing lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount > 0 );
+ XMP_AtomicDecrement ( this->lockCount ); // ! Do these before unlocking, that might release a waiting thread.
+ #endif
+ bool forWriting = this->beingWritten;
+ this->beingWritten = false;
+
+ if ( forWriting ) {
+ XMP_BasicRWLock_ReleaseFromWrite ( this->lock );
+ } else {
+ XMP_BasicRWLock_ReleaseFromRead ( this->lock );
+ }
+
+ #if TraceThreadLocks
+ fprintf ( stderr, "Released lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+}
+
+// =================================================================================================
+
+#if UseHomeGrownLock
+
+ #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild
+
+ // -----------------------------------------------------------------------------------------
+
+ // About pthread mutexes and conditions:
+ //
+ // The mutex protecting the condition must be locked before waiting for the condition. A
+ // thread can wait for a condition to be signaled by calling the pthread_cond_wait
+ // subroutine. The subroutine atomically unlocks the mutex and blocks the calling thread
+ // until the condition is signaled. When the call returns, the mutex is locked again.
+
+ #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+ #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+ #define InitializeBasicQueue(queue) { int err = pthread_cond_init ( &queue, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicQueue(queue) { int err = pthread_cond_destroy ( &queue ); XMP_Enforce ( err == 0 ); }
+
+ #define WaitOnBasicQueue(queue,mutex) { int err = pthread_cond_wait ( &queue, &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseOneBasicQueue(queue) { int err = pthread_cond_signal ( &queue ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseAllBasicQueue(queue) { int err = pthread_cond_broadcast ( &queue ); XMP_Enforce ( err == 0 ); }
+
+ // -----------------------------------------------------------------------------------------
+
+ #elif XMP_WinBuild
+
+ // -----------------------------------------------------------------------------------------
+
+ #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); }
+ #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); }
+
+ #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); }
+ #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); }
+
+ #if ! BuildLocksForWinXP
+
+ // About Win32 condition variables (not on XP):
+ //
+ // Condition variables enable threads to atomically release a lock and enter the
+ // sleeping state. They can be used with critical sections or slim reader/writer (SRW)
+ // locks. Condition variables support operations that "wake one" or "wake all" waiting
+ // threads. After a thread is woken, it re-acquires the lock it released when the thread
+ // entered the sleeping state.
+
+ #define InitializeBasicQueue(queue) { InitializeConditionVariable ( &queue ); }
+ #define TerminateBasicQueue(queue) /* Do nothing. */
+
+ #define WaitOnBasicQueue(queue,mutex) \
+ { BOOL ok = SleepConditionVariableCS ( &queue, &mutex, INFINITE /* timeout */ ); XMP_Enforce ( ok ); }
+
+ #define ReleaseOneBasicQueue(queue) { WakeConditionVariable ( &queue ); }
+ #define ReleaseAllBasicQueue(queue) { WakeAllConditionVariable ( &queue ); }
+
+ #else
+
+ // Need to create our own queue for Windows XP. This is not a general queue, it depends
+ // on the usage inside XMP_HomeGrownLock where the queueMutex guarantees that the
+ // queueing operations are done single threaded.
+
+ #define InitializeBasicQueue(queue) /* Do nothing. */
+ #define TerminateBasicQueue(queue) /* Do nothing. */
+
+ #define WaitOnBasicQueue(queue,mutex) { queue.Wait ( mutex ); }
+ #define ReleaseOneBasicQueue(queue) { queue.ReleaseOne(); }
+ #define ReleaseAllBasicQueue(queue) { queue.ReleaseAll(); }
+
+ // -------------------------------------------------------------------------------------
+
+ XMP_WinXP_HGQueue::XMP_WinXP_HGQueue() : queueEvent(0), waitCount(0), releaseAll(false)
+ {
+ this->queueEvent = CreateEvent ( NULL, FALSE, TRUE, NULL ); // Auto reset, initially clear.
+ XMP_Enforce ( this->queueEvent != 0 );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ XMP_WinXP_HGQueue::~XMP_WinXP_HGQueue()
+ {
+ CloseHandle ( this->queueEvent );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::Wait ( XMP_BasicMutex & queueMutex )
+ {
+ ++this->waitCount; // ! Does not need atomic increment, protected by queue mutex.
+ ReleaseBasicMutex ( queueMutex );
+ DWORD status = WaitForSingleObject ( this->queueEvent, INFINITE );
+ if ( status != WAIT_OBJECT_0 ) XMP_Throw ( "Failure from WaitForSingleObject", kXMPErr_ExternalFailure );
+ AcquireBasicMutex ( queueMutex );
+ --this->waitCount; // ! Does not need atomic decrement, protected by queue mutex.
+
+ if ( this->releaseAll ) {
+ if ( this->waitCount == 0 ) {
+ this->releaseAll = false;
+ } else {
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::ReleaseOne()
+ {
+ XMP_Assert ( ! this->releaseAll );
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::ReleaseAll()
+ {
+ this->releaseAll = true;
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+
+ #endif
+
+ // -----------------------------------------------------------------------------------------
+
+ #endif
+
+ // =============================================================================================
+
+ XMP_HomeGrownLock::XMP_HomeGrownLock() : lockCount(0), readersWaiting(0), writersWaiting(0), beingWritten(false)
+ {
+ InitializeBasicMutex ( this->queueMutex );
+ InitializeBasicQueue ( this->writerQueue );
+ InitializeBasicQueue ( this->readerQueue );
+ }
+
+ // =============================================================================================
+
+ XMP_HomeGrownLock::~XMP_HomeGrownLock()
+ {
+ TerminateBasicMutex ( this->queueMutex );
+ TerminateBasicQueue ( this->writerQueue );
+ TerminateBasicQueue ( this->readerQueue );
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::AcquireForRead()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ ++this->readersWaiting; // ! Does not need atomic increment, protected by queue mutex.
+ while ( (this->beingWritten) || (this->writersWaiting > 0) ) {
+ // Don't allow more readers if writers are waiting.
+ WaitOnBasicQueue ( this->readerQueue, this->queueMutex );
+ }
+ --this->readersWaiting; // ! Does not need atomic decrement, protected by queue mutex.
+ XMP_Assert ( ! this->beingWritten );
+
+ ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex.
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::AcquireForWrite()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ ++this->writersWaiting; // ! Does not need atomic increment, protected by queue mutex.
+ while ( this->lockCount > 0 ) {
+ WaitOnBasicQueue ( this->writerQueue, this->queueMutex );
+ }
+ --this->writersWaiting; // ! Does not need atomic decrement, protected by queue mutex.
+ XMP_Assert ( (! this->beingWritten) && (this->lockCount == 0) );
+
+ ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex.
+ this->beingWritten = true;
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::ReleaseFromRead()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ XMP_Assert ( (! this->beingWritten) && (this->lockCount > 0) );
+ --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex.
+
+ if ( this->writersWaiting > 0 ) {
+ ReleaseOneBasicQueue ( this->writerQueue );
+ } else if ( this->readersWaiting > 0 ) {
+ ReleaseAllBasicQueue ( this->readerQueue );
+ }
+
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::ReleaseFromWrite()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ XMP_Assert ( this->beingWritten && (this->lockCount == 1) );
+ --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex.
+ this->beingWritten = false;
+
+ if ( this->writersWaiting > 0 ) {
+ ReleaseOneBasicQueue ( this->writerQueue );
+ } else if ( this->readersWaiting > 0 ) {
+ ReleaseAllBasicQueue ( this->readerQueue );
+ }
+ }
+
+ // =============================================================================================
+
+#endif
+
+// =================================================================================================
+// Data structure dumping utilities
+// ================================
+
+void
+DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon )
+{
+
+ char buffer [20];
+ bool prevNormal;
+ XMP_Status status = 0;
+
+ XMP_StringPtr spanStart, spanEnd;
+ XMP_StringPtr valueEnd = &value[0] + value.size();
+
+ spanStart = &value[0];
+ while ( spanStart < valueEnd ) {
+
+ // Output the next span of regular characters.
+ for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
+ if ( *spanEnd > 0x7F ) break;
+ if ( (*spanEnd < 0x20) && (*spanEnd != kTab) && (*spanEnd != kLF) ) break;
+ }
+ if ( spanStart != spanEnd ) status = (*outProc) ( refCon, spanStart, (XMP_StringLen)(spanEnd-spanStart) );
+ if ( status != 0 ) break;
+ spanStart = spanEnd;
+
+ // Output the next span of irregular characters.
+ prevNormal = true;
+ for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
+ if ( ((0x20 <= *spanEnd) && (*spanEnd <= 0x7F)) || (*spanEnd == kTab) || (*spanEnd == kLF) ) break;
+ char space = ' ';
+ if ( prevNormal ) space = '<';
+ status = (*outProc) ( refCon, &space, 1 );
+ if ( status != 0 ) break;
+ OutProcHexByte ( *spanEnd );
+ prevNormal = false;
+ }
+ if ( ! prevNormal ) {
+ status = (*outProc) ( refCon, ">", 1 );
+ if ( status != 0 ) return;
+ }
+ spanStart = spanEnd;
+
+ }
+
+} // DumpClearString
+
+// -------------------------------------------------------------------------------------------------
+
+static void
+DumpStringMap ( const XMP_StringMap & map, XMP_StringPtr label, XMP_TextOutputProc outProc, void * refCon )
+{
+ XMP_cStringMapPos currPos;
+ XMP_cStringMapPos endPos = map.end();
+
+ size_t maxLen = 0;
+ for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
+ size_t currLen = currPos->first.size();
+ if ( currLen > maxLen ) maxLen = currLen;
+ }
+
+ OutProcNewline();
+ OutProcLiteral ( label );
+ OutProcNewline();
+
+ for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
+ OutProcNChars ( " ", 2 );
+ DumpClearString ( currPos->first, outProc, refCon );
+ OutProcPadding ( maxLen - currPos->first.size() );
+ OutProcNChars ( " => ", 4 );
+ DumpClearString ( currPos->second, outProc, refCon );
+ OutProcNewline();
+ }
+
+} // DumpStringMap
+
+// =================================================================================================
+// Namespace Tables
+// =================================================================================================
+
+XMP_NamespaceTable::XMP_NamespaceTable ( const XMP_NamespaceTable & presets )
+{
+ XMP_AutoLock presetLock ( &presets.lock, kXMP_ReadLock );
+
+ this->uriToPrefixMap = presets.uriToPrefixMap;
+ this->prefixToURIMap = presets.prefixToURIMap;
+
+} // XMP_NamespaceTable::XMP_NamespaceTable
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::Define ( XMP_StringPtr _uri, XMP_StringPtr _suggPrefix,
+ XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen )
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_WriteLock );
+ bool prefixMatches = false;
+
+ XMP_Assert ( (_uri != 0) && (*_uri != 0) && (_suggPrefix != 0) && (*_suggPrefix != 0) );
+
+ XMP_VarString uri ( _uri );
+ XMP_VarString suggPrefix ( _suggPrefix );
+ if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':';
+ VerifySimpleXMLName ( _suggPrefix, _suggPrefix+suggPrefix.size()-1 ); // Exclude the colon.
+
+ XMP_StringMapPos uriPos = this->uriToPrefixMap.find ( uri );
+
+ if ( uriPos == this->uriToPrefixMap.end() ) {
+
+ // The URI is not yet registered, make sure we use a unique prefix.
+
+ XMP_VarString uniqPrefix ( suggPrefix );
+ int suffix = 0;
+ char buffer [32]; // AUDIT: Plenty of room for the "_%d_" suffix.
+
+ while ( true ) {
+ if ( this->prefixToURIMap.find ( uniqPrefix ) == this->prefixToURIMap.end() ) break;
+ ++suffix;
+ snprintf ( buffer, sizeof(buffer), "_%d_:", suffix ); // AUDIT: Using sizeof for snprintf length is safe.
+ uniqPrefix = suggPrefix;
+ uniqPrefix.erase ( uniqPrefix.size()-1 ); // ! Remove the trailing ':'.
+ uniqPrefix += buffer;
+ }
+
+ // Add the new namespace to both maps.
+
+ XMP_StringPair newNS ( uri, uniqPrefix );
+ uriPos = this->uriToPrefixMap.insert ( this->uriToPrefixMap.end(), newNS );
+
+ newNS.first.swap ( newNS.second );
+ (void) this->prefixToURIMap.insert ( this->prefixToURIMap.end(), newNS );
+
+ }
+
+ // Return the actual prefix and see if it matches the suggested prefix.
+
+ if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str();
+ if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size();
+
+ prefixMatches = ( uriPos->second == suggPrefix );
+ return prefixMatches;
+
+} // XMP_NamespaceTable::Define
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::GetPrefix ( XMP_StringPtr _uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+ bool found = false;
+
+ XMP_Assert ( (_uri != 0) && (*_uri != 0) );
+
+ XMP_VarString uri ( _uri );
+ XMP_cStringMapPos uriPos = this->uriToPrefixMap.find ( uri );
+
+ if ( uriPos != this->uriToPrefixMap.end() ) {
+ if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str();
+ if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size();
+ found = true;
+ }
+
+ return found;
+
+} // XMP_NamespaceTable::GetPrefix
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::GetURI ( XMP_StringPtr _prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+
+ bool found = false;
+
+ XMP_Assert ( (_prefix != 0) && (*_prefix != 0) );
+
+ XMP_VarString prefix ( _prefix );
+ if ( prefix[prefix.size()-1] != ':' ) prefix += ':';
+ XMP_cStringMapPos prefixPos = this->prefixToURIMap.find ( prefix );
+
+ if ( prefixPos != this->prefixToURIMap.end() ) {
+ if ( uriPtr != 0 ) *uriPtr = prefixPos->second.c_str();
+ if ( uriLen != 0 ) *uriLen = (XMP_StringLen)prefixPos->second.size();
+ found = true;
+ }
+
+ return found;
+
+} // XMP_NamespaceTable::GetURI
+
+// =================================================================================================
+
+void XMP_NamespaceTable::Dump ( XMP_TextOutputProc outProc, void * refCon ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+
+ XMP_cStringMapPos p2uEnd = this->prefixToURIMap.end(); // ! Move up to avoid gcc complaints.
+ XMP_cStringMapPos u2pEnd = this->uriToPrefixMap.end();
+
+ DumpStringMap ( this->prefixToURIMap, "Dumping namespace prefix to URI map", outProc, refCon );
+
+ if ( this->prefixToURIMap.size() != this->uriToPrefixMap.size() ) {
+ OutProcLiteral ( "** bad namespace map sizes **" );
+ XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure );
+ }
+
+ for ( XMP_cStringMapPos nsLeft = this->prefixToURIMap.begin(); nsLeft != p2uEnd; ++nsLeft ) {
+
+ XMP_cStringMapPos nsOther = this->uriToPrefixMap.find ( nsLeft->second );
+ if ( (nsOther == u2pEnd) || (nsLeft != this->prefixToURIMap.find ( nsOther->second )) ) {
+ OutProcLiteral ( " ** bad namespace URI ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+
+ for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != p2uEnd; ++nsRight ) {
+ if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
+ if ( nsLeft->second == nsRight->second ) {
+ OutProcLiteral ( " ** duplicate namespace URI ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+ }
+
+ }
+
+ for ( XMP_cStringMapPos nsLeft = this->uriToPrefixMap.begin(); nsLeft != u2pEnd; ++nsLeft ) {
+
+ XMP_cStringMapPos nsOther = this->prefixToURIMap.find ( nsLeft->second );
+ if ( (nsOther == p2uEnd) || (nsLeft != this->uriToPrefixMap.find ( nsOther->second )) ) {
+ OutProcLiteral ( " ** bad namespace prefix ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+
+ for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != u2pEnd; ++nsRight ) {
+ if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
+ if ( nsLeft->second == nsRight->second ) {
+ OutProcLiteral ( " ** duplicate namespace prefix ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+ }
+
+ }
+
+} // XMP_NamespaceTable::Dump
+
+// =================================================================================================
+static XMP_Bool matchdigit ( XMP_StringPtr text ) {
+ if ( *text >= '0' && *text <= '9' )
+ return true;
+ return false;
+}
+
+static XMP_Bool matchUpperCase ( XMP_StringPtr text ) {
+ if ( *text >= 'A' && *text <= 'Z' )
+ return true;
+ return false;
+}
+
+static XMP_Bool matchLowerCase ( XMP_StringPtr text ) {
+ if ( *text >= 'a' && *text <= 'z' )
+ return true;
+ return false;
+}
+
+/* matchhere: search for regexp at beginning of text */
+static XMP_Bool matchhere ( XMP_StringPtr regexp, XMP_StringPtr text ) {
+ if ( regexp[0] == '\0' )
+ return true;
+ if ( regexp[0] == '\\' ) {
+ if ( regexp[1] == 'd' ) {
+ if ( matchdigit(text) )
+ return matchhere ( regexp+2, text+1 );
+ else
+ return false;
+ }
+ else if ( regexp[1] == 'W' ) {
+ if ( matchUpperCase(text) )
+ return matchhere ( regexp+2, text+1 );
+ else
+ return false;
+ }
+ else if ( regexp[1] == 'w' ) {
+ if ( matchLowerCase(text) )
+ return matchhere ( regexp+2, text+1 );
+ else
+ return false;
+ }
+ }
+
+ if ( regexp[0] == '$' && regexp[1] == '\0' )
+ return *text == '\0';
+
+ if ( *text != '\0' && regexp[0] == *text )
+ return matchhere ( regexp+1, text+1 );
+ return 0;
+}
+
+/* match: search for regexp anywhere in text */
+static XMP_Bool match ( XMP_StringPtr regexp, XMP_StringPtr text ) {
+ if ( regexp[0] == '^' )
+ return matchhere ( regexp+1, text );
+ do { /* must look even if string is empty */
+ if ( matchhere ( regexp, text ) )
+ return true;
+ } while ( *text++ != '\0' );
+ return false;
+}
+
+XMP_Bool XMP_RegExp::Match ( XMP_StringPtr s )
+{
+ if ( regExpStr.size() == 0 )
+ return true;
+ if ( s == NULL )
+ return false;
+ return match ( this->regExpStr.c_str(), s );
+}
+// =================================================================================================