Skip to content

Commit

Permalink
[highsource#433] SimpleToString first pass with dep-free execution
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentschoelens committed Oct 20, 2023
1 parent 7085316 commit 6b41967
Show file tree
Hide file tree
Showing 17 changed files with 627 additions and 225 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.namespace.QName;

import org.jvnet.jaxb.lang.JAXBToStringStrategy;
import org.jvnet.jaxb.lang.ToString;
import org.jvnet.jaxb.lang.ToStringStrategy;
import org.jvnet.jaxb.locator.ObjectLocator;
import org.jvnet.jaxb.plugin.AbstractParameterizablePlugin;
import com.sun.codemodel.JType;
import com.sun.tools.xjc.model.CCustomizations;
import com.sun.tools.xjc.outline.Aspect;
import org.jvnet.jaxb.plugin.ComposedIgnoring;
import org.jvnet.jaxb.plugin.CustomizedIgnoring;
import org.jvnet.jaxb.plugin.Ignoring;
import org.jvnet.jaxb.plugin.codegenerator.AbstractCodeGeneratorPlugin;
import org.jvnet.jaxb.plugin.codegenerator.CodeGenerator;
import org.jvnet.jaxb.plugin.util.FieldOutlineUtils;
import org.jvnet.jaxb.plugin.util.StrategyClassUtils;
import org.jvnet.jaxb.util.ClassUtils;
import org.jvnet.jaxb.util.CustomizationUtils;
import org.jvnet.jaxb.util.FieldAccessorFactory;
import org.jvnet.jaxb.util.FieldUtils;
import org.jvnet.jaxb.util.PropertyFieldAccessorFactory;
import org.jvnet.jaxb.xjc.outline.FieldAccessorEx;
import org.xml.sax.ErrorHandler;

import com.sun.codemodel.JBlock;
import com.sun.codemodel.JCodeModel;
Expand All @@ -28,213 +32,155 @@
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JVar;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.FieldOutline;
import com.sun.tools.xjc.outline.Outline;

public class SimpleToStringPlugin extends AbstractParameterizablePlugin {

@Override
public String getOptionName() {
return "XsimpleToString";
}

@Override
public String getUsage() {
// TODO
return "TBD";
}

private FieldAccessorFactory fieldAccessorFactory = PropertyFieldAccessorFactory.INSTANCE;

public FieldAccessorFactory getFieldAccessorFactory() {
return fieldAccessorFactory;
}

public void setFieldAccessorFactory(
FieldAccessorFactory fieldAccessorFactory) {
this.fieldAccessorFactory = fieldAccessorFactory;
}

private String toStringStrategyClass = JAXBToStringStrategy.class.getName();

public void setToStringStrategyClass(String toStringStrategy) {
this.toStringStrategyClass = toStringStrategy;
}

public String getToStringStrategyClass() {
return toStringStrategyClass;
}

public JExpression createToStringStrategy(JCodeModel codeModel) {
return StrategyClassUtils.createStrategyInstanceExpression(codeModel,
ToStringStrategy.class, getToStringStrategyClass());
}

private Ignoring ignoring = new CustomizedIgnoring(
org.jvnet.jaxb.plugin.tostring.Customizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.tostring.LegacyCustomizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.Customizations.IGNORED_ELEMENT_NAME,
public class SimpleToStringPlugin extends
AbstractCodeGeneratorPlugin<ToStringArguments> {

@Override
public String getOptionName() {
return "XsimpleToString";
}

@Override
public String getUsage() {
// TODO
return "TBD";
}

private FieldAccessorFactory fieldAccessorFactory = PropertyFieldAccessorFactory.INSTANCE;

public FieldAccessorFactory getFieldAccessorFactory() {
return fieldAccessorFactory;
}

public void setFieldAccessorFactory(FieldAccessorFactory fieldAccessorFactory) {
this.fieldAccessorFactory = fieldAccessorFactory;
}

@Override
protected QName getSpecialIgnoredElementName() {
return org.jvnet.jaxb.plugin.tostring.Customizations.IGNORED_ELEMENT_NAME;
}

private Ignoring ignoring = new ComposedIgnoring(
logger,
new CustomizedIgnoring(
org.jvnet.jaxb.plugin.tostring.Customizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.Customizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.Customizations.GENERATED_ELEMENT_NAME),
new CustomizedIgnoring(
org.jvnet.jaxb.plugin.tostring.LegacyCustomizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.LegacyCustomizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.LegacyCustomizations.GENERATED_ELEMENT_NAME));

@Override
public Ignoring getIgnoring() {
return ignoring;
}

@Override
public void setIgnoring(Ignoring ignoring) {
this.ignoring = ignoring;
}

@Override
public Collection<QName> getCustomizationElementNames() {
return Arrays.asList(
org.jvnet.jaxb.plugin.tostring.Customizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.tostring.Customizations.MASKED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.tostring.Customizations.DATE_FORMAT_PATTERN,
org.jvnet.jaxb.plugin.tostring.LegacyCustomizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.Customizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.Customizations.GENERATED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.LegacyCustomizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.LegacyCustomizations.GENERATED_ELEMENT_NAME);

public Ignoring getIgnoring() {
return ignoring;
}

public void setIgnoring(Ignoring ignoring) {
this.ignoring = ignoring;
}

@Override
public Collection<QName> getCustomizationElementNames() {
return Arrays
.asList(org.jvnet.jaxb.plugin.tostring.Customizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.tostring.LegacyCustomizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.Customizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.Customizations.GENERATED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.LegacyCustomizations.IGNORED_ELEMENT_NAME,
org.jvnet.jaxb.plugin.LegacyCustomizations.GENERATED_ELEMENT_NAME);
}

@Override
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) {
for (final ClassOutline classOutline : outline.getClasses())
if (!getIgnoring().isIgnored(classOutline)) {
processClassOutline(classOutline);
}
return true;
}

protected void processClassOutline(ClassOutline classOutline) {
final JDefinedClass theClass = classOutline.implClass;
ClassUtils._implements(theClass, theClass.owner().ref(ToString.class));

@SuppressWarnings("unused")
final JMethod object$toString = generateObject$toString(classOutline,
theClass);
@SuppressWarnings("unused")
final JMethod toString$append = generateToString$append(classOutline,
theClass);
@SuppressWarnings("unused")
final JMethod toString$appendFields = generateToString$appendFields(
classOutline, theClass);
}

protected JMethod generateObject$toString(final ClassOutline classOutline,
final JDefinedClass theClass) {
final JCodeModel codeModel = theClass.owner();
final JMethod object$toString = theClass.method(JMod.PUBLIC,
codeModel.ref(String.class), "toString");
object$toString.annotate(Override.class);
{
final JBlock body = object$toString.body();

final JVar toStringStrategy =

body.decl(JMod.FINAL, codeModel.ref(ToStringStrategy.class),
"strategy", createToStringStrategy(codeModel));

final JVar buffer = body.decl(JMod.FINAL,
codeModel.ref(StringBuilder.class), "buffer",
JExpr._new(codeModel.ref(StringBuilder.class)));
body.invoke("append").arg(JExpr._null()).arg(buffer)
.arg(toStringStrategy);
body._return(buffer.invoke("toString"));
}
return object$toString;
}

protected JMethod generateToString$append(final ClassOutline classOutline,
final JDefinedClass theClass) {
final JCodeModel codeModel = theClass.owner();
final JMethod toString$append = theClass.method(JMod.PUBLIC,
codeModel.ref(StringBuilder.class), "append");
toString$append.annotate(Override.class);
{

final JVar locator = toString$append.param(ObjectLocator.class,
"locator");
final JVar buffer = toString$append.param(StringBuilder.class,
"buffer");
final JVar toStringStrategy = toString$append.param(
ToStringStrategy.class, "strategy");

final JBlock body = toString$append.body();

body.invoke(toStringStrategy, "appendStart").arg(locator)
.arg(JExpr._this()).arg(buffer);
body.invoke("appendFields").arg(locator).arg(buffer)
.arg(toStringStrategy);
body.invoke(toStringStrategy, "appendEnd").arg(locator)
.arg(JExpr._this()).arg(buffer);
body._return(buffer);
}
return toString$append;
}

protected JMethod generateToString$appendFields(ClassOutline classOutline,
final JDefinedClass theClass) {
final JCodeModel codeModel = theClass.owner();

final JMethod toString$appendFields = theClass.method(JMod.PUBLIC,
codeModel.ref(StringBuilder.class), "appendFields");
toString$appendFields.annotate(Override.class);
{
final JVar locator = toString$appendFields.param(
ObjectLocator.class, "locator");
final JVar buffer = toString$appendFields.param(
StringBuilder.class, "buffer");
final JVar toStringStrategy = toString$appendFields.param(
ToStringStrategy.class, "strategy");
final JBlock body = toString$appendFields.body();

final Boolean superClassImplementsToString = StrategyClassUtils
.superClassImplements(classOutline, ignoring,
ToString.class);

if (superClassImplementsToString == null) {
// No superclass
} else if (superClassImplementsToString.booleanValue()) {
body.invoke(JExpr._super(), "appendFields").arg(locator)
.arg(buffer).arg(toStringStrategy);
} else {
// Superclass does not implement ToString
}

final FieldOutline[] declaredFields = FieldOutlineUtils.filter(
classOutline.getDeclaredFields(), getIgnoring());

if (declaredFields.length > 0) {

for (final FieldOutline fieldOutline : declaredFields) {
final JBlock block = body.block();
final FieldAccessorEx fieldAccessor = getFieldAccessorFactory()
.createFieldAccessor(fieldOutline, JExpr._this());
final JVar theValue = block.decl(
fieldAccessor.getType(),
"the"
+ fieldOutline.getPropertyInfo().getName(
true));
final JExpression valueIsSet = (fieldAccessor.isAlwaysSet() || fieldAccessor
.hasSetValue() == null) ? JExpr.TRUE
: fieldAccessor.hasSetValue();

fieldAccessor.toRawValue(block, theValue);

block.invoke(toStringStrategy, "appendField")
.arg(locator)
.arg(JExpr._this())
.arg(JExpr.lit(fieldOutline.getPropertyInfo()
.getName(false))).arg(buffer).arg(theValue)
.arg(valueIsSet);
}
}
body._return(buffer);
}
return toString$appendFields;
}

}

@Override
protected CodeGenerator<ToStringArguments> createCodeGenerator(JCodeModel codeModel) {
return new ToStringCodeGenerator(codeModel);
}

@Override
protected void generate(ClassOutline classOutline, JDefinedClass theClass) {
final JCodeModel codeModel = theClass.owner();
final JMethod object$toString = theClass.method(JMod.PUBLIC,
codeModel.ref(String.class), "toString");
object$toString.annotate(Override.class);
{
final JBlock body = object$toString.body();

final JVar buffer = body.decl(JMod.FINAL,
codeModel.ref(StringBuilder.class), "buffer",
JExpr._new(codeModel.ref(StringBuilder.class)));

body.invoke(buffer, "append").arg(JExpr._this().invoke("getClass").invoke("getSimpleName"));
body.invoke(buffer, "append").arg("@");
body.invoke(buffer, "append").arg(codeModel.ref(Integer.class).staticInvoke("toHexString").arg(JExpr._this().invoke("hashCode")));
body.invoke(buffer, "append").arg("[");

Map<String, FieldOutline> mapDeclaredFields = new TreeMap<>();
for (final FieldOutline fieldOutline : FieldOutlineUtils.filter(classOutline.getDeclaredFields(), getIgnoring())) {
mapDeclaredFields.put(fieldOutline.getPropertyInfo().getName(false), fieldOutline);
}

Boolean superClassNotIgnored = StrategyClassUtils.superClassNotIgnored(classOutline, ignoring);
ClassOutline superClass = classOutline.getSuperClass();
while (Boolean.TRUE.equals(superClassNotIgnored) && superClass != null) {
for (final FieldOutline fieldOutline : FieldOutlineUtils.filter(superClass.getDeclaredFields(), getIgnoring())) {
mapDeclaredFields.putIfAbsent(fieldOutline.getPropertyInfo().getName(false), fieldOutline);
}
superClassNotIgnored = StrategyClassUtils.superClassNotIgnored(superClass, ignoring);
superClass = superClass.getSuperClass();
}

appendFieldsIn(mapDeclaredFields, classOutline, theClass, body, buffer);

body.invoke(buffer, "append").arg("]");

body._return(buffer.invoke("toString"));
}
}

private void appendFieldsIn(Map<String, FieldOutline> declaredFields, ClassOutline classOutline, JDefinedClass theClass, JBlock body, JVar buffer) {
final JCodeModel codeModel = theClass.owner();
if (declaredFields.size() > 0) {
Iterator<Map.Entry<String, FieldOutline>> fieldIterator = declaredFields.entrySet().iterator();
while (fieldIterator.hasNext()) {
Map.Entry<String, FieldOutline> entryFieldOutline = fieldIterator.next();
final String privateFieldName = entryFieldOutline.getKey();
final FieldOutline fieldOutline = entryFieldOutline.getValue();
final JBlock block = body.block();
final FieldAccessorEx fieldAccessor = getFieldAccessorFactory().createFieldAccessor(fieldOutline, JExpr._this());
// declare var and affect it with field value
final JVar theValue = block.decl(
fieldAccessor.getType(),
"the" + fieldOutline.getPropertyInfo().getName(true));
fieldAccessor.toRawValue(block, theValue);
final JType exposedType = fieldAccessor.getType();

final Collection<JType> possibleTypes = FieldUtils
.getPossibleTypes(fieldOutline, Aspect.EXPOSED);
final boolean isAlwaysSet = fieldAccessor.isAlwaysSet();

final JExpression hasSetValue = (fieldAccessor.isAlwaysSet() || fieldAccessor
.hasSetValue() == null) ? JExpr.TRUE
: fieldAccessor.hasSetValue();

CCustomizations customizations = CustomizationUtils.getCustomizations(fieldOutline);
getCodeGenerator().generate(
block,
exposedType,
possibleTypes,
isAlwaysSet,
new ToStringArguments(codeModel, classOutline, buffer,
privateFieldName, theValue, hasSetValue, customizations));
if (fieldIterator.hasNext()) {
block.invoke(buffer, "append").arg(ToStringCodeGenerationImplementor.FIELD_SEPARATOR);
}
}
}
}
}
Loading

0 comments on commit 6b41967

Please sign in to comment.