Overview
    This section documents the format for the transformer files (*.fmx). These
    files live in your install/transformers directory, and are read by Workbench
    on each startup.

Versioning:
    You MUST create a new version of a transformer when:
      - changing the output port definitions (add/remove)
      - changing the choice set in a choice type parameter (eg. LISTBOX, LOOKUP_CHOICE, etc)
      - changing a default value of a parameter
      - changing the default behavior of the transformer for a given value of a parameter
      - changing the type of a parameter from not encoded to encoded (ie. adding _ENCODED).  See PR76931)
      - often when adding/removing attributes from the output ports (debatable - use judgement)
      - ...
   You do NOT need to create a new version of a transformer when:
      - adding a new parameter that has a reasonable default value
      - adding/removing attributes from the output ports (debatable-use judgement)
      - changing the type of a parameter
      - changing the category
   
   When creating a new version and changing a parameter, you should consider if 
   the parameter you change is backwards compatible with older presets (see FMEENGINE-48843)
   In the event that you do not want an older preset to be 'pulled' into this version, you 
   need to renamed the parameter.  Note this is separate from 'upgrade' transformer infrastructure.
   That is, you can still map the old to the new name.  The restoration of presets does NOT
   go through any parameter upgrade infrastructure.  It simply replaces the old value with the new
   one for a given parameter.

Refreshing transformer in Workbench
    IMPORTANT: In order to see changes in your transformer in Workbench, you
    have to:
    - delete the transformer from the workspace
    - refresh the transformer list (there is a refresh button at the bottom of
      the list)
    - undo delete (Ctrl+Z) of transformer.


