# #-- # Copyright (c) 2006-2007, John Mettraux, OpenWFE.org # 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 the "OpenWFE" nor the names of its contributors 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. #++ # # $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $ # # # "made in Japan" # # John Mettraux at openwfe.org # require 'openwfe/utils' require 'openwfe/logging' require 'openwfe/contextual' require 'openwfe/rudefinitions' require 'openwfe/util/dollar' module OpenWFE # # FlowExpression # # # The base class for all OpenWFE flow expression classes. # It gathers all the methods for attributes and variable lookup. # class FlowExpression include Contextual, Logging, OwfeServiceLocator attr_accessor \ :fei, \ :parent_id, \ :environment_id, \ :attributes, \ :children, \ :apply_time def initialize (fei, parent_id, env_id, app_context, attributes) super() # # very necessary as this class includes the MonitorMixin @fei = fei @parent_id = parent_id @environment_id = env_id @application_context = app_context @attributes = attributes @apply_time = nil #ldebug do # "initialize()\n"+ # "self : #{@fei}\n"+ # "parent : #{@parent_id}" #end end # # the two most important methods for flow expressions # # this default implementation immediately replies to the # parent expression # def apply (workitem) get_parent().reply(workitem) if @parent_id end # # this default implementation immediately replies to the # parent expression # def reply (workitem) reply_to_parent(workitem) end # # Triggers the reply to the parent expression (of course, via the # expression pool). # Expressions do call this method when their job is done and the flow # should resume without them. # def reply_to_parent (workitem) get_expression_pool.reply_to_parent(self, workitem) end # # a default implementation for cancel : # cancels all the children # Attempts to return an InFlowWorkItem # def cancel () return nil if not @children inflowitem = nil @children.each do |child| next if child.kind_of? String i = get_expression_pool().cancel(child) inflowitem = i if not inflowitem end return inflowitem end # # some convenience methods # # Returns the parent expression (not as a FlowExpressionId but directly # as the FlowExpression instance it is). # def get_parent () parent, parent_fei = get_expression_pool().fetch(@parent_id) return parent end # # Stores itself in the expression pool. # It's very important for expressions in persisted context to save # themselves as soon as their state changed. # Else this information would be lost at engine restart or # simply if the expression got swapped out of memory and reloaded later. # def store_itself () ldebug { "store_itself() for #{@fei.to_debug_s}" } #ldebug { "store_itself() \n#{OpenWFE::caller_to_s(0, 6)}" } return get_expression_pool().update(self) end # # Returns the environment instance this expression uses. # An environment is a container (a scope) for variables in the process # definition. # Environments themselves are FlowExpression instances. # def get_environment () return nil if not @environment_id env, fei = get_expression_pool().fetch(@environment_id) return env end # # Returns true if the expression's environment was generated # for itself (usually DefineExpression do have such envs) # def owns_its_environment? () #ldebug { "owns_its_environment?() #{@fei.to_debug_s}" } return false if not @environment_id ei = @fei.dup() vi = @environment_id.dup() ei.expression_name = "neutral" vi.expression_name = "neutral" #ldebug do # "owns_its_environment?()\n"+ # " exp #{ei.to_debug_s}\n"+ # " env #{vi.to_debug_s}" #end return ei == vi end # # Sets a variable in the current environment. Is usually # called by the 'set' expression. # # The variable name may be prefixed by / to indicate process level scope # or by // to indicate engine level (global) scope. # def set_variable (varname, value) get_environment()[varname] = value end # # Looks up the value of a variable in the current environment. # If not found locally will lookup at the process level and even # further in the engine scope. # # The variable name may be prefixed by / to indicate process level scope # or by // to indicate engine level (global) scope. # def lookup_variable (varname) get_environment()[varname] end # # Unsets a variable in the current environment. # # The variable name may be prefixed by / to indicate process level scope # or by // to indicate engine level (global) scope. # def delete_variable (varname) get_environment().delete(varname) end # # Looks up the value for an attribute of this expression. # # if the expression is # # # # then # # participant_expression.lookup_attribute("toto", wi) # # will yield "toto" # # The various methods for looking up attributes do perform dollar # variable substitution. # It's ok to pass a Symbol for the attribute name. # def lookup_attribute (attname, workitem, default=nil) attname = symbol_to_attname(attname) \ if attname.kind_of? Symbol #ldebug { "lookup_attribute() '#{attname}' in #{@fei.to_debug_s}" } text = @attributes[attname] default = default.to_s if default return default if not text return OpenWFE::dosub(text, self, workitem) end # # A convenience method for looking up a boolean value. # It's ok to pass a Symbol for the attribute name. # def lookup_boolean_attribute (attname, workitem, default=false) value = lookup_attribute(attname, workitem) return default if not value return value.downcase == 'true' end # # Returns a hash of all the FlowExpression attributes with their # values having undergone dollar variable substitution. # If the attributes parameter is set (to an Array instance) then # only the attributes named in that list will be looked up. # # It's ok to pass an array of Symbol instances for the attributes # parameter. # def lookup_attributes (workitem, _attributes=nil) result = {} return result if not @attributes _attributes = @attributes.keys if not _attributes _attributes.each do |k| k = k.to_s v = @attributes[k] result[k] = OpenWFE::dosub(v, self, workitem) #ldebug do # "lookup_attributes() added '#{k}' -> '#{result[k]}'" #end end return result end # # For an expression like # # iterator :on_value => "a, b, c", to-variable => "v0" do # # ... # end # # this call # # lookup_comma_list_attribute(:on_value, wi) # # will return # # [ 'a', 'b', 'c' ] # def lookup_comma_list_attribute (attname, workitem, default=nil) a = lookup_attribute(attname, workitem, default) return nil if not a result = [] a.split(',').each do |elt| elt = elt.strip result << elt if elt.length > 0 end return result end # # creates a new environment just for this expression # def new_environment () ldebug { "new_environment() for #{@fei.to_debug_s}" } @environment_id = @fei.dup @environment_id.expression_name = EN_ENVIRONMENT parent_fei = nil parent = nil parent, _fei = get_expression_pool().fetch(@parent_id) \ if @parent_id parent_fei = parent.environment_id if parent env = Environment\ .new(@environment_id, parent_fei, nil, @application_context, nil) ldebug { "new_environment() is #{env.fei.to_debug_s}" } return env.store_itself() end # # Takes care of removing all the children of this expression, if any. # def clean_children return unless @children @children.each do |child_fei| get_expression_pool.remove(child_fei) \ if child_fei.kind_of? FlowExpressionId end end # # Currently only used by dollar.rb and its ${r:some_ruby_code}, # returns the binding in this flow expression. # def get_binding return binding() end # # Used like the classical Ruby synchronize, but as the OpenWFE # expression pool manages its own set of monitores, it's one of those # monitors that is used. But the synchronize code looks like the class # just included the MonitorMixin. No hassle. # def synchronize #ldebug { "synchronize() ---in--- for #{@fei.to_debug_s}" } get_expression_pool.get_monitor(@fei).synchronize do yield end #ldebug { "synchronize() --out-- for #{@fei.to_debug_s}" } end # # Some eye candy # def to_s s = "* #{@fei.to_debug_s}" if @parent_id s << "\n `--p--> #{@parent_id.to_debug_s}" end if @environment_id s << "\n `--e--> #{@environment_id.to_debug_s}" end if @children @children.each do |c| sc = if c.kind_of?(OpenWFE::FlowExpressionId) c.to_debug_s else ">#{c.to_s}<" end s << "\n `--c--> #{sc}" end end return s end protected def symbol_to_attname (s) attname = s.to_s attname = OpenWFE::to_dash(attname) return attname end end end