# JavaPatternWorker.java

    /*
    ** Module   : JavaPatternWorker.java
    ** Abstract : Pattern worker with specific knowledge of the Java language
    **
    ** Copyright (c) 2005-2022, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
    ** 001 GES 20050324   @20599 Created initial version. Provides Java AST
    **                           creation/persistence services and resolves
    **                           token names to token types.
    ** 002 GES 20050425   @20863 Additional functions added for peer and
    **                           arbitrary node creation directly from rules.
    ** 003 ECF 20050501   @20975 Changed superclass to
    **                           convert.AbstractConversionWorker. Abstracted
    **                           useful common conversion functionality into
    **                           that class so it can be shared among workers
    **                           which need to process peer, target ASTs.
    ** 004 ECF 20050526   @21290 Changes to support new expression engine
    **                           implementation. Changed library registration
    **                           mechanism based on superclass' modifications
    **                           to support single library per pattern worker
    **                           limit.
    ** 005 ECF 20050628   @21600 Added variant of persistJavaFile which
    **                           accepts a filename. The original no-arg
    **                           version now uses this new method as a worker.
    ** 006 ECF 20050707   @21674 Modified persistJavaFile. Creates directories
    **                           in target file's path if not already present.
    ** 007 ECF 20050721   @21770 Added getSectionAnchor convenience methods.
    **                           Allow simple access to a class section AST
    **                           which serves as an anchor point to which
    **                           class member ASTs are added.
    ** 008 GES 20050928   @22874 Improved createJavaAst() methods to provide
    **                           new index option.
    ** 009 ECF 20050928   @22885 Modified getSectionAnchor(). Added support
    **                           for interfaces.
    ** 010 GES 20060131   @24165 Added a tracking mode by which external
    **                           callers can capture a list of all files
    **                           that were persisted between 2 specific
    **                           points in time.
    ** 011 GES 20090429   @42062 Match package and class name changes.
    ** 012 GES 20090515   @42228 Moved to AstManager from AstRegistry/AstPersister.
    ** 013 SVL 20130624          Added support for virtual files.
    ** 014 CA  20131023          Changed needed to support in-memory AST storage.
    ** 015 ECF 20140405          Replaced static variables with context local work area.
    ** 016 ECF 20140913          Prevent generateJavaFilename from creating names with multiple jast
    **                           suffixes.
    ** 017 ECF 20150715          Replace StringBuffer with StringBuilder.
    ** 018 CA  20201008          Added createJavaFile(String), to create a standalone COMPILE_UNIT AST.
    **     CA  20210917          Javadoc fixes.
    **     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.
    **     TJD 20220504          Java 11 compatibility minor changes
    */
    /*
    ** 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.uast;

    import java.io.*;
    import java.util.*;
    import com.goldencode.ast.*;
    import com.goldencode.p2j.cfg.*;
    import com.goldencode.p2j.convert.*;
    import com.goldencode.p2j.pattern.*;
    import com.goldencode.p2j.security.ContextLocal;

    /**
     * A pattern worker that provides Java AST services and conversion of Java
     * language token names (as defined by the {@link JavaTokenTypes}) to their
     * integral types.
     */
    public final class JavaPatternWorker
    extends AbstractConversionWorker
    implements JavaTokenTypes
    {
       /**
        * Text to append to a filename to generate a unique name for persisting
        * a Java AST.
        */
       public static final String JAST_POSTFIX = ".jast";

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

       /**
        * Cache current instance of the helper so that common methods have
        * access.
        */
       private JavaAstHelper helper = null;

       /**
        * Worker routine to centralize creation of new Java ASTs.  All ID setup
        * and parent linkages are established if the parent AST is passed as
        * a parameter, otherwise only an unidentified and standalone AST is
        * created.  If a parent AST is passed, a resolver instance should also
        * be passed.
        *
        * @param    type
        *           The token type for the Java AST.
        * @param    text
        *           The text for the AST.
        * @param    parent
        *           The parent AST node or <code>null</code>.
        * @param    resolver
        *           The current instance of the {@link AstSymbolResolver} in use
        *           or <code>null</code>.
        * @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.
        *
        * @return   The AST that was created.
        */
       public static JavaAst createAst(int               type,
                                       String            text,
                                       JavaAst           parent,
                                       AstSymbolResolver resolver,
                                       int               index)
       {
          JavaAst ast = new JavaAst();
          initializeAst(ast, type, text, parent, resolver, index);

          return ast;
       }

       /**
        * Worker routine to peer AST creation.  All ID setup and parent linkages
        * are established and bidirectional peer ID annotations are written
        * in source and target nodes.
        *
        * @param    type
        *           The token type for the Java AST.
        * @param    text
        *           The text for the AST.
        * @param    parent
        *           The parent AST node.
        * @param    resolver
        *           The current instance of the {@link AstSymbolResolver} in use.
        * @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.
        *
        * @return   The AST that was created.
        */
       public static JavaAst createPeerAst(Aast              source,
                                           int               type,
                                           String            text,
                                           JavaAst           parent,
                                           AstSymbolResolver resolver,
                                           int               index)
       {
          JavaAst target = createAst(type, text, parent, resolver, index);

          // save our cross-references
          crossReferencePeerAsts(source, target);

          return target;
       }

       /**
        * Worker routine to centralize creation of a new Java AST that is a
        * root node.  A new file ID is registered in the {@link AstManager}
        * and set into the AST.
        *
        * @param    file
        *           The file in which to persist the new AST.
        * @param    resolver
        *           The current instance of the {@link AstSymbolResolver} in use
        *           or <code>null</code>.
        *
        * @return   The root AST that was created or <code>null</code> on error.
        */
       public static JavaAst createRootAst(File file, AstSymbolResolver resolver)
       {
          JavaAst result = createAst(COMPILE_UNIT,
                                     "compilation unit",
                                     null,
                                     null,
                                     -1);
          initializeRootAst(result, file, resolver);

          return result;
       }

       /**
        * Worker routine to centralize creation of a new Java AST that is a root node.  A new file ID
        * is registered in the {@link AstManager} and set into the AST.
        *
        * @param    filename
        *           The name of the file in which to persist the new AST. Can be real or virtual (i.e.
        *           the file can be never persisted into the file system, the name is used to map some
        *           ID to the AST).
        * @param    resolver
        *           The current instance of the {@link AstSymbolResolver} in use
        *           or <code>null</code>.
        *
        * @return   The root AST that was created or <code>null</code> on error.
        */
       public static JavaAst createRootAst(String filename, AstSymbolResolver resolver)
       {
          JavaAst result = createAst(COMPILE_UNIT,
                                     "compilation unit",
                                     null,
                                     null,
                                     -1);
          initializeRootAst(result, filename, resolver);

          return result;
       }

       /**
        * Generate a unique Java AST persistence filename based on the passed
        * filename.
        *
        * @param    filename
        *           The filename used as a base for the Java AST filename.
        *
        * @return   A unique filename based on the source filename with '.jast'
        *           appended.
        */
       public static String generateJavaFilename(String filename)
       {
          if (filename.endsWith(JAST_POSTFIX))
          {
             return filename;
          }

          StringBuilder sb = new StringBuilder(filename);

          return sb.append(JAST_POSTFIX).toString();
       }

       /**
        * Clears past contents of the new files list and enables tracking mode
        * where all JAST files that are persisted are logged in a set that
        * can be queried by {@link #getPersistedFiles}.  Use
        * {@link #disableNewFileTracking} when the tracking session is no
        * longer needed.
        */
       public static void enableNewFileTracking()
       {
          WorkArea wa = context.get();
          wa.pfiles = new HashSet<>();
          wa.track = true;
       }

       /**
        * Disables tracking mode such that any JAST files that are persisted
        * will no longer be logged in the set that can be queried by
        * {@link #getPersistedFiles}.  Does not alter the contents of the current
        * persisted files list.  Use this to finish a session started with
        * {@link #enableNewFileTracking}
        */
       public static void disableNewFileTracking()
       {
          context.get().track = false;
       }

       /**
        * Accesses the list of files persisted since the last call
        * to {@link #enableNewFileTracking}.  There are no duplicate entries in
        * the list.
        *
        * @return   The list of files that have been recently persisted.
        */
       public static String[] getPersistedFiles()
       {
          return (String[]) context.get().pfiles.toArray(new String[0]);
       }

       /**
        * Default constructor which initializes libraries.
        */
       public JavaPatternWorker()
       {
          super();
          helper = new JavaAstHelper();
          setLibrary(helper);
       }

       /**
        * This method is called each time the pattern engine needs to resolve
        * a string constant into a numeric, boolean, or string literal value.
        * Currently, it only handles Java language token names.

        * @param   constant
        *          A case-insensitive token name to be resolved to a token type.
        *
        * @return  The <code>Long</code> value associated with the token type
        *          represented by the token name <code>constant</code>, or
        *          <code>null</code> if the given constant is not the name of a
        *          valid, Java language token.
        */
       public Object resolveConstant(String constant)
       {
          int type = JavaSymbolResolver.lookupTokenType(constant);

          return (type >= 0 ? Long.valueOf(type) : null);
       }

       /**
        * Optionally creates or loads a Java target AST that is associated with
        * the passed-in source AST.  This processing only occurs if auto load
        * is enabled.  If a persisted AST exists with the source file name
        * and a postfix of '.jast', then it will be loaded.  Otherwise a new
        * Java AST will be created.
        * <p>
        * Any changes made during processing of the previous AST will be lost if
        * they haven't been persisted by this point.
        *
        * @param    ast
        *           AST about to be processed by the pattern engine.
        */
       public void visitAst(Aast ast)
       {
          WorkArea wa = context.get();

          // reset the current/root AST to null
          wa.root = wa.current = null;
          getResolver().setTargetRootAst(null);

          if (wa.autoload)
          {
             String filename = generateJavaFilename(ast);
             filename = Configuration.normalizeFilename(filename);

             try
             {
                if (AstManager.get().isExistingAst(filename))
                {
                   helper.loadJavaFile(filename, true);
                }
                else
                {
                   // create a new target tree
                   wa.root = wa.current = createRootAst(new File(filename), getResolver());
                }
             }

             catch (Exception exc)
             {
                // some failure, we just won't have a Java AST!
             }
          }
       }

       /**
        * Generate a unique Java AST persistence filename based on the current
        * source AST filename.
        *
        * @return   A unique filename based on the source filename with '.jast'
        *           appended.
        */
       private String generateJavaFilename(Aast ast)
       {
          return generateJavaFilename(ast.getFilename());
       }

       /**
        * Helper to create, edit, move and delete Java ASTs.
        */
       public class JavaAstHelper
       {
          /**
           * Enables or disables autoloading of the target Java AST each time a
           * new Progress source AST is visited.  If a persistent Java AST is
           * not found, one will be created.
           *
           * @param    auto
           *           <code>true</code> to enable autoloading.
           *
           * @return   Always <code>true</code>.
           */
          public boolean setAutoload(boolean auto)
          {
             context.get().autoload = auto;
             return true;
          }

          /**
           * Exposes the root node ID of the current Java file as a variable.
           *
           * @return   The root node ID of the current Java file.
           */
          public long getJavaRootId()
          {
             WorkArea wa = context.get();

             if (wa.root == null)
                return -1L;

             return wa.root.getId().longValue();
          }

          /**
           * Search the current source AST node and up each ancestor node in the
           * path to the root to find the first instance of the 'peerid'
           * annotation.  This allows Java ASTs to be attached to the right block
           * of code, without necessarily knowing what parent might be enclosing
           * it.  This is exposed as a ruleset variable.
           *
           * @return   The first 'peerid' annotation found in the current node
           *           or the closest ancestor node that has a valid peerid.
           *           Returns -1 if no peerid is found in any ancestor.
           */
          public long getClosestPeerId()
          {
             return JavaPatternWorker.getClosestPeerId(getCopy());
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent.
           * <p>
           * The new node will be added as the last child of the parent.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The ID of the parent AST node.
           *
           * @return   The unique ID if the AST was created successfully or
           *           <code>null</code> if the parent ID is invalid, if the type
           *           is not valid or on any unexpected error during AST
           *           creation.
           */
          public Long createJavaAst(long type, String text, Long parent)
          {
             return createJavaAst(type, text, parent, -1);
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent.
           * <p>
           * The new node will be added as the last child of the parent.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The parent AST node.
           *
           * @return   The AST which was created or <code>null</code> if the
           *           parent node is invalid, if the type is not valid or on any
           *           unexpected error during AST creation.
           */
          public Aast createJavaAst(long type, String text, Aast parent)
          {
             return createJavaAst(type, text, parent, -1);
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent at the
           * given index position.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The ID of the parent AST node.
           * @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.
           *
           * @return   The unique ID if the AST was created successfully or
           *           <code>null</code> if the parent ID is invalid, if the type
           *           is not valid or on any unexpected error during AST
           *           creation.
           */
          public Long createJavaAst(long   type,
                                    String text,
                                    Long   parent,
                                    int    index)
          {
             JavaAst jparent = (JavaAst) getResolver().getAst(parent);

             if (jparent == null)
             {
                return null;
             }

             JavaAst node = (JavaAst) createJavaAst(type, text, jparent, index);

             if (node != null)
             {
                return node.getId();
             }

             return null;
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent at the
           * given index position.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The parent AST node.
           * @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.
           *
           * @return   The AST which was created or <code>null</code> if the
           *           parent node is invalid, if the type is not valid or on any
           *           unexpected error during AST creation.
           */
          public Aast createJavaAst(long   type,
                                    String text,
                                    Aast   parent,
                                    int    index)
          {
             if (parent == null)
             {
                return null;
             }

             return createAst((int)type, text, (JavaAst) parent, getResolver(), index);
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent. Most
           * importantly, on success the ID of the generated AST node will be
           * stored in the current source AST node's 'peerid' annotation. This
           * provides a standard mechanism to duplicate the structure of a
           * source AST in the target Java AST.  The source AST's ID will also
           * be stored in the target Java AST 'peerid' annotation which allows
           * subsequent bidirectional referencing between these peers. This
           * is used in cases where the conversion is simple enough to drive
           * it completely from the rule set rather than from a custom worker.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The ID of the parent AST node.
           *
           * @return   The unique ID if the AST was created successfully or
           *           <code>null</code> if the parent ID is invalid, if the type
           *           is not valid or on any unexpected error during AST
           *           creation.
           */
          public Long createPeerAst(long type, String text, Long parent)
          {
             return createPeerAst(type, text, parent, -1);
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent. Most
           * importantly, on success the ID of the generated AST node will be
           * stored in the current source AST node's 'peerid' annotation. This
           * provides a standard mechanism to duplicate the structure of a
           * source AST in the target Java AST.  The source AST's ID will also
           * be stored in the target Java AST 'peerid' annotation which allows
           * subsequent bidirectional referencing between these peers. This
           * is used in cases where the conversion is simple enough to drive
           * it completely from the rule set rather than from a custom worker.
           * <p>
           * The new node will be added as the last child of the parent.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The ID of the parent AST node.
           *
           * @return   The AST which was created or <code>null</code> if the
           *           parent node is invalid, if the type is not valid or on any
           *           unexpected error during AST creation.
           */
          public Aast createPeerAst(long type, String text, Aast parent)
          {
             return createPeerAst(type, text, parent, -1);
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent. Most
           * importantly, on success the ID of the generated AST node will be
           * stored in the current source AST node's 'peerid' annotation. This
           * provides a standard mechanism to duplicate the structure of a
           * source AST in the target Java AST.  The source AST's ID will also
           * be stored in the target Java AST 'peerid' annotation which allows
           * subsequent bidirectional referencing between these peers. This
           * is used in cases where the conversion is simple enough to drive
           * it completely from the rule set rather than from a custom worker.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The ID of the parent AST node.
           * @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.
           *
           * @return   The unique ID if the AST was created successfully or
           *           <code>null</code> if the parent ID is invalid, if the type
           *           is not valid or on any unexpected error during AST
           *           creation.
           */
          public Long createPeerAst(long   type,
                                    String text,
                                    Long   parent,
                                    int    index)
          {
             JavaAst jparent = (JavaAst) getResolver().getAst(parent);

             if (jparent == null)
             {
                return null;
             }

             JavaAst node = (JavaAst) createPeerAst(type, text, jparent, index);

             if (node != null)
             {
                return node.getId();
             }

             return null;
          }

          /**
           * General purpose Java AST creation function which can create a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the specified parent. Most
           * importantly, on success the ID of the generated AST node will be
           * stored in the current source AST node's 'peerid' annotation. This
           * provides a standard mechanism to duplicate the structure of a
           * source AST in the target Java AST.  The source AST's ID will also
           * be stored in the target Java AST 'peerid' annotation which allows
           * subsequent bidirectional referencing between these peers. This
           * is used in cases where the conversion is simple enough to drive
           * it completely from the rule set rather than from a custom worker.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           * @param    parent
           *           The ID of the parent AST node.
           * @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.
           *
           * @return   The AST which was created or <code>null</code> if the
           *           parent node is invalid, if the type is not valid or on any
           *           unexpected error during AST creation.
           */
          public Aast createPeerAst(long   type,
                                    String text,
                                    Aast   parent,
                                    int    index)
          {
             if (parent == null)
             {
                return null;
             }

             return JavaPatternWorker.createPeerAst(getCopy(),
                                                    (int) type,
                                                    text,
                                                    (JavaAst) parent,
                                                    getResolver(),
                                                    index);
          }

          /**
           * Simplest case for  Java AST creation which creates a
           * <code>JavaAst</code> node using the passed token type and text
           * and then it will attach that node to the parent specified by the
           * 'peerid' annotation of the source AST's parent node. This greatly
           * simplifies creation of entire hierarchies of structure that
           * match between the source and target ASTs.  This uses the services
           * of {@link #createPeerAst(long,String,Long)}.
           * <p>
           * The new node will be added as the last child of the parent.
           *
           * @param    type
           *           The token type for the Java AST.
           * @param    text
           *           The text for the AST.
           *
           * @return   The unique ID if the AST was created successfully or
           *           <code>null</code> if the parent ID is invalid, if the type
           *           is not valid or on any unexpected error during AST
           *           creation.
           */
          public Long createPeerAst(long type, String text)
          {
             Long id = (Long) getCopy().getParent().getAnnotation("peerid");

             if (id == null)
                return null;

             return createPeerAst(type, text, id);
          }

          /**
           * Convenience method to retrieve the class section AST which anchors
           * a certain category of class member. The target class is the
           * top level class defined in the current compile unit.
           *
           * @param   type
           *          Token type indicating which section is required. Must be
           *          within the range <code>CS_BEGIN &lt; <i>type</i> &lt;
           *          CS_END</code>.
           *
           * @return  AST node representing the anchor point for the specified
           *          section, or <code>null</code> if the section does not
           *          exist.
           *
           * @throws  IllegalArgumentException
           *          if <code>type</code> is not within the valid range of
           *          token types specified above.
           */
          public Aast getSectionAnchor(int type)
          {
             WorkArea wa = context.get();

             Aast ast = wa.root.getImmediateChild(KW_CLASS, null);
             if (ast == null)
             {
                ast = wa.root.getImmediateChild(KW_INTERFACE, null);
             }

             return getSectionAnchor(ast.getId(), type);
          }

          /**
           * Convenience method to retrieve the class section AST which anchors
           * a certain category of class member.
           *
           * @param   classId
           *          ID of the class AST within which the target section
           *          resides.
           * @param   type
           *          Token type indicating which section is required. Must be
           *          within the range <code>CS_BEGIN &lt; <i>type</i> &lt;
           *          CS_END</code>.
           *
           * @return  AST node representing the anchor point for the specified
           *          section, or <code>null</code> if the section does not
           *          exist.
           *
           * @throws  IllegalArgumentException
           *          if <code>type</code> is not within the valid range of
           *          token types specified above.
           */
          public Aast getSectionAnchor(Long classId, int type)
          {
             return getSectionAnchor(getResolver().getAst(classId), type);
          }

          /**
           * Convenience method to retrieve the class section AST which anchors
           * a certain category of class member.
           *
           * @param   classAst
           *          The class AST within which the target section resides.
           * @param   type
           *          Token type indicating which section is required. Must be
           *          within the range <code>CS_BEGIN &lt; <i>type</i> &lt;
           *          CS_END</code>.
           *
           * @return  AST node representing the anchor point for the specified
           *          section, or <code>null</code> if the section does not
           *          exist.
           *
           * @throws  IllegalArgumentException
           *          if <code>type</code> is not within the valid range of
           *          token types specified above.
           */
          public Aast getSectionAnchor(Aast classAst, int type)
          {
             if (type <= CS_BEGIN || type >= CS_END)
             {
                throw new IllegalArgumentException(
                   "Invalid class section token type:  " + type);
             }

             return classAst.getImmediateChild(type, null);
          }

          /**
           * Creates a new Java target AST that is associated with a name of the
           * caller's selection, and sets this AST as the current target AST.
           * <p>
           * Any changes made during processing of the previous AST will be lost
           * if they haven't been persisted by this point.
           *
           * @param   filename
           *          The filename to use as the Java AST filename.
           * @param   projectRelative
           *          <code>true</code> if the filename is relative to the
           *          project home directory, <code>false</code> if the filename
           *          is absolute or relative to the current directory.
           */
          public boolean createJavaFile(String filename, boolean projectRelative)
          {
             return createJavaFile(filename, projectRelative, false);
          }

          /**
           * Creates a new Java target AST that is associated with a name of the
           * caller's selection, and sets this AST as the current target AST.
           * <p>
           * Any changes made during processing of the previous AST will be lost
           * if they haven't been persisted by this point.
           *
           * @param   filename
           *          The filename to use as the Java AST filename.
           */
          public JavaAst createJavaFile(String filename)
          {
             JavaAst result = createAst(COMPILE_UNIT, "compilation unit", null, null, -1);
             result.brainwash(filename);

             return result;
          }

          /**
           * Creates a new Java target AST that is associated with a name of the
           * caller's selection, and sets this AST as the current target AST.
           * <p>
           * Any changes made during processing of the previous AST will be lost
           * if they haven't been persisted by this point.
           *
           * @param   filename
           *          The filename to use as the Java AST filename.
           * @param   projectRelative
           *          <code>true</code> if the filename is relative to the
           *          project home directory, <code>false</code> if the filename
           *          is absolute or relative to the current directory.
           * @param   virtualFile
           *          If <code>true</code> then the file is not supposed to be persisted into the file
           *          system, the file name is used to map some ID to the AST.
           */
          public boolean createJavaFile(String filename, boolean projectRelative, boolean virtualFile)
          {
             WorkArea wa = context.get();

             try
             {
                if (virtualFile)
                {
                   wa.root = wa.current = createRootAst(filename, getResolver());
                }
                else
                {
                   File file;

                   if (projectRelative)
                   {
                      file = Configuration.toFile(filename);
                   }
                   else
                   {
                      file = new File(filename);
                   }

                   // create a new target tree
                   wa.root = wa.current = createRootAst(file, getResolver());
                }
             }

             catch (Exception exc)
             {
                // some failure, we just won't have a Java AST!
                return false;
             }

             return true;
          }

          /**
           * Loads a persisted Java target AST that is associated with a name of
           * the caller's selection, and sets this AST as the current target AST.
           * <p>
           * Any changes made during processing of the previous AST will be lost
           * if they haven't been persisted by this point.
           *
           * @param   filename
           *          The filename from which to read the Java AST.
           * @param   projectRelative
           *          <code>true</code> if the filename is relative to the
           *          project home directory, <code>false</code> if the filename
           *          is absolute or relative to the current directory.
           */
          public boolean loadJavaFile(String filename, boolean projectRelative)
          {
             WorkArea wa = context.get();

             try
             {
                if (projectRelative)
                {
                   filename = Configuration.normalizeFilename(filename);
                }

                if (AstManager.get().isExistingAst(filename))
                {
                   // read the existing target tree
                   wa.root = wa.current = (JavaAst) AstManager.get().loadTree(filename);

                   // set the current Java target AST into the resolver
                   getResolver().setTargetRootAst(wa.root);

                   // for each node in the loaded tree, register them with the
                   // resolver
                   Iterator nodes = wa.root.iterator();

                   while (nodes.hasNext())
                   {
                      Aast next = (Aast) nodes.next();
                      getResolver().registerAst(next);
                   }
                }
                else
                {
                   return false;
                }
             }

             catch (Exception exc)
             {
                // some failure, we just won't have a Java AST!
                return false;
             }

             return true;
          }

          /**
           * Save the current target Java AST to file if the tree is not empty
           * (the root has at least 1 child).  If this AST is empty, it will be
           * removed from the {@link AstManager} which will essentially disable
           * further use of this AST.
           *
           * @return   <code>true</code> if the tree is persisted,
           *           <code>false</code> if the tree is empty or on any failure
           *           during persistence.
           *
           * @throws   ConfigurationException
           *           if environment is not properly configured
           * @throws   AstException
           *           if there is an error writing Java AST.
           */
          public boolean persistJavaFile()
          throws ConfigurationException,
                 AstException,
                 IOException
          {
             return persistJavaFile(getSource().getFilename());
          }

          /**
           * Save the specified Java AST to file if the tree is not empty (the root has at least 1
           * child).  If this AST is empty, it will be removed from the {@link AstManager} which will
           * essentially disable further use of this AST.
           *
           * @param    ast
           *           The AST to be persisted.
           *
           * @return   <code>true</code> if the tree is persisted,
           *           <code>false</code> if the tree is empty or on any failure during persistence.
           *
           * @throws   ConfigurationException
           *           if environment is not properly configured
           * @throws   AstException
           *           if there is an error writing Java AST.
           */
          public boolean persistJavaAst(JavaAst ast)
          throws ConfigurationException,
                 AstException,
                 IOException
          {
             return persistJavaAst(ast.getFilename(), ast);
          }

          /**
           * Save the specified Java AST to file if the tree is not empty (the root has at least 1
           * child).  If this AST is empty, it will be removed from the {@link AstManager} which will
           * essentially disable further use of this AST.
           *
           * @param    filename
           *           Base filename (without <code>jast</code> extension) under which to save the
           *           Java AST. Must be an absolute path or relative to the project root.
           * @param    ast
           *           The AST to be persisted.
           *
           * @return   <code>true</code> if the tree is persisted,
           *           <code>false</code> if the tree is empty or on any failure during persistence.
           *
           * @throws   ConfigurationException
           *           if environment is not properly configured
           * @throws   AstException
           *           if there is an error writing Java AST.
           */
          public boolean persistJavaAst(String filename, JavaAst ast)
          throws ConfigurationException,
                 AstException,
                 IOException
          {
             WorkArea wa = context.get();

             filename = Configuration.normalizeFilename(filename);

             // bypass persistence if there is an empty tree
             if (ast.getNumImmediateChildren() == 0)
             {
                try
                {
                   // no need to maintain this value in the registry
                   AstManager.get().removeTree(filename);
                }
                catch (Exception exc)
                {
                   // best efforts only
                }

                return false;
             }

             // read the existing target tree
             AstManager.get().saveTree(ast, filename, false);

             // save this into the list of new files if tracking is enabled
             if (wa.track)
             {
                wa.pfiles.add(filename);
             }

             return true;
          }

          /**
           * Save the current target Java AST to file if the tree is not empty
           * (the root has at least 1 child).  If this AST is empty, it will be
           * removed from the {@link AstManager} which will essentially disable
           * further use of this AST.
           *
           * @param    filename
           *           Base filename (without <code>jast</code> extension) under
           *           which to save the Java AST. Must be an absolute path or
           *           relative to the project root.
           *
           * @return   <code>true</code> if the tree is persisted,
           *           <code>false</code> if the tree is empty or on any failure
           *           during persistence.
           *
           * @throws   ConfigurationException
           *           if environment is not properly configured
           * @throws   AstException
           *           if there is an error writing Java AST.
           */
          public boolean persistJavaFile(String filename)
          throws ConfigurationException,
                 AstException,
                 IOException
          {
             filename = generateJavaFilename(filename);

             return persistJavaAst(filename, context.get().root);
          }
       }

       /**
        * Context local work area.
        */
       private static class WorkArea
       {
          /** The list of persisted files between 2 arbitrary points in time. */
          private Set<String> pfiles = null;

          /** <code>true</code> if persisted file names must be tracked. */
          private boolean track = false;

          /**
           * If <code>true</code>, the target Java AST will be created or loaded
           * when a new source AST is visited.
           */
          private boolean autoload = false;

          /** Current active Java AST associated with the current source AST. */
          private JavaAst current = null;

          /** Root of the target tree currently being processed. */
          private JavaAst root = null;
       }
    }