Structure
          TRANSFORMER_NAME:      <name>
          VERSION:               <version>
          [ALIASES:              <alias1> <alias2> ...]
          CATEGORY:              <category1>[,<category2>]+ -> see comment below for category list & descriptions.
          INPUT_TAGS:            <inputTags>
          OUTPUT_TAGS:           <outputTags>
          DYNAMIC_INPUT_TAGS:    [<tagName> [<tagName>]+]
          [MULTIPROCESS:         <groupby param>]
          [TAG_DESCRIPTION       <tagName>: <description>]
          PRESERVES_ATTRIBUTES:  <preservesAttributes>
          [PRESERVES_GEOMETRY:   <preservesGeometry>]
          FEATURE_HOLDING:       [NONE] [GROUPED] [ALL]
          ATTRIBUTES_ADDED:      <addedAttributes>
          [GEOMETRY_ADDED:       [<tagName> [<tagName>]+]]
          LIST_ACTION:           <listAction>
          [VISIBLE:              <yes|no|if_licensed>]
          [REPLACED_BY:          <transformerName>[:<version>]]
          [PARAMETER_MAPPING:    [<newParameter>;$(<oldParameter>)]* [<newParameter>;<constantValue>]*]
          [TAG_MAPPING:          [IN;<oldInputTag>;<newInputTag>]* [OUT;<oldOutputTag>;<newOutputTag>]*]
          [NUKERS_REQUIRED:      <yes|no>]
          [DECORATION_NAME:      <decoration image base name>]
          [DECORATION_LEFT:      <yes|no>]
          [CANVAS_PROPERTIES:    BANNER_NAME;<banner image base name> [prop;value]*]]
          [ARCGIS_COMPATIBILITY: <ARCGIS_AUTO|ARCMAP|ARCPRO>]
          [AUTO_PUBLISH:         <parmName>[ <parmName>]+]
          [OUTPUT_PORT_CONFIG:   <JSON outputPorts spec>]

          (This block is repeated for each parameter:)
          PARAMETER_NAME: <parmName>
          PARAMETER_TYPE: [<flags>] <parmType> <paramConfig>
          PARAMETER_PROMPT: <parmPrompt>
          PARAMETER_DEFAULT: <parmDefault>
          [PARAMETER_LOOKUP: <lookupParamName> <choice1>%<choice2>%choice3>]
          # Note: rather than parameter_lookup, consider using CHOICE_LOOKUP variants of gui types.  They are more robust when terminology changes.
          # because they store the looked up rather than the string value of the choice

          CHANGE_LOG_START
            <changes>
          CHANGE_LOG_END

          TEMPLATE_START
            <template>
          TEMPLATE_END

    <name> and <category> can be any text string.

    <version> can be any integer and should start at 1. Only the highest-
    numbered version is made available for adding into a workspace, but old
    versions should be left in the file for backwards-compatibility
    with older workspaces.   See the 'Transformer Versions' section below for
    more information on why you would want to make a new version of a
    transformer.

    ALIASES specifies a list of alternate names for this transformer. They're
    separated by spaces. This is useful if a transformer is renamed so customers can continue to
    find the transformer using the old name.

    VISIBLE [<yes|no|if_licensed>
       This optional directive can be used to mark the transformer as deprecated
       by setting its value to "no". Its main purpose although is to control if a 
       transformer will be visible in GUI for adding to canvas using Quick Add or 
       Transformer Gallery.
       yes [Default] : This is the default, meaning transformer is visible in GUI
                       and can be selected from Quick Add or Transformer Gallery.
       no            : Transformer is not visible in GUI from Quick Add or 
                       Transformer Gallery and cannot be added to the canvas. 
                       Existing workspaces using that transformer will still 
                       work as expected.
       if_licensed   : Transformer is only visible if it is licensed to use on
                       the platform

    REPLACED_BY: <transformerName>[:<version>]
       This optional directive is used when a transformer is deprecated by setting
       VISIBLE to NO. Its value is the name of the replacing transformer along
       with its (replacing transformer) current version.

    PARAMETER_MAPPING: [<newParameter>;<new value>]*[;<valueMap>]
       This optional directive can be used when parameter is renamed within the
       same transformer or when a transformer is deprecated. When transformer is
       deprecated this can help map over the old parameters to parameters in the
       new transformer. This mapping would be specified in the version previous
       to where the changes are made. E.g. If parameter is renamed in version 3
       and a new version 4 of transformer is created. Then PARAMETER_MAPPING
       directive will go in version 3 of the transformer indicating how to
       upgrade from version 3 to 4.

       <new value> can be:
       - parameter name from the old version E.g. NEW_PARM;$(OLD_PARM)
       - FMEParsableText encoded literal string E.g. NEW_PARM;Replace<space>with<space>Point
       - combination of both E.g.  NEW_PARM;[<literal-string>]$(OLD_PARM)[<literal-string>]
       - upgrading parameter type from ATTR to OR_ATTR: NEW_PARM;$(OLD_PARM)

       <valueMap> syntax is [<oldValue>,<newValue>]*
       Is for CHOICE* types where old choice value could be mapped to
       new choice value. E.g. 
       MODE;$(ALGORITHM_NAME);Five<space>Color,Map<space>Coloring:<space>Five<space>Color,Simple,Map<space>Coloring:<space>N<space>Color<space>(Fast)
       <oldValue> could also be one of these when we need to map the output value
       based on if the old value was empty or not.
       __fme_upgrade_empty_value__
       __fme_upgrade_non_empty_value__
       __fme_upgrade_unused_value__
       E.g. 
       POLY_GENERATE_LIST_GROUP;$(POLY_LIST_NAME);__fme_upgrade_empty_value__,No,__fme_upgrade_non_empty_value__,Yes
       The above line will amount to checking/unchecking an active group (POLY_GENERATE_LIST_GROUP)
       based on if the list name (POLY_LIST_NAME) parameter was set or not

    TAG_MAPPING: [IN;<oldInputTag>;<newInputTag>]* [OUT;<oldOutputTag>;<newOutputTag>]*]
       If input or output tags get renamed from one version to other, this directive
       specifies how the old tags be mapped to new ones when upgrading transformers.
       Just like PARAMETER_MAPPING, this directive is specified in the version 
       previous to where the change is made.

    AUTO_PUBLISH: <parmName>[ <parmName>]+
      Any parameters specified by this directive will be published automatically
      when the transformer is added to a workspace. Does nothing if a given parameter 
      does not support publishing.

    Inputs and Outputs
       <inputTags> is a list of all input tags for the transformer.
       <outputTags> is a list of all output tags for the transformer.

       Each have a syntax of form:

        [<alias>:]<actualname>[:<condition>] [[<alias>:]<name>[:<condition>]]*

        <alias> is the aliased name that will show in workbench and <actualname>
        is the tag used inside of FME.  Either the alias or actualname must be specified. 
  
        For input tags, the <actualname> may be left empty if you don't want to pipe an input
        directly into a factory, e.g. NeighborPairFinder.fmx uses "CANDIDATE:" as an input tag.
        If a given input has a blank input type, this can be denoted by <BLANK>. An input tag of 
        <NO_INPUT> means the transformer takes no inputs.

        For output tags, <BLANK> means there are NO outputs, as opposed to an output without a name.
        For this, aliasing is for display purposes only.  

        <condition> indicates an optional condition must be true for the port to be shown the user 
        in the gui.  As a result of not showing in the GUI, the port will also not be written to the mapping file (unless it has a nuker).
        Condition syntax is:
         PARM,[<boolOp>],<parm[.#]>,<FMEParsableTextvalue>,[[<boolOp>],<parm[.#]>,<FMEParsableTextvalue>]* - the port will only be included
              if the value of one of the <param> matches the cooresponding <FMEParsableTextvalue>.  

              A <FMEParsableTextvalue> of FME_RUNTIME_VALUE indicates the port should only be included 
              when the value of the parameter is unknown until runtime (ie. is set to an expression or published
              parameter).  This is useful for <Rejected> ports where the rejection can only occur if the
              user inputs an expression that results in invalid data (eg. cloner, decelerator).
              
              For LISTBOX types, the check for equality will check against each item that has been selected
              by the user.

             <boolOp> values are NOT, OR, AND, OR_NOT, AND_NOT.  Allows the condition to be negated
              and joined.  If nothing is specified for a given param-value pair, then OR is assumed

       Examples with conditions:
         from cloner  <REJECTED>:PARM,COPIES,FME_RUNTIME_VALUE
         will only include the <Rejected> port if COPIES parameter is an expression

         from Intersector  COLLAPSED:PARM,NOT,CLEANING_TOLERANCE,None
         will only include the COLLAPSED port if CLEANING_TOLERANCE parameter is not 'None'

    OUTPUT_PORT_CONFIG:   <JSON outputPorts spec>
         This provides output port information in JSON list, as described for
         the "outputPorts" key in [transformer.md]. Any newly defined ports are
         added as defined to the list of OUTPUT_TAGs. Information for known ports
         is merged with the existing port information, such as from PRESERVES_ATTRIBUTES
         and ATTRIBUTES_ADDED. This tag is specifically useful for defining output
         ports' preservesGeometry and geometryAdded.

    DYNAMIC_INPUT_TAGS
        These are special input tags, that will never end up in the mapping file but
        are there for the benefit of the view. These tag names can have spaces,
        hence are FMEParsableText encoded. There can be no link made to these tags, rather
        the process of making a link to this tag will invoke a custom behaviour
        that is to be determined by the transformer. Mostly this is used to create
        new ports whose definition is based on the schema from the incoming link.
        (See InlineQuerier or MaptextLabeller transformers)

    MULTIPROCESS
        <groupby param> is the name of the group by parameter for use when doing
        the multiprocess. In general, this flag should only be added if the
        transformer is CPU intensive. For multiprocess to make sense to use, the
        overhead of launching a new process and transferring the features back
        and forth should be insignificant compared to the processing time for
        each group. See dissolver for example. When using this option, inform
        the tech pubs team to add the multiprocess documentation to your
        transformer.


    Schema Transformation

      PRESERVES_ATTRIBUTES
        <preservesAttributes> specifies whether the attributes on the input side
        of the transformer will be propagated through to the outputs. This can
        be yes (meaning all attributes are propagated to all outputs), no
        (meaning none are propagated), or a list of the form:

              OUTPUT1:INPUT1[:<condition>] OUTPUT2:INPUT2[:<condition>] ...

        Which specifies that the output tag OUTPUT1 takes attributes from
        INPUT1, and so on. <BLANK> inputs can be specified by using OUTPUT1:. *
        can be used as a wildcard in the specification of input and/or output
        (but not as a substring wildcard).
        
        <condition> indicates some optional condition for the schema transformation.
        This can be:
            - PARM:<parm>[:<parm>]* - the attributes will only be preserved if logical 
              AND operations on >=1 bool parameters, <parm>, equals 'yes' or 'true'
            - LIST_PARM:<listParm> - the attributes will be preserved if the list
              parameter <listParm> on the transformer is not blank. 
            - PREFIX:<prefixParm> - all attributes will be prefixed with the value from 
              parameter <prefixParm>
            - PREFIX:<FMEParsableTextPrefixString> - all attributes will be prefixed with the decoded value of <FMEParsableTextPrefixString>
            - PARM_WITH_PREFIX:<parm>[:<parm>]*:<prefixParm> - the attributes will only be
              preserved if logical AND operations on >=1 bool parameters, <parm>, equals 'yes' 
              or 'true', and all attributes will be prefixed with the value from 
              parameter <prefixParm>
            - PARM_WITH_PREFIX:<parm>[:<parm>]*:<FMEParsableTextPrefixString> - the attributes will only
              be preserved if logical AND operations on >=1 bool parameters, <parm>, equals 'yes'
              or 'true', and all attributes will be prefixed with the decoded value of <FMEParsableTextPrefixString>
            - GROUP_BY:<groupByParm> - the attributes included in the value of the
              parameter <groupByParm> will be preserved.
            - PARM_WITH_GROUP_BY:<parm>:<groupByParm> - the attributes will only be
              preserved if the parameter <parm> is set to 'yes' or 'true'. In
              addition, all attributes included in the value of the parameter
              <groupByParm> will be preserved. e.g. Dissolver.fmx
            - LIST_PARM_WITH_GROUP_BY:<listParm>:<groupByParm> - the attributes
              will only be preserved if the parameter <listParm> is not blank.
              In addition, all attributes included in the value of the parameter
              <groupByParm> will be preserved.

        Note that if you have aliased the input, the INPUT tag should use the
        alias tag not the real tag.

    ATTRIBUTES_ADDED
        <addedAttributes> specifies the attributes that will be added to output
        tags. The syntax is:
               Per output port:  [<portname>]:<attrdef>[:PARM | LIST_PARM | PARM_NULL]:[<condition>]
               All output ports: <attrdef>[ <attrdef>]

               <portname> : Name of the output port
               <attrdef>  : Attribute definition specified as comma separated list
                            <attr1>[;<attr_type>][,<attr2>,<macro>,<attr3>,...[;<attr_type>]]*
               <attr_type>: Can be any one of the type defined for FFS format. Left hand side
                            of the ATTR_TYPE_MAP in ffs.fmf. 
                            List of available type names:
                            - char(width)
                            - varchar(width)
                            - buffer
                            - xml
                            - json
                            - binary(width)
                            - varbinary(width)
                            - binarybuffer
                            - datetime
                            - time
                            - date
                            - real64
                            - real32
                            - int32
                            - uint32
                            - int64
                            - uint64
                            - logical
                            - int16
                            - uint16
                            - int8
                            - uint8
                            - number(width,decimal) : Avoid using this type, but if you have to then
                                                      make sure it is encoded like number(20<comma>10)

               As a shortcut, type name can be specified for the first attribute in the list and
               it will apply to all the following attributes in the list until a new type is 
               specified for the attribute in that list. E.g.
                    fme_primary_axis;real64 fme_secondary_axis fme_rotation;real32 fme_start_angle fme_sweep_angle
               fme_primary_axis and fme_secondary_axis will be of type real64 and all the other types will be real32
               

        <macro> can be the value of a parameter. A macro can also be combined
        with a constant string (eg. $(FOO)Hi). If a list parameter is specified,
        each entry in the list is evaluated separately and added to the list of
        output attributes.

        <condition> indicates some optional condition to satisfy in order for
        attributes to be added. This can be:

         - PARM_COND:[<boolOp>],<parm[.#]>,<FMEParsableTextvalue>,[[<boolOp>],<parm[.#]>,<FMEParsableTextvalue>]* -
           the attributes will only be added if the condition evaluates to true.
           If nothing is specified for a given param-value pair, then OR is assumed.
           If the specified parameter cannot be found, that part of the condition will be false.
           See Inputs and Outputs for more detail, as is the same evaluation syntax.

         The following conditions are deprecated. Please use PARM_COND instead.

         - PARM:<parm> (deprecated) - the attributes will only be added if the parameter
           <parm> is set to 'yes' or 'true'.
         - PARM:<parm>:<val1>,<val2> (deprecated) - the attributes will only be added if
           the parameter <parm> is set to <val1> or <val2>
         - LIST_PARM:<listParm> (deprecated) - the attributes will be added if the list
           parameter <listParm> on the transformer is not blank.
         - PARM_NULL:<parm>:<yes | no > (deprecated) - if 'yes', the attributes will be added
           only if the parameter is null. If 'no', the attributes will be added
           only the parameter is not null.

        If a parameter refers to a global parameter, the attributes will always
        be added. This is because we do not know the value of the parameter
        until runtime. Better to show too much than too little.

        Note that when using conditions, if the parameter specified in the
        condition refers to a global parameter, the attributes will always be
        added. this is because we do not know the value of the attribute until
        runtime, thus must show the user ALL possibilities at the workspace
        building phase.

        For backward compatibility, a list of values separated by a ' ' is also
        valid: <val1> <val2>

        Examples of ways to specify the added attributes:

              <attribute> [...]
              e.g. ATTR1 ATTR2

        The example above will added ATTR1 and ATTR2 to... all outputs?.

              <output tag>:<attribute> [<output tag>:<attribute>]
              e.g. OUTPUT1:ATTR1 OUTPUT1:ATTR2 OUTPUT2:ATTR2 ...

        The example above will add attribute ATTR1 and ATTR2 on OUTPUT1 tag and
        ATTR2 on OUTPUT2 tag

              <output tag>:{}.<attribute> [<output tag>:{}.<attribute>]
              e.g. OUTPUT1:{}.ATTR1 OUTPUT1:{}.ATTR2 OUTPUT2:ATTR2 ...

        The example above will add ATTR1 and ATTR2 to the list name if there was
        one specified. So if the list name was "myList" then OUTPUT1 will have
        two attributes myList{}.ATTR1 and myList{}.ATTR2. See its use in
        NeighborFinder.fmx

        Example From AttributeExposer:
            ATTRIBUTES_ADDED: OUTPUT:$(NEW_ATTRIBUTES)
            ....
            PARAMETER_NAME: NEW_ATTRIBUTES
            PARAMETER_TYPE: OPTIONAL USER_DEFINED_LIST

        In this case, the FMEParsableText space delimited values 
        specified in NEW_ATTRIBUTES will be added to the output port OUTPUT. 

        Example From PointCloudCoercer

              COERCED:$(POINT_COMPONENTS_ATTR):PARM:POINT_COMPONENTS_PRESERVE_TYPE:ATTRIBUTES

        The example above will add the values in $(POINT_COMPONENTS_ATTR) as
        attributes only if the macro POINT_COMPONENTS_PRESERVE_TYPE has value
        ATTRIBUTES.

        Example from StreamOrderCalculator

              ATTRIBUTES_ADDED: NETWORK:$(STRAHLER_ATTR):PARM:STREAM_ORDER_TYPE:Strahler NETWORK:$(STRAHLER_ATTR),$(HORTON_ATTR):PARM:STREAM_ORDER_TYPE:Horton

        This says: On the NETWORK output port, display the attribute
        $(STRAHLER_ATTR) if "Strahler" is specified as the STREAM_ORDER_TYPE.
        Also, on the NETWORK output port, display the attributes
        $(STRAHLER_ATTR) and $(HORTON_ATTR) if "Horton" is specified as the
        STREAM_ORDER_TYPE.

    PRESERVES_GEOMETRY
        <preservesGeometry> specifies whether the geometry on the input side
        of the transformer will be propagated through to the outputs.
        This should be a list of the form:

        OUTPUT1:INPUT1[:Yes|No] [OUTPUT2:INPUT2[:Yes|No]] ...

        Which specifies that the output tag OUTPUT1 does or doesn't preserve geometry
        from INPUT1, and so on. * can be used as a wildcard in the specification of input ports.

        If not specified, geometry from all input ports is assumed to be
        preserved on all output ports.

    GEOMETRY_ADDED
      specifies a list of output tags that should have an unnamed geometry added to their output schema.

    LIST_ACTION
        <listAction> can take any of the following forms and may be repeated
        multiple times(e.g as in NeighborFinder.fmx), one for each list
        parameter:

         - BUILD_ALL <list_parm> [TRANS_LIST]
         - EXPLODE <list_parm> [TRANS_LIST]
         - REMOVE <list_parm> - KEEP <list_parm>

        REMOVE removes all list attributes specified in <list_parm>

        KEEP removes all list attributes except those specified in <list_parm>

        BUILD_ALL makes a list (of name specified in parameter list_parm) adding incoming 
          attributes to the list based on the [TRANS_LIST] specification

        EXPLODE explodes the list in list_parm into its elements.

        TRANS_LIST specifies the transfer of list attributes from input to
        output and has the format:

           OUTPUT1:INPUT1[INCLUDE_ATTRS:parmName] OUTPUT2:INPUT2[INCLUDE_ATTRS:parmName] ...

        If TRANS_LIST is empty, every output takes from every input.  If INCLUDE_ATTRS is specified,
        only the attributes indicated in parmName are transferred.


    NUKERS_REQUIRED
        When set to yes, this forces the OUTPUT clause to always be written out,
        and clean up any features that will come out. Only required for certain
        factories that require an OUTPUT clause, or for any transformer that
        does its work on the OUTPUT clause of a TeeFactory.

    FEATURE_HOLDING [NONE|GROUPED|ALL]
         - NONE: The transformer has a mode where features will be fully
           processed as they enter.
         - GROUPED: This tag means the transformer has a Group By parameter,
           and is expected to hold features if that parameter is populated.
           Unclear if it is supposed to imply that features are expected to
           be ordered and will be released as soon as a feature in a different
           group is seen.
         - ALL: The transformer has a mode where input features will be
           stored until the transformer is notified that the final feature
           has been received.

        Explanation for the Clipper transformer:
            The Clipper for example by default for both vector and raster input
            (which dictates use of two different factories) is ALL because the
            Clipper Type is Multiple Clippers and it must wait until all input
            clipper features are known. Setting the Group By simply changes this
            to GROUPED as expected. However, setting the Clipper Type to Single
            Clipper or Clippers First will allow the clipping to occur
            immediately on subsequent input clippee features, thus rendering the
            feature holding mode NONE. GROUPED is ambiguous in that a factory
            with a Group By parameter can either hold all feature or hold no
            features if the Group By is not set. So GROUPED
            should probably always appear with at least one of NONE or ALL.


    Parameters
        The PARAMETER_* block defines input parameters for the transformer. This
        block may be repeated multiple times, one for each parameter. <parmName>
        can be any valid name. <parmType> is the type of the parameter, more on
        that below. <parmPrompt> is a prompt shown to the user when this
        parameter is asked for. <parmDefault> is the default value for the
        parameter.

        For a list of valid values for <parmType>, see the GUI Type
        Documentation on http://docs.safe.com/fme/html/FME_GuiType/

        The parameter type also has several optional flags: 
           OPTIONAL - specifies a blank value is valid for this parameter.
           NO_CONDTIONAL - specifies that the conditional option will not be 
           available on the parameters drop down menu
           DISABLED_ONLY 
           ALWAYS_ENABLED
           FORCE_DEFS
           IGNORE - Used when the parameter is only required by the <xformer>cfg.cpp class and not by the GUI.  
                    The parameter will exist 
                    but not be shown in the navigator, tooltips
                    or included in the qfmequery dialog instance.  

           NO_SORT
           NULLABLE - parameter accepts FME_NULL_VALUE and parameter menu should have 'set to null' option
           NO_OP - parameter menu should have the 'no op' option
           ABORTABLE - parameter menu should have the abort option
           SHOW_ZIP_BUTTON - parameter will allow zip files
           LITERAL - parameter only accepts literal values
           IGNORE_VALUE
           NO_EDIT - parameter will be in the query but will not be visible to the user.
                     It will be shown in the nav tree and in the tooltip UNLESS
                     its type or description precludes doing so.  For example,
                     GROUP never appears in nav tree and a TEXT field that has
                     no prompt specified will never appear in the tooltip or nav tree.
                     
        If you are using an OR_ATTR type or de-align with attribute names, be
        sure that your transformer can handle attribute names containing spaces.

        Parameter values are referenced by enclosing the parameter name in
        $(). E.g. For a parameter with a name of A_PARAMETER, it is referenced
        as $(A_PARAMETER). In order to preserve spaces in parameter values,
        the parameter name references should be enclosed in "".
        E.g. The parameter reference $(A_PARAMETER) should be "$(A_PARAMETER)".

        The PARAMETER_LOOKUP directive should be used sparingly and is only used if the
        parameter is a CHOICE or LISTBOX type.  Instead, consider using 
        LOOKUP_CHOICE or LOOKUP_LISTBOX variants of gui types.  They store the looked up rather than 
        the string value of the choice so are more robust as terminology changes.  However, this directive
        when a single choice maps to >1 value. It allows you to use full-length
        English versions of each option in the CHOICE line, providing a nice
        alternative to obscure factory directives. Macro name used in
        PARAMETER_LOOKUP directive cannot be the same as the name used for
        PARAMETER_NAME directive. Once PARAMETER_LOOKUP is defined, then the
        template section of the transformer should use the macro name defined in
        PARAMETER_LOOKUP directive.

        Here's an example:
            PARAMETER_NAME: OBSCURE_PARAM             
            PARAMETER_TYPE: CHOICE "A description of an obscure option"%"Another description"
            PARAMETER_LOOKUP: OBSCURE_PARAM_LOOKUP
            OBSCURE_DIRECTIVE%ANOTHER_OBSCURE_DIRECTIVE . . . TEMPLATE_START
            FACTORY_DEF * TestFactory FACTORY_NAME $(XFORMER_NAME)_filter
            $(INPUT_LINES) TEST @TCL("AS_remove_empty_attributes
            $(OBSCURE_PARAM_LOOKUP)") == "0" OUTPUT PASSED FEATURE_TYPE
            $(OUTPUT_OUTPUT_FTYPE)

    Changes
       Changes to the transformer are listed between the CHANGE_LOG_START and
       CHANGE_LOG_END section. Log message can include url which will be converted
       to clickable hyperlink when displayed in help browser.

    Decoration
        The Transformer Decoration is an optional extra image added to the
        left or right side of the Transformer node in the Workbench Canvas.
        DECORATION_NAME is the base name of the decoration image found in the
        active Workbench theme. Supported file type is .svg
        DECORATION_LEFT is true if the decoration is to be displayed on the
        left side of the Transformer node and false otherwise.
        This has no effect if DECORATION_NAME is not set or the file is not found.

    CANVAS_PROPERTIES: [<prop name>;<prop value>]*      (Internal Only) 
        NOTE: This is strictly for Internal Use Only at this time.
        Value of this tag is expected to be space delimited string with one or
        more of <prop name>;<prop value>.
       
        Following properties are currently supported:
        BANNER_NAME;<image base name>
        SHOW_IN_NAVBAR;[Yes|No]
        EXCLUDE_ACTIONS; <List of actions to exclude from context menu>
        OPEN_FOLDER;PATH,<macro>[,FORMAT,<macro>][,SUPPORTS_IF,<cond>]
        INSPECT;FORMAT,<macro>,DATASET,<macro>,RUNTIME_MACROS,<macro>[,COORDSYS,<macro>,FEATURE_TYPES,<macro>][,SUPPORTS_IF,<cond>]
         
        BANNER_NAME:
        Using this tag we can configure transformer node's appearance and behaviour
        on the canvas and navbar. The transformer banner is generally fixed to 
        use a standard image whose base name is set to 'banner_xformer'. In some 
        rare cases where we want to have custom banner image, it can be specified 
        using BANNER_NAME property. Currently if this tag is set then we assume 
        the following:
        - Input and Output ports are hidden, that means the input and output
          ports (triangles) will show up at the banner level
        - Node is not editable
        - Node will not have any visible text item
        
        SHOW_IN_NAVBAR:
        If its value is set to No (default is Yes if not specified) then the 
        transformer node won't show in the navbar except when performing 
        workspace search.

        EXCLUDE_ACTIONS:
        Value is a comma delimited list of action names to exclude from node's 
        context menu. 

        OPEN_FOLDER:
        Requires PATH parameter and optionally a FORMAT can be specified. When
        FORMAT is specified then the context menu will be disabled under certain
        circumstances where PATH is not a valid file system path. Formats with
        dataset type (formats.db) of DATABASE, ODBC and NONE will cause the context
        menu option to be disabled

        INSPECT:
        Expected value for FORMAT is the format short name (formats.db). DATASET
        is expected to be a valid file system path name. RUNTIME_MACROS is a
        comma delimited SDF encoded string of parameters as name-value pairs

        SUPPORTS_IF:
         valid for INSPECT and OPEN_FOLDER - indicates a condition which
         must be met for the inspect or open folder to be supported.
         Current syntax supports form PARMNAME=VAL only which would mean
         the open/inspect is only supported when PARMNAME is equal to VAL


    CATEGORY:  The valid categories are:
          3D                              Operations for 3D formats and 3D geometry including appearances
          Attributes                      Operations for attribute/list management
          Values                          Operations that return a calculated value
          Cartography and Reports         Operations that prepare and style data for cartographic purposes
          Coordinates                     Operations for coordinate reprojection and warping
          Data Quality                    Operations for attribute/geometric data QA and validation
          Filters and Joins               Operations for dividing and merging data flows
          Format Specifics                Operations that are related to a specific format
          Geometries                      Operations that create geometry or transform it to a different geometry type (usually independent of other features)
          Integrations                    Operations that integrate FME with another tool, be it R or a web service
          Point Clouds                    Operations relating specifically to Point Cloud features
          Rasters                         Operations relating specifically to Raster features
          Spatial Analysis                Operations that return the result of a spatial analysis
          Strings                         Operations that manipulate string contents, including dates
          Web                             Operations specific to web-related issues such as web services, or web-based usage of a language like XML or JSON
          Workflows                       Operations that control workflow, such as Job submitters
          Samples                         Transformers in the plugin directory that are example so users can write their own transformers
          TestSuite Tools (Safe Internal) Transformers in the testsuite directory used by Safe Developers to write tests

    Template
        Everything in-between TEMPLATE_START and TEMPLATE_END is what is
        actually inserted into the mapping file. TEMPLATE_END is the end of the
        transformer definition (and beginning of another). The transformer name
        can be inserted using the tag $(XFORMER_NAME) and you'll need it as
        almost everything is prepended with it for namespacing.

        Input tags can be specified using $(INPUT_<type>_LINES) to insert tags
        of a given <type>, or $(INPUT_LINES) to put all input lines at once.

        Output tags can be specified using $(OUTPUT_<type>_FTYPE) to insert
        output tags for the feature type <type>. Output functions can be
        inserted by specifying $(OUTPUT_<type>_FUNCS)

        Parameters can be substituted into the template by encasing it in $()
        For example $(MYPARM), to substitute the parameter named MYPARM.

        If you have optional parameters do not rely on WHOLE_LINE to do the removal.  Instead, 
        use the {*} rather than * in the template start.  This indicates that  { } will be the indicator of 
        the keyword value rather than space.  Otherwise, if the user publishes the 
        parameter and leaves it blank, the parser will fail because the keyword has no value.
            FACTORY_DEF {*} MyFactory
              ....
              KEYWORD { $(MYPARM) }


        You can write all parameters and their values using the 
        $(FME_BRACED_PARM_VAL_LIST) keyword (formally FME_PARM_VAL_LIST).
        This is handy if you have a factory whose keywords exactly match all the parameters
        in your GUI. In this case, the data is written as:
                parm1 { val1 }
                parm2 { val2 }
                ....etc... 

        Similarly, you can get a list of all parameters using the FME_PARM_LIST
        keyword.  This will give a comma delimited list of all parameters in the
        form: 
               parm1, parm2, etc
        
        The template is basically a list of factories that work with each other
        to process the features coming from the ports specified in INPUT_TAGS
        and finally output features to the ports specified in OUTPUT_TAGS (there
        is a default output tag of OUTPUT). Look at the header files for the
        different factories to see how their specific syntax works. Some common
        ones are TeeFactory (used to route output) and TestFactory(used to route
        output based upon some test). Functions can be applied on features by
        using the @Function syntax either upon input (on the INPUT FEATURE_TYPE
        line) or upon output(on the OUTPUT FEATURE_TYPE line). For example, the
        following line:

               OUTPUT FEATURE_TYPE ___BOUNDING_LINE___
                  @Coordinate(REMOVE,4)
                  @Coordinate(REMOVE,3)
                  @Coordinate(REMOVE,1)

        ... will output ___BOUNDING_LINE___ features after applying the
        Coordinate(REMOVE, ...) function on them 3 times.

        To execute a function from tcl, you will need to use FME_Execute. For
        example:
          FME_Execute Geometry GET_CONVEXITY_CLASS

        A more involved example that sets a global variable:
            Tcl2 proc $(XFORMER_NAME)_setFeatType {} {
                global FME_FeatureType;
                set ft {$(XFORMER_NAME)_};
                append ft [FME_Execute Geometry GET_CONVEXITY_CLASS];
                set FME_FeatureType $ft;
            }
            Then we can use this function like this: @Tcl2($(XFORMER_NAME)_setFeatType)

        To dynamically change the output feature type, you need to modify the
        tcl global variable FME_FeatureType. You can easily do that using the
        function @FeatureType. Here is an example from the ConvexityFilter
        transformer. This transformer routes features to different ports by
        depending on what the result of applying @Geometry(GET_CONVEXITY_CLASS)
        is:
            FACTORY_DEF * TeeFactory
               FACTORY_NAME $(XFORMER_NAME)
               $(INPUT_LINES)
               OUTPUT FEATURE_TYPE * @FeatureType(@Concatenate($(XFORMER_NAME)_,@Geometry(GET_CONVEXITY_CLASS)))

        Good luck!

Transformer Versions
     - Transformers are versioned. We add functionality to functions/factories
       all the time, but we want to make sure old workspaces act exactly as they
       always have, so each transformer definition has a VERSION clause.
     - Some types of changes can be made without making a new version:
        - Adding a new parameter (make sure the default value doesn't change the
          semantics of the transformer!)
        - Changing the default value of a parameter, or a parameter prompt
        - Removing a parameter (if it no longer makes sense)
        - Changing the transformer's category or aliases
        - Adding things to the ATTRIBUTES_ADDED line
        - Changing the transformer description or tooltip
        - Changing the transformer template, IF the change does not change the
          default operation of the transformer
        - Changing the name of the OUTPUT_TAG, if there is only one.
     - Other types of changes should only be made by making a new version
       of the transformer:
       - Adding or removing INPUT_TAG or OUTPUT_TAG values
       - Changing the PRESERVES_ATTRIBUTES line
       - Changing the LIST_ACTION line
       - Changing the transformer template, if the change does change the default
         operation of the transformer
       - Changing the list of displayed values for CHOICE, ACTIVECHOICE or LISTBOX
         types's configuration on the PARAMETER_TYPE line. Basically any type that
         works with PARAMETER_LOOKUP clause (This is required because the displayed
         value is used to store the parameter value in the workspace.)

Upgrading Transformers
    Transformers can now be upgraded to their latest version. Here are few things
    to keep in mind when transformer is updated in any form:
    - Briefly describe the changes done to the transformer in CHANGE_LOG section
    - If any of the input or output tags were renamed/removed then specify
      TAG_MAPPING clause. This is required so that the links can be maintained 
      after upgrading the transformer
    - If parameters were renamed/removed then specify PARAMETER_MAPPING clause
      if parameters values can be mapped over to new parameter. If the change is
      complex then you may be required to implement upgradeTransformer() 
      to correctly map over the parameters from old to new version

Transformer Localization
    Localizations for each transformer are possible, and are taken at runtime
    from a file named "transformers-localized.xy", where xy is the locale the
    localization is for.

Transformer Template Keywords
    The following are built in substitutions for mapping templates:
    XFORMER_NAME_ENCODED & XFORMER_NAME - the transformer name input by the user
    XFORMER_VERSION - the transformer version

    These are useful to avoid having to keep your factory template in synch with your parameter listings:
    FME_BRACED_PARM_VAL_LIST - parameters and their values written in 'key { value }' form assuming {*} syntax is used on the transformer
    FME_PARM_VAL_LIST  - parameters and their values written in 'key value'
    FME_PARM_LIST - list of parameter names separated by ,.  No values included

Custom Code in Transformers
    (Internal only) Sometimes the functionality above is not enough to fully
    capture the behaviour of a transformer. This usually happens for more
    complicated transformers, which require extended setup by the user. For
    instance, the Joiner would be nearly impossible to configure using a bare
    set of parameters, as above, and would never be able to write out the
    correct mapping file code. For these situations, you must write custom C++
    code for your transformer.

Renaming Transformers
    Follow these steps to rename an existing "OldName.fmx" transformer to
    "NewName.fmx"
     - Using Git (or what ever version control is in use) first rename 
       "OldName.fmx" to "NewName.fmx". This will ensure we don't lose history 
       for the "OldName.fmx"
     - Now in "NewName.fmx" add your new version if necessary, leaving the old versions
       around for backwards compatibility
     - For all of the versions in NewName.fmx, add the line "ALIASES: OldName"
     - Update the list of transformers in "bundles/esridesktop/esri-interop-transformers.txt"
       by removing OldName.fmx and adding NewName.fmx
     - Commit changes and you are done


Transformer Gotchas
    The following is a list of gotchas and best practices to consider when
    writing transformer templates:
    
       - When in doubt ensure your parameter reference is in quotes "$(myParam)".
         String values that contains spaces and are not quoted will have the spaces
         removed.
       - make all parameters OR_ATTR types whenever reasonably possible.
       - WHOLE_LINE is deceiving. Always handle empty value in your
         function/factory. This is because if the parameter is published the value
         is only known at runtime thus whole_line is not done and the line is written to the mapping file.
         At runtime, the line is not removed but the parameter set to the value of "".
         See PR8182
       - only use the transfomer name if you really need a transformer name. If it
         is just to have a unique name and the user does not see it, use the
         FME_UUID macro instead (e.g. for Tcl function names)
       - If at all possible do NOT look for 'gui-isms' in fmx or C++ code.
          - Do not check for <Unused>!  Instead check the value of the MACRO (active*) that made
            the parameter unused.  For example, if the user publishes the active choice
            parameter, then <Unused> will not be written out because we do not know at 
            mapping file generation time whether or not the parameter actually is unused.
          - @Value() - instead of using @Value, pass the attribute value into the
            tcl function as a parameter (see stylers).
       - be aware that if the ACTIVECHOICE or ACTIVECHECK parameter is linked to
         a user parameter, the GUI can NOT know the value of the choice until
         runtime. As a result, it will write out ALL the dependent parameters to
         the mapping file.  this is a good example why you should not check for <Unused> (see above)
       - if you change the choice values seen by the user, you almost always have
         to update the version of the transformer
       - when writing TCL that access macros directly, ensure that special characters
         are handled correctly since the FME parser can do strange things. Test with
         a string like: "PARAN (and spaces)".
       - when setting the default values for your transformer with ACTIVECHOICE,
         set them consistently to what the GUI would normally save. This
         probably means the default value for some dependent parameters will be
         <Unused>.
       - [common and counter-intuitive] Up the transformer version when changing the
         list of displayed values for CHOICE or LISTBOX (or similar) types's
         configuration on the PARAMETER_TYPE line. (This is required because the
         displayed value is used to store the parameter value in the workspace.)

Deprecated Sections:
   WHOLE_LINE - don't use this flag.  See PR8182 for good examples why.  the gotchas section
                also covers why.

   TOOLTIP_START
   <tooltip> - you only need this if you have a non-standard tooltip.  Workbench will generate a nice looking tooltip for you.
   TOOLTIP_END