text: # -*- python -*-
  #                           Package   : omniidl
  # ami.py                    Created on: 2011/01/18
  #			    Author    : Duncan Grisby
  #
  #    Copyright (C) 2011-2012 Apasphere Ltd
  #
  #  This file is part of omniidl.
  #
  #  omniidl is free software; you can redistribute it and/or modify it
  #  under the terms of the GNU General Public License as published by
  #  the Free Software Foundation; either version 2 of the License, or
  #  (at your option) any later version.
  #
  #  This program is distributed in the hope that it will be useful,
  #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  #  General Public License for more details.
  #
  #  You should have received a copy of the GNU General Public License
  #  along with this program.  If not, see http://www.gnu.org/licenses/
  #
  # Description:
  #   
  #   Functions to build AMI implied-IDL structures. When used as an
  #   omniidl back-end, outputs the generated IDL.
  
  """AMI support"""
  
  from omniidl import idlast, idltype, idlutil, idlvisitor, output
  from omniidl_be.dump import DumpVisitor
  import sys
  
  usage_string = """\
    -Wbinline       Output declarations from #included files inline
    -Wbdump         Dump original IDL as well as AMI IDL"""
  
  
  #
  # Static declarations of types defined in messaging.idl
  
  reply_handler_decl = idlast.Interface("messaging.idl", 0, 0, [], [],
                                        "ReplyHandler",
                                        ["Messaging", "ReplyHandler"],
                                        "IDL:omg.org/Messaging/ReplyHandler:1.0",
                                        0, 0, [])
  reply_handler_decl._setContents([])
  
  poller_decl = idlast.ValueAbs("messaging.idl", 0, 0, [], [],
                                "Poller",
                                ["Messaging", "Poller"],
                                "IDL:omg.org/Messaging/Poller/1.0",
                                [], [])
  poller_decl._setContents([])
  
  exception_holder_decl = idlast.Value("messaging.idl", 0, 0, [], [],
                                       "ExceptionHolder",
                                       ["Messaging", "ExceptionHolder"],
                                       "IDL:omg.org/Messaging/ExceptionHolder:1.0",
                                       0, [], 0, [])
  exception_holder_decl._setContents([])
  
  exception_holder_type = idltype.Declared(exception_holder_decl,
                                           exception_holder_decl.scopedName(),
                                           idltype.tk_value, 0)
  
  messaging_module_decl = idlast.Module("messaging.idl", 0, 0, [], [],
                                        "Messaging",
                                        ["Messaging"],
                                        "IDL:omg.org/Messaging:1.0",
                                        [reply_handler_decl,
                                         poller_decl,
                                         exception_holder_decl])
  
  #
  # Main visitor class
  
  class AMIVisitor(idlvisitor.AstVisitor, idlvisitor.TypeVisitor):
  
      def __init__(self, inject=1):
          self.current = []
          self.added   = []
          self.inject  = inject
  
          idlast.registerDecl(messaging_module_decl.scopedName(),
                              messaging_module_decl)
  
          idlast.registerDecl(reply_handler_decl.scopedName(),
                              reply_handler_decl)
  
          idlast.registerDecl(poller_decl.scopedName(),
                              poller_decl)
  
          idlast.registerDecl(exception_holder_decl.scopedName(),
                              exception_holder_decl)
  
  
      def result(self):
          return idlast.AST("<generated>", self.current, [], [])
          
  
      def visitAST(self, node):
          new_contents = [messaging_module_decl]
  
          for n in node.declarations():
              self.added = [n]
              n.accept(self)
              new_contents.extend(self.added)
  
          if self.inject:
              node.declarations()[:] = new_contents
  
  
      def visitModule(self, node):
          saved_current = self.current
          saved_added   = self.added
          self.current = []
  
          new_contents = []
  
          for n in node.definitions():
              self.added = [n]
              n.accept(self)
              new_contents.extend(self.added)
  
          if self.inject:
              node.definitions()[:] = new_contents
  
          new_mod = idlast.Module(node.file(), node.line(), node.mainFile(),
                                  [], [], node.identifier(),
                                  node.scopedName(), node.repoId(),
                                  self.current)
  
          self.current = saved_current
          self.current.append(new_mod)
          self.added = saved_added
  
  
  
      def _callables(self, node):
          """
          _callables(node) -> operations, clashes
  
          Return the operations for an interface, converting attributes
          into get/set operations.
  
          Also return a set of defined names used to avoid name clashes.
          """
          operations = []
          clashes    = set()
  
          for cb in node.callables():
  
              if isinstance(cb, idlast.Operation):
  
                  clashes.add(cb.identifier())
  
                  if cb.oneway():
                      continue
  
                  operations.append(cb)
  
  
              elif isinstance(cb, idlast.Attribute):
  
                  for dec in cb.declarators():
  
                      clashes.add(dec.identifier())
  
                      op_ident  = "get_" + dec.identifier()
                      op_scope  = node.scopedName() + [op_ident]
                      op_repoId = "IDL:%s:1.0" % idlutil.slashName(op_scope)
  
                      op = idlast.Operation(dec.file(), dec.line(),
                                            dec.mainFile(), [], [], 0,
                                            cb.attrType(),
                                            op_ident, op_scope, op_repoId,
                                            [], [], [])
  
                      op._ami_declarator = dec
                      operations.append(op)
  
                      if cb.readonly():
                          continue
  
                      op_ident  = "set_" + dec.identifier()
                      op_scope  = node.scopedName() + [op_ident]
                      op_repoId = "IDL:%s:1.0" % idlutil.slashName(op_scope)
  
                      params = [idlast.Parameter(dec.file(), dec.line(),
                                                 dec.mainFile(), [], [],
                                                 0, cb.attrType(),
                                                 "attr_" + dec.identifier())]
  
                      op = idlast.Operation(dec.file(), dec.line(),
                                            dec.mainFile(), [], [], 0,
                                            idltype.Base(idltype.tk_void),
                                            op_ident, op_scope, op_repoId,
                                            params, [], [])
  
                      op._ami_declarator = dec
                      operations.append(op)
                      
          return operations, clashes
  
  
      def _amiName(self, scopedName, kind):
          ident  = scopedName[-1]
          scope  = scopedName[:-1]
          prefix = ""
  
          while 1:
              candidate = "AMI_%s%s%s" % (prefix, ident, kind)
              try:
                  idlast.findDecl(scope + [candidate])
  
              except idlast.DeclNotFound:
                  return candidate
  
              prefix = prefix + "AMI_"
  
  
      def _prefixName(self, prefix, oident, clashes):
          ami = ""
          while 1:
              ident = "%s%s%s" % (prefix, ami, oident)
              if ident not in clashes:
                  return ident
              ami = ami + "ami_"
  
  
      def _suffixName(self, suffix, oident, clashes):
          ami = ""
          while 1:
              ident = "%s%s%s" % (oident, ami, suffix)
              if ident not in clashes:
                  clashes.add(ident)
                  return ident
              ami = ami + "_ami"
  
  
      def _handler(self, node):
          """
          _handler(node) -> (handler_fwd, handler)
          
          Build ReplyHandler interface.
          """
  
          def amiHandler(n):
              while isinstance(n, idlast.Declarator):
                  n = n.fullDecl()
              return n._ami_handler
  
          if node.inherits():
              base_handlers = [ amiHandler(n) for n in node.inherits() ]
          else:
              base_handlers = [ reply_handler_decl ]
  
          handler_ident  = self._amiName(node.scopedName(), "Handler")
          handler_scope  = node.scopedName()[:-1] + [handler_ident]
          handler_repoId = "IDL:%s:1.0" % idlutil.slashName(handler_scope)
  
          comment = idlast.Comment("// ReplyHandler for interface %s" %
                                   node.identifier(),
                                   node.file(), node.line())
  
          handler_fwd = idlast.Forward(node.file(), node.line(),
                                       node.mainFile(), [], [],
                                       handler_ident, handler_scope,
                                       handler_repoId, 0, 0)
  
          handler = idlast.Interface(node.file(), node.line(),
                                     node.mainFile(), [], [comment],
                                     handler_ident, handler_scope,
                                     handler_repoId, 0, 0, base_handlers)
          handler._ami_gen = 1
  
          operations = []
  
          # Success methods
  
          callables, clashes = self._callables(node)
  
          for cb in callables:
  
              op_scope  = handler_scope + [cb.identifier()]
              op_repoId = "IDL:%s:1.0" % idlutil.slashName(op_scope)
  
              params = []
  
              if cb.returnType().kind() != idltype.tk_void:
                  params.append(idlast.Parameter(cb.file(), cb.line(),
                                                 cb.mainFile(), [], [],
                                                 0, cb.returnType(),
                                                 "ami_return_val"))
              for param in cb.parameters():
                  if param.is_out():
                      params.append(idlast.Parameter(param.file(),
                                                     param.line(),
                                                     param.mainFile(),
                                                     [], [],
                                                     0, param.paramType(),
                                                     param.identifier()))
  
              op = idlast.Operation(cb.file(), cb.line(),
                                    cb.mainFile(), [], [],
                                    0, idltype.Base(idltype.tk_void),
                                    cb.identifier(), op_scope, op_repoId,
                                    params, [], [])
  
              if hasattr(cb, "_ami_declarator"):
                  if cb.identifier().startswith("set"):
                      cb._ami_declarator._ami_set_handler = op
                  else:
                      cb._ami_declarator._ami_get_handler = op
              else:
                  cb._ami_handler = op
  
              operations.append(op)
  
  
          # _excep methods
          exceps = []
  
          for cb in callables:
  
              ident = cb.identifier()
  
              excep_ident = self._suffixName("_excep", ident, clashes)
  
              params = [idlast.Parameter("<builtin>", 0, 0, [], [],
                                         0, exception_holder_type,
                                         "excep_holder")]
  
              op_scope  = handler_scope + [excep_ident]
              op_repoId = "IDL:%s:1.0" % idlutil.slashName(op_scope)
  
              op = idlast.Operation(cb.file(), cb.line(),
                                    cb.mainFile(), [], [],
                                    0, idltype.Base(idltype.tk_void),
                                    excep_ident, op_scope, op_repoId,
                                    params, [], [])
  
              
  
              if hasattr(cb, "_ami_declarator"):
                  if cb.identifier().startswith("set"):
                      cb._ami_declarator._ami_set_handler_excep = op
                  else:
                      cb._ami_declarator._ami_get_handler_excep = op
              else:
                  cb._ami_handler_excep = op
  
              exceps.append(op)
              
  
          handler._setContents(operations + exceps)
          idlast.registerDecl(handler.scopedName(), handler)
          
          node._ami_handler = handler
  
          return handler_fwd, handler
  
  
      def _poller(self, node):
          """
          _poller(node) -> (poller_fwd, poller)
          
          Build Poller valuetype.
          """
          def amiPoller(n):
              while isinstance(n, idlast.Declarator):
                  n = n.fullDecl()
              return n._ami_poller
  
          if node.inherits():
              base_pollers = [ amiPoller(n) for n in node.inherits() ]
          else:
              base_pollers = [ poller_decl ]
  
          poller_ident  = self._amiName(node.scopedName(), "Poller")
          poller_scope  = node.scopedName()[:-1] + [poller_ident]
          poller_repoId = "IDL:%s:1.0" % idlutil.slashName(poller_scope)
  
          comment = idlast.Comment("// Poller valuetype for interface %s" %
                                   node.identifier(),
                                   node.file(), node.line())
  
          poller_fwd = idlast.ValueForward(node.file(), node.line(),
                                           node.mainFile(), [], [],
                                           poller_ident, poller_scope,
                                           poller_repoId, 1)
  
          poller = idlast.ValueAbs(node.file(), node.line(),
                                   node.mainFile(), [], [comment],
                                   poller_ident, poller_scope, poller_repoId,
                                   base_pollers, [])
          poller._ami_gen = 1
  
          operations = []
  
          callables, clashes = self._callables(node)
  
          for cb in callables:
  
              if cb.oneway():
                  continue
  
              op_scope  = poller_scope + [cb.identifier()]
              op_repoId = "IDL:%s:1.0" % idlutil.slashName(op_scope)
  
              params = [idlast.Parameter(cb.file(), cb.line(),
                                         cb.mainFile(), [], [],
                                         0,
                                         idltype.Base(idltype.tk_ulong),
                                         "ami_timeout")]
  
              if cb.returnType().kind() != idltype.tk_void:
                  params.append(idlast.Parameter(cb.file(), cb.line(),
                                                 cb.mainFile(), [], [],
                                                 1, cb.returnType(),
                                                 "ami_return_val"))
              for param in cb.parameters():
                  if param.is_out():
                      params.append(idlast.Parameter(param.file(),
                                                     param.line(),
                                                     param.mainFile(),
                                                     [], [],
                                                     1, param.paramType(),
                                                     param.identifier()))
  
              op = idlast.Operation(cb.file(), cb.line(),
                                    cb.mainFile(), [], [],
                                    0, idltype.Base(idltype.tk_void),
                                    cb.identifier(), op_scope, op_repoId,
                                    params, cb.raises(), [])
  
              if hasattr(cb, "_ami_declarator"):
                  if cb.identifier().startswith("set"):
                      cb._ami_declarator._ami_set_poller = op
                  else:
                      cb._ami_declarator._ami_get_poller = op
  
                  op._ami_from = cb._ami_declarator
              else:
                  cb._ami_poller = op
                  op._ami_from   = cb
  
              operations.append(op)
  
  
          poller._setContents(operations)
          idlast.registerDecl(poller.scopedName(), poller)
  
          node._ami_poller = poller
          poller._ami_from = node
  
          return poller_fwd, poller
  
  
      def _implied(self, node, handler, poller):
          """
          _implied(node, handler, poller) -> interface
  
          Generate implied client-side sendc / sendp operations. Returns
          a new idlast.Interface containing just those methods, and sets
          _ami_ops attribute on the original Interface node.
          """
          operations = []
  
          handler_type = idltype.Declared(handler, handler.scopedName(),
                                          idltype.tk_objref, 0)
  
          poller_type  = idltype.Declared(poller, poller.scopedName(),
                                          idltype.tk_value, 0)
  
          intf_scope = node.scopedName()
  
          callables, clashes = self._callables(node)
  
          for cb in callables:
  
              if cb.oneway():
                  continue
  
              params = []
              for param in cb.parameters():
                  if param.is_in():
                      params.append(idlast.Parameter(param.file(),
                                                     param.line(),
                                                     param.mainFile(),
                                                     [], [],
                                                     0, param.paramType(),
                                                     param.identifier()))
  
              # sendc...
  
              op_ident  = self._prefixName("sendc_", cb.identifier(), clashes)
              op_scope  = intf_scope + [op_ident]
              op_repoId = "IDL:%s:1.0" % idlutil.slashName(op_scope)
  
              handler_param = idlast.Parameter(cb.file(), cb.line(),
                                               cb.mainFile(), [], [],
                                               0,
                                               handler_type,
                                               "ami_handler")
  
              op = idlast.Operation(cb.file(), cb.line(),
                                    cb.mainFile(), [], [],
                                    0, idltype.Base(idltype.tk_void),
                                    op_ident, op_scope, op_repoId,
                                    [handler_param] + params,
                                    [], cb.contexts())
              operations.append(op)
  
              if hasattr(cb, "_ami_declarator"):
                  op._ami_from = cb._ami_declarator
  
                  if cb.identifier().startswith("set"):
                      cb._ami_declarator._ami_set_sendc = op
                      op._ami_setter = 1
                  else:
                      cb._ami_declarator._ami_get_sendc = op
                      op._ami_setter = 0
              else:
                  cb._ami_sendc = op
                  op._ami_from  = cb
  
  
              # sendp...
  
              op_ident  = self._prefixName("sendp_", cb.identifier(), clashes)
              op_scope  = intf_scope + [op_ident]
              op_repoId = "IDL:%s:1.0" % idlutil.slashName(op_scope)
  
              poller_param  = idlast.Parameter(cb.file(), cb.line(),
                                               cb.mainFile(), [], [],
                                               0,
                                               poller_type,
                                               "ami_poller")
  
              op = idlast.Operation(cb.file(), cb.line(),
                                    cb.mainFile(), [], [],
                                    0, poller_type,
                                    op_ident, op_scope, op_repoId,
                                    params[:], [], cb.contexts())
  
              operations.append(op)
  
              if hasattr(cb, "_ami_declarator"):
                  op._ami_from = cb._ami_declarator
  
                  if cb.identifier().startswith("set"):
                      cb._ami_declarator._ami_set_sendp = op
                      op._ami_setter = 1
                  else:
                      cb._ami_declarator._ami_get_sendp = op
                      op._ami_setter = 0
              else:
                  cb._ami_sendp = op
                  op._ami_from  = cb
  
                  
          node._ami_ops = operations
  
          comment = idlast.Comment("// AMI implied operations for interface %s" %
                                   node.identifier(),
                                   node.file(), node.line())
  
          implied_intf  = idlast.Interface(node.file(), node.line(),
                                           node.mainFile(), node.pragmas(),
                                           [comment], node.identifier(),
                                           node.scopedName(), node.repoId(),
                                           node.abstract(), node.local(),
                                           node.inherits())
          implied_intf._setContents(operations)
  
          return implied_intf
  
  
      def visitInterface(self, node):
          if node.local():
              return
  
          handler_fwd, handler = self._handler(node)
          poller_fwd,  poller  = self._poller(node)
          implied_ops_intf     = self._implied(node, handler_fwd, poller_fwd)
  
          self.current.append(handler)
          self.current.append(poller)
          self.current.append(implied_ops_intf)
          self.added = [handler_fwd, poller_fwd, node, handler, poller]
  
  
  
  def run(tree, args):
      inline = 0
      dump   = 0
  
      for arg in args:
          if arg == "inline":
              inline = 1
  
          elif arg == "dump":
              dump = 1
  
          else:
              sys.stderr.write(main.cmdname + ": Warning: ami "
                               "back-end does not understand argument: %s\n" %
                               arg)
  
      av = AMIVisitor(dump)
      tree.accept(av)
  
      st = output.Stream(sys.stdout, 2)
      dv = DumpVisitor(st, inline)
  
      if dump:
          tree.accept(dv)
  
      else:
          new_tree = av.result()
          new_tree.accept(dv)
  