Skip to content

Commit

Permalink
add const rule
Browse files Browse the repository at this point in the history
Adds rule to generate java constants on "const" properties
  • Loading branch information
volomas committed Aug 21, 2024
1 parent 34cc693 commit 5a47f07
Show file tree
Hide file tree
Showing 5 changed files with 373 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* Copyright © 2010-2020 Nokia
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jsonschema2pojo.rules;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.jsonschema2pojo.Schema;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.text.ParseException;
import java.util.Date;
import java.util.Objects;

import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.joinWith;
import static org.apache.commons.lang3.StringUtils.splitByCharacterTypeCamelCase;

public class ConstRule implements Rule<JFieldVar, JFieldVar> {

private final RuleFactory ruleFactory;
private final JDefinedClass jclass;

public ConstRule(RuleFactory ruleFactory, JDefinedClass jclass) {
this.ruleFactory = ruleFactory;
this.jclass = jclass;
}

@Override
public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) {
boolean constPresent = node != null && isNotEmpty(node.asText());
JType propertyType = field.type();

if (!constPresent) {
return field;
}

String constName = joinWith("_", splitByCharacterTypeCamelCase(nodeName)).toUpperCase();
JFieldVar myConstant = jclass.field(JMod.PUBLIC | JMod.STATIC | JMod.FINAL,
propertyType,
constName,
getValue(propertyType, node));

return field;
}

static JExpression getValue(JType type, JsonNode node) {
Objects.requireNonNull(node);
String fieldType = type.fullName();
if (fieldType.startsWith(String.class.getName())) {
return getConstValue(type, node);
} else {
return getConstValue(type, node);
}
}

static JExpression getConstValue(JType fieldType, JsonNode node) {
return getConstValue(fieldType, node.asText());
}

static JExpression getConstValue(JType fieldType, String value) {

fieldType = fieldType.unboxify();

if (fieldType.fullName().equals(String.class.getName())) {
return JExpr.lit(value);

} else if (fieldType.fullName().equals(int.class.getName())) {
return JExpr.lit(Integer.parseInt(value));

} else if (fieldType.fullName().equals(BigInteger.class.getName())) {
return JExpr._new(fieldType).arg(JExpr.lit(value));

} else if (fieldType.fullName().equals(double.class.getName())) {
return JExpr.lit(Double.parseDouble(value));

} else if (fieldType.fullName().equals(BigDecimal.class.getName())) {
return JExpr._new(fieldType).arg(JExpr.lit(value));

} else if (fieldType.fullName().equals(boolean.class.getName())) {
return JExpr.lit(Boolean.parseBoolean(value));

} else if (fieldType.fullName().equals(DateTime.class.getName()) || fieldType.fullName().equals(Date.class.getName())) {
long millisecs = parseDateToMillisecs(value);

JInvocation newDateTime = JExpr._new(fieldType);
newDateTime.arg(JExpr.lit(millisecs));

return newDateTime;

} else if (fieldType.fullName().equals(LocalDate.class.getName()) || fieldType.fullName().equals(LocalTime.class.getName())) {

JInvocation stringParseableTypeInstance = JExpr._new(fieldType);
stringParseableTypeInstance.arg(JExpr.lit(value));
return stringParseableTypeInstance;

} else if (fieldType.fullName().equals(long.class.getName())) {
return JExpr.lit(Long.parseLong(value));

} else if (fieldType.fullName().equals(float.class.getName())) {
return JExpr.lit(Float.parseFloat(value));

} else if (fieldType.fullName().equals(URI.class.getName())) {
JInvocation invokeCreate = fieldType.owner().ref(URI.class).staticInvoke("create");
return invokeCreate.arg(JExpr.lit(value));
} else {
return JExpr._null();

}

}

