void
 LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
                               const Config& config,
                               const ConstContextRcPtr & context,
                               CachedFileRcPtr untypedCachedFile,
                               const FileTransform& fileTransform,
                               TransformDirection dir) const
 {
     LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile);
     
     // This should never happen.
     if(!cachedFile)
     {
         std::ostringstream os;
         os << "Cannot build .cdl Op. Invalid cache type.";
         throw Exception(os.str().c_str());
     }
     
     TransformDirection newDir = CombineTransformDirections(dir,
         fileTransform.getDirection());
     if(newDir == TRANSFORM_DIR_UNKNOWN)
     {
         std::ostringstream os;
         os << "Cannot build ASC FileTransform,";
         os << " unspecified transform direction.";
         throw Exception(os.str().c_str());
     }
     
     // Below this point, we should throw ExceptionMissingFile on
     // errors rather than Exception
     // This is because we've verified that the cdl file is valid,
     // at now we're only querying whether the specified cccid can
     // be found.
     //
     // Using ExceptionMissingFile enables the missing looks fallback
     // mechanism to function properly.
     // At the time ExceptionMissingFile was named, we errently assumed
     // a 1:1 relationship between files and color corrections, which is
     // not true for .cdl files.
     //
     // In a future OCIO release, it may be more appropriate to
     // rename ExceptionMissingFile -> ExceptionMissingCorrection.
     // But either way, it's what we should throw below.
     
     std::string cccid = fileTransform.getCCCId();
     cccid = context->resolveStringVar(cccid.c_str());
     
     if(cccid.empty())
     {
         std::ostringstream os;
         os << "You must specify which cccid to load from the ccc file";
         os << " (either by name or index).";
         throw ExceptionMissingFile(os.str().c_str());
     }
     
     bool success=false;
     
     // Try to parse the cccid as a string id
     CDLTransformMap::const_iterator iter = cachedFile->transformMap.find(cccid);
     if(iter != cachedFile->transformMap.end())
     {
         success = true;
         BuildCDLOps(ops,
                     config,
                     *(iter->second),
                     newDir);
     }
     
     // Try to parse the cccid as an integer index
     // We want to be strict, so fail if leftover chars in the parse.
     if(!success)
     {
         int cccindex=0;
         if(StringToInt(&cccindex, cccid.c_str(), true))
         {
             int maxindex = ((int)cachedFile->transformVec.size())-1;
             if(cccindex<0 || cccindex>maxindex)
             {
                 std::ostringstream os;
                 os << "The specified cccindex " << cccindex;
                 os << " is outside the valid range for this file [0,";
                 os << maxindex << "]";
                 throw ExceptionMissingFile(os.str().c_str());
             }
             
             success = true;
             BuildCDLOps(ops,
                         config,
                         *cachedFile->transformVec[cccindex],
                         newDir);
         }
     }
     
     if(!success)
     {
         std::ostringstream os;
         os << "You must specify a valid cccid to load from the ccc file";
         os << " (either by name or index). id='" << cccid << "' ";
         os << "is not found in the file, and is not parsable as an ";
         os << "integer index.";
         throw ExceptionMissingFile(os.str().c_str());
     }
 }
 void BuildLookOps(OpRcPtrVec & ops,
                   ConstColorSpaceRcPtr & currentColorSpace,
                   bool skipColorSpaceConversions,
                   const Config& config,
                   const ConstContextRcPtr & context,
                   const LookParseResult & looks)
 {
     const LookParseResult::Options & options = looks.getOptions();
     
     if(options.empty())
     {
         // Do nothing
     }
     else if(options.size() == 1)
     {
         // As an optimization, if we only have a single look option,
         // just push back onto the final location
         RunLookTokens(ops,
                       currentColorSpace,
                       skipColorSpaceConversions,
                       config,
                       context,
                       options[0]);
     }
     else
     {
         // If we have multiple look options, try each one in order,
         // and if we can create the ops without a missing file exception,
         // push back it's results and return
         
         bool success = false;
         std::ostringstream os;
         
         OpRcPtrVec tmpOps;
         ConstColorSpaceRcPtr cs;
         
         for(unsigned int i=0; i<options.size(); ++i)
         {
             cs = currentColorSpace;
             tmpOps.clear();
             
             try
             {
                 RunLookTokens(tmpOps,
                               cs,
                               skipColorSpaceConversions,
                               config,
                               context,
                               options[i]);
                 success = true;
                 break;
             }
             catch(ExceptionMissingFile & e)
             {
                 if(i != 0) os << "  ...  ";
                 
                 os << "(";
                 LookParseResult::serialize(os, options[i]);
                 os << ") " << e.what();
             }
         }
         
         if(success)
         {
             currentColorSpace = cs;
             std::copy(tmpOps.begin(), tmpOps.end(), std::back_inserter(ops));
         }
         else
         {
             throw ExceptionMissingFile(os.str().c_str());
         }
     }
 }