PageRenderTime 125ms CodeModel.GetById 15ms app.highlight 97ms RepoModel.GetById 2ms app.codeStats 0ms

/src/core/Config.cpp

http://github.com/imageworks/OpenColorIO
C++ | 1957 lines | 1514 code | 336 blank | 107 comment | 189 complexity | 86cd1be9ee9af0728bff52be00b4832d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
   3All Rights Reserved.
   4
   5Redistribution and use in source and binary forms, with or without
   6modification, are permitted provided that the following conditions are
   7met:
   8* Redistributions of source code must retain the above copyright
   9  notice, this list of conditions and the following disclaimer.
  10* Redistributions in binary form must reproduce the above copyright
  11  notice, this list of conditions and the following disclaimer in the
  12  documentation and/or other materials provided with the distribution.
  13* Neither the name of Sony Pictures Imageworks nor the names of its
  14  contributors may be used to endorse or promote products derived from
  15  this software without specific prior written permission.
  16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27*/
  28
  29
  30#include <cstdlib>
  31#include <cstring>
  32#include <set>
  33#include <sstream>
  34#include <fstream>
  35#include <utility>
  36#include <vector>
  37
  38#include <OpenColorIO/OpenColorIO.h>
  39
  40#include "HashUtils.h"
  41#include "Logging.h"
  42#include "LookParse.h"
  43#include "Display.h"
  44#include "MathUtils.h"
  45#include "Mutex.h"
  46#include "OpBuilders.h"
  47#include "PathUtils.h"
  48#include "ParseUtils.h"
  49#include "Processor.h"
  50#include "PrivateTypes.h"
  51#include "pystring/pystring.h"
  52#include "OCIOYaml.h"
  53
  54OCIO_NAMESPACE_ENTER
  55{
  56    namespace
  57    {
  58        const char * OCIO_CONFIG_ENVVAR = "OCIO";
  59        const char * OCIO_ACTIVE_DISPLAYS_ENVVAR = "OCIO_ACTIVE_DISPLAYS";
  60        const char * OCIO_ACTIVE_VIEWS_ENVVAR = "OCIO_ACTIVE_VIEWS";
  61        
  62        enum Sanity
  63        {
  64            SANITY_UNKNOWN = 0,
  65            SANITY_SANE,
  66            SANITY_INSANE
  67        };
  68        
  69        // These are the 709 primaries specified by the ASC.
  70        const float DEFAULT_LUMA_COEFF_R = 0.2126f;
  71        const float DEFAULT_LUMA_COEFF_G = 0.7152f;
  72        const float DEFAULT_LUMA_COEFF_B = 0.0722f;
  73        
  74        const char * INTERNAL_RAW_PROFILE = 
  75        "ocio_profile_version: 1\n"
  76        "strictparsing: false\n"
  77        "roles:\n"
  78        "  default: raw\n"
  79        "displays:\n"
  80        "  sRGB:\n"
  81        "  - !<View> {name: Raw, colorspace: raw}\n"
  82        "colorspaces:\n"
  83        "  - !<ColorSpace>\n"
  84        "      name: raw\n"
  85        "      family: raw\n"
  86        "      equalitygroup:\n"
  87        "      bitdepth: 32f\n"
  88        "      isdata: true\n"
  89        "      allocation: uniform\n"
  90        "      description: 'A raw color space. Conversions to and from this space are no-ops.'\n";
  91    }
  92    
  93    
  94    ///////////////////////////////////////////////////////////////////////////
  95    
  96    const char * GetVersion()
  97    {
  98        return OCIO_VERSION;
  99    }
 100    
 101    int GetVersionHex()
 102    {
 103        return OCIO_VERSION_HEX;
 104    }
 105    
 106    namespace
 107    {
 108        ConstConfigRcPtr g_currentConfig;
 109        Mutex g_currentConfigLock;
 110    }
 111    
 112    ConstConfigRcPtr GetCurrentConfig()
 113    {
 114        AutoMutex lock(g_currentConfigLock);
 115        
 116        if(!g_currentConfig)
 117        {
 118            g_currentConfig = Config::CreateFromEnv();
 119        }
 120        
 121        return g_currentConfig;
 122    }
 123    
 124    void SetCurrentConfig(const ConstConfigRcPtr & config)
 125    {
 126        AutoMutex lock(g_currentConfigLock);
 127        
 128        g_currentConfig = config->createEditableCopy();
 129    }
 130    
 131    namespace
 132    {
 133    
 134    // Environment
 135    std::string LookupEnvironment(const StringMap & env, const std::string & name)
 136    {
 137        StringMap::const_iterator iter = env.find(name);
 138        if(iter == env.end()) return "";
 139        return iter->second;
 140    }
 141    
 142    // Roles
 143    // (lower case role name: colorspace name)
 144    std::string LookupRole(const StringMap & roles, const std::string & rolename)
 145    {
 146        StringMap::const_iterator iter = roles.find(pystring::lower(rolename));
 147        if(iter == roles.end()) return "";
 148        return iter->second;
 149    }
 150    
 151    
 152    void GetFileReferences(std::set<std::string> & files,
 153                           const ConstTransformRcPtr & transform)
 154    {
 155        if(!transform) return;
 156        
 157        if(ConstGroupTransformRcPtr groupTransform = \
 158            DynamicPtrCast<const GroupTransform>(transform))
 159        {
 160            for(int i=0; i<groupTransform->size(); ++i)
 161            {
 162                GetFileReferences(files, groupTransform->getTransform(i));
 163            }
 164        }
 165        else if(ConstFileTransformRcPtr fileTransform = \
 166            DynamicPtrCast<const FileTransform>(transform))
 167        {
 168            files.insert(fileTransform->getSrc());
 169        }
 170    }
 171    
 172    void GetColorSpaceReferences(std::set<std::string> & colorSpaceNames,
 173                                 const ConstTransformRcPtr & transform)
 174    {
 175        if(!transform) return;
 176        
 177        if(ConstGroupTransformRcPtr groupTransform = \
 178            DynamicPtrCast<const GroupTransform>(transform))
 179        {
 180            for(int i=0; i<groupTransform->size(); ++i)
 181            {
 182                GetColorSpaceReferences(colorSpaceNames, groupTransform->getTransform(i));
 183            }
 184        }
 185        else if(ConstColorSpaceTransformRcPtr colorSpaceTransform = \
 186            DynamicPtrCast<const ColorSpaceTransform>(transform))
 187        {
 188            colorSpaceNames.insert(colorSpaceTransform->getSrc());
 189            colorSpaceNames.insert(colorSpaceTransform->getDst());
 190        }
 191        else if(ConstDisplayTransformRcPtr displayTransform = \
 192            DynamicPtrCast<const DisplayTransform>(transform))
 193        {
 194            colorSpaceNames.insert(displayTransform->getInputColorSpaceName());
 195        }
 196        else if(ConstLookTransformRcPtr lookTransform = \
 197            DynamicPtrCast<const LookTransform>(transform))
 198        {
 199            colorSpaceNames.insert(colorSpaceTransform->getSrc());
 200            colorSpaceNames.insert(colorSpaceTransform->getDst());
 201        }
 202    }
 203    
 204    
 205    bool FindColorSpaceIndex(int * index,
 206                             const ColorSpaceVec & colorspaces,
 207                             const std::string & csname)
 208    {
 209        if(csname.empty()) return false;
 210        
 211        std::string csnamelower = pystring::lower(csname);
 212        for(unsigned int i = 0; i < colorspaces.size(); ++i)
 213        {
 214            if(csnamelower == pystring::lower(colorspaces[i]->getName()))
 215            {
 216                if(index) *index = i;
 217                return true;
 218            }
 219        }
 220        
 221        return false;
 222    }
 223        
 224    } // namespace
 225    
 226    class Config::Impl
 227    {
 228    public:
 229        StringMap env_;
 230        ContextRcPtr context_;
 231        std::string description_;
 232        ColorSpaceVec colorspaces_;
 233        StringMap roles_;
 234        LookVec looksList_;
 235        
 236        DisplayMap displays_;
 237        StringVec activeDisplays_;
 238        StringVec activeDisplaysEnvOverride_;
 239        StringVec activeViews_;
 240        StringVec activeViewsEnvOverride_;
 241        
 242        mutable std::string activeDisplaysStr_;
 243        mutable std::string activeViewsStr_;
 244        mutable StringVec displayCache_;
 245        
 246        // Misc
 247        std::vector<float> defaultLumaCoefs_;
 248        bool strictParsing_;
 249        
 250        mutable Sanity sanity_;
 251        mutable std::string sanitytext_;
 252        
 253        mutable Mutex cacheidMutex_;
 254        mutable StringMap cacheids_;
 255        mutable std::string cacheidnocontext_;
 256        
 257        OCIOYaml io_;
 258        
 259        Impl() : 
 260            context_(Context::Create()),
 261            strictParsing_(true),
 262            sanity_(SANITY_UNKNOWN)
 263        {
 264            char* activeDisplays = std::getenv(OCIO_ACTIVE_DISPLAYS_ENVVAR);
 265            SplitStringEnvStyle(activeDisplaysEnvOverride_, activeDisplays);
 266            
 267            char * activeViews = std::getenv(OCIO_ACTIVE_VIEWS_ENVVAR);
 268            SplitStringEnvStyle(activeViewsEnvOverride_, activeViews);
 269            
 270            defaultLumaCoefs_.resize(3);
 271            defaultLumaCoefs_[0] = DEFAULT_LUMA_COEFF_R;
 272            defaultLumaCoefs_[1] = DEFAULT_LUMA_COEFF_G;
 273            defaultLumaCoefs_[2] = DEFAULT_LUMA_COEFF_B;
 274        }
 275        
 276        ~Impl()
 277        {
 278        
 279        }
 280        
 281        Impl& operator= (const Impl & rhs)
 282        {
 283            env_ = rhs.env_;
 284            context_ = rhs.context_->createEditableCopy();
 285            description_ = rhs.description_;
 286            
 287            // Deep copy the colorspaces
 288            colorspaces_.clear();
 289            colorspaces_.reserve(rhs.colorspaces_.size());
 290            for(unsigned int i=0; i<rhs.colorspaces_.size(); ++i)
 291            {
 292                colorspaces_.push_back(rhs.colorspaces_[i]->createEditableCopy());
 293            }
 294            
 295            // Deep copy the looks
 296            looksList_.clear();
 297            looksList_.reserve(rhs.looksList_.size());
 298            for(unsigned int i=0; i<rhs.looksList_.size(); ++i)
 299            {
 300                looksList_.push_back(rhs.looksList_[i]->createEditableCopy());
 301            }
 302            
 303            // Assignment operator will suffice for these
 304            roles_ = rhs.roles_;
 305            
 306            displays_ = rhs.displays_;
 307            activeDisplays_ = rhs.activeDisplays_;
 308            activeViews_ = rhs.activeViews_;
 309            activeViewsEnvOverride_ = rhs.activeViewsEnvOverride_;
 310            activeDisplaysEnvOverride_ = rhs.activeDisplaysEnvOverride_;
 311            activeDisplaysStr_ = rhs.activeDisplaysStr_;
 312            displayCache_ = rhs.displayCache_;
 313            
 314            defaultLumaCoefs_ = rhs.defaultLumaCoefs_;
 315            strictParsing_ = rhs.strictParsing_;
 316            
 317            sanity_ = rhs.sanity_;
 318            sanitytext_ = rhs.sanitytext_;
 319            
 320            cacheids_ = rhs.cacheids_;
 321            cacheidnocontext_ = cacheidnocontext_;
 322            return *this;
 323        }
 324        
 325        // Any time you modify the state of the config, you must call this
 326        // to reset internal cache states.  You also should do this in a
 327        // thread safe manner by acquiring the cacheidMutex_;
 328        void resetCacheIDs();
 329        
 330        // Get all internal transforms (to generate cacheIDs, validation, etc).
 331        // This currently crawls colorspaces + looks
 332        void getAllIntenalTransforms(ConstTransformVec & transformVec) const;
 333    };
 334    
 335    
 336    ///////////////////////////////////////////////////////////////////////////
 337    
 338    ConfigRcPtr Config::Create()
 339    {
 340        return ConfigRcPtr(new Config(), &deleter);
 341    }
 342    
 343    void Config::deleter(Config* c)
 344    {
 345        delete c;
 346    }
 347    
 348    ConstConfigRcPtr Config::CreateFromEnv()
 349    {
 350        char* file = std::getenv(OCIO_CONFIG_ENVVAR);
 351        if(file) return CreateFromFile(file);
 352        
 353        std::ostringstream os;
 354        os << "Color management disabled. ";
 355        os << "(Specify the $OCIO environment variable to enable.)";
 356        LogInfo(os.str());
 357        
 358        std::istringstream istream;
 359        istream.str(INTERNAL_RAW_PROFILE);
 360        
 361        ConfigRcPtr config = Config::Create();
 362        config->getImpl()->io_.open(istream, config);
 363        return config;
 364    }
 365    
 366    ConstConfigRcPtr Config::CreateFromFile(const char * filename)
 367    {
 368        std::ifstream istream(filename);
 369        if(istream.fail()) {
 370            std::ostringstream os;
 371            os << "Error could not read '" << filename;
 372            os << "' OCIO profile.";
 373            throw Exception (os.str().c_str());
 374        }
 375        
 376        ConfigRcPtr config = Config::Create();
 377        config->getImpl()->io_.open(istream, config, filename);
 378        return config;
 379    }
 380    
 381    ConstConfigRcPtr Config::CreateFromStream(std::istream & istream)
 382    {
 383        ConfigRcPtr config = Config::Create();
 384        config->getImpl()->io_.open(istream, config);
 385        return config;
 386    }
 387    
 388    ///////////////////////////////////////////////////////////////////////////
 389    
 390    
 391    
 392    Config::Config()
 393    : m_impl(new Config::Impl)
 394    {
 395    }
 396    
 397    Config::~Config()
 398    {
 399        delete m_impl;
 400        m_impl = NULL;
 401    }
 402    
 403    ConfigRcPtr Config::createEditableCopy() const
 404    {
 405        ConfigRcPtr config = Config::Create();
 406        *config->m_impl = *m_impl;
 407        return config;
 408    }
 409    
 410    void Config::sanityCheck() const
 411    {
 412        if(getImpl()->sanity_ == SANITY_SANE) return;
 413        if(getImpl()->sanity_ == SANITY_INSANE)
 414        {
 415            throw Exception(getImpl()->sanitytext_.c_str());
 416        }
 417        
 418        getImpl()->sanity_ = SANITY_INSANE;
 419        getImpl()->sanitytext_ = "";
 420        
 421        
 422        ///// COLORSPACES
 423        StringSet existingColorSpaces;
 424        
 425        // Confirm all ColorSpaces are valid
 426        for(unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
 427        {
 428            if(!getImpl()->colorspaces_[i])
 429            {
 430                std::ostringstream os;
 431                os << "Config failed sanitycheck. ";
 432                os << "The colorspace at index " << i << " is null.";
 433                getImpl()->sanitytext_ = os.str();
 434                throw Exception(getImpl()->sanitytext_.c_str());
 435            }
 436            
 437            const char * name = getImpl()->colorspaces_[i]->getName();
 438            if(!name || strlen(name) == 0)
 439            {
 440                std::ostringstream os;
 441                os << "Config failed sanitycheck. ";
 442                os << "The colorspace at index " << i << " is not named.";
 443                getImpl()->sanitytext_ = os.str();
 444                throw Exception(getImpl()->sanitytext_.c_str());
 445            }
 446            
 447            std::string namelower = pystring::lower(name);
 448            StringSet::const_iterator it = existingColorSpaces.find(namelower);
 449            if(it != existingColorSpaces.end())
 450            {
 451                std::ostringstream os;
 452                os << "Config failed sanitycheck. ";
 453                os << "Two colorspaces are defined with the same name, '";
 454                os << namelower << "'.";
 455                getImpl()->sanitytext_ = os.str();
 456                throw Exception(getImpl()->sanitytext_.c_str());
 457            }
 458            
 459            existingColorSpaces.insert(namelower);
 460        }
 461        
 462        // Confirm all roles are valid
 463        {
 464            for(StringMap::const_iterator iter = getImpl()->roles_.begin(),
 465                end = getImpl()->roles_.end(); iter!=end; ++iter)
 466            {
 467                int csindex = -1;
 468                if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->second))
 469                {
 470                    std::ostringstream os;
 471                    os << "Config failed sanitycheck. ";
 472                    os << "The role '" << iter->first << "' ";
 473                    os << "refers to a colorspace, '" << iter->second << "', ";
 474                    os << "which is not defined.";
 475                    getImpl()->sanitytext_ = os.str();
 476                    throw Exception(getImpl()->sanitytext_.c_str());
 477                }
 478                
 479                // Confirm no name conflicts between colorspaces and roles
 480                if(FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->first))
 481                {
 482                    std::ostringstream os;
 483                    os << "Config failed sanitycheck. ";
 484                    os << "The role '" << iter->first << "' ";
 485                    os << " is in conflict with a colorspace of the same name.";
 486                    getImpl()->sanitytext_ = os.str();
 487                    throw Exception(getImpl()->sanitytext_.c_str());
 488                }
 489            }
 490        }
 491        
 492        ///// DISPLAYS
 493        
 494        int numviews = 0;
 495        
 496        // Confirm all Displays transforms refer to colorspaces that exit
 497        for(DisplayMap::const_iterator iter = getImpl()->displays_.begin();
 498            iter != getImpl()->displays_.end();
 499            ++iter)
 500        {
 501            std::string display = iter->first;
 502            const ViewVec & views = iter->second;
 503            if(views.empty())
 504            {
 505                std::ostringstream os;
 506                os << "Config failed sanitycheck. ";
 507                os << "The display '" << display << "' ";
 508                os << "does not define any views.";
 509                getImpl()->sanitytext_ = os.str();
 510                throw Exception(getImpl()->sanitytext_.c_str());
 511            }
 512            
 513            for(unsigned int i=0; i<views.size(); ++i)
 514            {
 515                if(views[i].name.empty() || views[i].colorspace.empty())
 516                {
 517                    std::ostringstream os;
 518                    os << "Config failed sanitycheck. ";
 519                    os << "The display '" << display << "' ";
 520                    os << "defines a view with an empty name and/or colorspace.";
 521                    getImpl()->sanitytext_ = os.str();
 522                    throw Exception(getImpl()->sanitytext_.c_str());
 523                }
 524                
 525                int csindex = -1;
 526                if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, views[i].colorspace))
 527                {
 528                    std::ostringstream os;
 529                    os << "Config failed sanitycheck. ";
 530                    os << "The display '" << display << "' ";
 531                    os << "refers to a colorspace, '" << views[i].colorspace << "', ";
 532                    os << "which is not defined.";
 533                    getImpl()->sanitytext_ = os.str();
 534                    throw Exception(getImpl()->sanitytext_.c_str());
 535                }
 536                
 537                // Confirm looks references exist
 538                LookParseResult looks;
 539                const LookParseResult::Options & options = looks.parse(views[i].looks);
 540                
 541                for(unsigned int optionindex=0;
 542                    optionindex<options.size();
 543                    ++optionindex)
 544                {
 545                    for(unsigned int tokenindex=0;
 546                        tokenindex<options[optionindex].size();
 547                        ++tokenindex)
 548                    {
 549                        std::string look = options[optionindex][tokenindex].name;
 550                        
 551                        if(!look.empty() && !getLook(look.c_str()))
 552                        {
 553                            std::ostringstream os;
 554                            os << "Config failed sanitycheck. ";
 555                            os << "The display '" << display << "' ";
 556                            os << "refers to a look, '" << look << "', ";
 557                            os << "which is not defined.";
 558                            getImpl()->sanitytext_ = os.str();
 559                            throw Exception(getImpl()->sanitytext_.c_str());
 560                        }
 561                    }
 562                }
 563                
 564                ++numviews;
 565            }
 566        }
 567        
 568        // Confirm at least one display entry exists.
 569        if(numviews == 0)
 570        {
 571            std::ostringstream os;
 572            os << "Config failed sanitycheck. ";
 573            os << "No displays are specified.";
 574            getImpl()->sanitytext_ = os.str();
 575            throw Exception(getImpl()->sanitytext_.c_str());
 576        }
 577        
 578        // Confirm for all Transforms that reference internal colorspaces,
 579        // the named space exists
 580        {
 581            ConstTransformVec allTransforms;
 582            getImpl()->getAllIntenalTransforms(allTransforms);
 583            
 584            std::set<std::string> colorSpaceNames;
 585            for(unsigned int i=0; i<colorSpaceNames.size(); ++i)
 586            {
 587                GetColorSpaceReferences(colorSpaceNames, allTransforms[i]);
 588            }
 589            
 590            for(std::set<std::string>::iterator iter = colorSpaceNames.begin();
 591                iter != colorSpaceNames.end(); ++iter)
 592            {
 593                int csindex = -1;
 594                if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, *iter))
 595                {
 596                    std::ostringstream os;
 597                    os << "Config failed sanitycheck. ";
 598                    os << "This config references a ColorSpace, '" << *iter << "', ";
 599                    os << "which is not defined.";
 600                    getImpl()->sanitytext_ = os.str();
 601                    throw Exception(getImpl()->sanitytext_.c_str());
 602                }
 603            }
 604        }
 605        
 606        ///// LOOKS
 607        
 608        // For all looks, confirm the process space exists and the look is named
 609        for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
 610        {
 611            std::string name = getImpl()->looksList_[i]->getName();
 612            if(name.empty())
 613            {
 614                std::ostringstream os;
 615                os << "Config failed sanitycheck. ";
 616                os << "The look at index '" << i << "' ";
 617                os << "does not specify a name.";
 618                getImpl()->sanitytext_ = os.str();
 619                throw Exception(getImpl()->sanitytext_.c_str());
 620            }
 621            
 622            std::string processSpace = getImpl()->looksList_[i]->getProcessSpace();
 623            if(processSpace.empty())
 624            {
 625                std::ostringstream os;
 626                os << "Config failed sanitycheck. ";
 627                os << "The look '" << name << "' ";
 628                os << "does not specify a process space.";
 629                getImpl()->sanitytext_ = os.str();
 630                throw Exception(getImpl()->sanitytext_.c_str());
 631            }
 632            
 633            int csindex=0;
 634            if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, processSpace))
 635            {
 636                std::ostringstream os;
 637                os << "Config failed sanitycheck. ";
 638                os << "The look '" << name << "' ";
 639                os << "specifies a process color space, '";
 640                os << processSpace << "', which is not defined.";
 641                getImpl()->sanitytext_ = os.str();
 642                throw Exception(getImpl()->sanitytext_.c_str());
 643            }
 644        }
 645        
 646        
 647        
 648        // Everything is groovy.
 649        getImpl()->sanity_ = SANITY_SANE;
 650    }
 651    
 652    ///////////////////////////////////////////////////////////////////////////
 653    
 654    const char * Config::getDescription() const
 655    {
 656        return getImpl()->description_.c_str();
 657    }
 658    
 659    void Config::setDescription(const char * description)
 660    {
 661        getImpl()->description_ = description;
 662        
 663        AutoMutex lock(getImpl()->cacheidMutex_);
 664        getImpl()->resetCacheIDs();
 665    }
 666    
 667    
 668    // RESOURCES //////////////////////////////////////////////////////////////
 669    
 670    ConstContextRcPtr Config::getCurrentContext() const
 671    {
 672        return getImpl()->context_;
 673    }
 674    
 675    void Config::addEnvironmentVar(const char * name, const char * defaultValue)
 676    {
 677        if(defaultValue)
 678        {
 679            getImpl()->env_[std::string(name)] = std::string(defaultValue);
 680            getImpl()->context_->setStringVar(name, defaultValue);
 681        }
 682        else
 683        {
 684            StringMap::iterator iter = getImpl()->env_.find(std::string(name));
 685            if(iter != getImpl()->env_.end()) getImpl()->env_.erase(iter);
 686        }
 687        
 688        AutoMutex lock(getImpl()->cacheidMutex_);
 689        getImpl()->resetCacheIDs();
 690    }
 691    
 692    int Config::getNumEnvironmentVars() const
 693    {
 694        return static_cast<int>(getImpl()->env_.size());
 695    }
 696    
 697    const char * Config::getEnvironmentVarNameByIndex(int index) const
 698    {
 699        if(index < 0 || index >= (int)getImpl()->env_.size()) return "";
 700        StringMap::const_iterator iter = getImpl()->env_.begin();
 701        for(int i = 0; i < index; ++i) ++iter;
 702        return iter->first.c_str();
 703    }
 704    
 705    const char * Config::getEnvironmentVarDefault(const char * name) const
 706    {
 707        return LookupEnvironment(getImpl()->env_, name).c_str();
 708    }
 709    
 710    void Config::clearEnvironmentVars()
 711    {
 712        getImpl()->env_.clear();
 713        getImpl()->context_->clearStringVars();
 714        
 715        AutoMutex lock(getImpl()->cacheidMutex_);
 716        getImpl()->resetCacheIDs();
 717    }
 718    
 719    void Config::setEnvironmentMode(EnvironmentMode mode)
 720    {
 721        getImpl()->context_->setEnvironmentMode(mode);
 722        
 723        AutoMutex lock(getImpl()->cacheidMutex_);
 724        getImpl()->resetCacheIDs();
 725    }
 726    
 727    EnvironmentMode Config::getEnvironmentMode() const
 728    {
 729        return getImpl()->context_->getEnvironmentMode();
 730    }
 731    
 732    void Config::loadEnvironment()
 733    {
 734        getImpl()->context_->loadEnvironment();
 735        
 736        AutoMutex lock(getImpl()->cacheidMutex_);
 737        getImpl()->resetCacheIDs();
 738    }
 739    
 740    const char * Config::getSearchPath() const
 741    {
 742        return getImpl()->context_->getSearchPath();
 743    }
 744    
 745    void Config::setSearchPath(const char * path)
 746    {
 747        getImpl()->context_->setSearchPath(path);
 748        
 749        AutoMutex lock(getImpl()->cacheidMutex_);
 750        getImpl()->resetCacheIDs();
 751    }
 752    
 753    const char * Config::getWorkingDir() const
 754    {
 755        return getImpl()->context_->getWorkingDir();
 756    }
 757    
 758    void Config::setWorkingDir(const char * dirname)
 759    {
 760        getImpl()->context_->setWorkingDir(dirname);
 761        
 762        AutoMutex lock(getImpl()->cacheidMutex_);
 763        getImpl()->resetCacheIDs();
 764    }
 765    
 766    
 767    ///////////////////////////////////////////////////////////////////////////
 768    
 769    int Config::getNumColorSpaces() const
 770    {
 771        return static_cast<int>(getImpl()->colorspaces_.size());
 772    }
 773    
 774    const char * Config::getColorSpaceNameByIndex(int index) const
 775    {
 776        if(index<0 || index >= (int)getImpl()->colorspaces_.size())
 777        {
 778            return "";
 779        }
 780        
 781        return getImpl()->colorspaces_[index]->getName();
 782    }
 783    
 784    ConstColorSpaceRcPtr Config::getColorSpace(const char * name) const
 785    {
 786        int index = getIndexForColorSpace(name);
 787        if(index<0 || index >= (int)getImpl()->colorspaces_.size())
 788        {
 789            return ColorSpaceRcPtr();
 790        }
 791        
 792        return getImpl()->colorspaces_[index];
 793    }
 794    
 795    int Config::getIndexForColorSpace(const char * name) const
 796    {
 797        int csindex = -1;
 798        
 799        // Check to see if the name is a color space
 800        if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
 801        {
 802            return csindex;
 803        }
 804        
 805        // Check to see if the name is a role
 806        std::string csname = LookupRole(getImpl()->roles_, name);
 807        if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
 808        {
 809            return csindex;
 810        }
 811        
 812        // Is a default role defined?
 813        // (And, are we allowed to use it)
 814        if(!getImpl()->strictParsing_)
 815        {
 816            csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
 817            if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
 818            {
 819                return csindex;
 820            }
 821        }
 822        
 823        return -1;
 824    }
 825    
 826    void Config::addColorSpace(const ConstColorSpaceRcPtr & original)
 827    {
 828        ColorSpaceRcPtr cs = original->createEditableCopy();
 829        
 830        std::string name = cs->getName();
 831        if(name.empty())
 832            throw Exception("Cannot addColorSpace with an empty name.");
 833        
 834        // Check to see if the colorspace already exists
 835        int csindex = -1;
 836        if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
 837        {
 838            getImpl()->colorspaces_[csindex] = cs;
 839        }
 840        else
 841        {
 842            // Otherwise, add it
 843            getImpl()->colorspaces_.push_back( cs );
 844        }
 845        
 846        AutoMutex lock(getImpl()->cacheidMutex_);
 847        getImpl()->resetCacheIDs();
 848    }
 849    
 850    void Config::clearColorSpaces()
 851    {
 852        getImpl()->colorspaces_.clear();
 853    }
 854    
 855    
 856    
 857    
 858    
 859    
 860    const char * Config::parseColorSpaceFromString(const char * str) const
 861    {
 862        if(!str) return "";
 863        
 864        // Search the entire filePath, including directory name (if provided)
 865        // convert the filename to lowercase.
 866        std::string fullstr = pystring::lower(std::string(str));
 867        
 868        // See if it matches a lut name.
 869        // This is the position of the RIGHT end of the colorspace substring, not the left
 870        int rightMostColorPos=-1;
 871        std::string rightMostColorspace = "";
 872        int rightMostColorSpaceIndex = -1;
 873        
 874        // Find the right-most occcurance within the string for each colorspace.
 875        for (unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
 876        {
 877            std::string csname = pystring::lower(getImpl()->colorspaces_[i]->getName());
 878            
 879            // find right-most extension matched in filename
 880            int colorspacePos = pystring::rfind(fullstr, csname);
 881            if(colorspacePos < 0)
 882                continue;
 883            
 884            // If we have found a match, move the pointer over to the right end of the substring
 885            // This will allow us to find the longest name that matches the rightmost colorspace
 886            colorspacePos += (int)csname.size();
 887            
 888            if ( (colorspacePos > rightMostColorPos) ||
 889                 ((colorspacePos == rightMostColorPos) && (csname.size() > rightMostColorspace.size()))
 890                )
 891            {
 892                rightMostColorPos = colorspacePos;
 893                rightMostColorspace = csname;
 894                rightMostColorSpaceIndex = i;
 895            }
 896        }
 897        
 898        if(rightMostColorSpaceIndex>=0)
 899        {
 900            return getImpl()->colorspaces_[rightMostColorSpaceIndex]->getName();
 901        }
 902        
 903        if(!getImpl()->strictParsing_)
 904        {
 905            // Is a default role defined?
 906            std::string csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
 907            if(!csname.empty())
 908            {
 909                int csindex = -1;
 910                if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
 911                {
 912                    // This is necessary to not return a reference to
 913                    // a local variable.
 914                    return getImpl()->colorspaces_[csindex]->getName();
 915                }
 916            }
 917        }
 918        
 919        return "";
 920    }
 921    
 922    bool Config::isStrictParsingEnabled() const
 923    {
 924        return getImpl()->strictParsing_;
 925    }
 926    
 927    void Config::setStrictParsingEnabled(bool enabled)
 928    {
 929        getImpl()->strictParsing_ = enabled;
 930        
 931        AutoMutex lock(getImpl()->cacheidMutex_);
 932        getImpl()->resetCacheIDs();
 933    }
 934    
 935    // Roles
 936    void Config::setRole(const char * role, const char * colorSpaceName)
 937    {
 938        // Set the role
 939        if(colorSpaceName)
 940        {
 941            getImpl()->roles_[pystring::lower(role)] = std::string(colorSpaceName);
 942        }
 943        // Unset the role
 944        else
 945        {
 946            StringMap::iterator iter = getImpl()->roles_.find(pystring::lower(role));
 947            if(iter != getImpl()->roles_.end())
 948            {
 949                getImpl()->roles_.erase(iter);
 950            }
 951        }
 952        
 953        AutoMutex lock(getImpl()->cacheidMutex_);
 954        getImpl()->resetCacheIDs();
 955    }
 956    
 957    int Config::getNumRoles() const
 958    {
 959        return static_cast<int>(getImpl()->roles_.size());
 960    }
 961    
 962    bool Config::hasRole(const char * role) const
 963    {
 964        return LookupRole(getImpl()->roles_, role) == "" ? false : true;
 965    }
 966    
 967    const char * Config::getRoleName(int index) const
 968    {
 969        if(index < 0 || index >= (int)getImpl()->roles_.size()) return "";
 970        StringMap::const_iterator iter = getImpl()->roles_.begin();
 971        for(int i = 0; i < index; ++i) ++iter;
 972        return iter->first.c_str();
 973    }
 974    
 975    ///////////////////////////////////////////////////////////////////////////
 976    //
 977    // Display/View Registration
 978    
 979    
 980    const char * Config::getDefaultDisplay() const
 981    {
 982        if(getImpl()->displayCache_.empty())
 983        {
 984            ComputeDisplays(getImpl()->displayCache_,
 985                            getImpl()->displays_,
 986                            getImpl()->activeDisplays_,
 987                            getImpl()->activeDisplaysEnvOverride_);
 988        }
 989        
 990        int index = -1;
 991        
 992        if(!getImpl()->activeDisplaysEnvOverride_.empty())
 993        {
 994            StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplaysEnvOverride_,
 995                                                           getImpl()->displayCache_);
 996            if(!orderedDisplays.empty())
 997            {
 998                index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
 999            }
1000        }
1001        else if(!getImpl()->activeDisplays_.empty())
1002        {
1003            StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplays_,
1004                                                           getImpl()->displayCache_);
1005            if(!orderedDisplays.empty())
1006            {
1007                index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
1008            }
1009        }
1010        
1011        if(index >= 0)
1012        {
1013            return getImpl()->displayCache_[index].c_str();
1014        }
1015        
1016        if(!getImpl()->displayCache_.empty())
1017        {
1018            return getImpl()->displayCache_[0].c_str();
1019        }
1020        
1021        return "";
1022    }
1023
1024
1025    int Config::getNumDisplays() const
1026    {
1027        if(getImpl()->displayCache_.empty())
1028        {
1029            ComputeDisplays(getImpl()->displayCache_,
1030                            getImpl()->displays_,
1031                            getImpl()->activeDisplays_,
1032                            getImpl()->activeDisplaysEnvOverride_);
1033        }
1034        
1035        return static_cast<int>(getImpl()->displayCache_.size());
1036    }
1037
1038    const char * Config::getDisplay(int index) const
1039    {
1040        if(getImpl()->displayCache_.empty())
1041        {
1042            ComputeDisplays(getImpl()->displayCache_,
1043                            getImpl()->displays_,
1044                            getImpl()->activeDisplays_,
1045                            getImpl()->activeDisplaysEnvOverride_);
1046        }
1047        
1048        if(index>=0 || index < static_cast<int>(getImpl()->displayCache_.size()))
1049        {
1050            return getImpl()->displayCache_[index].c_str();
1051        }
1052        
1053        return "";
1054    }
1055    
1056    const char * Config::getDefaultView(const char * display) const
1057    {
1058        if(getImpl()->displayCache_.empty())
1059        {
1060            ComputeDisplays(getImpl()->displayCache_,
1061                            getImpl()->displays_,
1062                            getImpl()->activeDisplays_,
1063                            getImpl()->activeDisplaysEnvOverride_);
1064        }
1065        
1066        if(!display) return "";
1067        
1068        DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1069        if(iter == getImpl()->displays_.end()) return "";
1070        
1071        const ViewVec & views = iter->second;
1072        
1073        StringVec masterViews;
1074        for(unsigned int i=0; i<views.size(); ++i)
1075        {
1076            masterViews.push_back(views[i].name);
1077        }
1078        
1079        int index = -1;
1080        
1081        if(!getImpl()->activeViewsEnvOverride_.empty())
1082        {
1083            StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViewsEnvOverride_,
1084                                                           masterViews);
1085            if(!orderedViews.empty())
1086            {
1087                index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
1088            }
1089        }
1090        else if(!getImpl()->activeViews_.empty())
1091        {
1092            StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViews_,
1093                                                           masterViews);
1094            if(!orderedViews.empty())
1095            {
1096                index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
1097            }
1098        }
1099        
1100        if(index >= 0)
1101        {
1102            return views[index].name.c_str();
1103        }
1104        
1105        if(!views.empty())
1106        {
1107            return views[0].name.c_str();
1108        }
1109        
1110        return "";
1111    }
1112
1113    int Config::getNumViews(const char * display) const
1114    {
1115        if(getImpl()->displayCache_.empty())
1116        {
1117            ComputeDisplays(getImpl()->displayCache_,
1118                            getImpl()->displays_,
1119                            getImpl()->activeDisplays_,
1120                            getImpl()->activeDisplaysEnvOverride_);
1121        }
1122        
1123        if(!display) return 0;
1124        
1125        DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1126        if(iter == getImpl()->displays_.end()) return 0;
1127        
1128        const ViewVec & views = iter->second;
1129        return static_cast<int>(views.size());
1130    }
1131
1132    const char * Config::getView(const char * display, int index) const
1133    {
1134        if(getImpl()->displayCache_.empty())
1135        {
1136            ComputeDisplays(getImpl()->displayCache_,
1137                            getImpl()->displays_,
1138                            getImpl()->activeDisplays_,
1139                            getImpl()->activeDisplaysEnvOverride_);
1140        }
1141        
1142        if(!display) return "";
1143        
1144        DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1145        if(iter == getImpl()->displays_.end()) return "";
1146        
1147        const ViewVec & views = iter->second;
1148        return views[index].name.c_str();
1149    }
1150
1151    const char * Config::getDisplayColorSpaceName(const char * display, const char * view) const
1152    {
1153        if(!display || !view) return "";
1154        
1155        DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1156        if(iter == getImpl()->displays_.end()) return "";
1157        
1158        const ViewVec & views = iter->second;
1159        int index = find_view(views, view);
1160        if(index<0) return "";
1161        
1162        return views[index].colorspace.c_str();
1163    }
1164    
1165    const char * Config::getDisplayLooks(const char * display, const char * view) const
1166    {
1167        if(!display || !view) return "";
1168        
1169        DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1170        if(iter == getImpl()->displays_.end()) return "";
1171        
1172        const ViewVec & views = iter->second;
1173        int index = find_view(views, view);
1174        if(index<0) return "";
1175        
1176        return views[index].looks.c_str();
1177    }
1178    
1179    void Config::addDisplay(const char * display, const char * view,
1180                            const char * colorSpaceName, const char * lookName)
1181    {
1182        
1183        if(!display || !view || !colorSpaceName || !lookName) return;
1184        
1185        AddDisplay(getImpl()->displays_,
1186                   display, view, colorSpaceName, lookName);
1187        getImpl()->displayCache_.clear();
1188        
1189        AutoMutex lock(getImpl()->cacheidMutex_);
1190        getImpl()->resetCacheIDs();
1191    }
1192    
1193    void Config::clearDisplays()
1194    {
1195        getImpl()->displays_.clear();
1196        getImpl()->displayCache_.clear();
1197        
1198        AutoMutex lock(getImpl()->cacheidMutex_);
1199        getImpl()->resetCacheIDs();
1200    }
1201    
1202    void Config::setActiveDisplays(const char * displays)
1203    {
1204        getImpl()->activeDisplays_.clear();
1205        SplitStringEnvStyle(getImpl()->activeDisplays_, displays);
1206        
1207        getImpl()->displayCache_.clear();
1208        
1209        AutoMutex lock(getImpl()->cacheidMutex_);
1210        getImpl()->resetCacheIDs();
1211    }
1212
1213    const char * Config::getActiveDisplays() const
1214    {
1215        getImpl()->activeDisplaysStr_ = JoinStringEnvStyle(getImpl()->activeDisplays_);
1216        return getImpl()->activeDisplaysStr_.c_str();
1217    }
1218    
1219    void Config::setActiveViews(const char * views)
1220    {
1221        getImpl()->activeViews_.clear();
1222        SplitStringEnvStyle(getImpl()->activeViews_, views);
1223        
1224        getImpl()->displayCache_.clear();
1225        
1226        AutoMutex lock(getImpl()->cacheidMutex_);
1227        getImpl()->resetCacheIDs();
1228    }
1229
1230    const char * Config::getActiveViews() const
1231    {
1232        getImpl()->activeViewsStr_ = JoinStringEnvStyle(getImpl()->activeViews_);
1233        return getImpl()->activeViewsStr_.c_str();
1234    }
1235    
1236    ///////////////////////////////////////////////////////////////////////////
1237    
1238    
1239    void Config::getDefaultLumaCoefs(float * c3) const
1240    {
1241        memcpy(c3, &getImpl()->defaultLumaCoefs_[0], 3*sizeof(float));
1242    }
1243    
1244    void Config::setDefaultLumaCoefs(const float * c3)
1245    {
1246        memcpy(&getImpl()->defaultLumaCoefs_[0], c3, 3*sizeof(float));
1247        
1248        AutoMutex lock(getImpl()->cacheidMutex_);
1249        getImpl()->resetCacheIDs();
1250    }
1251    
1252    
1253    
1254    
1255    ///////////////////////////////////////////////////////////////////////////
1256    
1257    
1258    
1259    
1260    ConstLookRcPtr Config::getLook(const char * name) const
1261    {
1262        std::string namelower = pystring::lower(name);
1263        
1264        for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
1265        {
1266            if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
1267            {
1268                return getImpl()->looksList_[i];
1269            }
1270        }
1271        
1272        return ConstLookRcPtr();
1273    }
1274    
1275    int Config::getNumLooks() const
1276    {
1277        return static_cast<int>(getImpl()->looksList_.size());
1278    }
1279    
1280    const char * Config::getLookNameByIndex(int index) const
1281    {
1282        if(index<0 || index>=static_cast<int>(getImpl()->looksList_.size()))
1283        {
1284            return "";
1285        }
1286        
1287        return getImpl()->looksList_[index]->getName();
1288    }
1289    
1290    void Config::addLook(const ConstLookRcPtr & look)
1291    {
1292        std::string name = look->getName();
1293        if(name.empty())
1294            throw Exception("Cannot addLook with an empty name.");
1295        
1296        std::string namelower = pystring::lower(name);
1297        
1298        // If the look exists, replace it
1299        for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
1300        {
1301            if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
1302            {
1303                getImpl()->looksList_[i] = look->createEditableCopy();
1304                return;
1305            }
1306        }
1307        
1308        // Otherwise, add it
1309        getImpl()->looksList_.push_back(look->createEditableCopy());
1310        
1311        AutoMutex lock(getImpl()->cacheidMutex_);
1312        getImpl()->resetCacheIDs();
1313    }
1314    
1315    void Config::clearLooks()
1316    {
1317        getImpl()->looksList_.clear();
1318        
1319        AutoMutex lock(getImpl()->cacheidMutex_);
1320        getImpl()->resetCacheIDs();
1321    }
1322    
1323    ///////////////////////////////////////////////////////////////////////////
1324    
1325    
1326    
1327    ConstProcessorRcPtr Config::getProcessor(const ConstColorSpaceRcPtr & src,
1328                                             const ConstColorSpaceRcPtr & dst) const
1329    {
1330        ConstContextRcPtr context = getCurrentContext();
1331        return getProcessor(context, src, dst);
1332    }
1333    
1334    ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
1335                                             const ConstColorSpaceRcPtr & src,
1336                                             const ConstColorSpaceRcPtr & dst) const
1337    {
1338        if(!src)
1339        {
1340            throw Exception("Config::GetProcessor failed. Source colorspace is null.");
1341        }
1342        if(!dst)
1343        {
1344            throw Exception("Config::GetProcessor failed. Destination colorspace is null.");
1345        }
1346        
1347        ProcessorRcPtr processor = Processor::Create();
1348        processor->getImpl()->addColorSpaceConversion(*this, context, src, dst);
1349        processor->getImpl()->finalize();
1350        return processor;
1351    }
1352    
1353    ConstProcessorRcPtr Config::getProcessor(const char * srcName,
1354                                             const char * dstName) const
1355    {
1356        ConstContextRcPtr context = getCurrentContext();
1357        return getProcessor(context, srcName, dstName);
1358    }
1359    
1360    //! Names can be colorspace name or role name
1361    ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
1362                                             const char * srcName,
1363                                             const char * dstName) const
1364    {
1365        ConstColorSpaceRcPtr src = getColorSpace(srcName);
1366        if(!src)
1367        {
1368            std::ostringstream os;
1369            os << "Could not find colorspace '" << srcName << "'.";
1370            throw Exception(os.str().c_str());
1371        }
1372        
1373        ConstColorSpaceRcPtr dst = getColorSpace(dstName);
1374        if(!dst)
1375        {
1376            std::ostringstream os;
1377            os << "Could not find colorspace '" << dstName << "'.";
1378            throw Exception(os.str().c_str());
1379        }
1380        
1381        return getProcessor(context, src, dst);
1382    }
1383    
1384    
1385    ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform) const
1386    {
1387        return getProcessor(transform, TRANSFORM_DIR_FORWARD);
1388    }
1389    
1390    
1391    ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform,
1392                                             TransformDirection direction) const
1393    {
1394        ConstContextRcPtr context = getCurrentContext();
1395        return getProcessor(context, transform, direction);
1396    }
1397    
1398    ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
1399                                             const ConstTransformRcPtr& transform,
1400                                             TransformDirection direction) const
1401    {
1402        ProcessorRcPtr processor = Processor::Create();
1403        processor->getImpl()->addTransform(*this, context, transform, direction);
1404        processor->getImpl()->finalize();
1405        return processor;
1406    }
1407    
1408    std::ostream& operator<< (std::ostream& os, const Config& config)
1409    {
1410        config.serialize(os);
1411        return os;
1412    }
1413    
1414    ///////////////////////////////////////////////////////////////////////////
1415    //  CacheID
1416    
1417    const char * Config::getCacheID() const
1418    {
1419        return getCacheID(getCurrentContext());
1420    }
1421    
1422    const char * Config::getCacheID(const ConstContextRcPtr & context) const
1423    {
1424        AutoMutex lock(getImpl()->cacheidMutex_);
1425        
1426        // A null context will use the empty cacheid
1427        std::string contextcacheid = "";
1428        if(context) contextcacheid = context->getCacheID();
1429        
1430        StringMap::const_iterator cacheiditer = getImpl()->cacheids_.find(contextcacheid);
1431        if(cacheiditer != getImpl()->cacheids_.end())
1432        {
1433            return cacheiditer->second.c_str();
1434        }
1435        
1436        // Include the hash of the yaml config serialization
1437        if(getImpl()->cacheidnocontext_.empty())
1438        {
1439            std::stringstream cacheid;
1440            serialize(cacheid);
1441            std::string fullstr = cacheid.str();
1442            getImpl()->cacheidnocontext_ = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
1443        }
1444        
1445        // Also include all file references, using the context (if specified)
1446        std::string fileReferencesFashHash = "";
1447        if(context)
1448        {
1449            std::ostringstream filehash;
1450            
1451            ConstTransformVec allTransforms;
1452            getImpl()->getAllIntenalTransforms(allTransforms);
1453            
1454            std::set<std::string> files;
1455            for(unsigned int i=0; i<allTransforms.size(); ++i)
1456            {
1457                GetFileReferences(files, allTransforms[i]);
1458            }
1459            
1460            for(std::set<std::string>::iterator iter = files.begin();
1461                iter != files.end(); ++iter)
1462            {
1463                if(iter->empty()) continue;
1464                filehash << *iter << "=";
1465                
1466                try
1467                {
1468                    std::string resolvedLocation = context->resolveFileLocation(iter->c_str());
1469                    filehash << GetFastFileHash(resolvedLocation) << " ";
1470                }
1471                catch(...)
1472                {
1473                    filehash << "? ";
1474                    continue;
1475                }
1476            }
1477            
1478            std::string fullstr = filehash.str();
1479            fileReferencesFashHash = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
1480        }
1481        
1482        getImpl()->cacheids_[contextcacheid] = getImpl()->cacheidnocontext_ + ":" + fileReferencesFashHash;
1483        return getImpl()->cacheids_[contextcacheid].c_str();
1484    }
1485    
1486    
1487    ///////////////////////////////////////////////////////////////////////////
1488    //  Serialization
1489    
1490    void Config::serialize(std::ostream& os) const
1491    {
1492        try
1493        {
1494            getImpl()->io_.write(os, this);
1495        }
1496        catch( const std::exception & e)
1497        {
1498            std::ostringstream error;
1499            error << "Error building YAML: " << e.what();
1500            throw Exception(error.str().c_str());
1501        }
1502    }
1503    
1504    void Config::Impl::resetCacheIDs()
1505    {
1506        cacheids_.clear();
1507        cacheidnocontext_ = "";
1508        sanity_ = SANITY_UNKNO

Large files files are truncated, but you can click here to view the full file