#
#  $Id: Plugin.py,v 1.12 1999/12/14 10:42:36 rob Exp $
#
#  Copyright 1998-1999 Rob Tillotson <robt@debian.org>
#  All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee or royalty is
#  hereby granted, provided that the above copyright notice appear in
#  all copies and that both the copyright notice and this permission
#  notice appear in supporting documentation or portions thereof,
#  including modifications, that you you make.
#
#  THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
#  THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
#  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
#  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
#  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
#  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE!
#
"""Sulfur Plug-in Architecture.
"""

__version__ = '$Id: Plugin.py,v 1.12 1999/12/14 10:42:36 rob Exp $'

__copyright__ = 'Copyright 1999 Rob Tillotson <robt@debian.org>'

import sys, os, string, imp, operator
import Registry
import Options
from misc import *

class Plugin:
    """
    Superclass for all plug-ins.
    """
    name = ''
    author = ''
    description = ''
    version = ''
    url = ''
    type = ''
    type_name = ''
    properties = []
    is_plugin = 1
    options = []
    
    # needed by find_plugins
    is_plugin = 1
    
    def __init__(self):
	self.context = None
	self.logfile = sys.stdout
	
	self.option_values = {}
	self.option_refs = {}

	cl = [self.__class__]+parent_classes(self.__class__)
	cl.reverse()
	for c in cl:
	    for o in c.options:
		self.option_values[o.name] = o.default
		self.option_refs[o.name] = o
	    
	self.registry = None
	self.config_path = self.type + '::' + self.name
	
#	self.config_section = ''
#	self.config_prefix = self.type + '.' + self.name + '.'
	
    def __repr__(self):
	return '<Plugin: %s: %s>' % (self.type, self.name)

    __str__ = __repr__
    
    def log(self, str, name=None):
	"""Write a string to the log file.
	"""
	if name is None: name = self.name

	if self.context is not None and hasattr(self.context, 'log'):
	    self.context.log(str, name)
	else:
	    self.logfile.write('%s: %s\n' % (name, str))

    def error(self, str, level='warning', name=None):
	"""Report an error.
	"""
	if name is None: name = self.name

	if self.context is not None and hasattr(self.context, 'error'):
	    self.context.error(str, level, name)
	else:
	    self.logfile.write('%s: %s: %s\n' % (name, level, str))
	
    def status(self):
	"""Return a status string.
	"""
	return ''
    
    # Application context and some useful redirected APIs
    def set_context(self, c):
	self.context = c

#      def get_plugin(self, *a, **kw):
#  	return apply(self.context.get_plugin, a, kw)

#      def list_plugins(self, *a, **kw):
#  	return apply(self.context.list_plugins, a, kw)

#      def list_plugin_info(self, *a, **kw):
#  	return apply(self.context.list_plugin_info, a, kw)
    
	
    #
    # Overload __setattr__ and __getattr__ to transparently treat options
    # like instance attributes
    #
    def __getattr__(self, k):
	if self.__dict__.has_key('option_values') and self.option_values.has_key(k):
	    return self.option_values[k]
	else:
	    if self.context is not None: return getattr(self.context, k)
	    else: raise AttributeError, k

    def has_option(self, k):
	return hasattr(self, 'option_values') and self.option_values.has_key(k)

    def get_option(self, k):
	if hasattr(self, 'option_values'): return self.option_values[k]
	else: raise KeyError, k

    def set_option(self, k, v):
	if hasattr(self, 'option_values'):
	    self.option_refs[k].validate(v)
	    self.option_values[k] = v
	else:
	    raise KeyError, k

    def append_option(self, k, v): # append to a multiple option value
	if hasattr(self, 'option_values'):
	    if not self.option_refs[k].has_flag(Options.O_MULTIPLE):
		raise RuntimeError, '%s does not allow multiple values' % k
	    self.option_refs[k].validate(v)
	    if type(self.option_values[k]) != type([]):
		if self.option_values[k]:
		    self.option_values[k] = [ self.option_values[k] ]
		else:
		    self.option_values[k] = []
	    self.option_values[k].append(v)
	else:
	    raise KeyError, k
	
    def configure(self, options=None):
	"""Set plug-in options.  The optional parameter is either a
	dictionary (containing option values, keyed by name) or a
	Registry object; if either is supplied, the conduit will be
	configured from that data.  If no parameter is supplied, the
	current option values will be returned as a dictionary.
	"""
	if options is None:
	    d = {}
	    d.update(self.option_values)
	    return d
	elif hasattr(options, 'is_registry'):
	    # FIXME: this section needs general cleanup
	    self.registry = options
	    for k, o in self.option_refs.items():
		if self.config_path: p = self.config_path + '::' + k
		else: p = k
		if self.registry.has_key(p):
		    v = options.get(p)
		    if o.has_flag(Options.O_MULTIPLE):
			if type(v) != type([]) and type(v) != type(()):
			    v = [ v ]
			try:
			    self.option_values[k] = map(lambda v,obj=o: obj.from_str(v), v)
			except Options.ValidationError:
			    self.option_values[k] = o.default
		    else:
			try:
			    self.option_values[k] = o.from_str(v)
			except Options.ValidationError:
			    self.option_values[k] = o.default

#		# backward compatibility
#		elif self.config_section == 'Pyrite' and \
#		     self.registry.has_key('PalmPython',
#					   self.config_prefix+k):
#		    v = options.get('PalmPython',
#				    self.config_prefix+k)
#		    try:
#			self.option_values[k] = o.from_str(v)
#		    except Options.ValidationError:
#			self.option_values[k] = o.default
			
		else:
		    self.option_values[k] = o.default
	else:
	    for k, o in self.option_refs.items():
		if options.has_key(k):
		    v = options.get(k)
		    if o.has_flag(Options.O_MULTIPLE):
			if type(v) != type([]) and type(v) != type(()):
			    v = [ v ]
			try:
			    map(lambda v,obj=o: o.validate(v), v)
			except Options.ValidationError:
			    self.option_values[k] = o.default
			else:
			    self.option_values[k] = v
		    else:
			try:
			    o.validate(options[k])
			except Options.ValidationError:
			    self.option_values[k] = o.default
			else:
			    self.option_values[k] = options[k]
		else:
		    self.option_values[k] = o.default


    def has_property(self, prop):
	"""Test whether this plug-in has the named property.
	"""
	return prop in self.properties
    

