JsonGenerator.java
/*
* Copyright 2015 Providence Authors
*
* 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 net.morimekta.providence.generator.format.json;
import net.morimekta.providence.generator.Generator;
import net.morimekta.providence.generator.GeneratorException;
import net.morimekta.providence.generator.util.FileManager;
import net.morimekta.providence.model.ConstType;
import net.morimekta.providence.model.EnumType;
import net.morimekta.providence.model.EnumValue;
import net.morimekta.providence.model.FieldRequirement;
import net.morimekta.providence.model.FieldType;
import net.morimekta.providence.model.FunctionType;
import net.morimekta.providence.model.MessageType;
import net.morimekta.providence.model.MessageVariant;
import net.morimekta.providence.model.ProgramType;
import net.morimekta.providence.model.ServiceType;
import net.morimekta.providence.model.TypedefType;
import net.morimekta.providence.reflect.ProgramRegistry;
import net.morimekta.providence.reflect.contained.CProgram;
import net.morimekta.providence.reflect.model.AnnotationDeclaration;
import net.morimekta.providence.reflect.model.ConstDeclaration;
import net.morimekta.providence.reflect.model.Declaration;
import net.morimekta.providence.reflect.model.EnumDeclaration;
import net.morimekta.providence.reflect.model.EnumValueDeclaration;
import net.morimekta.providence.reflect.model.FieldDeclaration;
import net.morimekta.providence.reflect.model.IncludeDeclaration;
import net.morimekta.providence.reflect.model.MessageDeclaration;
import net.morimekta.providence.reflect.model.MethodDeclaration;
import net.morimekta.providence.reflect.model.NamespaceDeclaration;
import net.morimekta.providence.reflect.model.ProgramDeclaration;
import net.morimekta.providence.reflect.model.ServiceDeclaration;
import net.morimekta.providence.reflect.model.TypedefDeclaration;
import net.morimekta.providence.reflect.util.ReflectionUtils;
import net.morimekta.providence.serializer.JsonSerializer;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static net.morimekta.providence.model.Declaration.withDeclConst;
import static net.morimekta.providence.model.Declaration.withDeclEnum;
import static net.morimekta.providence.model.Declaration.withDeclMessage;
import static net.morimekta.providence.model.Declaration.withDeclService;
import static net.morimekta.providence.model.Declaration.withDeclTypedef;
/**
* Generate a simple JSON model of the program structure.
*/
public class JsonGenerator extends Generator {
private final JsonSerializer serializer;
public JsonGenerator(FileManager fileManager) {
super(fileManager);
serializer = new JsonSerializer().pretty();
}
@Override
public void generate(ProgramRegistry registry) throws IOException, GeneratorException {
ProgramType doc = convertProgram(registry.getProgramType());
CProgram program = registry.getProgram();
Path programFile = Paths.get(program.getProgramFilePath());
String programName = ReflectionUtils.programNameFromPath(programFile);
getFileManager().createIfMissingOrOutdated(
programFile, null, programName + ".json",
out -> {
serializer.serialize(out, doc);
out.write('\n');
});
}
private ProgramType convertProgram(ProgramDeclaration declaration) {
ProgramType._Builder program = ProgramType.builder();
program.setProgramName(declaration.getProgramName());
for (IncludeDeclaration include : declaration.getIncludes()) {
String includedProgram = ReflectionUtils.programNameFromPath(Paths.get(include.getFilePath()));
program.putInIncludes(include.getProgramName(), includedProgram + ".json");
}
for (NamespaceDeclaration namespace : declaration.getNamespaces()) {
program.putInNamespaces(namespace.getLanguage(), namespace.getNamespace());
}
for (Declaration decl : declaration.getDeclarationList()) {
if (decl instanceof TypedefDeclaration) {
program.addToDecl(convertTypedef((TypedefDeclaration) decl));
} else if (decl instanceof EnumDeclaration) {
program.addToDecl(convertEnum((EnumDeclaration) decl));
} else if (decl instanceof MessageDeclaration) {
program.addToDecl(convertMessage((MessageDeclaration) decl));
} else if (decl instanceof ConstDeclaration) {
program.addToDecl(convertConst((ConstDeclaration) decl));
} else if (decl instanceof ServiceDeclaration) {
program.addToDecl(convertService((ServiceDeclaration) decl));
}
}
return program.build();
}
private net.morimekta.providence.model.Declaration convertService(ServiceDeclaration decl) {
return withDeclService(ServiceType.builder()
.setDocumentation(decl.getDocumentation())
.setName(decl.getName())
.setExtend(decl.getExtending())
.setMethods(convertMethods(decl.getMethods()))
.setAnnotations(makeAnnotations(decl.getAnnotations()))
.build());
}
private List<FunctionType> convertMethods(List<MethodDeclaration> methods) {
List<FunctionType> functions = new ArrayList<>();
for (MethodDeclaration method : methods) {
functions.add(FunctionType
.builder()
.setDocumentation(method.getDocumentation())
.setOneWay(method.isOneWay() ? Boolean.TRUE : null)
.setReturnType(method.getReturnType())
.setName(method.getName())
.setParams(convertFields(method.getParams()))
.setExceptions(method.getThrowing() != null && !method.getThrowing().isEmpty() ?
convertFields(method.getThrowing()) : null)
.setAnnotations(makeAnnotations(method.getAnnotations()))
.build());
}
return functions;
}
private net.morimekta.providence.model.Declaration convertConst(ConstDeclaration decl) {
return withDeclConst(ConstType.builder()
.setDocumentation(decl.getDocumentation())
.setName(decl.getName())
.setType(decl.getType())
.setValue(decl.getValue())
.setAnnotations(makeAnnotations(decl.getAnnotations()))
.build());
}
private net.morimekta.providence.model.Declaration convertMessage(MessageDeclaration decl) {
return withDeclMessage(MessageType.builder()
.setDocumentation(decl.getDocumentation())
.setVariant(MessageVariant.findByName(decl.getVariant().toString()))
.setName(decl.getName())
.setImplementing(decl.getImplementing() == null ? null : decl.getImplementing().toString())
.setFields(convertFields(decl.getFields()))
.setAnnotations(makeAnnotations(decl.getAnnotations()))
.build());
}
private net.morimekta.providence.model.Declaration convertEnum(EnumDeclaration decl) {
List<EnumValue> values = new ArrayList<>();
for (EnumValueDeclaration value : decl.getValues()) {
values.add(EnumValue.builder()
.setDocumentation(value.getDocumentation())
.setName(value.getName())
.setId(value.getIdToken() == null ? null : value.getId())
.setAnnotations(makeAnnotations(value.getAnnotations()))
.build());
}
return withDeclEnum(EnumType.builder()
.setDocumentation(decl.getDocumentation())
.setName(decl.getName())
.setValues(values)
.setAnnotations(makeAnnotations(decl.getAnnotations()))
.build());
}
private List<FieldType> convertFields(List<FieldDeclaration> fields) {
if (fields == null) return null;
List<FieldType> out = new ArrayList<>();
for (FieldDeclaration field : fields) {
out.add(convertField(field));
}
return out;
}
private FieldType convertField(FieldDeclaration decl) {
return FieldType.builder()
.setDocumentation(decl.getDocumentation())
.setId(decl.getIdToken() == null ? null : decl.getId())
.setRequirement(decl.getRequirementToken() == null
? null
: FieldRequirement.findByName(decl.getRequirement().name()))
.setType(decl.getType())
.setName(decl.getName())
.setDefaultValue(decl.getDefaultValue())
.setAnnotations(makeAnnotations(decl.getAnnotations()))
.build();
}
private Map<String, String> makeAnnotations(List<AnnotationDeclaration> annotations) {
if (annotations == null) return null;
Map<String,String> map = new HashMap<>();
for (AnnotationDeclaration annotation : annotations) {
map.put(annotation.getTag(), annotation.getValue());
}
if (map.isEmpty()) return null;
return map;
}
private net.morimekta.providence.model.Declaration convertTypedef(TypedefDeclaration declaration) {
return withDeclTypedef(TypedefType.builder()
.setDocumentation(declaration.getDocumentation())
.setName(declaration.getName())
.setType(declaration.getType())
.build());
}
}