# $Id: s.Invoker.xotcl 1.11 01/03/23 21:55:32+01:00 neumann@somewhere.wu-wien.ac.at $
package provide xotcl::mos::invoker 0.8

Class AbstractInvoker
AbstractInvoker abstract instproc invokeCall {o method arguments}
AbstractInvoker abstract instproc eval {obj method arguments}
#
# error types are: tclError, invocationError
#
AbstractInvoker abstract instproc callError {type msg obj arguments} 

Class Invoker -superclass AbstractInvoker -parameter {{place [self]}}

Invoker instproc handleException {response} {
  if {[[self] isExceptionObject $response]} {
    set exceptionObj $response
    switch [$exceptionObj info class] {
      ::RedirectException {
	set obj [$exceptionObj obj]
	set method [$exceptionObj method]
	set arguments [$exceptionObj arguments]
	set response [[self] eval $obj $method $arguments]
      }
      ::ErrorException {
	set response [$exceptionObj set errorText]
      }
    }
    $exceptionObj destroy
  }
  return $response
}

Invoker instproc invokeCall {o s method arguments} {
  upvar $o obj $s status
  [self] instvar place
  set response ""
  if {[$place isExportedObj $obj]} {
    # if method is not given -> call default on the object
    if {$method == ""} {
      set method default
    }
    if {[$obj isExportedProc $method]} {
      #puts stderr "ExportedProcs of $obj: [$obj exportedProcs]"
      #puts stderr "Call: $obj -- $method -- $arguments"
      set response [[self] eval $obj $method $arguments]
    } else {
      #puts stderr "ExportedProcs of $obj: [$obj exportedProcs]"
      set response [[self] callError invocationError [$place startingObj] \
		      "Method not found or not exported" \
		      "$obj $method $arguments"]
      set status 405
    }
  } else {
    set called $obj
    set obj [$place startingObj]
    set response [[self] callError invocationError $obj \
		    "Object '$called' unknown" ""]
    set status 404
  }
  
  return [[self] handleException $response]
}

#
# tests whether "name" is an exception object or not
#
Invoker instproc isExceptionObject name {
  if {[Object isobject $name] && [$name istype Exception]} {
    return 1
  }
  return 0
}

#
# central eval  -- all remote call
# are invoked through this method
#
Invoker instproc eval {obj method arguments} {
  puts stderr "[clock format [clock seconds] \
	-format %Y/%m/%d@%H:%M:%S] \
	Eval Call: $obj $method $arguments"
  if {[catch {
    set r [::eval $obj $method $arguments]
  } ei]} {
    set r [[self] callError tclError $obj $ei "$obj $method $::errorInfo"]
  }
  return $r
}

Invoker instproc callError {type obj msg arguments} {
  [[self] set place]::error $type $obj $msg $arguments
}

Class ErrorMgr
ErrorMgr instproc isHtml o {
  if {[[self] isobject $o]} {
    if {[$o istype WebFacadeHtml]} {
      return 1
    }
  }
  return 0
}

ErrorMgr instproc invocationError {obj msg arguments} {
  [self] showCall
  set ee [ErrorException [self]::[[self] autoname ee]]
  $ee instvar errorText
  if {[[self] isHtml $obj]} {
    set errorText "<p> invocation error: $msg"
    if {[llength $arguments] > 0} {
      append errorText ":\n<p> object: '[lindex $arguments 0]' \n"
    } else {
      append errorText \n
    }
    if {[llength $arguments] > 1} {
      append errorText "<p> call: '[lrange $arguments 1 end]' \n"
    }
  } else {
    set errorText "invocation error: $msg $arguments"
  }
  return $ee
}

ErrorMgr instproc tclError {obj msg arguments} {
  set ee [ErrorException [self]::[[self] autoname ee]]
  if {[[self] isHtml $obj]} {
    $ee errorText "<p>tcl error: '$msg' \n<code><p><pre>$arguments</pre></code>"
  } else {
    $ee errorText "tcl error: '$msg'\n$::errorInfo"
  }
  return $ee
}

#
# exceptions in invocation behavior
#
Class Exception
#
# Execpetion that tells the invoker to redirect the call to
# parameters
#
Class RedirectException -superclass Exception -parameter {
  {obj ""}
  {method ""}
  {arguments ""}
}

Class ErrorException -superclass Exception -parameter {
  {errorText ""}
}
