PreprocessorHints.java
/*
** Module : PreprocessorHints.java
** Abstract : reads a preprocessor hints file and cleanly exposes the contents
**
** Copyright (c) 2005-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
** 001 NVS 20050525 @21287 Created initial version. This supports reading an XML file that
** "lives" in the same location (and basename) as the associated
** Progress 4GL source file to which it refers. The hints file is the
** source file's basename + ".pphints". Based on UastHints.java by GES.
** 002 NVS 20050601 @21366 Added getMaxIncludeDepth() method which gets the maximum nesting
** level (depth) of includes in this PreprocessorHints instance.
** 003 SIY 20051011 @23017 Added handling of null hintsRoot in getAllIncludes() and
** getNumUniqueIncludes().
** 004 GES 20060328 @25242 All public methods are now protected against a null hints root.
** 005 GES 20090515 @42218 Import changes.
** 006 CA 20140313 Allow unused include nodes with missing value argument.
** 007 ECF 20150715 Replace StringBuffer with StringBuilder.
** 008 EVL 20160223 Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
** 009 CA 20170714 Added getSource.
** 010 CA 20170825 Enhanced to allow collection and reporting of preprocessor constant
** symbols.
** 011 CA 20180731 getSymbolValue() now looks in the entire hint tree, for the specified
** symbol (uses a DFS search).
** 012 VVT 20220823 Fixed incorrect file name manipulation in createInclude() (see #6237-24)
** and minor code style refactorings.
** 013 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
*/
/*
** 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.preproc;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.logging.*;
import com.goldencode.p2j.convert.*;
import org.w3c.dom.*;
import com.goldencode.p2j.cfg.*;
import com.goldencode.p2j.util.*;
import com.goldencode.util.*;
/**
* Loads hints information from a preprocessor hints XML file into memory and
* exposes the data in an easily accessible format. The purpose of this class
* is to separate the hints persistence mechanism (i.e., XML) from the user
* of these hints.
* <p>
* Once constructed, the hints file has been loaded into memory and the
* contents are available via simple 'getter' method calls such as
* {@link #getArguments}. This class hides the storage mechanism and the
* file format from the callers. It also handles any conversion from the
* string oriented XML format to the proper data type of the value in
* question. Generally, if the hints file doesn't exist or if the value
* being queried doesn't exist in that hints file, the result from a
* lookup will be <code>null</code>.
* <p>
* The following is the structure of the XML file:
* <p>
* <ul>
* <li> hints (root element)
* <ul>
* <li> preprocessor-output (element, always 1)
* <ul>
* <li> include (element, may be nested, the top level element
* describes the original source file)
* <ul>
* <li> file (attribute, name of the included file)
* <li> start-line (attribute, line in the cache file
* where contribution of this include file begins)
* <li> start-column (attribute, column in the cache file
* where contribution of this include file begins)
* <li> end-line (attribute, line in the cache file
* where contribution of this include file ends)
* <li> end-column (attribute, column in the cache file
* where contribution of this include file ends)
* <li> reference (element, may be 0 or more)
* <ul>
* <li> name (attribute, referenced preprocessor name like 1 for
* {&1} or X for {&X}
* <li> type (attribute, the nature of the referenced name)
* <li> value (attribute, the substitution for the name)
* <li> line (attribute, together with column defines the
* location of the reference in the preprocessor output
* <li> column (attribute, together with line defines the
* location of the reference in the preprocessor output
* </ul>
* <li> argument (element, one per include file argument)
* <ul>
* <li> name (attribute, argument name
* <li> position (attribute, index of the argument in the list)
* <li> value (attribute, the value of the argument)
* <li> used (attribute, flag, "yes" means the argument was used
* in the include file at least once)
* </ul>
* </ul>
* </ul>
* </ul>
* </ul>
*
* @author NVS
* @version 1.0.0
*/
public class PreprocessorHints
implements HintsTags
{
/** Logger */
private static final ConversionStatus LOG = ConversionStatus.get(PreprocessorHints.class);
/**
* The standard suffix appended to a source code filename to create a
* unique filename for storing hints.
*/
private static final String HINTS_SUFFIX = ".pphints";
/** Normalized source filename */
private String source = null;
/** The hints tree built from the XML file */
private HintsTreeNode hintsRoot = null;
/**
* Constructs an instance, loads the associated hints file if it exists
* and parses the contents.
*
* @param sourceName
* The Progress 4GL source program's filename with which this
* hints file is associated.
* @throws PreprocessorException
* An unknown or unexpected failure.
*/
public PreprocessorHints(String sourceName)
throws PreprocessorException
{
source = Configuration.normalizeFilename(sourceName);
StringBuilder sb = new StringBuilder(sourceName);
sb.append(HINTS_SUFFIX);
File hints = new File(sb.toString());
if (hints.exists() && hints.isFile())
{
loadHints(hints);
}
}
/**
* Get the {@link #source} filename.
*
* @return See above.
*/
public String getSource()
{
return source;
}
/**
* Get the root of this preprocessor hints tree.
*
* @return See above.
*/
public HintsTreeNode getRoot()
{
return hintsRoot;
}
/**
* Dumps the tree.
*/
public void dumpHintsTree()
{
Iterator ti = hintsRoot.descendantsIterator();
HintsTreeNode tn = null;
int depth = 0;
IncludeHint ih = null;
List refs = null;
List args = null;
TreeNode[] path = null;
while (ti.hasNext())
{
tn = (HintsTreeNode)ti.next();
depth = tn.getDepth();
path = tn.getPath();
ih = tn.getIncludeHint();
refs = tn.getReferenceHints(false);
args = tn.getArgumentHints(false);
System.out.println("Level " + depth + ", file " + ih.getFilename());
System.out.println(" references: " + refs.size());
System.out.println(" arguments : " + args.size());
for (int i = 0; i < path.length; i ++)
System.out.println(" path[" + i + "]: " +
((HintsTreeNode)path[i]).getIncludeHint().getFilename());
}
}
/**
* Get the symbol value, performing a DFS search, from the root node.
*
* @param name
* The symbol name.
*
* @return The symbol value.
*/
public String getSymbolValue(String name)
{
return getSymbolValue(hintsRoot, name);
}
/**
* Get the symbol value, performing a DFS search, from the specified root node.
*
* @param root
* The hints node to start the search.
* @param name
* The symbol name.
*
* @return The symbol value.
*/
private String getSymbolValue(HintsTreeNode root, String name)
{
if (root == null)
{
return null;
}
String val = root.getSymbolValue(name);
if (val != null)
{
return val;
}
Iterator<HintsTreeNode> iter = (Iterator<HintsTreeNode>) root.childIterator();
while (val == null && iter.hasNext())
{
HintsTreeNode child = iter.next();
val = getSymbolValue(child, name);
}
return val;
}
/**
* Gets the array of argument hints for the top level file.
*
* @param deep
* tells to get only this node's hints (<code>false</code>) or
* everything
* @return list of ArgumentHint objects.
*/
public List getArguments(boolean deep)
{
if (hintsRoot == null)
return null;
return hintsRoot.getArgumentHints(deep);
}
/**
* Gets the array of reference hints for the top level file.
*
* @param deep
* tells to get only this node's hints (<code>false</code>) or
* everything
* @return array of ReferenceHint objects produced for the top level file.
*/
public List getReferences(boolean deep)
{
if (hintsRoot == null)
return null;
return hintsRoot.getReferenceHints(deep);
}
/**
* Locates the include file where specific line and column of the output
* came from.
*
* @param line
* line of the output file under consideration
* @param column
* column of the output file under consideration. line and column
* together refer to the position in the output file that is under
* consideration
* @return the innermost include file name or the source file name if no
* include file contributed to the specified output location.
*/
public String getSourceFor(int line, int column)
{
if (hintsRoot == null)
{
return source;
}
// locate the deepest node that contains this line and column
// this method always returns a valid node - hintsRoot if nothing else
HintsTreeNode htn = getPathFor(hintsRoot, line, column);
// get the path for this node
TreeNode[] path = htn.getPath();
// return the last item of the path
String filename = ((HintsTreeNode)path[path.length - 1]).
getIncludeHint().
getFilename();
return filename;
}
/**
* Locates all include files where specific line and column of the output
* came from.
*
* @param line
* line of the output file under consideration
* @param column
* column of the output file under consideration. line and column
* together refer to the position in the output file that is under
* consideration
* @return list of names of include files contributed to the contents of
* the output file at the specified location, or the source file
* name.
*/
public List getSourcesFor(int line, int column)
{
List list = new LinkedList();
if (hintsRoot == null)
{
list.add(source);
return list;
}
// locate the deepest node that contains this line and column
// this method always returns a valid node - hintsRoot if nothing else
HintsTreeNode htn = getPathFor(hintsRoot, line, column);
// get the path for this node
TreeNode[] path = htn.getPath();
for (int i = 0; i < path.length; i ++)
list.add(((HintsTreeNode)path[i]).getIncludeHint().getFilename());
// return the whole path
return list;
}
/**
* Returns the total number of included files.
*
* @return number of included files. may be 0.
*/
public int getNumAllIncludes()
{
if (hintsRoot == null)
return 0;
// enumerate all nodes
Iterator iter = hintsRoot.descendantsIterator();
// count all nodes
int i = 0;
while (iter.hasNext())
{
iter.next();
i ++;
}
return i - 1;
}
/**
* Returns the number of unique include files.
*
* @return number of unique include files. may be 0.
*/
public int getNumUniqueIncludes()
{
if (hintsRoot == null)
return 0;
// enumerate all nodes
Iterator iter = hintsRoot.descendantsIterator();
// collect unique includes
Set s = new HashSet();
HintsTreeNode htn = null;
String name = null;
while (iter.hasNext())
{
htn = (HintsTreeNode)iter.next();
name = htn.getIncludeHint().getFilename();
s.add(name);
}
return s.size() - 1;
}
/**
* Returns the array of names of all included files.
*
* @return List of names of included files. may be of size 0.
*/
public List getAllIncludes()
{
if (hintsRoot == null)
return null;
// enumerate all nodes
Iterator iter = hintsRoot.descendantsIterator();
// count all nodes
List list = new LinkedList();
HintsTreeNode htn = null;
String name = null;
iter.next(); // skip the top level node
while (iter.hasNext())
{
htn = (HintsTreeNode)iter.next();
name = htn.getIncludeHint().getFilename();
list.add(name);
}
return list;
}
/**
* Returns the array of names of unique include files.
*
* @return Set of names of unique include files. may be of size 0.
*/
public Set getUniqueIncludes()
{
Set s = new HashSet();
if (hintsRoot == null)
return s;
// enumerate all nodes
Iterator iter = hintsRoot.descendantsIterator();
// collect unique includes
HintsTreeNode htn = null;
String name = null;
iter.next(); // skip the top level node
while (iter.hasNext())
{
htn = (HintsTreeNode)iter.next();
name = htn.getIncludeHint().getFilename();
s.add(name);
}
return s;
}
/**
* Returns the maximum nesting level of includes for this instance.
*
* @return maximum nesting level or -1 if not initialized
*/
public int getMaxIncludeDepth()
{
return hintsRoot != null ? hintsRoot.getChildDepth() : -1;
}
/**
* Checks whether the specified file was included or not.
* If the filename is absolute, the check is done for the full name,
* otherwise for the specified components only.
*
* @param filename
* include file to check for inclusion from
*
* @return <code>true</code> if the file was included.
*
* @throws ConfigurationException
* @throws IOException
*/
public boolean isIncluded(String filename)
throws ConfigurationException,
IOException
{
if (hintsRoot == null)
return false;
File f = new File(filename);
boolean abs = f.isAbsolute();
String file = abs ? filename : File.separator + filename;
Set s = getUniqueIncludes();
String home = Configuration.home() + File.separator;
String test = null;
Iterator iter = s.iterator();
while (iter.hasNext())
{
test = (String)iter.next();
f = new File(home + test);
test = f.getCanonicalPath();
if (abs)
{
if (test.equals(file))
return true;
}
else
{
if (test.endsWith(file))
return true;
}
}
return false;
}
/**
* Checks whether the specified location in the file was included from
* an include file or not.
*
* @param line
* line number within the source file
* @param column
* column number within the source file; line and column specify
* the location within the file to check for inclusion
*
* @return <code>true</code> if the location was included.
*/
public boolean isIncluded(int line, int column)
{
return hintsRoot != null
&& !getSourceFor(line, column).equals(source);
}
/**
* Checks whether there are unused arguments to this file.
* Unused arguments may indicate potential coding problems.
*
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
* @return <code>true</code> if there are unused arguments.
*/
public boolean isUnused(boolean deep)
{
if (hintsRoot == null)
return false;
List hints = getArguments(deep);
Iterator iter = hints.iterator();
ArgumentHint hint = null;
while (iter.hasNext())
{
hint = (ArgumentHint)iter.next();
if (!hint.getUsed())
return true;
}
return false;
}
/**
* Checks whether the specified positional argument is used or not in
* this file.
* Unused arguments may indicate potential coding problems.
*
* @param posArgNum
* positional argument to test
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return <code>true</code> if the specified argument was not used.
*/
public boolean isUnused(int posArgNum, boolean deep)
{
if (hintsRoot == null)
return false;
String argName = "{" + posArgNum + "}";
List hints = getArguments(deep);
Iterator iter = hints.iterator();
ArgumentHint hint = null;
while (iter.hasNext())
{
hint = (ArgumentHint)iter.next();
if (!hint.getName().equals(argName))
continue;
return !hint.getUsed();
}
return false;
}
/**
* Checks whether the specified named argument is used or not in
* this file.
* Unused arguments may indicate potential coding problems.
*
* @param namedArg
* named argument to test; case does not matter
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return <code>true</code> if the specified argument was not used.
*/
public boolean isUnused(String namedArg, boolean deep)
{
if (hintsRoot == null)
return false;
List hints = getArguments(deep);
Iterator iter = hints.iterator();
ArgumentHint hint = null;
while (iter.hasNext())
{
hint = (ArgumentHint)iter.next();
if (hint.getName().compareToIgnoreCase(namedArg) != 0)
continue;
return !hint.getUsed();
}
return false;
}
/**
* Checks whether the specified variable was referenced or not in
* this file.
*
* @param var
* variable to test; case does not matter
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return <code>true</code> if the specified variable was referenced.
*/
public boolean isReferenced(String var, boolean deep)
{
if (hintsRoot == null)
return false;
List hints = getReferences(deep);
Iterator iter = hints.iterator();
ReferenceHint hint = null;
while (iter.hasNext())
{
hint = (ReferenceHint)iter.next();
if (hint.getName().compareToIgnoreCase(var) != 0)
continue;
return true;
}
return false;
}
/**
* Gets the number of references to the specified variable in
* this file.
*
* @param var
* variable to test; case does not matter
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return number of references
*/
public int getNumReferenced(String var, boolean deep)
{
if (hintsRoot == null)
return 0;
int count = 0;
List hints = getReferences(deep);
Iterator iter = hints.iterator();
ReferenceHint hint = null;
while (iter.hasNext())
{
hint = (ReferenceHint)iter.next();
if (hint.getName().compareToIgnoreCase(var) == 0)
count ++;
}
return count;
}
/**
* Gets the number of references to global variables in this file.
*
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return number of references
*/
public int getNumRefGlobal(boolean deep)
{
if (hintsRoot == null)
return 0;
return getNumRefs("global", deep);
}
/**
* Gets the number of references to scoped variables in this file.
*
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return number of references
*/
public int getNumRefScoped(boolean deep)
{
if (hintsRoot == null)
return 0;
return getNumRefs("scoped", deep);
}
/**
* Gets the number of references to builtin variables in this file.
*
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return number of references
*/
public int getNumRefBuiltin(boolean deep)
{
if (hintsRoot == null)
return 0;
return getNumRefs("builtin", deep);
}
/**
* Gets the number of references to arguments in this file.
*
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return number of references
*/
public int getNumRefArgument(boolean deep)
{
if (hintsRoot == null)
return 0;
return getNumRefs("argument", deep);
}
/**
* Gets the number of references to undefined variables in this file.
*
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return number of references
*/
public int getNumRefUndefined(boolean deep)
{
if (hintsRoot == null)
return 0;
return getNumRefs("undefined", deep);
}
/**
* Load preprocessor hints from an XML document associated with the
* specified filename.
*
* @param hintsFile
* The hints file to read.
*
* @throws PreprocessorException
* An unknown or unexpected failure.
*/
private void loadHints(File hintsFile)
throws PreprocessorException
{
try
{
Document dom = XmlHelper.parse(hintsFile);
Element root = dom.getDocumentElement();
// check the root and subroot nodes
if (!root.getTagName().toUpperCase().equals(
ELEM_TAG_ROOT.toUpperCase()))
throw new PreprocessorException("Invalid root node: " +
root.getTagName());
NodeList cl = root.getChildNodes();
Node cn = null;
Element subroot = null;
// check the subroot node
int elCount = 0;
for (int i = 0; cl != null && i < cl.getLength(); i ++)
{
cn = cl.item(i);
if (cn.getNodeType() != Node.ELEMENT_NODE)
continue;
elCount ++;
if (elCount == 1 && !cn.getNodeName().toUpperCase().equals(
ELEM_TAG_SUBROOT.toUpperCase()))
throw new PreprocessorException("Invalid subroot node: " +
cn.getNodeName());
if (elCount > 1)
throw new PreprocessorException("Multiple subroot nodes");
subroot = (Element)cn;
}
// create the hints tree recursively
String topName = hintsFile.getAbsolutePath();
topName = topName.substring(0, topName.length() -
HINTS_SUFFIX.length());
IncludeHint topInclude = new IncludeHint(topName);
topInclude.setStartLine(1);
topInclude.setStartColumn(1);
topInclude.setEndLine(Integer.MAX_VALUE);
topInclude.setEndColumn(Integer.MAX_VALUE);
hintsRoot = createHintsNode(topInclude, subroot);
// check for the top level include element presence:
// - it has to be the only node (element) under the subroot;
// - it has to have the file attribute matching the base filename
if (hintsRoot.getNumChildren() == 1)
{
HintsTreeNode first = (HintsTreeNode)hintsRoot.getFirstChild();
IncludeHint ih = first.getIncludeHint();
if (topInclude.getFilename().equals(ih.getFilename()))
{
first.setParent(null);
hintsRoot = first;
}
}
}
catch (Exception exc)
{
throw new PreprocessorException(
"Error loading hints file: " + hintsFile.getAbsolutePath(), exc);
}
}
/**
* Creates a hints tree node from the information in the passed DOM node.
*
* @param top
* the top level include hint for the subtree being created
* @param elem
* the DOM element to analyze
* @return <code>HintsTreeNode</code> representing the specified DOM node
* @throws PreprocessorException
* when the original XML document contains invalid nodes
*/
private HintsTreeNode createHintsNode(IncludeHint top, Element elem)
throws PreprocessorException
{
ReferenceHint[] refs = new ReferenceHint[0];
SymbolHint[] syms = new SymbolHint[0];
ArgumentHint[] args = new ArgumentHint[0];
List refList = new LinkedList();
List symList = new LinkedList();
List argList = new LinkedList();
String refTag = ELEM_TAG_REF.toUpperCase();
String symTag = ELEM_TAG_SYM.toUpperCase();
String argTag = ELEM_TAG_ARG.toUpperCase();
String incTag = ELEM_TAG_INCL.toUpperCase();
// enumerate all children elements of the passed element
NodeList cl = elem.getChildNodes();
Node cn = null;
// check and process element nodes
// valid elements are include, reference and argument
// first pass: checks, references and arguments
String name = null;
for (int i = 0; cl != null && i < cl.getLength(); i ++)
{
cn = cl.item(i);
if (cn.getNodeType() != Node.ELEMENT_NODE)
continue;
name = cn.getNodeName().toUpperCase();
if (name.equals(incTag))
continue;
if (name.equals(refTag))
{
refList.add(createReference((Element)cn));
continue;
}
if (name.equals(symTag))
{
symList.add(createSymbol((Element)cn));
continue;
}
if (name.equals(argTag))
{
argList.add(createArgument((Element)cn));
continue;
}
throw new PreprocessorException("Invalid element: " + name);
}
// create the HintsTree node
refs = (ReferenceHint[])refList.toArray(refs);
args = (ArgumentHint[])argList.toArray(args);
syms = (SymbolHint[])symList.toArray(syms);
HintsTreeNode ht = new HintsTreeNode(top, refs, syms, args);
HintsTreeNode in = null;
// second pass: inner includes
for (int i = 0; cl != null && i < cl.getLength(); i ++)
{
cn = cl.item(i);
if (cn.getNodeType() != Node.ELEMENT_NODE)
continue;
name = cn.getNodeName().toUpperCase();
if (!name.equals(incTag))
continue;
in = createInclude((Element)cn);
ht.addChild(in);
}
return ht;
}
/**
* Creates a <code>ReferenceHint</code> instance from the information
* in the passed DOM node.
*
* @param elem
* the DOM element to analyze
* @return <code>ReferenceHint</code> representing the specified DOM node
* @throws PreprocessorException
* when the original XML document contains invalid nodes
*/
private static ReferenceHint createReference(Element elem)
throws PreprocessorException
{
// get values of all mandatory attributes
String name = XmlHelper.getAttribute(elem, ATTR_TAG_REF_NAME);
String type = XmlHelper.getAttribute(elem, ATTR_TAG_REF_TYPE);
String value = XmlHelper.getAttribute(elem, ATTR_TAG_REF_VAL);
String line = XmlHelper.getAttribute(elem, ATTR_TAG_REF_LINE);
String col = XmlHelper.getAttribute(elem, ATTR_TAG_REF_COL);
if (name == null || type == null || value == null || line == null ||
col == null)
throw new PreprocessorException("incomplete reference element");
int lineNum = Integer.parseInt(line);
int colNum = Integer.parseInt(col);
ReferenceHint rh = new ReferenceHint(name, type, value);
rh.setLine(lineNum);
rh.setColumn(colNum);
return rh;
}
/**
* Creates a <code>SymbolHint</code> instance from the information in the passed DOM node.
*
* @param elem
* the DOM element to analyze
*
* @return <code>SymbolHint</code> representing the specified DOM node
*
* @throws PreprocessorException
* when the original XML document contains invalid nodes
*/
private static SymbolHint createSymbol(Element elem)
throws PreprocessorException
{
// get values of all mandatory attributes
String name = XmlHelper.getAttribute(elem, ATTR_TAG_SYM_NAME);
String type = XmlHelper.getAttribute(elem, ATTR_TAG_SYM_TYPE);
String value = XmlHelper.getAttribute(elem, ATTR_TAG_SYM_VAL);
if (name == null || type == null || value == null)
throw new PreprocessorException("incomplete symbol element");
SymbolHint sh = new SymbolHint(name, type, value);
return sh;
}
/**
* Creates an <code>ArgumentHint</code> instance from the information
* in the passed DOM node.
*
* @param elem
* the DOM element to analyze
* @return <code>ArgumentHint</code> representing the specified DOM node
* @throws PreprocessorException
* when the original XML document contains invalid nodes
*/
private static ArgumentHint createArgument(Element elem)
throws PreprocessorException
{
// get values of all mandatory attributes
String name = XmlHelper.getAttribute(elem, ATTR_TAG_ARG_NAME);
String pos = XmlHelper.getAttribute(elem, ATTR_TAG_ARG_POS);
String value = XmlHelper.getAttribute(elem, ATTR_TAG_ARG_VAL);
String used = XmlHelper.getAttribute(elem, ATTR_TAG_ARG_USED);
if (name == null || pos == null || used == null)
throw new PreprocessorException("incomplete argument element");
if (value == null && "yes".equalsIgnoreCase(used))
{
throw new PreprocessorException("incomplete argument element: used, with missing value");
}
int posNum = Integer.parseInt(pos);
boolean usedFlag = false;
used = used.toUpperCase();
if (used.equals("YES"))
usedFlag = true;
return new ArgumentHint(name, posNum, value, usedFlag);
}
/**
* Creates an <code>ArgumentHint</code> instance from the information
* in the passed DOM node and then creates a HintsTreeNode from it.
*
* @param elem
* the DOM element to analyze
* @return <code>HintsTreeNode</code> representing the specified DOM node
* @throws PreprocessorException
* when the original XML document contains invalid nodes
*/
private HintsTreeNode createInclude(Element elem)
throws PreprocessorException
{
// get values of all mandatory attributes
String file = XmlHelper.getAttribute(elem, ATTR_TAG_FILE);
String slin = XmlHelper.getAttribute(elem, ATTR_TAG_START_LINE);
String scol = XmlHelper.getAttribute(elem, ATTR_TAG_START_COL);
String elin = XmlHelper.getAttribute(elem, ATTR_TAG_END_LINE);
String ecol = XmlHelper.getAttribute(elem, ATTR_TAG_END_COL);
if (file == null || slin == null || scol == null || elin == null ||
ecol == null)
throw new PreprocessorException("incomplete include element");
int sline = Integer.parseInt(slin);
int scolm = Integer.parseInt(scol);
int eline = Integer.parseInt(elin);
int ecolm = Integer.parseInt(ecol);
if (!Paths.get(file).isAbsolute())
{
// denormalize the file
try
{
file = Paths.get(Configuration.home(), file).toString();
}
catch (ConfigurationException xcpt)
{
throw new PreprocessorException("Project home misconfigured", xcpt);
}
}
// create instance of IncludeHint
IncludeHint ih = new IncludeHint(file);
ih.setStartLine(sline);
ih.setStartColumn(scolm);
ih.setEndLine(eline);
ih.setEndColumn(ecolm);
// use this instance of IncludeHint to process the element recursively
HintsTreeNode ht = createHintsNode(ih, elem);
return ht;
}
/**
* Locates all include files where specific line and column of the output
* came from.
*
* @param node
* starting node for this search
* @param line
* line of the output file under consideration
* @param column
* column of the output file under consideration. line and column
* together refer to the position in the output file that is under
* consideration
* @return hints tree node contributed to the contents of the output
* file at the specified location or the input node.
*/
private HintsTreeNode getPathFor(HintsTreeNode node, int line, int column)
{
// enumerate immediate children of the given node
Iterator thisIter = node.childIterator();
// find a child node contributing to the specified location.
// a node contributes to line, column if its range of output positions
// covers the specified line and column
HintsTreeNode current = null;
IncludeHint ih = null;
int startLine = 0;
int endLine = 0;
int startCol = 0;
int endCol = 0;
while (thisIter.hasNext())
{
current = (HintsTreeNode)thisIter.next();
ih = current.getIncludeHint();
startLine = ih.getStartLine();
endLine = ih.getEndLine();
startCol = ih.getStartColumn();
endCol = ih.getEndColumn();
// this include begins after the position - stop looking
if (line < startLine || (line == startLine && column < startCol))
return node;
// the covering include? Note that the end column does NOT belong
// to this include
if ((line > startLine || (line == startLine && column >= startCol))
&& (line < endLine || (line == endLine && column < endCol)))
return getPathFor(current, line, column);
}
// no covering include
return node;
}
/**
* Gets the number of references to the specified type variables in this
* file.
*
* @param type
* type of reference to check
* @param deep
* tells to look only at this node's hints (<code>false</code>) or
* everything
*
* @return number of references
*/
private int getNumRefs(String type, boolean deep)
{
int count = 0;
List hints = getReferences(deep);
Iterator iter = hints.iterator();
ReferenceHint hint = null;
while (iter.hasNext())
{
hint = (ReferenceHint)iter.next();
if (hint.getType().equals(type))
count ++;
}
return count;
}
/**
* Command line driver used for testing.
*
* @param cargs
* command line parameters.
* Only one parameter is used and it is the base filename.
*
* @throws ConfigurationException
* @throws IOException
*/
public static void main(String[] cargs)
throws ConfigurationException,
IOException
{
if (cargs.length != 3)
{
LOG.log(Level.SEVERE, "Correct syntax is 'PreprocessorHints file line# col#'");
System.exit(1);
}
PreprocessorHints ph = null;
try
{
ph = new PreprocessorHints(cargs[0]);
}
catch (PreprocessorException excpt)
{
LOG.log(Level.SEVERE, "", excpt);
System.exit(1);
}
LOG.log(Level.INFO,"Loaded hints file successfully");
ph.dumpHintsTree();
List args = ph.getArguments(false);
if (args != null && args.size() != 0)
{
System.out.println("Arguments were:");
Iterator iter = args.iterator();
ArgumentHint arg = null;
while (iter.hasNext())
{
arg = (ArgumentHint)iter.next();
System.out.println(" name: " + arg.getName());
System.out.println(" pos: " + arg.getPosition());
System.out.println(" value: " + arg.getValue());
System.out.println(" used: " + arg.getUsed());
}
}
else
System.out.println("No arguments were given");
args = ph.getArguments(true);
System.out.println("Deep arguments search: " + args.size());
if (args != null && args.size() != 0)
{
Iterator iter = args.iterator();
ArgumentHint arg = null;
while (iter.hasNext())
{
arg = (ArgumentHint)iter.next();
System.out.println(" name: " + arg.getName());
System.out.println(" pos: " + arg.getPosition());
System.out.println(" value: " + arg.getValue());
System.out.println(" used: " + arg.getUsed());
}
}
List refs = ph.getReferences(false);
if (refs != null && refs.size() != 0)
{
System.out.println("References were:");
Iterator iter = refs.iterator();
ReferenceHint ref = null;
while (iter.hasNext())
{
ref = (ReferenceHint)iter.next();
System.out.println(" name: " + ref.getName());
System.out.println(" type: " + ref.getType());
System.out.println(" value: " + ref.getValue());
System.out.println(" line: " + ref.getLine());
System.out.println(" column: " + ref.getColumn());
}
}
else
System.out.println("No references were found");
refs = ph.getReferences(true);
System.out.println("Deep references search: " + refs.size());
if (refs != null && refs.size() != 0)
{
Iterator iter = refs.iterator();
ReferenceHint ref = null;
while (iter.hasNext())
{
ref = (ReferenceHint)iter.next();
System.out.println(" name: " + ref.getName());
System.out.println(" type: " + ref.getType());
System.out.println(" value: " + ref.getValue());
System.out.println(" line: " + ref.getLine());
System.out.println(" column: " + ref.getColumn());
}
}
int line = Integer.parseInt(cargs[1]);
int col = Integer.parseInt(cargs[2]);
String lastIncl = ph.getSourceFor(line, col);
System.out.println("Contributing include file name for line " + cargs[1] +
" column " + cargs[2] + " is:");
System.out.println(" " + lastIncl);
List allIncl = ph.getSourcesFor(line, col);
System.out.println("Contributing include file names for line " + cargs[1] +
" column " + cargs[2] + " are:");
System.out.println(" " + allIncl.toString());
System.out.println("Total includes " + ph.getNumAllIncludes());
System.out.println("Unique includes " + ph.getNumUniqueIncludes());
List allNames = ph.getAllIncludes();
System.out.println("All include file names:");
System.out.println(" " + allNames.toString());
Set uniqueNames = ph.getUniqueIncludes();
System.out.println("Unique include file names:");
System.out.println(" " + uniqueNames.toString());
System.out.println("isIncluded(file): " + ph.isIncluded("file"));
System.out.println("isIncluded(ile): " + ph.isIncluded("ile"));
System.out.println("isIncluded(preproc/file): " + ph.isIncluded("preproc/file"));
System.out.println("isIncluded(reproc/file): " + ph.isIncluded("reproc/file"));
String fname = "/home/nvs/projects/p2j/testcases/preproc/file";
System.out.println("isIncluded(" + fname + "): " + ph.isIncluded(fname));
System.out.println("isIncluded(10, 10): " + ph.isIncluded(10, 10));
System.out.println("isIncluded(60, 15): " + ph.isIncluded(60, 15));
System.out.println("isIncluded(71, 11): " + ph.isIncluded(71, 11));
System.out.println("isUnused(false): " + ph.isUnused(false));
System.out.println("isUnused(true): " + ph.isUnused(true));
System.out.println("isUnused(1,false): " + ph.isUnused(1,false));
System.out.println("isUnused(2,false): " + ph.isUnused(2,false));
System.out.println("isUnused(3,false): " + ph.isUnused(3,false));
System.out.println("isUnused(4,false): " + ph.isUnused(4,false));
System.out.println("isUnused(5,false): " + ph.isUnused(5,false));
System.out.println("isUnused(6,false): " + ph.isUnused(6,false));
System.out.println("isUnused(7,false): " + ph.isUnused(7,false));
System.out.println("isUnused(8,false): " + ph.isUnused(8,false));
System.out.println("isUnused(1,true): " + ph.isUnused(1,true));
System.out.println("isUnused(2,true): " + ph.isUnused(2,true));
System.out.println("isUnused(3,true): " + ph.isUnused(3,true));
System.out.println("isUnused(4,true): " + ph.isUnused(4,true));
System.out.println("isUnused(5,true): " + ph.isUnused(5,true));
System.out.println("isUnused(6,true): " + ph.isUnused(6,true));
System.out.println("isUnused(7,true): " + ph.isUnused(7,true));
System.out.println("isUnused(8,true): " + ph.isUnused(8,true));
System.out.println("isUnused(named, false): " + ph.isUnused("named", false));
System.out.println("isUnused(named1, false): " + ph.isUnused("named1", false));
System.out.println("isUnused(named2, false): " + ph.isUnused("named2", false));
System.out.println("isUnused(named, true): " + ph.isUnused("named", true));
System.out.println("isUnused(named1, true): " + ph.isUnused("named1", true));
System.out.println("isUnused(named2, true): " + ph.isUnused("named2", true));
System.out.println("isReferenced(opsys, false): " + ph.isReferenced("opsys", false));
System.out.println("isReferenced(xyz, false): " + ph.isReferenced("xyz", false));
System.out.println("isReferenced(opsys, true): " + ph.isReferenced("opsys", true));
System.out.println("isReferenced(xyz, true): " + ph.isReferenced("xyz", true));
System.out.println("getNumReferenced(opsys, false): " + ph.getNumReferenced("opsys", false));
System.out.println("getNumReferenced(xyz, false): " + ph.getNumReferenced("xyz", false));
System.out.println("getNumReferenced(opsys, true): " + ph.getNumReferenced("opsys", true));
System.out.println("getNumReferenced(xyz, true): " + ph.getNumReferenced("xyz", true));
System.out.println("getNumRefGlobal(false): " + ph.getNumRefGlobal(false));
System.out.println("getNumRefGlobal(true): " + ph.getNumRefGlobal(true));
System.out.println("getNumRefScoped(false): " + ph.getNumRefScoped(false));
System.out.println("getNumRefScoped(true): " + ph.getNumRefScoped(true));
System.out.println("getNumRefBuiltin(false): " + ph.getNumRefBuiltin(false));
System.out.println("getNumRefBuiltin(true): " + ph.getNumRefBuiltin(true));
System.out.println("getNumRefArgument(false): " + ph.getNumRefArgument(false));
System.out.println("getNumRefArgument(true): " + ph.getNumRefArgument(true));
System.out.println("getNumRefUndefined(false): " + ph.getNumRefUndefined(false));
System.out.println("getNumRefUndefined(true): " + ph.getNumRefUndefined(true));
System.out.println("Max include nesting is " + ph.getMaxIncludeDepth());
}
}