# TemplateWorker.java

    /*
    ** Module   : TemplateWorker.java
    ** Abstract : provides functions for accessing J2SE system properties
    **
    ** Copyright (c) 2005-2022, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------------Description----------------------------------
    ** 001 GES 20050620   @21527 Created initial version supporting a simple
    **                           AST template loading and grafting mechanism.
    ** 002 GES 20050622   @21541 Added depot services to store and retrieve
    **                           ASTs between rulesets that are disconnected
    **                           in time and place (still requires such
    **                           sharing to occur within a single pattern
    **                           engine run).  Also added resolver
    **                           registration to the grafting process.
    ** 003 ECF 20050627   @21555 Modification to 'graft' user function to be
    **                           more lenient. The source parameter may now be
    **                           null, even if the grafted template contains
    **                           an AST which has a peernode annotation. The
    **                           peernode will still be returned from the
    **                           graft method in this case.
    ** 004 ECF 20050711   @21688 Added a variant of graft method. This version
    **                           takes an Aast as the parent, rather than an
    **                           ID. This is needed when grafting to a parent
    **                           which is not part of the AST registry.
    ** 005 GES 20050819   @22186 Added graftAt() support for adding at a
    **                           specific index.
    ** 006 GES 20051013   @23059 Removed requirement that parent be non-null
    **                           in graft/graftAt variants.
    ** 007 GES 20090424   @41935 Import change.
    ** 008 GES 20090429   @42049 Match package and class name changes.
    ** 009 GES 20090515   @42214 Moved to AstManager from AstRegistry/AstPersister.
    ** 010 GES 20090518   @42389 Import change.
    ** 011 SVL 20130624          Allow to load templates at server run time.
    ** 012 ECF 20140808          Set all AST nodes IDs to zero when loaded from XML, as a safety precaution.
    ** 013 ECF 20150715          Replace StringBuffer with StringBuilder.
    ** 014 CA  20170825          Allow graftAt to work without having to attach to a parent.
    ** 015 CA  20191008          Template ASTs are allowed to have inlined shadow nodes.
    ** 016 CA  20211214          The AST manager plugin must be context-local, for runtime conversion, as the
    **                           InMemoryRegistryPlugin is not thread-safe.
    **                           All worker's state must be context-local, for runtime conversion to work in
    **                           multi-context mode, as the pattern workers are singletons.
    **     OM  20220512          Developer mode: allows to load resources from file system if jar is not built.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.pattern;

    import java.io.*;
    import java.util.*;
    import java.util.concurrent.*;

    import org.w3c.dom.*;
    import com.goldencode.ast.*;
    import com.goldencode.util.*;
    import com.goldencode.p2j.cfg.*;
    import com.goldencode.p2j.security.*;

    /**
     * Provides a set of user-defined functions for accessing libraries of
     * pre-defined AST subtrees.  Each AST subtree is called a 'template' and
     * can be accessed by a name which must be unique within a given template
     * library.  Each template library is an XML file stored in a directory
     * in the <code>patpath</code>.  All template library filenames must end
     * in a suffix specified by {@link #TPL_SUFFIX}.
     * <p>
     * Each template is defined in the format required to be loaded using the
     * <code>AstPersister</code>.  Each valid template must be rooted at
     * an <code>AstPersister.ELEM_ROOT</code> element.
     * <p>
     * A generic facility for text replacements is provided.  This facility is
     * implemented after the reading of the elements from the XML file but
     * before those elements are converted into an AST subtree by the persister.
     * This allows any attribute in the same template to be customized on the fly
     * for different purposes.  Since the persister translates everything from
     * its text form into a binary AST form, the result of these text
     * transformations is the same as if the original XML file had been hard
     * coded to the runtime values provided.  Thus token types, token text and
     * other attributes (even annotations) can be transformed in this manner.
     * Any replaceable text must be formatted with <code>${key}</code> where
     * a list of string-based key/value pairs are passed at runtime and are used
     * as the replacements for each matching key found.  Note that the
     * <code>${</code> and <code>}</code> are only stored in the XML file, they
     * are not actually passed as part of the key in the replacements list.
     * <p>
     * This class also provides depot services to store and retrieve ASTs between
     * rulesets that are disconnected in time and place (it still requires such
     * sharing to occur within a single pattern engine run). For example, if
     * a tree transformation requires a separation between the location at which
     * an AST subtree is created and the location at which that subtree is
     * attached, the subtree can be stored in the depot under a name and then
     * retrieved 'downstream' in the same pattern engine run (the same pipeline).
     * Please note that only 1 AST can be stored per name so this facility
     * operates like a shared local variable pool of ASTs.
     */
    public class TemplateWorker
    extends AbstractPatternWorker
    {
       /** Suffix for all template library filenames. */
       public static final String TPL_SUFFIX = ".tpl";

       /** Annotation name to mark a template node as the peer node. */
       public static final String PEERNODE = "peernode";

       /** Context local work area. */
       private static final ContextLocal<WorkArea> context = new ContextLocal<WorkArea>()
       {
          protected WorkArea initialValue()
          {
             return new WorkArea();
          }
       };

       /**
        * Default constructor which calls the super constructor and registers
        * libraries with the pattern engine.
        */
       public TemplateWorker()
       {
          super();
          setLibrary(new Template());
       }

       /**
        * This is a pattern engine library that provides easy access to
        * libraries of pre-defined AST subtrees.
        */
       public class Template
       {
          /** Names a given template. */
          private static final String ATTR_NAME = "name";

          /** Caches the list of loaded AST templates. */
          private final Map<String, Node> templates = new ConcurrentHashMap<>();

          /** Caches the list of loaded XML libraries (to avoid reloading). */
          private final Set<String> libraries = Collections.newSetFromMap(new ConcurrentHashMap<>());

          /**
           * Load a named template library from an XML file. The file is named
           * <code>&lt;name&gt;.&lt;tpl&gt;</code>. it must be located somewhere
           * in the path defined by the global configuration parameter
           * <code>patpath</code>.
           *
           * @param   name
           *          Root name of the XML library containing AST templates.
           *
           * @throws  ConfigurationException
           *          if any error occurs loading the template library.
           */
          public void load(String name)
          throws ConfigurationException
          {
             // avoid reloading
             if (libraries.contains(name))
             {
                return;
             }

             Element root = null;
             Document dom = null;

             try
             {
                if (Configuration.isRuntimeConfig())
                {
                   InputStream stream = ConfigLoader.getConversionResource(name, TPL_SUFFIX);
                   if (stream != null)
                   {
                      dom = XmlHelper.parse(stream);
                   }
                }

                // developer mode: allow resource to be loaded at runtime from file system if present, avoiding
                // having the jar built
                if (dom == null)
                {
                   File file = ConfigLoader.findConfigurationFile(name, TPL_SUFFIX);
                   dom = XmlHelper.parse(file);
                }

                if (dom == null)
                {
                   throw new ConfigurationException("Cannot find '" + name + TPL_SUFFIX + "' resource");
                }
                root = dom.getDocumentElement();
             }
             catch (Exception exc)
             {
                throw new ConfigurationException("Cannot load template library named " + name + ".", exc);
             }

             // cache our library name so it doesn't get reloaded
             libraries.add(name);

             // tag name of the element we are looking for
             String search = XmlFilePlugin.ELEM_ROOT;

             // the list of children will include #Text and other DOM defined
             // trash... get the first of our children
             Node tpl = root.getFirstChild();

             // search for all children that have a tagName of ELEM_ROOT
             while (tpl != null)
             {
                // is this a template?
                if (tpl.getNodeName().equals(search))
                {
                   // walk child ast nodes, setting any non-zero id attribute to zero as a safety
                   // precaution
                   zeroAstIds((Element) tpl);

                   // we have a template, get its name and cache it
                   String tname = ((Element) tpl).getAttribute(ATTR_NAME);

                   if (tname.trim().length() == 0)
                   {
                      String errmsg = "Missing name for element " + search + " in template library " + name + ".";
                      throw new ConfigurationException(errmsg);
                   }

                   templates.put(tname, tpl);
                }

                tpl = tpl.getNextSibling();
             }
          }

          /**
           * Lookup a named template, process any attribute text replacements
           * (if specified), attach the new AST subtree as the last child of the
           * specified parent and, if any of the AST nodes has a
           * <code>PEERNODE</code> attribute AND the <code>source</code>
           * parameter is not <code>null</code>, cross-reference that node with
           * <code>source</code>, using the respective 'peerid' values. Any
           * specified replacement key found in the form <code>${key}</code>
           * will be replaced with the matching value.  The object array must
           * be made as sequential pairs of key string and matching value string.
           *
           * @param    name
           *           The name of the template to use.  This template must have
           *           been previously loaded using {@link #load}.
           * @param    source
           *           The source node to annotate if the <code>PEERNODE</code>
           *           annotation exists in the AST subtree. May be
           *           <code>null</code>.
           * @param    parentid
           *           The ID of the target AST to be used as a parent for this
           *           subtree. If <code>null</code> no attachment occurs but the
           *           tree is still created.
           * @param    replacements
           *           An array of key/value string pairs where any keys found
           *           in the template node's attribute values will be replaced
           *           with the associated values.
           *
           * @return   The node which was specified as the peer node or the
           *           root node of the newly created subtree if no peer node
           *           was specified.
           *
           * @throws   AstException
           *           if there is any problem creating or attaching the template.
           */
          public Aast graft(String name, Aast source, long parentid, Object[] replacements)
          throws AstException
          {
             return graftAt(name, source, parentid, -1, replacements);
          }

          /**
           * Lookup a named template, process any attribute text replacements
           * (if specified), attach the new AST subtree as a child of the
           * specified parent AT THE SPECIFIED INDEX and, if any of the AST nodes
           * has a <code>PEERNODE</code> attribute AND the <code>source</code>
           * parameter is not <code>null</code>, cross-reference that node with
           * <code>source</code>, using the respective 'peerid' values. Any
           * specified replacement key found in the form <code>${key}</code>
           * will be replaced with the matching value.  The object array must
           * be made as sequential pairs of key string and matching value string.
           *
           * @param    name
           *           The name of the template to use.  This template must have
           *           been previously loaded using {@link #load}.
           * @param    source
           *           The source node to annotate if the <code>PEERNODE</code>
           *           annotation exists in the AST subtree. May be
           *           <code>null</code>.
           * @param    parentid
           *           The ID of the target AST to be used as a parent for this
           *           subtree. If <code>null</code> no attachment occurs but the
           *           tree is still created.
           * @param    index
           *           0 to insert as the first child, -1 to insert as the last
           *           child or a 0-based index to specify an exact child index
           *           at which to insert the new child.  All current children at
           *           that index or at a greater index, are moved right. If the
           *           index is greater than the current number of children, the
           *           new child will be added at the end.
           * @param    replacements
           *           An array of key/value string pairs where any keys found
           *           in the template node's attribute values will be replaced
           *           with the associated values.
           *
           * @return   The node which was specified as the peer node or the
           *           root node of the newly created subtree if no peer node
           *           was specified.
           *
           * @throws   AstException
           *           if there is any problem creating or attaching the template.
           */
          public Aast graftAt(String name, Aast source, long parentid, int index, Object[] replacements)
          throws AstException
          {
             // obtain the parent node
             Aast parent = getResolver().getAst(parentid);

             if (parent == null)
             {
                throw new IllegalArgumentException("Invalid parentid:  " + parentid);
             }

             return graftAt(false, name, source, parent, index, replacements);
          }

          /**
           * Lookup a named template, process any attribute text replacements
           * (if specified), attach the new AST subtree as the last child of the
           * specified parent and, if any of the AST nodes has a
           * <code>PEERNODE</code> attribute AND the <code>source</code>
           * parameter is not <code>null</code>, cross-reference that node with
           * <code>source</code>, using the respective 'peerid' values. Any
           * specified replacement key found in the form <code>${key}</code>
           * will be replaced with the matching value.  The object array must
           * be made as sequential pairs of key string and matching value string.
           *
           * @param    name
           *           The name of the template to use.  This template must have
           *           been previously loaded using {@link #load}.
           * @param    source
           *           The source node to annotate if the <code>PEERNODE</code>
           *           annotation exists in the AST subtree. May be
           *           <code>null</code>.
           * @param    parent
           *           The target AST to be used as a parent for this subtree.
           *           If <code>null</code> no attachment occurs but the tree
           *           is still created.
           * @param    replacements
           *           An array of key/value string pairs where any keys found
           *           in the template node's attribute values will be replaced
           *           with the associated values.
           *
           * @return   The node which was specified as the peer node or the
           *           root node of the newly created subtree if no peer node
           *           was specified.
           *
           * @throws   AstException
           *           if there is any problem creating or attaching the template.
           */
          public Aast graft(String name, Aast source, Aast parent, Object[] replacements)
          throws AstException
          {
             return graftAt(false, name, source, parent, -1, replacements);
          }

          /**
           * Lookup a named template, process any attribute text replacements
           * (if specified), attach the new AST subtree as a child of the
           * specified parent AT THE SPECIFIED INDEX and, if any of the AST nodes
           * has a <code>PEERNODE</code> attribute AND the <code>source</code>
           * parameter is not <code>null</code>, cross-reference that node with
           * <code>source</code>, using the respective 'peerid' values. Any
           * specified replacement key found in the form <code>${key}</code>
           * will be replaced with the matching value.  The object array must
           * be made as sequential pairs of key string and matching value string.
           *
           * @param    name
           *           The name of the template to use.  This template must have
           *           been previously loaded using {@link #load}.
           * @param    source
           *           The source node to annotate if the <code>PEERNODE</code>
           *           annotation exists in the AST subtree. May be
           *           <code>null</code>.
           * @param    parent
           *           The target AST to be used as a parent for this subtree.
           *           If <code>null</code> no attachment occurs but the tree
           *           is still created.
           * @param    index
           *           0 to insert as the first child, -1 to insert as the last
           *           child or a 0-based index to specify an exact child index
           *           at which to insert the new child.  All current children at
           *           that index or at a greater index, are moved right. If the
           *           index is greater than the current number of children, the
           *           new child will be added at the end.
           * @param    replacements
           *           An array of key/value string pairs where any keys found
           *           in the template node's attribute values will be replaced
           *           with the associated values.
           *
           * @return   The node which was specified as the peer node or the
           *           root node of the newly created subtree if no peer node
           *           was specified.
           *
           * @throws   AstException
           *           if there is any problem creating or attaching the template.
           */
          public Aast graftAt(String   name,
                              Aast     source,
                              Aast     parent,
                              int      index,
                              Object[] replacements)
          {
             return graftAt(false, name, source, parent, index, replacements);
          }

          /**
           * Lookup a named template, process any attribute text replacements
           * (if specified), attach the new AST subtree as a child of the
           * specified parent AT THE SPECIFIED INDEX and, if any of the AST nodes
           * has a <code>PEERNODE</code> attribute AND the <code>source</code>
           * parameter is not <code>null</code>, cross-reference that node with
           * <code>source</code>, using the respective 'peerid' values. Any
           * specified replacement key found in the form <code>${key}</code>
           * will be replaced with the matching value.  The object array must
           * be made as sequential pairs of key string and matching value string.
           *
           * @param    inlineShadows
           *           Flag indicating that the template needs to have the shadows inlined.
           * @param    name
           *           The name of the template to use.  This template must have
           *           been previously loaded using {@link #load}.
           * @param    source
           *           The source node to annotate if the <code>PEERNODE</code>
           *           annotation exists in the AST subtree. May be
           *           <code>null</code>.
           * @param    parent
           *           The target AST to be used as a parent for this subtree.
           *           If <code>null</code> no attachment occurs but the tree
           *           is still created.
           * @param    index
           *           0 to insert as the first child, -1 to insert as the last
           *           child or a 0-based index to specify an exact child index
           *           at which to insert the new child.  All current children at
           *           that index or at a greater index, are moved right. If the
           *           index is greater than the current number of children, the
           *           new child will be added at the end.
           * @param    replacements
           *           An array of key/value string pairs where any keys found
           *           in the template node's attribute values will be replaced
           *           with the associated values.
           *
           * @return   The node which was specified as the peer node or the
           *           root node of the newly created subtree if no peer node
           *           was specified.
           *
           * @throws   AstException
           *           if there is any problem creating or attaching the template.
           */
          public Aast graftAt(boolean  inlineShadows,
                              String   name,
                              Aast     source,
                              Aast     parent,
                              int      index,
                              Object[] replacements)
          throws AstException
          {
             Node tpl = templates.get(name);

             if (tpl == null)
             {
                throw new IllegalArgumentException("Template named '" + name + "' doesn't exist.");
             }

             // make a temporary deep copy of this subtree
             Node template = tpl.cloneNode(true);

             HashMap<String, String> replace = null;

             // create a map of all parameters to be replaced
             if (replacements != null)
             {
                replace = new HashMap<>();
                for (int i = 0; i < replacements.length; i += 2)
                {
                   replace.put(replacements[i] == null ? null : replacements[i].toString(),
                               replacements[i + 1] == null ? null : replacements[i + 1].toString());
                }
             }

             // "sweep" all replacement keys with their runtime values
             processReplacements(template, replace);

             // instantiate the AST subtree
             Aast result = XmlFilePlugin.loadTree((Element)template, inlineShadows);

             // graft and register the subtree
             if (parent != null)
             {
                attach(parent, result, index);
             }

             // if one of the nodes was marked as the peer node, implement the
             // proper peer relationship with the passed source node
             Iterator<Aast> iter = result.iterator();

             while (iter.hasNext())
             {
                Aast child = iter.next();

                // detect if this node is marked as the peer
                if (child.isAnnotation(PEERNODE))
                {
                   // return this node instead of the root
                   result = child;

                   // remove the annotation since it was for our use only
                   result.removeAnnotation(PEERNODE);

                   // fixup the peer ids
                   if (source != null)
                   {
                      source.putAnnotation("peerid", result.getId());
                      result.putAnnotation("peerid", source.getId());
                   }
                }
             }

             return result;
          }

          /**
           * Grafts the child to the parent as the last childe and registers
           * the subtree (rooted at the child) with the resolver.  All nodes
           * within the child subtree will have their IDs and parents fixed up to
           * properly integrate with the new tree.
           *
           * @param    parent
           *           The node at which to graft the child subtree.  If
           *           <code>null</code> this method immediately returns.
           * @param    child
           *           The root of the subtree to be grafted.
           *
           * @throws   AstException
           *           if there is any problem grafting the child to the parent.
           */
          public void attach(Aast parent, Aast child)
          throws AstException
          {
             attach(parent, child, -1);
          }

          /**
           * Grafts the child to the parent at the specified index and registers
           * the subtree (rooted at the child) with the resolver.  All nodes
           * within the child subtree will have their IDs and parents fixed up to
           * properly integrate with the new tree.
           *
           * @param    parent
           *           The node at which to graft the child subtree.  If
           *           <code>null</code> this method immediately returns.
           * @param    child
           *           The root of the subtree to be grafted.
           * @param    index
           *           0 to insert as the first child, -1 to insert as the last
           *           child or a 0-based index to specify an exact child index
           *           at which to insert the new child.  All current children at
           *           that index or at a greater index, are moved right. If the
           *           index is greater than the current number of children, the
           *           new child will be added at the end.
           *
           * @throws   AstException
           *           if there is any problem grafting the child to the parent.
           */
          public void attach(Aast parent, Aast child, int index)
          throws AstException
          {
             if (parent == null)
             {
                // nothing to do
                return;
             }

             // graft the subtree to the parent
             parent.graftAt(child, index);

             // register all children with the resolver if parent has an ID
             if (parent.getId() != null)
             {
                Iterator<Aast> iter = child.iterator();

                while (iter.hasNext())
                {
                   Aast next = iter.next();
                   getResolver().registerAst(next);
                }
             }
          }

          /**
           * Adds or replaces the given AST in the depot using the specified name
           * as a key by which the AST can be retrieved later.
           *
           * @param    name
           *           The name to use as a key for the depot.
           * @param    ast
           *           The AST to add to the depot.
           *
           * @return   Always <code>true</code>.
           */
          public boolean addToDepot(String name, Aast ast)
          {
             context.get().depot.put(name, ast);
             return true;
          }

          /**
           * Returns the AST found in the depot by the given name, removing it
           * first. If no such AST exists, <code>null</code> is returned.
           *
           * @param    name
           *           The name to use as a key for the depot lookup.
           *
           * @return   The AST found or <code>null</code> if no such AST exists.
           */
          public Aast removeFromDepot(String name)
          {
             return context.get().depot.remove(name);
          }

          /**
           * Returns the AST found in the depot by the given name or
           * <code>null</code> if no such AST exists.
           *
           * @param    name
           *           The name to use as a key for the depot lookup.
           *
           * @return   The AST found or <code>null</code> if no such AST exists.
           */
          public Aast getFromDepot(String name)
          {
             return context.get().depot.get(name);
          }

          /**
           * Detects if an AST exists in the depot under the given name.
           *
           * @param    name
           *           The name to use as a key for the depot lookup.
           */
          public boolean isInDepot(String name)
          {
             return context.get().depot.containsKey(name);
          }

          /**
           * Recursively sweep all attribute values of this node and its subtree,
           * based on a map of key/value pairs.  Replacements occur for any
           * instances of <code>${key}</code> in any attribute value.  Once the
           * current node's attributes are processed, all child nodes are visited
           * and this method is called recursively for each child.
           *
           * @param    tpl
           *           The node whose attributes must be processed.
           * @param    replacements
           *           A collection of key/value pairs.
           */
          private void processReplacements(Node tpl, Map<String, String> replacements)
          {
             // is there anything to do?
             if (replacements == null)
             {
                return;
             }

             // read all attributes from this node
             NamedNodeMap list = tpl.getAttributes();

             // are there any attributes?
             if (list != null)
             {
                // loop through elements and sweep each one
                int len = list.getLength();

                for (int i = 0; i < len; i++)
                {
                   Node next = list.item(i);

                   // process the value replacements for all ${key} constructs
                   String replace = StringHelper.sweep(next.getNodeValue(), replacements);

                   // save our changes
                   next.setNodeValue(replace);
                }
             }

             // process all children recursively
             Node child = tpl.getFirstChild();

             while (child != null)
             {
                processReplacements(child, replacements);
                child = child.getNextSibling();
             }
          }

          /**
           * As a safety measure, set all AST IDs to 0, so they will have appropriate, available IDs
           * applied when the template is grafted to a parent AST.
           *
           * @param   tpl
           *          Java template XML element, whose descendant elements represent ASTs.
           */
          private void zeroAstIds(Element tpl)
          {
             NodeList descendants = tpl.getElementsByTagName(XmlFilePlugin.ELEM_AST);
             int len = descendants.getLength();

             for (int i = 0; i < len; i++)
             {
                Element next = (Element) descendants.item(i);
                if (next.hasAttribute(XmlFilePlugin.ATTR_ID))
                {
                   next.setAttribute(XmlFilePlugin.ATTR_ID, "0");
                }
             }
          }
       }

       /**
        * Context local work area.
        */
       private static class WorkArea
       {
          /** Temporary storage for ASTs that are created but not yet attached. */
          private final Map<String, Aast> depot = new HashMap<>();
      }
    }