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

org.apache.juneau.jsonschema.JsonSchemaGeneratorSession Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you 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.apache.juneau.jsonschema;

import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.ObjectUtils.*;
import static org.apache.juneau.jsonschema.TypeCategory.*;

import java.lang.reflect.*;
import java.util.*;

import org.apache.juneau.*;
import org.apache.juneau.json.*;
import org.apache.juneau.jsonschema.annotation.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.transform.*;

/**
 * Session object that lives for the duration of a single use of {@link JsonSchemaSerializer}.
 *
 * 

* This class is NOT thread safe. * It is typically discarded after one-time use although it can be reused within the same thread. */ public class JsonSchemaGeneratorSession extends BeanTraverseSession { private final JsonSchemaGenerator ctx; private final Map defs; private JsonSerializerSession jsSession; /** * Create a new session using properties specified in the context. * * @param ctx * The context creating this session object. * The context contains all the configuration settings for this object. * @param args * Runtime arguments. * These specify session-level information such as locale and URI context. * It also include session-level properties that override the properties defined on the bean and * serializer contexts. */ protected JsonSchemaGeneratorSession(JsonSchemaGenerator ctx, BeanSessionArgs args) { super(ctx, args); this.ctx = ctx; defs = isUseBeanDefs() ? new TreeMap() : null; } /** * Returns the JSON-schema for the specified object. * * @param o * The object. *
Can either be a POJO or a Class/Type. * @return The schema for the type. * @throws Exception */ public ObjectMap getSchema(Object o) throws Exception { return getSchema(toClassMeta(o), "root", null, false, false, null); } /** * Returns the JSON-schema for the specified type. * * @param type The object type. * @return The schema for the type. * @throws Exception */ public ObjectMap getSchema(Type type) throws Exception { return getSchema(getClassMeta(type), "root", null, false, false, null); } /** * Returns the JSON-schema for the specified type. * * @param cm The object type. * @return The schema for the type. * @throws Exception */ public ObjectMap getSchema(ClassMeta cm) throws Exception { return getSchema(cm, "root", null, false, false, null); } @SuppressWarnings({ "unchecked", "rawtypes" }) private ObjectMap getSchema(ClassMeta eType, String attrName, String[] pNames, boolean exampleAdded, boolean descriptionAdded, JsonSchemaBeanPropertyMeta jsbpm) throws Exception { if (ctx.isIgnoredType(eType)) return null; ObjectMap out = new ObjectMap(); if (eType == null) eType = object(); ClassMeta aType; // The actual type (will be null if recursion occurs) ClassMeta sType; // The serialized type PojoSwap pojoSwap = eType.getPojoSwap(this); aType = push(attrName, eType, null); sType = eType.getSerializedClassMeta(this); String type = null, format = null; Object example = null, description = null; boolean useDef = isUseBeanDefs() && sType.isBean() && pNames == null; if (useDef) { exampleAdded = false; descriptionAdded = false; } if (useDef && defs.containsKey(getBeanDefId(sType))) { pop(); return new ObjectMap().append("$ref", getBeanDefUri(sType)); } ObjectMap ds = getDefaultSchemas().get(sType.getInnerClass().getName()); if (ds != null && ds.containsKey("type")) { pop(); return out.appendAll(ds); } JsonSchemaClassMeta jscm = null; if (pojoSwap != null && pojoSwap.getClass().getAnnotation(JsonSchema.class) != null) jscm = getClassMeta(pojoSwap.getClass()).getExtendedMeta(JsonSchemaClassMeta.class); if (jscm == null) jscm = sType.getExtendedMeta(JsonSchemaClassMeta.class); TypeCategory tc = null; if (sType.isNumber()) { tc = NUMBER; if (sType.isDecimal()) { type = "number"; if (sType.isFloat()) { format = "float"; } else if (sType.isDouble()) { format = "double"; } } else { type = "integer"; if (sType.isShort()) { format = "int16"; } else if (sType.isInteger()) { format = "int32"; } else if (sType.isLong()) { format = "int64"; } } } else if (sType.isBoolean()) { tc = BOOLEAN; type = "boolean"; } else if (sType.isMap()) { tc = MAP; type = "object"; } else if (sType.isBean()) { tc = BEAN; type = "object"; } else if (sType.isCollection()) { tc = COLLECTION; type = "array"; } else if (sType.isArray()) { tc = ARRAY; type = "array"; } else if (sType.isEnum()) { tc = ENUM; type = "string"; } else if (sType.isCharSequence() || sType.isChar()) { tc = STRING; type = "string"; } else if (sType.isUri()) { tc = STRING; type = "string"; format = "uri"; } else { tc = STRING; type = "string"; } // Add info from @JsonSchema on bean property. if (jsbpm != null) { out.appendIf(false, true, true, "type", jsbpm.getType()); out.appendIf(false, true, true, "format", jsbpm.getFormat()); } out.appendIf(false, true, true, "type", jscm.getType()); out.appendIf(false, true, true, "format", jscm.getFormat()); out.appendIf(false, true, true, "type", type); out.appendIf(false, true, true, "format", format); if (aType != null) { example = getExample(sType, tc, exampleAdded); description = getDescription(sType, tc, descriptionAdded); exampleAdded |= example != null; descriptionAdded |= description != null; if (tc == BEAN) { ObjectMap properties = new ObjectMap(); BeanMeta bm = getBeanMeta(sType.getInnerClass()); if (pNames != null) bm = new BeanMetaFiltered(bm, pNames); for (Iterator i = bm.getPropertyMetas().iterator(); i.hasNext();) { BeanPropertyMeta p = i.next(); if (p.canRead()) properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties(), exampleAdded, descriptionAdded, p.getExtendedMeta(JsonSchemaBeanPropertyMeta.class))); } out.put("properties", properties); } else if (tc == COLLECTION) { ClassMeta et = sType.getElementType(); if (sType.isCollection() && isParentClass(Set.class, sType.getInnerClass())) out.put("uniqueItems", true); out.put("items", getSchema(et, "items", pNames, exampleAdded, descriptionAdded, null)); } else if (tc == ARRAY) { ClassMeta et = sType.getElementType(); if (sType.isCollection() && isParentClass(Set.class, sType.getInnerClass())) out.put("uniqueItems", true); out.put("items", getSchema(et, "items", pNames, exampleAdded, descriptionAdded, null)); } else if (tc == ENUM) { out.put("enum", getEnums(sType)); } else if (tc == MAP) { ObjectMap om = getSchema(sType.getValueType(), "additionalProperties", null, exampleAdded, descriptionAdded, null); if (! om.isEmpty()) out.put("additionalProperties", om); } } // Add info from @JsonSchema on bean property. if (jsbpm != null) { out.appendIf(false, true, true, "description", jsbpm.getDescription()); out.appendIf(false, true, true, "x-example", jsbpm.getExample()); } out.appendIf(false, true, true, "description", jscm.getDescription()); out.appendIf(false, true, true, "x-example", jscm.getExample()); out.appendIf(false, true, true, "description", description); out.appendIf(false, true, true, "x-example", example); if (ds != null) out.appendAll(ds); if (useDef) { defs.put(getBeanDefId(sType), out); out = new ObjectMap().append("$ref", getBeanDefUri(sType)); } pop(); return out; } private List getEnums(ClassMeta cm) { List l = new ArrayList<>(); for (Enum e : getEnumConstants(cm.getInnerClass())) l.add(cm.toString(e)); return l; } private Object getExample(ClassMeta sType, TypeCategory t, boolean exampleAdded) throws Exception { boolean canAdd = isAllowNestedExamples() || ! exampleAdded; if (canAdd && (getAddExamplesTo().contains(t) || getAddExamplesTo().contains(ANY))) { Object example = sType.getExample(this); if (example != null) return JsonParser.DEFAULT.parse(toJson(example), Object.class); } return null; } private String toJson(Object o) throws SerializeException { if (jsSession == null) jsSession = ctx.getJsonSerializer().createSession(null); return jsSession.serializeToString(o); } private Object getDescription(ClassMeta sType, TypeCategory t, boolean descriptionAdded) { boolean canAdd = isAllowNestedDescriptions() || ! descriptionAdded; if (canAdd && (getAddDescriptionsTo().contains(t) || getAddDescriptionsTo().contains(ANY))) return sType.toString(); return null; } /** * Returns the definition ID for the specified class. * * @param cm The class to get the definition ID of. * @return The definition ID for the specified class. */ public String getBeanDefId(ClassMeta cm) { return getBeanDefMapper().getId(cm); } /** * Returns the definition URI for the specified class. * * @param cm The class to get the definition URI of. * @return The definition URI for the specified class. */ public java.net.URI getBeanDefUri(ClassMeta cm) { return getBeanDefMapper().getURI(cm); } /** * Returns the definition URI for the specified class. * * @param id The definition ID to get the definition URI of. * @return The definition URI for the specified class. */ public java.net.URI getBeanDefUri(String id) { return getBeanDefMapper().getURI(id); } /** * Returns the definitions that were gathered during this session. * *

* This map is modifiable and affects the map in the session. * * @return * The definitions that were gathered during this session, or null if {@link JsonSchemaGenerator#JSONSCHEMA_useBeanDefs} was not enabled. */ public Map getBeanDefs() { return defs; } /** * Adds a schema definition to this session. * * @param id The definition ID. * @param def The definition schema. * @return This object (for method chaining). */ public JsonSchemaGeneratorSession addBeanDef(String id, ObjectMap def) { if (defs != null) defs.put(id, def); return this; } //----------------------------------------------------------------------------------------------------------------- // Properties //----------------------------------------------------------------------------------------------------------------- /** * Configuration property: Use bean definitions. * * @see JsonSchemaGenerator#JSONSCHEMA_useBeanDefs * @return * true if schemas on beans will be serialized with '$ref' tags. */ protected final boolean isUseBeanDefs() { return ctx.isUseBeanDefs(); } /** * Configuration property: Allow nested examples. * * @see JsonSchemaGenerator#JSONSCHEMA_allowNestedExamples * @return * true if nested examples are allowed in schema definitions. */ protected final boolean isAllowNestedExamples() { return ctx.isAllowNestedExamples(); } /** * Configuration property: Allow nested descriptions. * * @see JsonSchemaGenerator#JSONSCHEMA_allowNestedDescriptions * @return * true if nested descriptions are allowed in schema definitions. */ protected final boolean isAllowNestedDescriptions() { return ctx.isAllowNestedDescriptions(); } /** * Configuration property: Bean schema definition mapper. * * @see JsonSchemaGenerator#JSONSCHEMA_beanDefMapper * @return * Interface to use for converting Bean classes to definition IDs and URIs. */ protected final BeanDefMapper getBeanDefMapper() { return ctx.getBeanDefMapper(); } /** * Configuration property: Add examples. * * @see JsonSchemaGenerator#JSONSCHEMA_addExamplesTo * @return * Set of categories of types that examples should be automatically added to generated schemas. */ protected final Set getAddExamplesTo() { return ctx.getAddExamplesTo(); } /** * Configuration property: Add descriptions to types. * * @see JsonSchemaGenerator#JSONSCHEMA_addDescriptionsTo * @return * Set of categories of types that descriptions should be automatically added to generated schemas. */ protected final Set getAddDescriptionsTo() { return ctx.getAddDescriptionsTo(); } /** * Configuration property: Default schemas. * * @see JsonSchemaGenerator#JSONSCHEMA_defaultSchemas * @return * Custom schema information for particular class types. */ protected final Map getDefaultSchemas() { return ctx.getDefaultSchemas(); } //----------------------------------------------------------------------------------------------------------------- // Utility methods //----------------------------------------------------------------------------------------------------------------- private ClassMeta toClassMeta(Object o) { if (o instanceof Type) return getClassMeta((Type)o); return getClassMetaForObject(o); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy