
org.codehaus.modello.plugin.jsonschema.JsonSchemaGenerator Maven / Gradle / Ivy
package org.codehaus.modello.plugin.jsonschema;
/*
* Copyright (c) 2013, Codehaus.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.codehaus.modello.ModelloException;
import org.codehaus.modello.ModelloParameterConstants;
import org.codehaus.modello.model.Model;
import org.codehaus.modello.model.ModelAssociation;
import org.codehaus.modello.model.ModelClass;
import org.codehaus.modello.model.ModelDefault;
import org.codehaus.modello.model.ModelField;
import org.codehaus.modello.plugins.xml.AbstractXmlJavaGenerator;
import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
import org.codehaus.plexus.util.StringUtils;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonGenerator.Feature;
/**
* @author Simone Tripodi
* @since 1.8
*/
public final class JsonSchemaGenerator
extends AbstractXmlJavaGenerator
{
public void generate( Model model, Properties parameters )
throws ModelloException
{
initialize( model, parameters );
try
{
generateJsonSchema( parameters );
}
catch ( IOException ioe )
{
throw new ModelloException( "Exception while generating JSON Schema.", ioe );
}
}
private void generateJsonSchema( Properties parameters )
throws IOException, ModelloException
{
Model objectModel = getModel();
File directory = getOutputDirectory();
if ( isPackageWithVersion() )
{
directory = new File( directory, getGeneratedVersion().toString() );
}
if ( !directory.exists() )
{
directory.mkdirs();
}
// we assume parameters not null
String schemaFileName = parameters.getProperty( ModelloParameterConstants.OUTPUT_JSONSCHEMA_FILE_NAME );
File schemaFile;
if ( schemaFileName != null )
{
schemaFile = new File( directory, schemaFileName );
}
else
{
schemaFile = new File( directory, objectModel.getId() + "-" + getGeneratedVersion() + ".schema.json" );
}
JsonGenerator generator = new JsonFactory()
.enable( Feature.AUTO_CLOSE_JSON_CONTENT )
.enable( Feature.AUTO_CLOSE_TARGET )
.enable( Feature.ESCAPE_NON_ASCII )
.enable( Feature.FLUSH_PASSED_TO_STREAM )
.enable( Feature.QUOTE_FIELD_NAMES )
.enable( Feature.QUOTE_NON_NUMERIC_NUMBERS )
.disable( Feature.WRITE_NUMBERS_AS_STRINGS )
.createGenerator( schemaFile, JsonEncoding.UTF8 );
generator.useDefaultPrettyPrinter();
ModelClass root = objectModel.getClass( objectModel.getRoot( getGeneratedVersion() ),
getGeneratedVersion() );
try
{
generator.writeStartObject();
generator.writeStringField( "$schema", "http://json-schema.org/draft-04/schema#" );
writeClassDocumentation( generator, root, true );
generator.writeObjectFieldStart( "definitions" );
for ( ModelClass current : objectModel.getClasses( getGeneratedVersion() ) )
{
if ( !root.equals( current ) )
{
writeClassDocumentation( generator, current, false );
}
}
// end "definitions"
generator.writeEndObject();
// end main object
generator.writeEndObject();
}
finally
{
generator.close();
}
}
private void writeClassDocumentation( JsonGenerator generator, ModelClass modelClass, boolean isRoot )
throws IOException
{
if ( !isRoot )
{
generator.writeObjectFieldStart( modelClass.getName() );
}
generator.writeStringField( "id", modelClass.getName() + '#' );
writeDescriptionField( generator, modelClass.getDescription() );
writeTypeField( generator, "object" );
generator.writeObjectFieldStart( "properties" );
List required = new LinkedList();
ModelClass reference = modelClass;
// traverse the whole modelClass hierarchy to create the nested Builder instance
while ( reference != null )
{
// collect parameters and set them in the instance object
for ( ModelField modelField : reference.getFields( getGeneratedVersion() ) )
{
if ( modelField.isRequired() )
{
required.add( modelField.getName() );
}
// each field is represented as object
generator.writeObjectFieldStart( modelField.getName() );
writeDescriptionField( generator, modelField.getDescription() );
if ( modelField instanceof ModelAssociation )
{
ModelAssociation modelAssociation = (ModelAssociation) modelField;
if ( modelAssociation.isOneMultiplicity() )
{
writeTypeField( generator, modelAssociation.getType() );
}
else
{
// MANY_MULTIPLICITY
writeTypeField( generator, "array" );
generator.writeObjectFieldStart( "items" );
String type = modelAssociation.getType();
String toType = modelAssociation.getTo();
if ( ModelDefault.LIST.equals( type ) || ModelDefault.SET.equals( type ) )
{
writeTypeField( generator, toType );
}
else
{
// Map or Properties
writeTypeField( generator, "object" );
generator.writeObjectFieldStart( "properties" );
XmlAssociationMetadata xmlAssociationMetadata =
(XmlAssociationMetadata) modelAssociation.getAssociationMetadata( XmlAssociationMetadata.ID );
if ( xmlAssociationMetadata.isMapExplode() )
{
// key
generator.writeObjectFieldStart( "key" );
writeTypeField( generator, "string" );
generator.writeEndObject();
// value
generator.writeObjectFieldStart( "value" );
writeTypeField( generator, toType );
generator.writeEndObject();
// properties
generator.writeEndObject();
// required field
generator.writeArrayFieldStart( "required" );
generator.writeString( "key" );
generator.writeString( "value" );
generator.writeEndArray();
}
else
{
generator.writeObjectFieldStart( "*" );
writeTypeField( generator, toType );
generator.writeEndObject();
}
}
// items
generator.writeEndObject();
}
}
else
{
writeTypeField( generator, modelField.getType() );
}
generator.writeEndObject();
}
if ( reference.hasSuperClass() )
{
reference = reference.getModel().getClass( reference.getSuperClass(), getGeneratedVersion() );
}
else
{
reference = null;
}
}
// end of `properties` element
generator.writeEndObject();
// write `required` sequence
if ( !required.isEmpty() )
{
generator.writeArrayFieldStart( "required" );
for ( String requiredField : required )
{
generator.writeString( requiredField );
}
generator.writeEndArray();
}
// end definition
if ( !isRoot )
{
generator.writeEndObject();
}
}
private static void writeDescriptionField( JsonGenerator generator, String description )
throws IOException
{
if ( !StringUtils.isEmpty( description ) )
{
generator.writeStringField( "description", description );
}
}
private void writeTypeField( JsonGenerator generator, String type )
throws IOException
{
if ( isClassInModel( type, getModel() ) )
{
generator.writeStringField( "$ref", "#/definitions/" + type );
return;
}
// try to make the input type compliant, as much as possible, to JSON Schema primitive types
// see http://json-schema.org/latest/json-schema-core.html#anchor8
if ( "boolean".equals( type ) || "Boolean".equals( type ) )
{
type = "boolean";
}
else if ( "int".equals( type ) || "Integer".equals( type ) )
{
type = "integer";
}
else if ( "short".equals( type ) || "Short".equals( type )
|| "long".equals( type ) || "Long".equals( type )
|| "double".equals( type ) || "Double".equals( type )
|| "float".equals( type ) || "Float".equals( type ) )
{
type = "number";
}
else if ( "String".equals( type ) )
{
type = "string";
}
// keep as it is otherwise
generator.writeStringField( "type", type );
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy