forked from highsource/jaxb-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[highsource#433] SimpleToString with dep-free execution
- Loading branch information
1 parent
7085316
commit febc5b0
Showing
19 changed files
with
713 additions
and
225 deletions.
There are no files selected for viewing
394 changes: 184 additions & 210 deletions
394
...jaxb-plugins/src/main/java/org/jvnet/jaxb/plugin/simpletostring/SimpleToStringPlugin.java
Large diffs are not rendered by default.
Oops, something went wrong.
136 changes: 136 additions & 0 deletions
136
...nt/jaxb-plugins/src/main/java/org/jvnet/jaxb/plugin/simpletostring/ToStringArguments.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package org.jvnet.jaxb.plugin.simpletostring; | ||
|
||
import com.sun.codemodel.JBlock; | ||
import com.sun.codemodel.JCodeModel; | ||
import com.sun.codemodel.JConditional; | ||
import com.sun.codemodel.JExpr; | ||
import com.sun.codemodel.JExpression; | ||
import com.sun.codemodel.JMod; | ||
import com.sun.codemodel.JType; | ||
import com.sun.codemodel.JVar; | ||
import com.sun.tools.xjc.model.CCustomizations; | ||
import com.sun.tools.xjc.outline.ClassOutline; | ||
import org.apache.commons.lang3.Validate; | ||
import org.jvnet.jaxb.plugin.codegenerator.Arguments; | ||
|
||
import java.util.Collection; | ||
import java.util.ListIterator; | ||
|
||
public class ToStringArguments implements Arguments<ToStringArguments> { | ||
|
||
private final JCodeModel codeModel; | ||
private final ClassOutline classOutline; | ||
private final JVar buffer; | ||
private final String fieldName; | ||
private final JVar value; | ||
private final JExpression hasSetValue; | ||
private final CCustomizations customizations; | ||
|
||
public ToStringArguments(JCodeModel codeModel, ClassOutline classOutline, JVar buffer, String fieldName, JVar value, JExpression hasSetValue, CCustomizations customizations) { | ||
this.codeModel = Validate.notNull(codeModel); | ||
this.classOutline = Validate.notNull(classOutline); | ||
this.buffer = Validate.notNull(buffer); | ||
this.fieldName = Validate.notNull(fieldName); | ||
this.value = Validate.notNull(value); | ||
this.hasSetValue = Validate.notNull(hasSetValue); | ||
this.customizations = customizations; | ||
} | ||
|
||
private JCodeModel getCodeModel() { | ||
return codeModel; | ||
} | ||
|
||
public JVar buffer() { | ||
return buffer; | ||
} | ||
|
||
public ClassOutline classOutline() { | ||
return classOutline; | ||
} | ||
|
||
public String fieldName() { | ||
return fieldName; | ||
} | ||
|
||
public JVar value() { | ||
return value; | ||
} | ||
|
||
public JExpression hasSetValue() { | ||
return hasSetValue; | ||
} | ||
|
||
public CCustomizations customizations() { | ||
return customizations; | ||
} | ||
|
||
private ToStringArguments spawn(String fieldName, JVar value, JExpression hasSetValue, CCustomizations customizations) { | ||
return new ToStringArguments(getCodeModel(), classOutline(), buffer(), fieldName, value, hasSetValue, customizations); | ||
} | ||
|
||
public ToStringArguments property(JBlock block, String propertyName, | ||
String propertyMethod, JType declarablePropertyType, | ||
JType propertyType, Collection<JType> possiblePropertyTypes) { | ||
final JVar propertyValue = block.decl(JMod.FINAL, | ||
declarablePropertyType, value().name() + propertyName, value().invoke(propertyMethod)); | ||
// We assume that primitive properties are always set | ||
boolean isAlwaysSet = propertyType.isPrimitive(); | ||
final JExpression propertyHasSetValue = isAlwaysSet ? JExpr.TRUE : propertyValue.ne(JExpr._null()); | ||
return spawn(propertyName, propertyValue, propertyHasSetValue, null); | ||
} | ||
|
||
public ToStringArguments iterator(JBlock block, JType elementType) { | ||
final JVar listIterator = block | ||
.decl(JMod.FINAL, getCodeModel().ref(ListIterator.class) | ||
.narrow(elementType), value().name() + "ListIterator", | ||
value().invoke("listIterator")); | ||
|
||
return spawn(fieldName(), listIterator, JExpr.TRUE, customizations()); | ||
} | ||
|
||
public ToStringArguments element(JBlock subBlock, JType elementType) { | ||
final JVar elementValue = subBlock.decl(JMod.FINAL, elementType, | ||
value().name() + "Element", value().invoke("next")); | ||
final boolean isElementAlwaysSet = elementType.isPrimitive(); | ||
final JExpression elementHasSetValue = isElementAlwaysSet ? JExpr.TRUE | ||
: elementValue.ne(JExpr._null()); | ||
return spawn(fieldName(), elementValue, elementHasSetValue, customizations()); | ||
|
||
} | ||
|
||
public JExpression _instanceof(JType type) { | ||
return value()._instanceof(type); | ||
} | ||
|
||
public ToStringArguments cast(String suffix, JBlock block, | ||
JType jaxbElementType, boolean suppressWarnings) { | ||
final JVar castedValue = block.decl(JMod.FINAL, jaxbElementType, | ||
value().name() + suffix, JExpr.cast(jaxbElementType, value())); | ||
if (suppressWarnings) { | ||
castedValue.annotate(SuppressWarnings.class).param("value", "unchecked"); | ||
} | ||
return spawn(fieldName(), castedValue, JExpr.TRUE, customizations()); | ||
} | ||
|
||
public JBlock ifHasSetValue(JBlock block, boolean isAlwaysSet, boolean checkForNullRequired) { | ||
if (isAlwaysSet || !checkForNullRequired) { | ||
return block; | ||
} else { | ||
return block._if(hasSetValue())._then(); | ||
} | ||
} | ||
|
||
public JConditional ifConditionHasSetValue(JBlock block, boolean isAlwaysSet, boolean checkForNullRequired) { | ||
if (isAlwaysSet || !checkForNullRequired) { | ||
return null; | ||
} else { | ||
return block._if(hasSetValue()); | ||
} | ||
} | ||
|
||
public JBlock _while(JBlock block) { | ||
final JBlock subBlock = block._while(value().invoke("hasNext")).body(); | ||
return subBlock; | ||
} | ||
|
||
} |
200 changes: 200 additions & 0 deletions
200
...src/main/java/org/jvnet/jaxb/plugin/simpletostring/ToStringCodeGenerationImplementor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
package org.jvnet.jaxb.plugin.simpletostring; | ||
|
||
import com.sun.codemodel.JBlock; | ||
import com.sun.codemodel.JClass; | ||
import com.sun.codemodel.JCodeModel; | ||
import com.sun.codemodel.JConditional; | ||
import com.sun.codemodel.JExpr; | ||
import com.sun.codemodel.JExpression; | ||
import com.sun.codemodel.JFieldVar; | ||
import com.sun.codemodel.JMod; | ||
import com.sun.tools.xjc.model.CCustomizations; | ||
import com.sun.tools.xjc.model.CPluginCustomization; | ||
import org.jvnet.jaxb.plugin.codegenerator.AbstractCodeGenerationImplementor; | ||
import org.jvnet.jaxb.plugin.tostring.Customizations; | ||
import org.jvnet.jaxb.plugin.tostring.DateFormatClass; | ||
import org.jvnet.jaxb.util.CustomizationUtils; | ||
|
||
import javax.xml.datatype.XMLGregorianCalendar; | ||
import java.time.ZoneId; | ||
import java.time.ZonedDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
import java.time.temporal.Temporal; | ||
import java.util.Arrays; | ||
import java.util.Calendar; | ||
import java.util.Date; | ||
import java.util.Map; | ||
import java.util.TreeMap; | ||
|
||
public class ToStringCodeGenerationImplementor extends | ||
AbstractCodeGenerationImplementor<ToStringArguments> { | ||
|
||
private static final JExpression EQ_EXPR = JExpr.lit("="); | ||
private static final JExpression NULL_EXPR = JExpr.lit("<null>"); | ||
private static final JExpression MASKED_EXPR = JExpr.lit("****"); | ||
public static final String FIELD_SEPARATOR = ", "; | ||
public static final Map<String, String> DATE_TIME_FORMATTER_BY_PATTERN = new TreeMap<>(); | ||
public int dateTimeFormatterIndex = 0; | ||
private final String defaultDateFormatterRef; | ||
private final String defaultDateFormatterPattern; | ||
public ToStringCodeGenerationImplementor(JCodeModel codeModel, String defaultDateFormatterRef, String defaultDateFormatterPattern) { | ||
super(codeModel); | ||
this.defaultDateFormatterRef = defaultDateFormatterRef; | ||
this.defaultDateFormatterPattern = defaultDateFormatterPattern; | ||
} | ||
|
||
private void ifHasSetValueAppendToStringElseAppendNull( | ||
ToStringArguments arguments, JBlock block, | ||
JExpression valueToString, boolean isAlwaysSet, | ||
boolean checkForNullRequired) { | ||
block.invoke(arguments.buffer(), "append").arg(arguments.fieldName()); | ||
block.invoke(arguments.buffer(), "append").arg(EQ_EXPR); | ||
|
||
JConditional conditionalHasSetValue = arguments.ifConditionHasSetValue(block, isAlwaysSet, checkForNullRequired); | ||
CCustomizations customizations = arguments.customizations(); | ||
|
||
// date / temporal specific treatments | ||
if (arguments.value().type() instanceof JClass) { | ||
valueToString = handlePossibleDateField(arguments, valueToString); | ||
} | ||
boolean isMasked = false; | ||
CPluginCustomization maskedCustomization = customizations == null ? null | ||
: customizations.find(Customizations.MASKED_ELEMENT_NAME.getNamespaceURI(), Customizations.MASKED_ELEMENT_NAME.getLocalPart()); | ||
|
||
if (maskedCustomization != null) { | ||
isMasked = true; | ||
maskedCustomization.markAsAcknowledged(); | ||
} | ||
if (conditionalHasSetValue == null) { | ||
block.invoke(arguments.buffer(), "append").arg(isMasked ? MASKED_EXPR : valueToString); | ||
} else { | ||
conditionalHasSetValue._then().invoke(arguments.buffer(), "append").arg(isMasked ? MASKED_EXPR : valueToString); | ||
conditionalHasSetValue._else().invoke(arguments.buffer(), "append").arg(NULL_EXPR); | ||
} | ||
} | ||
|
||
private JExpression handlePossibleDateField(ToStringArguments arguments, JExpression valueToString) { | ||
boolean isDate = getCodeModel().ref(Date.class).isAssignableFrom((JClass) arguments.value().type()); | ||
boolean isCalendar = getCodeModel().ref(Calendar.class).isAssignableFrom(((JClass) arguments.value().type())); | ||
boolean isXMLCalendar = getCodeModel().ref(XMLGregorianCalendar.class).isAssignableFrom(((JClass) arguments.value().type())); | ||
boolean isTemporal = getCodeModel().ref(Temporal.class).isAssignableFrom(((JClass) arguments.value().type())); | ||
if (isDate || isCalendar || isXMLCalendar || isTemporal) { | ||
CCustomizations customizations = arguments.customizations(); | ||
CPluginCustomization formatDateCustomization = customizations == null ? null | ||
: customizations.find(Customizations.DATE_FORMAT_PATTERN.getNamespaceURI(), Customizations.DATE_FORMAT_PATTERN.getLocalPart()); | ||
if (formatDateCustomization != null || defaultDateFormatterRef != null || defaultDateFormatterPattern != null) { | ||
JExpression defaultExpr = arguments.value(); | ||
if (isXMLCalendar) { | ||
defaultExpr = defaultExpr.invoke("toGregorianCalendar"); | ||
} | ||
if (!isTemporal) { | ||
defaultExpr = getCodeModel().ref(ZonedDateTime.class) | ||
.staticInvoke("ofInstant") | ||
.arg(defaultExpr.invoke("toInstant")) | ||
.arg(getCodeModel().ref(ZoneId.class).staticInvoke("systemDefault")); | ||
} | ||
DateFormatClass dateFormatClass = formatDateCustomization == null ? | ||
null : (DateFormatClass) CustomizationUtils.unmarshall(Customizations.getContext(), formatDateCustomization); | ||
String formatRef = dateFormatClass == null ? defaultDateFormatterRef : dateFormatClass.getFormatRef(); | ||
String format = dateFormatClass == null ? defaultDateFormatterPattern : dateFormatClass.getFormat(); | ||
if (formatRef != null) { | ||
try { | ||
// validate the ref | ||
DateTimeFormatter.class.getField(formatRef); | ||
valueToString = getCodeModel().ref(DateTimeFormatter.class).staticRef(formatRef) | ||
.invoke("format").arg(defaultExpr); | ||
if (formatDateCustomization != null) { | ||
formatDateCustomization.markAsAcknowledged(); | ||
} | ||
} catch (NoSuchFieldException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} else if (format != null) { | ||
// validate the pattern | ||
DateTimeFormatter.ofPattern(format); | ||
String staticFieldName = DATE_TIME_FORMATTER_BY_PATTERN.get(format); | ||
if (staticFieldName == null) { | ||
staticFieldName = "DATE_TIME_FORMATTER_" + dateTimeFormatterIndex++; | ||
DATE_TIME_FORMATTER_BY_PATTERN.put(format, staticFieldName); | ||
} | ||
JFieldVar field = arguments.classOutline().ref.fields().get(staticFieldName); | ||
if (field == null) { | ||
field = arguments.classOutline().ref.field(JMod.STATIC | JMod.FINAL | JMod.PRIVATE, | ||
getCodeModel().ref(DateTimeFormatter.class), | ||
staticFieldName, | ||
getCodeModel().ref(DateTimeFormatter.class).staticInvoke("ofPattern").arg(format)); | ||
} | ||
valueToString = defaultExpr.invoke("format").arg(field); | ||
if (formatDateCustomization != null) { | ||
formatDateCustomization.markAsAcknowledged(); | ||
} | ||
} | ||
} | ||
} | ||
return valueToString; | ||
} | ||
|
||
@Override | ||
public void onArray(JBlock block, boolean isAlwaysSet, ToStringArguments arguments) { | ||
ifHasSetValueAppendToStringElseAppendNull( | ||
arguments, | ||
block, | ||
getCodeModel().ref(Arrays.class).staticInvoke("toString") | ||
.arg(arguments.value()), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onBoolean(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onByte(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onChar(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onDouble(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onFloat(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onInt(ToStringArguments arguments, JBlock block, | ||
boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onLong(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onShort(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
|
||
@Override | ||
public void onObject(ToStringArguments arguments, JBlock block, boolean isAlwaysSet) { | ||
ifHasSetValueAppendToStringElseAppendNull(arguments, block, | ||
arguments.value(), isAlwaysSet, true); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...axb-plugins/src/main/java/org/jvnet/jaxb/plugin/simpletostring/ToStringCodeGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.jvnet.jaxb.plugin.simpletostring; | ||
|
||
import com.sun.codemodel.JCodeModel; | ||
import org.apache.commons.lang3.Validate; | ||
import org.jvnet.jaxb.plugin.codegenerator.CodeGenerationAbstraction; | ||
|
||
import java.time.format.DateTimeFormatter; | ||
|
||
public class ToStringCodeGenerator extends | ||
CodeGenerationAbstraction<ToStringArguments> { | ||
|
||
public ToStringCodeGenerator(JCodeModel codeModel, String defaultDateFormatterRef, String defaultDateFormatterPattern) { | ||
super(new ToStringCodeGenerationImplementor(Validate.notNull(codeModel), defaultDateFormatterRef, defaultDateFormatterPattern)); | ||
} | ||
|
||
} |
23 changes: 22 additions & 1 deletion
23
...gins-parent/jaxb-plugins/src/main/java/org/jvnet/jaxb/plugin/tostring/Customizations.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,32 @@ | ||
package org.jvnet.jaxb.plugin.tostring; | ||
|
||
import jakarta.xml.bind.JAXBContext; | ||
import jakarta.xml.bind.JAXBException; | ||
import org.glassfish.jaxb.runtime.v2.ContextFactory; | ||
|
||
import javax.xml.namespace.QName; | ||
|
||
public class Customizations { | ||
|
||
public static String NAMESPACE_URI = "urn:jaxb.jvnet.org:plugin:toString"; | ||
public static final String NAMESPACE_URI = "urn:jaxb.jvnet.org:plugin:toString"; | ||
|
||
public static QName IGNORED_ELEMENT_NAME = new QName(NAMESPACE_URI, "ignored"); | ||
public static QName MASKED_ELEMENT_NAME = new QName(NAMESPACE_URI, "masked"); | ||
public static final QName DATE_FORMAT_PATTERN = new QName(NAMESPACE_URI, "date-format"); | ||
|
||
private static final JAXBContext context; | ||
static { | ||
try { | ||
context = ContextFactory.createContext( | ||
ObjectFactory.class.getPackage().getName(), | ||
ObjectFactory.class.getClassLoader(), | ||
null); | ||
} catch (JAXBException e) { | ||
throw new ExceptionInInitializerError(e); | ||
} | ||
} | ||
|
||
public static JAXBContext getContext() { | ||
return context; | ||
} | ||
} |
Oops, something went wrong.