private static long parseDateToMillisecs(String valueAsText) {

try {
return Long.parseLong(valueAsText);
} catch (NumberFormatException nfe) {
try {
return new StdDateFormat().parse(valueAsText).getTime();
} catch (ParseException pe) {
throw new IllegalArgumentException("Unable to parse this string as a date: " + valueAsText);
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.jsonschema2pojo.rules;

import com.sun.codemodel.JExpression;
import jakarta.validation.constraints.Email;
import org.jsonschema2pojo.GenerationConfig;
import org.jsonschema2pojo.JsonPointerUtils;
import org.jsonschema2pojo.Schema;
Expand Down Expand Up @@ -119,6 +121,8 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef
ruleFactory.getPatternRule().apply(nodeName, node.get("pattern"), node, field, schema);
}

ruleFactory.getConstRule(jclass).apply(nodeName, node.get("const"), node, field, schema);

ruleFactory.getDefaultRule().apply(nodeName, node.get("default"), node, field, schema);

ruleFactory.getMinimumMaximumRule().apply(nodeName, node, parent, field, schema);
Expand Down Expand Up @@ -203,7 +207,7 @@ private void formatAnnotation(JFieldVar field, JDefinedClass clazz, JsonNode nod
ruleFactory.getAnnotator().timeField(field, clazz, node);
} else if ("email".equalsIgnoreCase(format) && ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()) {
if (ruleFactory.getGenerationConfig().isUseJakartaValidation()) {
field.annotate(jakarta.validation.constraints.Email.class);
field.annotate(Email.class);
} else {
field.annotate(javax.validation.constraints.Email.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ public Rule<JFieldVar, JFieldVar> getDefaultRule() {
return new DefaultRule(this);
}


public Rule<JFieldVar, JFieldVar> getConstRule(JDefinedClass jclass) {
return new ConstRule(this, jclass);
}

/**
* Provides a rule instance that should be applied when a property
* declaration is found in the schema, to assign any minimum/maximum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.*;
import static org.junit.Assert.*;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
Expand All @@ -37,7 +39,7 @@
import org.junit.Test;

public class DefaultIT {

@ClassRule public static Jsonschema2PojoRule classSchemaRule = new Jsonschema2PojoRule();
@Rule public Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule();

Expand Down Expand Up @@ -74,6 +76,162 @@ public void stringPropertyHasCorrectDefaultValue() throws NoSuchMethodException,

}

@Test
public void emptyConst()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {

Object instance = classWithDefaults.newInstance();

assertThrows(NoSuchFieldException.class, () -> classWithDefaults.getField("EMPTY_WITH_CONST"));
Method getter = classWithDefaults.getMethod("getEmptyWithConst");
assertNull(getter.invoke(instance));
}

@Test
public void stringConst()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchFieldException {

Object instance = classWithDefaults.newInstance();

Field field = classWithDefaults.getField("STRING_WITH_CONST");
Method getter = classWithDefaults.getMethod("getStringWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);
assertEquals("abc", value);
assertNull(getter.invoke(instance));
}

@Test
public void booleanConst()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchFieldException {

Object instance = classWithDefaults.newInstance();

Field field = classWithDefaults.getField("BOOLEAN_WITH_CONST");
Method getter = classWithDefaults.getMethod("getBooleanWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);
assertEquals(true, value);
assertNull(getter.invoke(instance));
}

@Test
public void integerConst()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchFieldException {

Object instance = classWithDefaults.newInstance();

Field field = classWithDefaults.getField("INTEGER_WITH_CONST");
Method getter = classWithDefaults.getMethod("getIntegerWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);
assertEquals(1, value);
assertNull(getter.invoke(instance));
}

@Test
public void numberConst()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchFieldException {

Object instance = classWithDefaults.newInstance();

Field field = classWithDefaults.getField("NUMBER_WITH_CONST");
Method getter = classWithDefaults.getMethod("getNumberWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);
assertEquals(1.1, value);
assertNull(getter.invoke(instance));
}

@Test
public void numberConstBigDecimal() throws Exception {
ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/default/default.json", "com.example", config("useBigDecimals", true));
Class<?> c = resultsClassLoader.loadClass("com.example.Default");

Field field = c.getField("NUMBER_WITH_CONST");

assertPublicStaticFinal(field);
Object value = field.get(null);

assertThat((BigDecimal) value, is(equalTo(new BigDecimal("1.1"))));
}

@Test
public void dateTimeAsMillisecPropertyHasCorrectConstValue()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchFieldException {

Field field = classWithDefaults.getField("DATE_TIME_WITH_CONST");
Method getter = classWithDefaults.getMethod("getDateTimeWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);

assertThat((Date) value, is(equalTo(new Date(123456789))));
}

private static void assertPublicStaticFinal(Field field) {
int modifiers = field.getModifiers();
assertTrue(Modifier.isPublic(modifiers));
assertTrue(Modifier.isStatic(modifiers));
assertTrue(Modifier.isFinal(modifiers));
}

@Test
public void dateTimeAsStringPropertyHasCorrectConstValue()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchFieldException {

Field field = classWithDefaults.getField("DATE_TIME_AS_STRING_WITH_CONST");
Method getter = classWithDefaults.getMethod("getDateTimeAsStringWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);

assertThat((Date) value, is(equalTo(new Date(1298539523112L))));
}

@Test
public void utcmillisecPropertyHasCorrectConstValue()
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchFieldException {

Field field = classWithDefaults.getField("UTCMILLISEC_WITH_CONST");
Method getter = classWithDefaults.getMethod("getUtcmillisecWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);

assertThat((Long) value, is(equalTo(123456789L)));
}

@Test
public void uriPropertyHasCorrectConstValue() throws Exception {
Field field = classWithDefaults.getField("URI_WITH_CONST");
Method getter = classWithDefaults.getMethod("getUriWithConst");

assertPublicStaticFinal(field);

Object value = field.get(null);

assertThat((URI) value, is(URI.create("http://example.com")));
}

@Test
public void integerPropertyHasCorrectDefaultValue() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {

Expand Down Expand Up @@ -270,7 +428,7 @@ public void simplePropertyCanHaveNullDefaultValue() throws NoSuchMethodException
assertThat(getter.invoke(instance), is(nullValue()));

}

@Test
public void arrayPropertyCanHaveNullDefaultValue() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {

Expand Down
Loading

0 comments on commit 5a47f07

Please sign in to comment.