summaryrefslogtreecommitdiff
path: root/XMPFiles/source/HandlerRegistry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'XMPFiles/source/HandlerRegistry.cpp')
-rw-r--r--XMPFiles/source/HandlerRegistry.cpp961
1 files changed, 961 insertions, 0 deletions
diff --git a/XMPFiles/source/HandlerRegistry.cpp b/XMPFiles/source/HandlerRegistry.cpp
new file mode 100644
index 0000000..144dbcf
--- /dev/null
+++ b/XMPFiles/source/HandlerRegistry.cpp
@@ -0,0 +1,961 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2011 Adobe Systems Incorporated
+// All Rights Reserved
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
+#include "public/include/XMP_Const.h"
+
+#include "XMPFiles/source/HandlerRegistry.h"
+#include "XMPFiles/source/PluginHandler/XMPAtoms.h"
+
+#if EnablePhotoHandlers
+ #include "XMPFiles/source/FileHandlers/JPEG_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/PSD_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/TIFF_Handler.hpp"
+#endif
+
+#if EnableDynamicMediaHandlers
+ #include "XMPFiles/source/FileHandlers/AIFF_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/ASF_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/FLV_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/MP3_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/MPEG2_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/MPEG4_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/P2_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/WAVE_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/RIFF_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/SWF_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/XDCAM_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp"
+#endif
+
+#if EnableMiscHandlers
+ #include "XMPFiles/source/FileHandlers/InDesign_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/PNG_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/PostScript_Handler.hpp"
+ #include "XMPFiles/source/FileHandlers/UCF_Handler.hpp"
+#endif
+
+//#if EnablePacketScanning
+//#include "XMPFiles/source/FileHandlers/Scanner_Handler.hpp"
+//#endif
+
+using namespace Common;
+using namespace XMP_PLUGIN;
+
+// =================================================================================================
+
+#if EnableDynamicMediaHandlers
+
+static const char * kP2ContentChildren[] = { "CLIP", "VIDEO", "AUDIO", "ICON", "VOICE", "PROXY", 0 };
+
+static inline bool CheckP2ContentChild ( const std::string & folderName )
+{
+ for ( int i = 0; kP2ContentChildren[i] != 0; ++i ) {
+ if ( folderName == kP2ContentChildren[i] ) return true;
+ }
+ return false;
+}
+
+#endif
+
+// =================================================================================================
+
+//
+// Static init
+//
+HandlerRegistry* HandlerRegistry::sInstance = 0;
+
+/*static*/ HandlerRegistry& HandlerRegistry::getInstance()
+{
+ if ( sInstance == 0 ) sInstance = new HandlerRegistry();
+ return *sInstance;
+}
+
+/*static*/ void HandlerRegistry::terminate()
+{
+ delete sInstance;
+ sInstance = 0;
+}
+
+HandlerRegistry::HandlerRegistry()
+{
+ mFolderHandlers = new XMPFileHandlerTable;
+ mNormalHandlers = new XMPFileHandlerTable;
+ mOwningHandlers = new XMPFileHandlerTable;
+ mReplacedHandlers = new XMPFileHandlerTable;
+}
+
+HandlerRegistry::~HandlerRegistry()
+{
+ delete mFolderHandlers;
+ delete mNormalHandlers;
+ delete mOwningHandlers;
+ delete mReplacedHandlers;
+}
+
+// =================================================================================================
+
+void HandlerRegistry::initialize()
+{
+
+ bool allOK = true; // All of the linked-in handler registrations must work, do one test at the end.
+
+ // -----------------------------------------
+ // Register the directory-oriented handlers.
+
+#if EnableDynamicMediaHandlers
+ allOK &= this->registerFolderHandler ( kXMP_P2File, kP2_HandlerFlags, P2_CheckFormat, P2_MetaHandlerCTor );
+ allOK &= this->registerFolderHandler ( kXMP_SonyHDVFile, kSonyHDV_HandlerFlags, SonyHDV_CheckFormat, SonyHDV_MetaHandlerCTor );
+ allOK &= this->registerFolderHandler ( kXMP_XDCAM_FAMFile, kXDCAM_HandlerFlags, XDCAM_CheckFormat, XDCAM_MetaHandlerCTor );
+ allOK &= this->registerFolderHandler ( kXMP_XDCAM_SAMFile, kXDCAM_HandlerFlags, XDCAM_CheckFormat, XDCAM_MetaHandlerCTor );
+ allOK &= this->registerFolderHandler ( kXMP_XDCAM_EXFile, kXDCAMEX_HandlerFlags, XDCAMEX_CheckFormat, XDCAMEX_MetaHandlerCTor );
+#endif
+
+ // ------------------------------------------------------------------------------------------
+ // Register the file-oriented handlers that don't want to open and close the file themselves.
+
+#if EnablePhotoHandlers
+ allOK &= this->registerNormalHandler ( kXMP_JPEGFile, kJPEG_HandlerFlags, JPEG_CheckFormat, JPEG_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_PhotoshopFile, kPSD_HandlerFlags, PSD_CheckFormat, PSD_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_TIFFFile, kTIFF_HandlerFlags, TIFF_CheckFormat, TIFF_MetaHandlerCTor );
+#endif
+
+#if EnableDynamicMediaHandlers
+ allOK &= this->registerNormalHandler ( kXMP_WMAVFile, kASF_HandlerFlags, ASF_CheckFormat, ASF_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_MP3File, kMP3_HandlerFlags, MP3_CheckFormat, MP3_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_WAVFile, kWAVE_HandlerFlags, WAVE_CheckFormat, WAVE_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_AVIFile, kRIFF_HandlerFlags, RIFF_CheckFormat, RIFF_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_SWFFile, kSWF_HandlerFlags, SWF_CheckFormat, SWF_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_MPEG4File, kMPEG4_HandlerFlags, MPEG4_CheckFormat, MPEG4_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_MOVFile, kMPEG4_HandlerFlags, MPEG4_CheckFormat, MPEG4_MetaHandlerCTor ); // ! Yes, MPEG-4 includes MOV.
+ allOK &= this->registerNormalHandler ( kXMP_FLVFile, kFLV_HandlerFlags, FLV_CheckFormat, FLV_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_AIFFFile, kAIFF_HandlerFlags, AIFF_CheckFormat, AIFF_MetaHandlerCTor );
+#endif
+
+#if EnableMiscHandlers
+ allOK &= this->registerNormalHandler ( kXMP_InDesignFile, kInDesign_HandlerFlags, InDesign_CheckFormat, InDesign_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_PNGFile, kPNG_HandlerFlags, PNG_CheckFormat, PNG_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_UCFFile, kUCF_HandlerFlags, UCF_CheckFormat, UCF_MetaHandlerCTor );
+ // ! EPS and PostScript have the same handler, EPS is a proper subset of PostScript.
+ allOK &= this->registerNormalHandler ( kXMP_EPSFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor );
+ allOK &= this->registerNormalHandler ( kXMP_PostScriptFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor );
+#endif
+
+ // ------------------------------------------------------------------------------------
+ // Register the file-oriented handlers that need to open and close the file themselves.
+
+#if EnableDynamicMediaHandlers
+ allOK &= this->registerOwningHandler ( kXMP_MPEGFile, kMPEG2_HandlerFlags, MPEG2_CheckFormat, MPEG2_MetaHandlerCTor );
+ allOK &= this->registerOwningHandler ( kXMP_MPEG2File, kMPEG2_HandlerFlags, MPEG2_CheckFormat, MPEG2_MetaHandlerCTor );
+#endif
+
+ if ( ! allOK ) XMP_Throw ( "Failure initializing linked-in file handlers", kXMPErr_InternalFailure );
+
+} // HandlerRegistry::initialize
+
+// =================================================================================================
+
+bool HandlerRegistry::registerFolderHandler( XMP_FileFormat format,
+ XMP_OptionBits flags,
+ CheckFolderFormatProc checkProc,
+ XMPFileHandlerCTor handlerCTor,
+ bool replaceExisting /*= false*/ )
+{
+ XMP_Assert ( format != kXMP_UnknownFile );
+
+ XMP_Assert ( flags & kXMPFiles_HandlerOwnsFile );
+ XMP_Assert ( flags & kXMPFiles_FolderBasedFormat );
+ XMP_Assert ( (flags & kXMPFiles_CanInjectXMP) ? (flags & kXMPFiles_CanExpand) : 1 );
+
+ if ( replaceExisting )
+ {
+ //
+ // Remember previous file handler for this format.
+ // Reject if there is already a replacement.
+ //
+ if( mReplacedHandlers->find( format ) == mReplacedHandlers->end() )
+ {
+ XMPFileHandlerInfo* standardHandler = this->getHandlerInfo( format );
+
+ if( standardHandler != NULL )
+ {
+ mReplacedHandlers->insert( mReplacedHandlers->end(), XMPFileHandlerTablePair( format, *standardHandler ) );
+ }
+ else
+ {
+ // skip registration if there is nothing to replace
+ return false;
+ }
+ }
+ else
+ {
+ // skip registration if there is already a replacing handler registered for this format
+ return false;
+ }
+
+ // remove existing handler
+ this->removeHandler ( format );
+ }
+ else
+ {
+ // skip registration if there is already a handler registered for this format
+ if ( this->getFormatInfo ( format ) ) return false;
+ }
+
+ //
+ // register handler
+ //
+ XMPFileHandlerInfo handlerInfo ( format, flags, checkProc, handlerCTor );
+ mFolderHandlers->insert ( mFolderHandlers->end(), XMPFileHandlerTablePair ( format, handlerInfo ) );
+
+ return true;
+
+} // HandlerRegistry::registerFolderHandler
+
+// =================================================================================================
+
+bool HandlerRegistry::registerNormalHandler( XMP_FileFormat format,
+ XMP_OptionBits flags,
+ CheckFileFormatProc checkProc,
+ XMPFileHandlerCTor handlerCTor,
+ bool replaceExisting /*= false*/ )
+{
+ XMP_Assert ( format != kXMP_UnknownFile );
+
+ XMP_Assert ( ! (flags & kXMPFiles_HandlerOwnsFile) );
+ XMP_Assert ( ! (flags & kXMPFiles_FolderBasedFormat) );
+ XMP_Assert ( (flags & kXMPFiles_CanInjectXMP) ? (flags & kXMPFiles_CanExpand) : 1 );
+
+ if ( replaceExisting )
+ {
+ //
+ // Remember previous file handler for this format.
+ // Reject if there is already a replacement.
+ //
+ if( mReplacedHandlers->find( format ) == mReplacedHandlers->end() )
+ {
+ XMPFileHandlerInfo* standardHandler = this->getHandlerInfo( format );
+
+ if( standardHandler != NULL )
+ {
+ mReplacedHandlers->insert( mReplacedHandlers->end(), XMPFileHandlerTablePair( format, *standardHandler ) );
+ }
+ else
+ {
+ // skip registration if there is nothing to replace
+ return false;
+ }
+ }
+ else
+ {
+ // skip registration if there is already a replacing handler registered for this format
+ return false;
+ }
+
+ // remove existing handler
+ this->removeHandler ( format );
+ }
+ else
+ {
+ // skip registration if there is already a handler registered for this format
+ if ( this->getFormatInfo ( format ) ) return false;
+ }
+
+ //
+ // register handler
+ //
+ XMPFileHandlerInfo handlerInfo ( format, flags, checkProc, handlerCTor );
+ mNormalHandlers->insert ( mNormalHandlers->end(), XMPFileHandlerTablePair ( format, handlerInfo ) );
+ return true;
+
+} // HandlerRegistry::registerNormalHandler
+
+// =================================================================================================
+
+bool HandlerRegistry::registerOwningHandler( XMP_FileFormat format,
+ XMP_OptionBits flags,
+ CheckFileFormatProc checkProc,
+ XMPFileHandlerCTor handlerCTor,
+ bool replaceExisting /*= false*/ )
+{
+ XMP_Assert ( format != kXMP_UnknownFile );
+
+ XMP_Assert ( flags & kXMPFiles_HandlerOwnsFile );
+ XMP_Assert ( ! (flags & kXMPFiles_FolderBasedFormat) );
+ XMP_Assert ( (flags & kXMPFiles_CanInjectXMP) ? (flags & kXMPFiles_CanExpand) : 1 );
+
+ if ( replaceExisting )
+ {
+ //
+ // Remember previous file handler for this format.
+ // Reject if there is already a replacement.
+ //
+ if( mReplacedHandlers->find( format ) == mReplacedHandlers->end() )
+ {
+ XMPFileHandlerInfo* standardHandler = this->getHandlerInfo( format );
+
+ if( standardHandler != NULL )
+ {
+ mReplacedHandlers->insert( mReplacedHandlers->end(), XMPFileHandlerTablePair( format, *standardHandler ) );
+ }
+ else
+ {
+ // skip registration if there is nothing to replace
+ return false;
+ }
+ }
+ else
+ {
+ // skip registration if there is already a replacing handler registered for this format
+ return false;
+ }
+
+ // remove existing handler
+ this->removeHandler ( format );
+ }
+ else
+ {
+ // skip registration if there is already a handler registered for this format
+ if ( this->getFormatInfo ( format ) ) return false;
+ }
+
+ //
+ // register handler
+ //
+ XMPFileHandlerInfo handlerInfo ( format, flags, checkProc, handlerCTor );
+ mOwningHandlers->insert ( mOwningHandlers->end(), XMPFileHandlerTablePair ( format, handlerInfo ) );
+ return true;
+
+} // HandlerRegistry::registerOwningHandler
+
+// =================================================================================================
+
+void HandlerRegistry::removeHandler ( XMP_FileFormat format ) {
+
+ XMPFileHandlerTablePos handlerPos;
+
+ handlerPos = mFolderHandlers->find ( format );
+ if ( handlerPos != mFolderHandlers->end() ) {
+ mFolderHandlers->erase ( handlerPos );
+ XMP_Assert ( ! this->getFormatInfo ( format ) );
+ return;
+ }
+
+ handlerPos = mNormalHandlers->find ( format );
+ if ( handlerPos != mNormalHandlers->end() ) {
+ mNormalHandlers->erase ( handlerPos );
+ XMP_Assert ( ! this->getFormatInfo ( format ) );
+ return;
+ }
+
+ handlerPos = mOwningHandlers->find ( format );
+ if ( handlerPos != mOwningHandlers->end() ) {
+ mOwningHandlers->erase ( handlerPos );
+ XMP_Assert ( ! this->getFormatInfo ( format ) );
+ return;
+ }
+
+} // HandlerRegistry::removeHandler
+
+// =================================================================================================
+
+XMP_FileFormat HandlerRegistry::getFileFormat( const std::string & fileExt, bool addIfNotFound /*= false*/ )
+{
+ if ( ! fileExt.empty() ) {
+ for ( int i=0; kFileExtMap[i].format != 0; ++i ) {
+ if ( fileExt == kFileExtMap[i].ext ) return kFileExtMap[i].format;
+ }
+ }
+
+ return ResourceParser::getPluginFileFormat ( fileExt, addIfNotFound );
+}
+
+// =================================================================================================
+
+XMPFileHandlerInfo* HandlerRegistry::getHandlerInfo( XMP_FileFormat format )
+{
+ XMPFileHandlerTablePos handlerPos;
+
+ handlerPos = mFolderHandlers->find( format );
+
+ if( handlerPos != mFolderHandlers->end() )
+ {
+ return &(handlerPos->second);
+ }
+
+ handlerPos = mNormalHandlers->find ( format );
+
+ if( handlerPos != mNormalHandlers->end() )
+ {
+ return &(handlerPos->second);
+ }
+
+ handlerPos = mOwningHandlers->find ( format );
+
+ if( handlerPos != mOwningHandlers->end() )
+ {
+ return &(handlerPos->second);
+ }
+
+ return NULL;
+}
+
+// =================================================================================================
+
+XMPFileHandlerInfo* HandlerRegistry::getStandardHandlerInfo( XMP_FileFormat format )
+{
+ XMPFileHandlerTablePos handlerPos = mReplacedHandlers->find( format );
+
+ if( handlerPos != mReplacedHandlers->end() )
+ {
+ return &(handlerPos->second);
+ }
+ else
+ {
+ return this->getHandlerInfo( format );
+ }
+}
+
+// =================================================================================================
+
+bool HandlerRegistry::isReplaced( XMP_FileFormat format )
+{
+ return ( mReplacedHandlers->find( format ) != mReplacedHandlers->end() );
+}
+
+// =================================================================================================
+
+bool HandlerRegistry::getFormatInfo( XMP_FileFormat format, XMP_OptionBits* flags /*= 0*/ )
+{
+ if ( flags == 0 ) flags = &voidOptionBits;
+
+ XMPFileHandlerInfo* handler = this->getHandlerInfo( format );
+
+ if( handler != NULL )
+ {
+ *flags = handler->flags;
+ }
+
+ return ( handler != NULL );
+} // HandlerRegistry::getFormatInfo
+
+// =================================================================================================
+
+XMPFileHandlerInfo* HandlerRegistry::pickDefaultHandler ( XMP_FileFormat format, const std::string & fileExt )
+{
+ if ( format == kXMP_UnknownFile ) format = this->getFileFormat ( fileExt );
+ if ( format == kXMP_UnknownFile ) return 0;
+
+ XMPFileHandlerTablePos handlerPos;
+
+ handlerPos = mNormalHandlers->find ( format );
+ if ( handlerPos != mNormalHandlers->end() ) return &handlerPos->second;
+
+ handlerPos = mOwningHandlers->find ( format );
+ if ( handlerPos != mOwningHandlers->end() ) return &handlerPos->second;
+
+ handlerPos = mFolderHandlers->find ( format );
+ if ( handlerPos != mFolderHandlers->end() ) return &handlerPos->second;
+
+ return 0;
+}
+
+// =================================================================================================
+
+XMPFileHandlerInfo* HandlerRegistry::selectSmartHandler( XMPFiles* session, XMP_StringPtr clientPath, XMP_FileFormat format, XMP_OptionBits openFlags )
+{
+
+ // The normal case for selectSmartHandler is when OpenFile is given a string file path. All of
+ // the stages described below have slight special cases when OpenFile is given an XMP_IO object
+ // for client-managed I/O. In that case the only handlers considered are those for embedded XMP
+ // that do not need to own the file.
+ //
+ // There are 4 stages in finding a handler, ending at the first success:
+ // 1. If the client passes in a format, try that handler.
+ // 2. Try all of the folder-oriented handlers.
+ // 3. Try a file-oriented handler based on the file extension.
+ // 4. Try all of the file-oriented handlers.
+ //
+ // The most common case is almost certainly #3, so we want to get there quickly. Most of the
+ // time the client won't pass in a format, so #1 takes no time. The folder-oriented handler
+ // checks are preceded by minimal folder checks. These checks are meant to be fast in the
+ // failure case. The folder-oriented checks have to go before the general file-oriented checks
+ // because the client path might be to one of the inner files, and we might have a file-oriented
+ // handler for that kind of file, but we want to recognize the clip. More details are below.
+ //
+ // In brief, the folder-oriented formats use shallow trees with specific folder names and
+ // highly stylized file names. The user thinks of the tree as a collection of clips, each clip
+ // is stored as multiple files for video, audio, metadata, etc. The folder-oriented stage has
+ // to be first because there can be files in the structure that are also covered by a
+ // file-oriented handler.
+ //
+ // In the file-oriented case, the CheckProc should do as little as possible to determine the
+ // format, based on the actual file content. If that is not possible, use the format hint. The
+ // initial CheckProc calls (steps 1 and 3) has the presumed format in this->format, the later
+ // calls (step 4) have kXMP_UnknownFile there.
+ //
+ // The folder-oriented checks need to be well optimized since the formats are relatively rare,
+ // but have to go first and could require multiple file system calls to identify. We want to
+ // get to the first file-oriented guess as quickly as possible, that is the real handler most of
+ // the time.
+ //
+ // The folder-oriented handlers are for things like P2 and XDCAM that use files distributed in a
+ // well defined folder structure. Using a portion of P2 as an example:
+ // .../MyMovie
+ // CONTENTS
+ // CLIP
+ // 0001AB.XML
+ // 0002CD.XML
+ // VIDEO
+ // 0001AB.MXF
+ // 0002CD.MXF
+ // VOICE
+ // 0001AB.WAV
+ // 0002CD.WAV
+ //
+ // The user thinks of .../MyMovie as the container of P2 stuff, in this case containing 2 clips
+ // called 0001AB and 0002CD. The exact folder structure and file layout differs, but the basic
+ // concepts carry across all of the folder-oriented handlers.
+ //
+ // The client path can be a conceptual clip path like .../MyMovie/0001AB, or a full path to any
+ // of the contained files. For file paths we have to behave the same as the implied conceptual
+ // path, e.g. we don't want .../MyMovie/CONTENTS/VOICE/0001AB.WAV to invoke the WAV handler.
+ // There might also be a mapping from user friendly names to clip names (e.g. Intro to 0001AB).
+ // If so that is private to the handler and does not affect this code.
+ //
+ // In order to properly handle the file path input we have to look for the folder-oriented case
+ // before any of the file-oriented cases. And since these are relatively rare, hence fail most of
+ // the time, we have to get in and out fast in the not handled case. That is what we do here.
+ //
+ // The folder-oriented processing done here is roughly:
+ //
+ // 1. Get the state of the client path: does-not-exist, is-file, is-folder, is-other.
+ // 2. Reject is-folder and is-other, they can't possibly be a valid case.
+ // 3. For does-not-exist:
+ // 3a. Split the client path into a leaf component and root path.
+ // 3b. Make sure the root path names an existing folder.
+ // 3c. Make sure the root folder has a viable top level child folder (e.g. CONTENTS).
+ // 4. For is-file:
+ // 4a. Split the client path into a root path, grandparent folder, parent folder, and leaf name.
+ // 4b. Make sure the parent or grandparent has a viable name (e.g. CONTENTS).
+ // 5. Try the registered folder handlers.
+ //
+ // For the common case of "regular" files, we should only get as far as 3b. This is just 1 file
+ // system call to get the client path state and some string processing.
+
+ bool readOnly = XMP_OptionIsClear( openFlags, kXMPFiles_OpenForUpdate );
+
+ Host_IO::FileMode clientMode;
+ std::string rootPath;
+ std::string leafName;
+ std::string fileExt;
+ std::string emptyStr;
+
+ XMPFileHandlerInfo* handlerInfo = 0;
+ bool foundHandler = false;
+
+ if ( openFlags & kXMPFiles_ForceGivenHandler ) {
+ // We're being told to blindly use the handler for the given format and nothing else.
+ return this->pickDefaultHandler ( format, emptyStr ); // Picks based on just the format.
+ }
+
+ if ( session->UsesClientIO() ) {
+
+ XMP_Assert ( session->ioRef != 0 );
+ clientMode = Host_IO::kFMode_IsFile;
+
+ } else {
+
+ clientMode = Host_IO::GetFileMode( clientPath );
+ if ( (clientMode == Host_IO::kFMode_IsFolder) || (clientMode == Host_IO::kFMode_IsOther) ) return 0;
+
+ rootPath = clientPath;
+ XIO::SplitLeafName ( &rootPath, &leafName );
+
+ if ( leafName.empty() ) return 0;
+
+ if ( clientMode == Host_IO::kFMode_IsFile ) {
+ // Only extract the file extension for existing files. Non-existing files can only be
+ // logical clip names, and they don't have file extensions.
+ XIO::SplitFileExtension ( &leafName, &fileExt );
+ }
+
+ }
+
+ session->format = kXMP_UnknownFile; // Make sure it is preset for later checks.
+ session->openFlags = openFlags;
+
+ // If the client passed in a format, try that first.
+
+ if( format != kXMP_UnknownFile )
+ {
+ handlerInfo = this->pickDefaultHandler( format, emptyStr ); // Picks based on just the format.
+
+ if( handlerInfo != 0 )
+ {
+ if( ( session->ioRef == 0 ) && (! ( handlerInfo->flags & kXMPFiles_HandlerOwnsFile ) ) )
+ {
+ session->ioRef = XMPFiles_IO::New_XMPFiles_IO( clientPath, readOnly );
+
+ if ( session->ioRef == 0 ) return 0;
+ }
+
+ session->format = format; // ! Hack to tell the CheckProc session is an initial call.
+
+ if( handlerInfo->flags & kXMPFiles_FolderBasedFormat )
+ {
+#if 0
+ std::string gpName, parentName;
+ if ( clientMode == Host_IO::kFMode_IsFile ) {
+ XIO::SplitLeafName( &rootPath, &parentName );
+ XIO::SplitLeafName( &rootPath, &gpName );
+ if ( format != kXMP_XDCAM_FAMFile ) MakeUpperCase( &gpName ); // ! Save the original case for XDCAM-FAM.
+ MakeUpperCase( &parentName );
+ }
+ CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, session );
+#else
+ // *** Don't try here yet. These are messy, needing existence checking and path processing.
+ // *** CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc);
+ // *** foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, session );
+ // *** Don't let OpenStrictly cause an early exit:
+ if( openFlags & kXMPFiles_OpenStrictly ) openFlags ^= kXMPFiles_OpenStrictly;
+#endif
+ }
+ else
+ {
+ bool tryThisHandler = true;
+
+ if( session->UsesClientIO() )
+ {
+ if ( (handlerInfo->flags & kXMPFiles_UsesSidecarXMP) ||
+ (handlerInfo->flags & kXMPFiles_HandlerOwnsFile) ) tryThisHandler = false;
+ }
+
+ if( tryThisHandler )
+ {
+ CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( format, clientPath, session->ioRef, session );
+ }
+ }
+
+ XMP_Assert( foundHandler || (session->tempPtr == 0) );
+
+ if ( foundHandler ) return handlerInfo;
+
+ handlerInfo = 0; // ! Clear again for later use.
+ }
+
+ if ( openFlags & kXMPFiles_OpenStrictly ) return 0;
+
+ }
+
+#if EnableDynamicMediaHandlers // All of the folder handlers are for dynamic media.
+
+ // Try the folder handlers if appropriate.
+
+ if( session->UsesLocalIO() )
+ {
+ XMP_Assert ( handlerInfo == 0 );
+ XMP_Assert ( (clientMode == Host_IO::kFMode_IsFile) || (clientMode == Host_IO::kFMode_DoesNotExist) );
+
+ std::string gpName, parentName;
+
+ if( clientMode == Host_IO::kFMode_DoesNotExist )
+ {
+ // 3. For does-not-exist:
+ // 3a. Split the client path into a leaf component and root path.
+ // 3b. Make sure the root path names an existing folder.
+ // 3c. Make sure the root folder has a viable top level child folder.
+
+ // ! This does "return 0" on failure, the file does not exist so a normal file handler can't apply.
+
+ if ( Host_IO::GetFileMode ( rootPath.c_str() ) != Host_IO::kFMode_IsFolder ) return 0;
+
+ session->format = checkTopFolderName ( rootPath );
+
+ if ( session->format == kXMP_UnknownFile ) return 0;
+
+ handlerInfo = this->tryFolderHandlers( session->format, rootPath, gpName, parentName, leafName, session ); // ! Parent and GP are empty.
+
+ return handlerInfo; // ! Return found handler or 0.
+ }
+
+ XMP_Assert ( clientMode == Host_IO::kFMode_IsFile );
+
+ // 4. For is-file:
+ // 4a. Split the client path into root, grandparent, parent, and leaf.
+ // 4b. Make sure the parent or grandparent has a viable name.
+
+ // ! Don't "return 0" on failure, this has to fall through to the normal file handlers.
+
+ XIO::SplitLeafName( &rootPath, &parentName );
+ XIO::SplitLeafName( &rootPath, &gpName );
+ std::string origGPName ( gpName ); // ! Save the original case for XDCAM-FAM.
+ MakeUpperCase( &parentName );
+ MakeUpperCase( &gpName );
+
+ session->format = checkParentFolderNames( rootPath, gpName, parentName, leafName );
+
+ if( session->format != kXMP_UnknownFile )
+ {
+ if( (session->format == kXMP_XDCAM_FAMFile) &&
+ ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) )
+ {
+ // ! The standard says Clip/Edit/Sub, but we just shifted to upper case.
+ gpName = origGPName; // ! XDCAM-FAM has just 1 level of inner folder, preserve the "MyMovie" case.
+ }
+
+ handlerInfo = tryFolderHandlers ( session->format, rootPath, gpName, parentName, leafName, session );
+ if ( handlerInfo != 0 ) return handlerInfo;
+
+ }
+ }
+
+#endif // EnableDynamicMediaHandlers
+
+ // Try an initial file-oriented handler based on the extension.
+
+ if( session->UsesLocalIO() )
+ {
+ handlerInfo = pickDefaultHandler ( kXMP_UnknownFile, fileExt ); // Picks based on just the extension.
+
+ if( handlerInfo != 0 )
+ {
+ if( (session->ioRef == 0) && (! (handlerInfo->flags & kXMPFiles_HandlerOwnsFile)) )
+ {
+ session->ioRef = XMPFiles_IO::New_XMPFiles_IO ( clientPath, readOnly );
+ if ( session->ioRef == 0 ) return 0;
+ }
+ else if( (session->ioRef != 0) && (handlerInfo->flags & kXMPFiles_HandlerOwnsFile) )
+ {
+ delete session->ioRef; // Close is implicit in the destructor.
+ session->ioRef = 0;
+ }
+
+ session->format = handlerInfo->format; // ! Hack to tell the CheckProc this is an initial call.
+ CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( handlerInfo->format, clientPath, session->ioRef, session );
+ XMP_Assert ( foundHandler || (session->tempPtr == 0) );
+
+ if ( foundHandler ) return handlerInfo;
+ }
+ }
+
+ // Search the handlers that don't want to open the file themselves.
+
+ if( session->ioRef == 0 )
+ {
+ session->ioRef = XMPFiles_IO::New_XMPFiles_IO ( clientPath, readOnly );
+ if ( session->ioRef == 0 ) return 0;
+ }
+
+ XMPFileHandlerTablePos handlerPos = mNormalHandlers->begin();
+
+ for( ; handlerPos != mNormalHandlers->end(); ++handlerPos )
+ {
+ session->format = kXMP_UnknownFile; // ! Hack to tell the CheckProc this is not an initial call.
+ handlerInfo = &handlerPos->second;
+ CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( handlerInfo->format, clientPath, session->ioRef, session );
+ XMP_Assert ( foundHandler || (session->tempPtr == 0) );
+ if ( foundHandler ) return handlerInfo;
+ }
+
+ // Search the handlers that do want to open the file themselves.
+
+ if( session->UsesLocalIO() )
+ {
+ delete session->ioRef; // Close is implicit in the destructor.
+ session->ioRef = 0;
+ handlerPos = mOwningHandlers->begin();
+
+ for( ; handlerPos != mOwningHandlers->end(); ++handlerPos )
+ {
+ session->format = kXMP_UnknownFile; // ! Hack to tell the CheckProc this is not an initial call.
+ handlerInfo = &handlerPos->second;
+ CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( handlerInfo->format, clientPath, session->ioRef, session );
+ XMP_Assert ( foundHandler || (session->tempPtr == 0) );
+ if ( foundHandler ) return handlerInfo;
+ }
+ }
+
+ // Failed to find a smart handler.
+
+ return 0;
+
+} // HandlerRegistry::selectSmartHandler
+
+// =================================================================================================
+
+#if EnableDynamicMediaHandlers
+
+XMPFileHandlerInfo* HandlerRegistry::tryFolderHandlers( XMP_FileFormat format,
+ const std::string & rootPath,
+ const std::string & gpName,
+ const std::string & parentName,
+ const std::string & leafName,
+ XMPFiles * parentObj )
+{
+ bool foundHandler = false;
+ XMPFileHandlerInfo * handlerInfo = 0;
+ XMPFileHandlerTablePos handlerPos;
+
+ // We know we're in a possible context for a folder-oriented handler, so try them.
+
+ if( format != kXMP_UnknownFile )
+ {
+
+ // Have an explicit format, pick that or nothing.
+ handlerPos = mFolderHandlers->find ( format );
+
+ if( handlerPos != mFolderHandlers->end() )
+ {
+ handlerInfo = &handlerPos->second;
+ CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, parentObj );
+ XMP_Assert ( foundHandler || (parentObj->tempPtr == 0) );
+ }
+ }
+ else
+ {
+ // Try all of the folder handlers.
+ for( handlerPos = mFolderHandlers->begin(); handlerPos != mFolderHandlers->end(); ++handlerPos )
+ {
+ handlerInfo = &handlerPos->second;
+ CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc);
+ foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, parentObj );
+ XMP_Assert ( foundHandler || (parentObj->tempPtr == 0) );
+ if ( foundHandler ) break; // ! Exit before incrementing handlerPos.
+ }
+ }
+
+ if ( ! foundHandler ) handlerInfo = 0;
+
+ return handlerInfo;
+}
+
+#endif
+
+// =================================================================================================
+
+#if EnableDynamicMediaHandlers
+
+/*static*/ XMP_FileFormat HandlerRegistry::checkTopFolderName( const std::string & rootPath )
+{
+ // This is called when the input path to XMPFiles::OpenFile does not name an existing file (or
+ // existing anything). We need to quickly decide if this might be a logical path for a folder
+ // handler. See if the root contains the top content folder for any of the registered folder
+ // handlers. This check does not have to be precise, the handler will do that. This does have to
+ // be fast.
+ //
+ // Since we don't have many folder handlers, this is simple hardwired code.
+
+ std::string childPath = rootPath;
+ childPath += kDirChar;
+ size_t baseLen = childPath.size();
+
+ // P2 .../MyMovie/CONTENTS/<group>/... - only check for CONTENTS/CLIP
+ childPath += "CONTENTS";
+ childPath += kDirChar;
+ childPath += "CLIP";
+ if ( Host_IO::GetFileMode ( childPath.c_str() ) == Host_IO::kFMode_IsFolder ) return kXMP_P2File;
+ childPath.erase ( baseLen );
+
+ // XDCAM-FAM .../MyMovie/<group>/... - only check for Clip and MEDIAPRO.XML
+ childPath += "Clip"; // ! Yes, mixed case.
+ if ( Host_IO::GetFileMode ( childPath.c_str() ) == Host_IO::kFMode_IsFolder ) {
+ childPath.erase ( baseLen );
+ childPath += "MEDIAPRO.XML";
+ if ( Host_IO::GetFileMode ( childPath.c_str() ) == Host_IO::kFMode_IsFile ) return kXMP_XDCAM_FAMFile;
+ }
+ childPath.erase ( baseLen );
+
+ // XDCAM-SAM .../MyMovie/PROAV/<group>/... - only check for PROAV/CLPR
+ childPath += "PROAV";
+ childPath += kDirChar;
+ childPath += "CLPR";
+ if ( Host_IO::GetFileMode ( childPath.c_str() ) == Host_IO::kFMode_IsFolder ) return kXMP_XDCAM_SAMFile;
+ childPath.erase ( baseLen );
+
+ // XDCAM-EX .../MyMovie/BPAV/<group>/... - check for BPAV/CLPR
+ childPath += "BPAV";
+ childPath += kDirChar;
+ childPath += "CLPR";
+ if ( Host_IO::GetFileMode ( childPath.c_str() ) == Host_IO::kFMode_IsFolder ) return kXMP_XDCAM_EXFile;
+ childPath.erase ( baseLen );
+
+ // Sony HDV .../MyMovie/VIDEO/HVR/<file>.<ext> - check for VIDEO/HVR
+ childPath += "VIDEO";
+ childPath += kDirChar;
+ childPath += "HVR";
+ if ( Host_IO::GetFileMode ( childPath.c_str() ) == Host_IO::kFMode_IsFolder ) return kXMP_SonyHDVFile;
+ childPath.erase ( baseLen );
+
+ return kXMP_UnknownFile;
+
+} // CheckTopFolderName
+
+#endif
+
+// =================================================================================================
+
+#if EnableDynamicMediaHandlers
+
+/*static*/ XMP_FileFormat HandlerRegistry::checkParentFolderNames( const std::string& rootPath,
+ const std::string& gpName,
+ const std::string& parentName,
+ const std::string& leafName )
+{
+ IgnoreParam ( parentName );
+
+ // This is called when the input path to XMPFiles::OpenFile names an existing file. We need to
+ // quickly decide if this might be inside a folder-handler's structure. See if the containing
+ // folders might match any of the registered folder handlers. This check does not have to be
+ // precise, the handler will do that. This does have to be fast.
+ //
+ // Since we don't have many folder handlers, this is simple hardwired code. Note that the caller
+ // has already shifted the names to upper case.
+
+ // P2 .../MyMovie/CONTENTS/<group>/<file>.<ext> - check CONTENTS and <group>
+ if ( (gpName == "CONTENTS") && CheckP2ContentChild ( parentName ) ) return kXMP_P2File;
+
+ // XDCAM-EX .../MyMovie/BPAV/CLPR/<clip>/<file>.<ext> - check for BPAV/CLPR
+ // ! This must be checked before XDCAM-SAM because both have a "CLPR" grandparent.
+ if ( gpName == "CLPR" ) {
+ std::string tempPath, greatGP;
+ tempPath = rootPath;
+ XIO::SplitLeafName ( &tempPath, &greatGP );
+ MakeUpperCase ( &greatGP );
+ if ( greatGP == "BPAV" ) return kXMP_XDCAM_EXFile;
+ }
+
+ // XDCAM-FAM .../MyMovie/<group>/<file>.<ext> - check that <group> is CLIP, or EDIT, or SUB
+ // ! The standard says Clip/Edit/Sub, but the caller has already shifted to upper case.
+ if ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) return kXMP_XDCAM_FAMFile;
+
+ // XDCAM-SAM .../MyMovie/PROAV/<group>/<clip>/<file>.<ext> - check for PROAV and CLPR or EDTR
+ if ( (gpName == "CLPR") || (gpName == "EDTR") ) {
+ std::string tempPath, greatGP;
+ tempPath = rootPath;
+ XIO::SplitLeafName ( &tempPath, &greatGP );
+ MakeUpperCase ( &greatGP );
+ if ( greatGP == "PROAV" ) return kXMP_XDCAM_SAMFile;
+ }
+
+ // Sony HDV .../MyMovie/VIDEO/HVR/<file>.<ext> - check for VIDEO and HVR
+ if ( (gpName == "VIDEO") && (parentName == "HVR") ) return kXMP_SonyHDVFile;
+
+ return kXMP_UnknownFile;
+
+} // CheckParentFolderNames
+
+#endif