text: ##########################################################################
  #
  #  Copyright (c) 2012, Image Engine Design Inc. All rights reserved.
  #
  #  Redistribution and use in source and binary forms, with or without
  #  modification, are permitted provided that the following conditions are
  #  met:
  #
  #      * Redistributions of source code must retain the above
  #        copyright notice, this list of conditions and the following
  #        disclaimer.
  #
  #      * Redistributions in binary form must reproduce the above
  #        copyright notice, this list of conditions and the following
  #        disclaimer in the documentation and/or other materials provided with
  #        the distribution.
  #
  #      * Neither the name of John Haddon nor the names of
  #        any other contributors to this software may be used to endorse or
  #        promote products derived from this software without specific prior
  #        written permission.
  #
  #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  #  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  #  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  #  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  #  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  #  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  #  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  #  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  #  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  #  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  #  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  #
  ##########################################################################
  
  import threading
  import traceback
  import weakref
  
  import IECore
  
  import Gaffer
  
  ## The OpMatcher class provides a means of searching for Ops suitable to
  # act upon a given input value.
  #
  # The following Op userData entries are supported :
  #
  # ["OpMatcher"]["ignore"] - when this BoolData is True, the Op is not
  # considered by the matcher.
  #
  # The following Parameter usedData entries are supported :
  #
  # ["OpMatcher"]["ignore"] - when this BoolData is True, the Parameter is not
  # considered by the matcher.
  class OpMatcher( object ) :
  
  	def __init__( self, classLoader, classNamesMatchString = "*", reportErrors=True ) :
  
  		# these are filled with tuples of the form ( opClass, parameter, parameterPath )
  		self.__ops = []
  
  		for className in classLoader.classNames( classNamesMatchString ) :
  
  			try :
  				opClass = classLoader.load( className )
  				opInstance = opClass()
  			except Exception as m :
  				if reportErrors :
  					IECore.msg( IECore.Msg.Level.Error, "GafferCortex.OpMatcher", "Error loading op \"%s\" : %s" % ( className, traceback.format_exc() ) )
  				continue
  
  			ignore = False
  			with IECore.IgnoredExceptions( KeyError ) :
  				# backwards compatibility with something proprietary
  				ignore = opInstance.userData()["UI"]["OpMatcher"]["ignore"].value
  			with IECore.IgnoredExceptions( KeyError ) :
  				ignore = opInstance.userData()["OpMatcher"]["ignore"].value
  			if ignore :
  				continue
  
  			parameters = []
  			self.__findParameters( opInstance.parameters(), parameters )
  			if len( parameters ) :
  				self.__ops.append( ( opClass, parameters ) )
  
  	## Returns a list of ( op, parameter ) tuples. Each op will be an Op instance
  	# where the corresponding parameter has already been set with parameterValue.
  	def matches( self, parameterValue ) :
  
  		processedValues = []
  		if isinstance( parameterValue, ( Gaffer.FileSystemPath, Gaffer.SequencePath ) ) :
  			# we might be able to match a single file
  			processedValues.append( IECore.StringData( str( parameterValue ) ) )
  			# or provide a single file input to an op which accepts multiple files
  			processedValues.append( IECore.StringVectorData( [ str( parameterValue ) ] ) )
  		elif isinstance( parameterValue, list ) :
  			processedValue = IECore.StringVectorData()
  			for value in parameterValue :
  				assert( isinstance( value, ( Gaffer.FileSystemPath, Gaffer.SequencePath ) ) )
  				processedValue.append( str( value ) )
  		elif isinstance( parameterValue, IECore.Object ) :
  			processedValue = parameterValue
  
  		if not processedValues :
  			return []
  
  		result = []
  		for opClass, parameters in self.__ops :
  			for testParameter, parameterPath in parameters :
  				for processedValue in processedValues :
  					if testParameter.valueValid( processedValue )[0] :
  						op = opClass()
  						parameter = op.parameters()
  						for name in parameterPath :
  							parameter = parameter[name]
  
  						parameter.setValue( processedValue )
  						result.append( ( op, parameter ) )
  
  		return result
  
  	__defaultInstances = weakref.WeakKeyDictionary()
  	__defaultInstancesMutex = threading.Lock()
  	## Returns an OpMatcher suitable for sharing by everyone - initialising one
  	# takes considerable time so it's preferable to reuse one if one has been created
  	# for the classLoader in question already. If classLoader is not specified then
  	# it defaults to IECore.ClassLoader.defaultOpLoader().
  	@classmethod
  	def defaultInstance( cls, classLoader=None ) :
  
  		if classLoader is None :
  			classLoader = IECore.ClassLoader.defaultOpLoader()
  
  		with cls.__defaultInstancesMutex :
  
  			result = cls.__defaultInstances.get( classLoader, None )
  			if result is None :
  				result = OpMatcher( classLoader )
  				cls.__defaultInstances[classLoader] = result
  
  			return result
  
  	def __findParameters( self, parameter, result, path = None ) :
  
  		if path is None :
  			path = []
  
  		for child in parameter.values() :
  
  			ignore = False
  			with IECore.IgnoredExceptions( KeyError ) :
  				# backwards compatibility with something proprietary
  				ignore = child.userData()["UI"]["OpMatcher"]["ignore"].value
  			with IECore.IgnoredExceptions( KeyError ) :
  				# backwards compatibility with something proprietary
  				ignore = child.userData()["OpMatcher"]["ignore"].value
  			if ignore :
  				continue
  
  			childPath = path + [ child.name ]
  
  			if isinstance( child, IECore.CompoundParameter ) :
  				self.__findParameters( child, result, childPath )
  			elif isinstance( child, ( IECore.PathParameter, IECore.PathVectorParameter ) ) :
  				if child.mustExist :
  					result.append( ( child, childPath ) )
  