SupportLevelDocumentationGenerator.java
/*
** Module : SupportLevelDocumentationGenerator.java
** Abstract : Generates textile wiki syntax to describe the support levels of 4GL features.
**
** Copyright (c) 2021-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- --------------------------------------Description----------------------------------------
** 001 GES 20210214 First version. Handles OO built-in classes/enums/interfaces, methods, events and
** properties.
** TJD 20220504 Java 11 compatibility minor changes
** 002 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.uast;
import java.lang.reflect.*;
import java.util.*;
import java.util.logging.*;
import com.goldencode.p2j.convert.*;
import org.reflections.*;
import com.goldencode.p2j.oo.lang.*;
import com.goldencode.p2j.report.*;
import com.goldencode.p2j.report.SupportLevelHelper.Descriptor;
import com.goldencode.p2j.util.*;
/**
* Generate textile wiki syntax to describe the support levels of 4GL features.
*/
public class SupportLevelDocumentationGenerator
implements ReportConstants
{
/** Logger. */
private static final ConversionStatus LOG = ConversionStatus.get(SupportLevelDocumentationGenerator.class);
/** Wrapper type package qualifier. */
private static final String WRAPPER_TYPE_PREFIX = "com.goldencode.p2j.util.";
/** Length of the wrapper type package qualifier. */
private static final int WRAPPER_PREFIX_SIZE = WRAPPER_TYPE_PREFIX.length();
/** Object wrapper type prefix. */
private static final String OBJECT_PREFIX = "object<? extends ";
/** Length of the object prefix. */
private static final int OBJECT_PREFIX_SIZE = OBJECT_PREFIX.length();
/** Object wrapper type prefix. */
private static final String ALT_OBJECT_PREFIX = "object<";
/** Length of the object prefix. */
private static final int ALT_OBJECT_PREFIX_SIZE = ALT_OBJECT_PREFIX.length();
/**
* Calculate the legacy type from the Java type.
*
* @param jtype
* The Java data type.
*
* @return The legacy 4GL type.
*/
public static String reverseType(String jtype, Map<String, String> map)
{
boolean wrapper = false;
String ptype = jtype;
int brackets = jtype.indexOf('[');
// remove the square brackets
if (brackets != -1)
{
jtype = jtype.substring(0, brackets);
}
if (jtype.startsWith(WRAPPER_TYPE_PREFIX))
{
wrapper = true;
ptype = jtype.substring(WRAPPER_PREFIX_SIZE);
}
if (ptype.startsWith("object"))
{
if (ptype.startsWith(OBJECT_PREFIX))
{
ptype = ptype.substring(OBJECT_PREFIX_SIZE, ptype.length() - 1);
}
if (ptype.startsWith(ALT_OBJECT_PREFIX))
{
ptype = ptype.substring(ALT_OBJECT_PREFIX_SIZE, ptype.length() - 1);
}
if (map.containsKey(ptype))
{
ptype = map.get(ptype);
}
}
else if (wrapper)
{
// non-object wrapper, nothing else to do
}
else if ("java.lang.Object".equals(ptype))
{
ptype = "POLYMORPHIC";
}
else if ("void".equals(ptype))
{
// nothing to do, we're good to go
}
else
{
LOG.log(Level.SEVERE, String.format("Cannot decode legacy type from %s!", jtype));
}
return ptype;
}
/**
* Describe the list of 4GL parameters as a text in the form "mode data_type(, mode datatype)*".
*
* @param parms
* The parameter list.
*
* @return The formatted descriptor.
*/
public static String render(LegacyParameter[] parms)
{
String result = "";
for (LegacyParameter p : parms)
{
String item = p.mode().toLowerCase() + " " + readType(p);
result += result.isEmpty() ? item : ", " + item;
}
return result;
}
/**
* Describe the list of Java parameters as a text in the form "data_type(, datatype)*".
*
* @param parms
* The parameter list.
*
* @return The formatted descriptor.
*/
public static String render(Type[] parms)
{
String result = "";
for (Type p : parms)
{
result += result.isEmpty() ? p.getTypeName() : ", " + p.getTypeName();
}
return result;
}
/**
* Describe the given data type, objects are reported as their qualified type names.
*
* @param p
* The parameter to inspect.
*
* @return The text name for the type.
*/
public static String readType(LegacyParameter p)
{
String type = p.type().toLowerCase();
return "object".equals(type) ? p.qualified() : type;
}
/**
* Generate the textile syntax for a table cell contents which describes a support level with the
* proper coloring.
*
* @param lvl
* The support level to describe.
*
* @return The wiki syntax for the given support level.
*/
public static String formatSupportLvl(SupportLevelHelper.Descriptor lvl)
{
return String.format("{background:%s;color:%s}. %s",
Descriptor.asRGBConstant(lvl.bg),
Descriptor.asRGBConstant(lvl.fg),
lvl.name);
}
/**
* Create a mapping of fully qualified Java class names to fully qualified legacy class names.
*
* @param legacyClasses
* All classes with legacy annotations.
*
* @return The mapping.
*/
public static Map<String, String> initNameLookup(Set<Class<? extends _BaseObject_>> legacyClasses)
{
Map<String, String> nameLookup = new HashMap<>();
for (Class<? extends _BaseObject_> cls : legacyClasses)
{
LegacyResource lr = cls.getAnnotation(LegacyResource.class);
if (lr == null)
{
LOG.log(Level.WARNING, "class " + cls + " doesn't have a LegacyResource annotation!");
}
else
{
String clsName = cls.getName();
if (cls == BaseObject.class)
{
clsName = _BaseObject_.class.getName();
}
// System.err.printf("MAP %s to %s\n", clsName, lr.resource());
nameLookup.put(clsName, lr.resource());
}
}
return nameLookup;
}
/**
* Command line interface to execute the document generation. WARNINGS and ERRORS are written to
* STDERR and the textile wiki content is written to STDOUT. Redirect these accordingly to capture
* the output. No parameters are needed on the command line.
*
* @param args
* Ignored.
*/
public static void main(String[] args)
{
Reflections reflections = new Reflections("com.goldencode.p2j.oo");
Set<Class<? extends _BaseObject_>> legacyClasses = reflections.getSubTypesOf(_BaseObject_.class);
Map<String, String> nameLookup = initNameLookup(legacyClasses);
System.out.println("|_\\2. Resource Type |_. Legacy Class |_. Java Class |_. Conversion Support |_. Runtime Support |");
for (Class<? extends _BaseObject_> cls : legacyClasses)
{
LegacyResourceSupport lrs = cls.getAnnotation(LegacyResourceSupport.class);
if (lrs == null)
{
LOG.log(Level.WARNING, "class " + cls + " doesn't have a LegacyResourceSupport annotation!");
}
else
{
String clsName = cls.getName();
if (cls == BaseObject.class)
{
clsName = _BaseObject_.class.getName();
}
boolean iface = cls.isInterface();
boolean lenum = com.goldencode.p2j.oo.lang.LegacyEnum.class.isAssignableFrom(cls);
String ooType = iface ? "INTERFACE" : (lenum ? "ENUM" : "CLASS");
String legacyName = nameLookup.get(clsName);
int lastDot = legacyName.lastIndexOf('.');
String baseLegacyClass = lastDot == -1 ? legacyName : legacyName.substring(lastDot + 1);
int lvl = lrs.supportLvl();
SupportLevelHelper.Descriptor cvtLvl = SupportLevelHelper.describe(lvl & CVT_LVL_MASK);
SupportLevelHelper.Descriptor rtLvl = SupportLevelHelper.describe(lvl & RT_LVL_MASK);
System.out.printf("|\\2. %s | %s | %s |%s |%s |\n", ooType, legacyName, clsName, formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
// print enum members
if (lenum)
{
for (Field f : cls.getDeclaredFields())
{
LegacySignature vs = f.getAnnotation(LegacySignature.class);
if (vs != null)
{
if (vs.type() == InternalEntry.Type.VARIABLE)
{
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | MEMBER | %s | %s |{background:#009900;color:#FFFFFF}. Full |{background:#009900;color:#FFFFFF}. Full |\n", vs.name(), f.getName());
}
}
}
}
// check the method names against NameConverter
for (Method m : cls.getDeclaredMethods())
{
LegacySignature ls = m.getAnnotation(LegacySignature.class);
LegacyResourceSupport supp = m.getAnnotation(LegacyResourceSupport.class);
if (ls != null)
{
if (supp == null)
{
LOG.log(Level.WARNING,
"method " + m.getName() + " of class " + cls +
" doesn't have a LegacyResourceSupport annotation!");
}
else
{
lvl = supp.supportLvl();
cvtLvl = SupportLevelHelper.describe(lvl & CVT_LVL_MASK);
rtLvl = SupportLevelHelper.describe(lvl & RT_LVL_MASK);
String jretType = m.getGenericReturnType().getTypeName();
String retType = reverseType(jretType, nameLookup);
LegacyParameter[] lparms = ls.parameters();
Type[] jparms = m.getGenericParameterTypes();
String typeDescr = ls.type().toString();
int extent = ls.extent();
String extentDescr = "";
if (extent != -2)
{
extentDescr = (extent == -1) ? "[]" : String.format("[%d]", extent);
}
switch (ls.type())
{
case METHOD:
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | %s | %s %s(%s) | %s %s(%s) |%s |%s |\n", typeDescr, retType, ls.name(), render(lparms), jretType, m.getName(), render(jparms), formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
break;
case GETTER:
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | PROPERTY GETTER | %s : %s%s | %s %s(%s) |%s |%s |\n", ls.name(), retType, extentDescr, jretType, m.getName(), render(jparms), formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
break;
case SETTER:
retType = reverseType(jparms[0].getTypeName(), nameLookup);
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | PROPERTY SETTER | %s : %s%s | %s %s(%s) |%s |%s |\n", ls.name(), retType, extentDescr, jretType, m.getName(), render(jparms), formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
break;
case RESIZE:
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | PROPERTY | @EXTENT(%s) = <int64>@ | %s %s(%s) |%s |%s |\n", ls.name(), jretType, m.getName(), render(jparms), formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
break;
case LENGTH:
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | PROPERTY | @integer EXTENT(%s)@ function | %s %s(%s) |%s |%s |\n", ls.name(), jretType, m.getName(), render(jparms), formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
break;
case CONSTRUCTOR:
case DESTRUCTOR:
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | %s | %s(%s) | %s(%s) |%s |%s |\n", typeDescr, baseLegacyClass, render(lparms), m.getName(), render(jparms), formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
break;
case PUBLISH:
case SUBSCRIBE:
case UNSUBSCRIBE:
typeDescr = "EVENT " + typeDescr;
System.out.printf("|{background:lightslategrey;color:lightslategrey}. ..... | %s | %s %s(%s) | %s %s(%s) |%s |%s |\n", typeDescr, retType, ls.name(), render(lparms), jretType, m.getName(), render(jparms), formatSupportLvl(cvtLvl), formatSupportLvl(rtLvl));
break;
default:
LOG.log(Level.SEVERE,
String.format(" UNKNOWN %s %s %s(%s) CVT %s RT %s (Java %s %s(%s))",
typeDescr,
retType,
ls.name(),
render(lparms),
cvtLvl.name,
rtLvl.name,
jretType,
m.getName(),
render(jparms)));
}
}
}
}
}
}
}
}