All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsonschema2pojo.rules.AdditionalPropertiesRule Maven / Gradle / Ivy

/**
 * 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 java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.StreamSupport;

import org.jsonschema2pojo.Schema;

import com.fasterxml.jackson.databind.JsonNode;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;

/**
 * Applies the "additionalProperties" JSON schema rule.
 *
 * @see http:/
 *      /tools.ietf.org/html/draft-zyp-json-schema-03#section-5.6
 */
public class AdditionalPropertiesRule implements Rule {

    private final RuleFactory ruleFactory;

    protected AdditionalPropertiesRule(RuleFactory ruleFactory) {
        this.ruleFactory = ruleFactory;
    }

    /**
     * Applies this schema rule to take the required code generation steps.
     * 

* If additionalProperties is specified and set to the boolean value * false, this rule does not make any change to the generated * Java type (the type does not allow additional properties). *

* If the additionalProperties node is null (not specified in * the schema) or empty, then a new bean property named * "additionalProperties", of type {@link Map}{@literal } is * added to the generated type (with appropriate accessors). The accessors * are annotated to allow unrecognised (additional) properties found in JSON * data to be marshalled/unmarshalled from/to this map. *

* If the additionalProperties node is present and specifies a schema, then * an "additionalProperties" map is added to the generated type. This time * the map values will be restricted and must be instances of a newly * generated Java type that will be created based on the * additionalProperties schema provided. If the schema does not specify the * javaType property, the name of the newly generated type will be derived * from the nodeName and the suffix 'Property'. * * @param nodeName * the name of the schema node for which the additionalProperties * node applies * @param node * the additionalProperties node itself, found in the schema (may * be null if not specified in the schema) * @param jclass * the Java type that is being generated to represent this schema * @return the given Java type jclass */ @Override public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDefinedClass jclass, Schema schema) { if (node != null && node.isBoolean() && node.asBoolean() == false) { // no additional properties allowed return jclass; } if (!this.ruleFactory.getGenerationConfig().isIncludeAdditionalProperties()) { // no additional properties allowed return jclass; } if (!ruleFactory.getAnnotator().isAdditionalPropertiesSupported()) { // schema allows additional properties, but serializer library can't support them return jclass; } JType propertyType; if (node != null && node.size() != 0) { String pathToAdditionalProperties; if (schema.getId() == null || schema.getId().getFragment() == null) { pathToAdditionalProperties = "#/additionalProperties"; } else { pathToAdditionalProperties = "#" + schema.getId().getFragment() + "/additionalProperties"; } Schema additionalPropertiesSchema = ruleFactory.getSchemaStore().create(schema, pathToAdditionalProperties, ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); propertyType = ruleFactory.getSchemaRule().apply(nodeName + "Property", node, parent, jclass, additionalPropertiesSchema); additionalPropertiesSchema.setJavaTypeIfEmpty(propertyType); } else { propertyType = jclass.owner().ref(Object.class); } JFieldVar field = addAdditionalPropertiesField(jclass, propertyType); addGetter(jclass, field); addSetter(jclass, propertyType, field); if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()) { ruleFactory.getValidRule().apply(nodeName, node, parent, field, schema); } if (ruleFactory.getGenerationConfig().isGenerateBuilders()) { addBuilder(jclass, propertyType, field); } return jclass; } private JFieldVar addAdditionalPropertiesField(JDefinedClass jclass, JType propertyType) { JClass propertiesMapType = jclass.owner().ref(Map.class); propertiesMapType = propertiesMapType.narrow(jclass.owner().ref(String.class), propertyType.boxify()); JClass propertiesMapImplType = jclass.owner().ref(LinkedHashMap.class); propertiesMapImplType = propertiesMapImplType.narrow(jclass.owner().ref(String.class), propertyType.boxify()); JFieldVar field = jclass.field(JMod.PRIVATE, propertiesMapType, "additionalProperties"); ruleFactory.getAnnotator().additionalPropertiesField(field, jclass, "additionalProperties"); field.init(JExpr._new(propertiesMapImplType)); return field; } private void addSetter(JDefinedClass jclass, JType propertyType, JFieldVar field) { JMethod setter = jclass.method(JMod.PUBLIC, void.class, "setAdditionalProperty"); ruleFactory.getAnnotator().anySetter(setter, jclass); JVar nameParam = setter.param(String.class, "name"); JVar valueParam = setter.param(propertyType, "value"); JInvocation mapInvocation = setter.body().invoke(JExpr._this().ref(field), "put"); mapInvocation.arg(nameParam); mapInvocation.arg(valueParam); } private JMethod addGetter(JDefinedClass jclass, JFieldVar field) { JMethod getter = jclass.method(JMod.PUBLIC, field.type(), "getAdditionalProperties"); ruleFactory.getAnnotator().anyGetter(getter, jclass); getter.body()._return(JExpr._this().ref(field)); return getter; } private JMethod addBuilder(JDefinedClass jclass, JType propertyType, JFieldVar field) { JMethod result = null; if(ruleFactory.getGenerationConfig().isUseInnerClassBuilders()) { result = addInnerBuilder(jclass, propertyType, field); } else { result = addLegacyBuilder(jclass, propertyType, field); } return result; } private JMethod addLegacyBuilder(JDefinedClass jclass, JType propertyType, JFieldVar field) { JMethod builder = jclass.method(JMod.PUBLIC, jclass, "withAdditionalProperty"); JVar nameParam = builder.param(String.class, "name"); JVar valueParam = builder.param(propertyType, "value"); JBlock body = builder.body(); JInvocation mapInvocation = body.invoke(JExpr._this().ref(field), "put"); mapInvocation.arg(nameParam); mapInvocation.arg(valueParam); body._return(JExpr._this()); return builder; } private JMethod addInnerBuilder(JDefinedClass jclass, JType propertyType, JFieldVar field) { Optional builderClass = StreamSupport .stream(Spliterators.spliteratorUnknownSize(jclass.classes(), Spliterator.ORDERED), false) .filter(definedClass -> definedClass.name().equals(getBuilderClassName(jclass))) .findFirst(); JMethod builder = builderClass.get().method(JMod.PUBLIC, builderClass.get(), "withAdditionalProperty"); JVar nameParam = builder.param(String.class, "name"); JVar valueParam = builder.param(propertyType, "value"); JBlock body = builder.body(); JInvocation mapInvocation = body.invoke(JExpr.ref(JExpr.cast(jclass, JExpr._this().ref("instance")), field), "put"); mapInvocation.arg(nameParam); mapInvocation.arg(valueParam); body._return(JExpr._this()); return builder; } private String getBuilderClassName(JDefinedClass c) { return ruleFactory.getNameHelper().getBaseBuilderClassName(c); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy