NameMappingWorker.java
/*
** Module : NameMappingWorker.java
** Abstract : maps Progress source names to Java class names
**
** Copyright (c) 2006-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
** 001 GES 20060201 @24183 Created initial version. Builds up maps
** which it uses to initialize and operate
** the SourceNameMapper runtime class.
** 002 GES 20060313 @25047 Some helpers to facilitate incremental name
** map processing (where only a subset of files
** can be processed at a time but they can
** still resolve classnames for files that were
** previously converted.
** 003 GES 20060319 @25113 Added helpers for external procedure
** signature processing.
** 004 GES 20101111 Pass conversion-time cfg values to convertName().
** 005 GES 20111213 resolveJavaClass returns null instead of throwing
** exception if the class was not found (4GL allows
** missing program name for RUN statements).
** 006 CA 20121220 Changes caused by SourceNameMapper API changes.
** 007 CS 20130304 Added classNameExists function which checks if a
** class with the specified name exists in the given
** package.
** 008 EVL 20130530 Removing propath member from class and overwriteMappingData() call.
** The method resolveJavaClass() has also been removed because it is
** never used anymore.
** 009 CA 20200412 Added incremental conversion support.
** 010 VVT 20240826 SourceNameMapper.convertJavaProg() renamed. See #8613-16.
*/
/*
** 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.convert;
import java.io.*;
import java.util.*;
import java.util.function.*;
import org.w3c.dom.*;
import com.goldencode.p2j.cfg.*;
import com.goldencode.p2j.pattern.*;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.xml.*;
import com.goldencode.util.*;
/**
* Gathers the mappings of Progress source names to Java class names and then
* uses these maps to initialize and operate the <code>SourceNameMapper</code>
* runtime class. This allows <code>RUN FILENAME</code> constructs to be
* mapped into the correct target classname.
* <p>
* This class only gathers the minimum data required to operate the
* <code>convertName</code> method of the <code>SourceNameMapper</code>
* class. This minimizes the work and code needed. In the future, it
* would be easily possible to gather the method level data which would
* allow additional Progress procedure/function to Java method mapping.
*/
public class NameMappingWorker
extends AbstractPatternWorker
{
/** Base path for all Java packages. */
private String pkgroot = null;
/** Base path for 4GL code. */
private String proroot = null;
/** Name element separator used for the Progress file name data. */
private String fileSep = null;
/** Path separator used for the Progress file name data. */
private String pathSep = null;
/** Case sensitivity flag for the Progress file name data. */
private boolean caseSens;
/** Flag to remember if we have initialized the source name mapper. */
private boolean initialized = false;
/**
* The in-memory mappings for all programs (previously converted or not). For incremental
* conversion, this will load an existing name_map.xml file.
*/
private Map<String, ProgramInfo> programs = new LinkedHashMap<>();
/**
* Default constructor which defines the symbol libraries to be registered.
*/
public NameMappingWorker()
{
super();
setLibrary(new Library());
// query base config values
pkgroot = Configuration.getParameter("pkgroot", "");
proroot = Configuration.getParameter("basepath", "./");
fileSep = Configuration.getParameter("file-separator", "/");
pathSep = Configuration.getParameter("path-separator", ":");
String cs = Configuration.getParameter("case-sensitive", "true");
caseSens = cs.equalsIgnoreCase("true");
}
/**
* Class whose instance is actually registered with the pattern engine's
* symbol resolver as the primary library of name mapping user
* functions.
*/
public class Library
{
/**
* Create a new converted program entry, with the given legacy and converted name.
*
* @param pname
* The legacy name.
* @param jname
* The converted name.
*
* @return A new {@link ProgramInfo} instance.
*/
public ProgramInfo createProgramInfo(String pname, String jname)
{
ProgramInfo pinfo = new ProgramInfo();
pinfo.attributes.put("pname", pname);
pinfo.attributes.put("jname", jname);
programs.put(pname, pinfo);
return pinfo;
}
/**
* Put an attribute with the given name and value for the specified method.
*
* @param info
* The method information object.
* @param name
* The attribute name.
* @param value
* The attribute value.
*/
public void putAttribute(MethodInfo info, String name, String value)
{
info.attributes.put(name, value);
}
/**
* Add a new converted method entry, with the given legacy and converted name, to the given
* program.
*
* @param parent
* The method's external program owner.
* @param pname
* The legacy name.
* @param jname
* The converted name.
*
* @return A new {@link MethodInfo} instance.
*/
public MethodInfo addMethod(ProgramInfo parent, String pname, String jname)
{
MethodInfo minfo = new MethodInfo();
parent.methods.add(minfo);
minfo.attributes.put("pname", pname);
minfo.attributes.put("jname", jname);
return minfo;
}
/**
* Add a new parameter to the given method (which can be an external program).
*
* @param parent
* The parameter's method owner.
* @param pname
* The legacy name.
* @param jname
* The converted name.
*
* @return A map to hold the parameter's attributes.
*/
public Map<String, String> addParameter(MethodInfo parent, String pname, String jname)
{
Map<String, String> param = new LinkedHashMap<>();
param.put("pname", pname);
param.put("jname", jname);
parent.parameters.add(param);
return param;
}
/**
* Add a new empty parameter to the given method (which can be an external program).
*
* @param parent
* The parameter's method owner.
*
* @return A map to hold the parameter's attributes.
*/
public Map<String, String> addParameter(MethodInfo parent)
{
Map<String, String> param = new LinkedHashMap<>();
parent.parameters.add(param);
return param;
}
/**
* Given a root node, store all the mappings from the {@link NameMappingWorker#programs} map.
*
* @param root
* The root element.
*/
public void storeMappings(XmlAst root)
{
BiConsumer<XmlAst, Map<String, String>> storeAttributes = (parent, m) ->
{
for (String name : m.keySet())
{
String value = m.get(name);
XmlAst elAttr = (XmlAst) XmlPatternWorker.createAst(XmlTokenTypes.ATTRIBUTE_NODE,
name,
parent);
XmlPatternWorker.createAst(XmlTokenTypes.CONTENT, value, elAttr);
}
};
BiConsumer<XmlAst, List<Map<String, String>>> storeParameters = (parent, l) ->
{
for (Map<String, String> params : l)
{
XmlAst elParam = (XmlAst) XmlPatternWorker.createAst(XmlTokenTypes.ELEMENT_NODE,
"parameter",
parent);
storeAttributes.accept(elParam, params);
}
};
for (String prog : programs.keySet())
{
ProgramInfo pinfo = programs.get(prog);
XmlAst elClass = (XmlAst) XmlPatternWorker.createAst(XmlTokenTypes.ELEMENT_NODE,
"class-mapping",
root);
storeAttributes.accept(elClass, pinfo.attributes);
storeParameters.accept(elClass, pinfo.parameters);
for (MethodInfo minfo : pinfo.methods)
{
XmlAst elMthd = (XmlAst) XmlPatternWorker.createAst(XmlTokenTypes.ELEMENT_NODE,
"method-mapping",
elClass);
storeAttributes.accept(elMthd, minfo.attributes);
storeParameters.accept(elMthd, minfo.parameters);
}
}
}
/**
* Given a filename with the XML structure with all the name mappings, load it in the
* {@link NameMappingWorker#programs} map.
*
* @param filename
* The file from which to read the name mappings.
*/
public void restoreMappings(String filename)
throws Exception
{
BiConsumer<Element, Map<String, String>> restoreAttributes = (el, m) ->
{
NamedNodeMap attrs = el.getAttributes();
for (int j = 0; j < attrs.getLength(); j++)
{
Node attr = attrs.item(j);
String attrName = attr.getNodeName();
String attrVal = attr.getNodeValue();
m.put(attrName, attrVal);
}
};
BiConsumer<Element, List<Map<String, String>>> restoreParameters = (el, l) ->
{
NodeList cparams = el.getElementsByTagName("parameter");
for (int j = 0; j < cparams.getLength(); j++)
{
Element param = (Element) cparams.item(j);
// keep only parameters which belong to the given node
if (param.getParentNode() != el)
{
continue;
}
Map<String, String> pattrs = new LinkedHashMap<>();
restoreAttributes.accept(param, pattrs);
l.add(pattrs);
}
};
FileInputStream fis = new FileInputStream(filename);
try
{
Document dom = XmlHelper.parse(fis);
// find the root node
Element root = dom.getDocumentElement();
NodeList cmaps = root.getElementsByTagName("class-mapping");
for (int i = 0; i < cmaps.getLength(); i++)
{
Element classMap = (Element) cmaps.item(i);
ProgramInfo pinfo = new ProgramInfo();
restoreAttributes.accept(classMap, pinfo.attributes);
restoreParameters.accept(classMap, pinfo.parameters);
programs.put(pinfo.attributes.get("pname"), pinfo);
// child method-mapping nodes
NodeList cmethods = classMap.getElementsByTagName("method-mapping");
for (int j = 0; j < cmethods.getLength(); j++)
{
Element method = (Element) cmethods.item(j);
MethodInfo minfo = new MethodInfo();
pinfo.methods.add(minfo);
restoreAttributes.accept(method, minfo.attributes);
restoreParameters.accept(method, minfo.parameters);
}
}
}
finally
{
fis.close();
}
}
/**
* Utility function which checks if a class name is registered in the given package location
* as a converted class.
*
* @param pkg
* The package in which we want to check if there is any class by the given name.
* @param name
* The class name we want to check for existence.
*
* @return <code>true</code> if a class with that name exists in the given
* package or <code>false</code> otherwise.
*/
public boolean classNameExists(String pkg, String name)
{
name = pkg + "." + name;
return (SourceNameMapper.getLegacySourceName(pkgroot, name) != null);
}
}
/**
* A simple class which holds a list of <code>class-mapping</code> information, from a
* name_map.xml file.
* <p>
* Each instance is implied to be the external program 'method'
*/
public static class ProgramInfo
extends MethodInfo
{
/** The list of method information. */
private List<MethodInfo> methods = new ArrayList<>();
}
/**
* A simple class which holds a list of <code>method-mapping</code> information, for a certain
* program, as it appears in a name_map.xml file.
*/
public static class MethodInfo
{
/** The method mapping attributes. */
protected Map<String, String> attributes = new LinkedHashMap<>();
/** The method's parameters (each with a map of XML attributes). */
protected List<Map<String, String>> parameters = new ArrayList<>();
}
}