Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'formatTypeMapping' config option to allow overriding types used for formats #923

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
Expand Down Expand Up @@ -177,6 +181,8 @@ public class Jsonschema2PojoTask extends Task implements GenerationConfig {

private Language targetLanguage = Language.JAVA;

private Map<String, String> formatTypeMapping = new HashMap<>();

/**
* Execute this task (it's expected that all relevant setters will have been
* called by Ant to provide task configuration <em>before</em> this method
Expand Down Expand Up @@ -871,6 +877,14 @@ public void setTargetLanguage(Language targetLanguage) {
this.targetLanguage = targetLanguage;
}

public void setFormatTypeMapping(Map<String, String> formatTypeMapping) {
this.formatTypeMapping = formatTypeMapping;
}
public void setFormatTypeMapping(String[] formatTypeMapping) {
this.formatTypeMapping = Arrays.stream(formatTypeMapping)
.collect(Collectors.toMap(m -> m.split(":")[0], m -> m.split(":")[1]));
}

@Override
public boolean isGenerateBuilders() {
return generateBuilders;
Expand Down Expand Up @@ -1182,5 +1196,10 @@ public SourceSortOrder getSourceSortOrder() {
public Language getTargetLanguage() {
return targetLanguage;
}

@Override
public Map<String, String> getFormatTypeMapping() {
return formatTypeMapping;
}

}
7 changes: 7 additions & 0 deletions jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,13 @@ <h3>Parameters</h3>
</td>
<td align="center" valign="top">No (default <code>OS</code>)</td>
</tr>
<tr>
<td valign="top">formatTypeMapping</td>
<td valign="top">A mapping from format identifier (e.g. 'uri') to Java type (e.g. 'java.net.URI'):
<code>&gt;format&lt;:&gt;fully.qualified.Type&lt;</code>.
</td>
<td align="center" valign="top">None (default <code>''</code> (none))</td>
</tr>

</table>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@
import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.jsonschema2pojo.AllFileFilter;
import org.jsonschema2pojo.AnnotationStyle;
Expand Down Expand Up @@ -215,6 +218,9 @@ public class Arguments implements GenerationConfig {

@Parameter(names = { "-tl", "--target-language" }, description = "The type of code that will be generated. Available options are: JAVA or SCALA")
private Language targetLanguage = Language.JAVA;

@Parameter(names = { "-ftm", "--format-type-mapping" }, description = "Mapping from format identifier to type: <format>:<fully.qualified.Type>.", variableArity = true)
private List<String> formatTypeMapping = new ArrayList<>();

private static final int EXIT_OKAY = 0;
private static final int EXIT_ERROR = 1;
Expand Down Expand Up @@ -532,9 +538,16 @@ public String getCustomDateTimePattern() {
public SourceSortOrder getSourceSortOrder() {
return sourceSortOrder;
}

@Override
public Language getTargetLanguage() {
return targetLanguage;
}

@Override
public Map<String, String> getFormatTypeMapping() {
return formatTypeMapping
.stream()
.collect(Collectors.toMap(m -> m.split(":")[0], m -> m.split(":")[1]));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;

import org.jsonschema2pojo.rules.RuleFactory;

Expand Down Expand Up @@ -434,5 +436,13 @@ public SourceSortOrder getSourceSortOrder() {
public Language getTargetLanguage() {
return Language.JAVA;
}

/**
* @return {@link Collections#emptyMap}
*/
@Override
public Map<String, String> getFormatTypeMapping() {
return Collections.emptyMap();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.FileFilter;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;

import org.jsonschema2pojo.rules.RuleFactory;

Expand Down Expand Up @@ -562,5 +563,12 @@ public interface GenerationConfig {
* </ul>
*/
Language getTargetLanguage();


/**
* Gets the 'formatTypeMapping' configuration option.
*
* @return An optional mapping from format identifier (e.g. 'uri') to
* fully qualified type name (e.g. 'java.net.URI').
*/
Map<String, String> getFormatTypeMapping();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
import static org.apache.commons.lang.StringUtils.*;

import java.net.URI;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;

Expand All @@ -48,15 +49,17 @@ public class FormatRule implements Rule<JType, JType> {
public static String ISO_8601_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

private final RuleFactory ruleFactory;
private final Map<String, Class<?>> formatTypeMapping;

protected FormatRule(RuleFactory ruleFactory) {
this.ruleFactory = ruleFactory;
this.formatTypeMapping = getFormatTypeMapping(ruleFactory.getGenerationConfig());
}

/**
* Applies this schema rule to take the required code generation steps.
* <p>
* This rule maps format values to Java types:
* This rule maps format values to Java types. By default:
* <ul>
* <li>"format":"date-time" =&gt; {@link java.util.Date} or {@link org.joda.time.DateTime} (if config useJodaDates is set)
* <li>"format":"date" =&gt; {@link String} or {@link org.joda.time.LocalDate} (if config useJodaLocalDates is set)
Expand Down Expand Up @@ -90,102 +93,86 @@ protected FormatRule(RuleFactory ruleFactory) {
@Override
public JType apply(String nodeName, JsonNode node, JsonNode parent, JType baseType, Schema schema) {

if (node.asText().equals("date-time")) {
return baseType.owner().ref(getDateTimeType());

} else if (node.asText().equals("date")) {
return baseType.owner().ref(getDateOnlyType());

} else if (node.asText().equals("time")) {
return baseType.owner().ref(getTimeOnlyType());

} else if (node.asText().equals("utc-millisec")) {
return unboxIfNecessary(baseType.owner().ref(Long.class), ruleFactory.getGenerationConfig());

} else if (node.asText().equals("regex")) {
return baseType.owner().ref(Pattern.class);

} else if (node.asText().equals("color")) {
return baseType.owner().ref(String.class);

} else if (node.asText().equals("style")) {
return baseType.owner().ref(String.class);

} else if (node.asText().equals("phone")) {
return baseType.owner().ref(String.class);

} else if (node.asText().equals("uri")) {
return baseType.owner().ref(URI.class);
Class<?> type = getType(node.asText());
if (type != null) {
JType jtype = baseType.owner().ref(type);
if (ruleFactory.getGenerationConfig().isUsePrimitives()) {
jtype = jtype.unboxify();
}
return jtype;
} else {
return baseType;
}
}

} else if (node.asText().equals("email")) {
return baseType.owner().ref(String.class);
private Class<?> getType(String format) {
return formatTypeMapping.getOrDefault(format, null);
}

} else if (node.asText().equals("ip-address")) {
return baseType.owner().ref(String.class);
private static Map<String, Class<?>> getFormatTypeMapping(GenerationConfig config) {

Map<String, Class<?>> mapping = new HashMap<>(14);
mapping.put("date-time", getDateTimeType(config));
mapping.put("date", getDateType(config));
mapping.put("time", getTimeType(config));
mapping.put("utc-millisec", Long.class);
mapping.put("regex", Pattern.class);
mapping.put("color", String.class);
mapping.put("style", String.class);
mapping.put("phone", String.class);
mapping.put("uri", URI.class);
mapping.put("email", String.class);
mapping.put("ip-address", String.class);
mapping.put("ipv6", String.class);
mapping.put("host-name", String.class);
mapping.put("uuid", UUID.class);

for (Map.Entry<String, String> override : config.getFormatTypeMapping().entrySet()) {
String format = override.getKey();
Class<?> type = tryLoadType(override.getValue(), format);
if (type != null) {
mapping.put(format, type);
}
}

} else if (node.asText().equals("ipv6")) {
return baseType.owner().ref(String.class);
return mapping;
}

} else if (node.asText().equals("host-name")) {
return baseType.owner().ref(String.class);
}
else if (node.asText().equals("uuid")) {
return baseType.owner().ref(UUID.class);
}
else {
return baseType;
private static Class<?> getDateTimeType(GenerationConfig config) {
Class<?> type = tryLoadType(config.getDateTimeType(), "data-time");
if (type != null) {
return type;
}

return config.isUseJodaDates() ? DateTime.class : Date.class;
}

private Class<?> getDateTimeType() {
String type=ruleFactory.getGenerationConfig().getDateTimeType();
if (!isEmpty(type)){
try {
Class<?> clazz=Thread.currentThread().getContextClassLoader().loadClass(type);
return clazz;
}
catch (ClassNotFoundException e) {
throw new GenerationException(format("could not load java type %s for date-time format", type), e);
}
private static Class<?> getDateType(GenerationConfig config) {
Class<?> type = tryLoadType(config.getDateType(), "data");
if (type != null) {
return type;
}
return ruleFactory.getGenerationConfig().isUseJodaDates() ? DateTime.class : Date.class;
return config.isUseJodaLocalDates() ? LocalDate.class : String.class;
}

private Class<?> getDateOnlyType() {
String type=ruleFactory.getGenerationConfig().getDateType();
if (!isEmpty(type)){
try {
Class<?> clazz=Thread.currentThread().getContextClassLoader().loadClass(type);
return clazz;
}
catch (ClassNotFoundException e) {
throw new GenerationException(format("could not load java type %s for date format", type), e);
}
private static Class<?> getTimeType(GenerationConfig config) {
Class<?> type = tryLoadType(config.getTimeType(), "time");
if (type != null) {
return type;
}
return ruleFactory.getGenerationConfig().isUseJodaLocalDates() ? LocalDate.class : String.class;
return config.isUseJodaLocalTimes() ? LocalTime.class : String.class;
}

private Class<?> getTimeOnlyType() {
String type=ruleFactory.getGenerationConfig().getTimeType();
if (!isEmpty(type)){
private static Class<?> tryLoadType(String typeName, String format) {
if (!isEmpty(typeName)) {
try {
Class<?> clazz=Thread.currentThread().getContextClassLoader().loadClass(type);
return clazz;
Class<?> type = Thread.currentThread().getContextClassLoader().loadClass(typeName);
return type;
}
catch (ClassNotFoundException e) {
throw new GenerationException(format("could not load java type %s for time format", type), e);
throw new GenerationException(format("could not load java type %s for %s", typeName, format), e);
}
}
return ruleFactory.getGenerationConfig().isUseJodaLocalTimes() ? LocalTime.class : String.class;
}

private JType unboxIfNecessary(JType type, GenerationConfig config) {
if (config.isUsePrimitives()) {
return type.unboxify();
} else {
return type;
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
public class FormatRuleJodaTest {

private GenerationConfig config = mock(GenerationConfig.class);
private FormatRule rule = new FormatRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore()));
private FormatRule rule;

private final String formatValue;
private final Class<?> expectedType;
Expand All @@ -66,6 +66,7 @@ public void setupConfig() {
when(config.isUseJodaLocalTimes()).thenReturn(true);
when(config.isUseJodaLocalDates()).thenReturn(true);
when(config.isUseJodaDates()).thenReturn(true);
rule = new FormatRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore()));
}

@Test
Expand Down
Loading