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 < <i>type</i> <
* 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 < <i>type</i> <
* 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 < <i>type</i> <
* 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;
}
}