Java tutorial
/* * ****************************************************************************** * Copyright 2014-2016 Spectra Logic Corporation. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the "license" file accompanying this file. * This file 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 com.spectralogic.ds3autogen.c; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.spectralogic.ds3autogen.api.CodeGenerator; import com.spectralogic.ds3autogen.api.FileUtils; import com.spectralogic.ds3autogen.api.models.apispec.Ds3ApiSpec; import com.spectralogic.ds3autogen.api.models.apispec.Ds3Element; import com.spectralogic.ds3autogen.api.models.apispec.Ds3Request; import com.spectralogic.ds3autogen.api.models.apispec.Ds3Type; import com.spectralogic.ds3autogen.api.models.docspec.Ds3DocSpec; import com.spectralogic.ds3autogen.c.converters.*; import com.spectralogic.ds3autogen.c.helpers.*; import com.spectralogic.ds3autogen.c.models.Enum; import com.spectralogic.ds3autogen.c.models.*; import com.spectralogic.ds3autogen.utils.ConverterUtil; import com.spectralogic.ds3autogen.utils.Ds3RequestClassificationUtil; import com.spectralogic.ds3autogen.utils.Helper; import com.spectralogic.ds3autogen.utils.collections.GuavaCollectors; import freemarker.template.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.file.Path; import java.nio.file.Paths; import java.text.ParseException; import java.util.Collection; import java.util.stream.Stream; public class CCodeGenerator implements CodeGenerator { private static final Logger LOG = LoggerFactory.getLogger(CCodeGenerator.class); private final Configuration config = new Configuration(Configuration.VERSION_2_3_23); private FileUtils fileUtils; public CCodeGenerator() throws TemplateModelException { config.setDefaultEncoding("UTF-8"); config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); config.setClassForTemplateLoading(CCodeGenerator.class, "/templates"); config.setSharedVariable("cTypeHelper", C_TypeHelper.getInstance()); config.setSharedVariable("enumHelper", EnumHelper.getInstance()); config.setSharedVariable("requestHelper", RequestHelper.getInstance()); config.setSharedVariable("helper", Helper.getInstance()); config.setSharedVariable("structHelper", StructHelper.getInstance()); config.setSharedVariable("structMemberHelper", StructMemberHelper.getInstance()); config.setSharedVariable("parameterHelper", ParameterHelper.getInstance()); } @Override public void generate(final Ds3ApiSpec spec, final FileUtils fileUtils, final Path destDir, final Ds3DocSpec docSpec) throws IOException { this.fileUtils = fileUtils; try { final ImmutableList<Request> allRequests = getAllRequests(spec, docSpec); final ImmutableList<Enum> allEnums = getAllEnums(spec); final ImmutableSet<String> enumNames = EnumHelper.getEnumNamesSet(allEnums); final ImmutableSet<String> arrayMemberTypes = getArrayMemberTypes(spec, enumNames); final ImmutableSet<String> embeddedTypes = getEmbeddedTypes(spec, enumNames); final ImmutableSet<String> responseTypes = RequestHelper.getResponseTypes(allRequests); final ImmutableSet<String> paginatedTypes = getPaginatedTypes(spec); final ImmutableList<Struct> allStructs = getAllStructs(spec, enumNames, responseTypes, embeddedTypes, arrayMemberTypes, paginatedTypes); generateHeader(allEnums, allStructs, allRequests); generateSource(allEnums, allStructs, allRequests); generateStaticFiles(); } catch (final ParseException e) { LOG.error("Caught exception: ", e); } } public void generateHeader(final ImmutableList<Enum> allEnums, final ImmutableList<Struct> allStructs, final ImmutableList<Request> allRequests) throws IOException, ParseException { final Path path = Paths.get("src/ds3.h"); final Header header = HeaderConverter.toHeader(allEnums, allStructs, allRequests); processTemplate(header, "header-templates/ds3_h.ftl", fileUtils.getOutputFile(path)); } public void generateSource(final ImmutableList<Enum> allEnums, final ImmutableList<Struct> allStructs, final ImmutableList<Request> allRequests) throws IOException, ParseException { final Source source = SourceConverter.toSource(allEnums, allStructs, allRequests); final Path path = Paths.get("src/ds3.c"); processTemplate(source, "source-templates/ds3_c.ftl", fileUtils.getOutputFile(path)); } public void generateStaticFiles() throws IOException { final Path ds3RequestPath = Paths.get("src/ds3_request.h"); processTemplate(null, "other-templates/ds3_request_h.ftl", fileUtils.getOutputFile(ds3RequestPath)); final Path ds3NetPath = Paths.get("src/ds3_net.c"); processTemplate(null, "other-templates/ds3_net_c.ftl", fileUtils.getOutputFile(ds3NetPath)); final Path ds3NetHeaderPath = Paths.get("src/ds3_net.h"); processTemplate(null, "other-templates/ds3_net_h.ftl", fileUtils.getOutputFile(ds3NetHeaderPath)); final Path ds3ConnectionPath = Paths.get("src/ds3_connection.c"); processTemplate(null, "other-templates/ds3_connection_c.ftl", fileUtils.getOutputFile(ds3ConnectionPath)); final Path ds3ConnectionHeaderPath = Paths.get("src/ds3_connection.h"); processTemplate(null, "other-templates/ds3_connection_h.ftl", fileUtils.getOutputFile(ds3ConnectionHeaderPath)); } public static ImmutableList<Enum> getAllEnums(final Ds3ApiSpec spec) throws ParseException { final ImmutableList.Builder<Enum> allEnumsBuilder = ImmutableList.builder(); if (ConverterUtil.hasContent(spec.getTypes())) { for (final Ds3Type ds3TypeEntry : spec.getTypes().values()) { if (ConverterUtil.isEmpty(ds3TypeEntry.getEnumConstants())) continue; allEnumsBuilder.add(EnumConverter.toEnum(ds3TypeEntry)); } } return allEnumsBuilder.build(); } public static ImmutableList<Struct> getAllStructs(final Ds3ApiSpec spec, final ImmutableSet<String> enumNames, final ImmutableSet<String> responseTypes, final ImmutableSet<String> embeddedTypes, final ImmutableSet<String> arrayMemberTypes, final ImmutableSet<String> paginatedTypes) throws ParseException { final ImmutableList.Builder<Struct> allStructsBuilder = ImmutableList.builder(); if (ConverterUtil.hasContent(spec.getTypes())) { for (final Ds3Type ds3TypeEntry : spec.getTypes().values()) { if (ConverterUtil.hasContent(ds3TypeEntry.getEnumConstants())) continue; final Struct structEntry = StructConverter.toStruct(ds3TypeEntry, enumNames, responseTypes, embeddedTypes, arrayMemberTypes, paginatedTypes); allStructsBuilder.add(structEntry); } } return allStructsBuilder.build(); } /** * Find all types that are used as an array member, for generation of a parser for a list of a type. Only applies * to types that are wrapped in a common element. */ public static ImmutableSet<String> getArrayMemberTypes(final Ds3ApiSpec spec, final ImmutableSet<String> enumTypes) { return spec.getTypes().values().stream().flatMap(type -> type.getElements().stream()) .filter(element -> element.getType().equalsIgnoreCase("array")) // only want types that array member types .filter(element -> !element.getComponentType().contains("UUID")) // ignore UUID .filter(element -> element.getDs3Annotations().stream() .flatMap(anno -> anno.getDs3AnnotationElements().stream()) .anyMatch(annoElem -> annoElem.getValue().equals("SINGLE_BLOCK_FOR_ALL_ELEMENTS"))) // only want wrapped array types .filter(element -> !enumTypes.contains(EnumHelper.getDs3Type(element.getComponentType()))) // ignore enums .map(element -> StructHelper.getResponseTypeName(element.getComponentType())) .collect(GuavaCollectors.immutableSet()); } /** * Find all types that are embedded members. Many 'top-level' types are not embedded and therefore those parsers * are useless. */ public static ImmutableSet<String> getEmbeddedTypes(final Ds3ApiSpec spec, final ImmutableSet<String> enumTypes) { final ImmutableSet<String> embeddedTypes = spec.getTypes().values().stream() .flatMap(type -> type.getElements().stream()) .filter(element -> !element.getType().equalsIgnoreCase("array")).map(Ds3Element::getType) .collect(GuavaCollectors.immutableSet()); final ImmutableSet<String> embeddedComponentTypes = spec.getTypes().values().stream() .flatMap(type -> type.getElements().stream()) .filter(element -> element.getType().equalsIgnoreCase("array")).map(Ds3Element::getComponentType) .collect(GuavaCollectors.immutableSet()); final ImmutableSet<String> basicTypes = ImmutableSet.of("boolean", "java.lang.Boolean", "int", "java.lang.Integer", "long", "java.lang.Long", "double", "java.lang.Double", "java.lang.String", "java.util.UUID", "java.util.Date", "java.lang.object", "com.spectralogic.util.db.lang.SqlOperation"); return Stream.of(embeddedTypes, embeddedComponentTypes).flatMap(Collection::stream) .filter(type -> !enumTypes.contains(StructHelper.getResponseTypeName(type))) .filter(type -> !basicTypes.contains(type)).map(StructHelper::getResponseTypeName).sorted() .collect(GuavaCollectors.immutableSet()); } /** * Requests with optional paging require an extra "ds3_paging" member */ public static ImmutableSet<String> getPaginatedTypes(final Ds3ApiSpec spec) { return spec.getRequests().stream().filter(Ds3RequestClassificationUtil::supportsPaginationRequest) .map(req -> RequestConverter.getResponseType(req.getDs3ResponseCodes())) .collect(GuavaCollectors.immutableSet()); } public static ImmutableList<Request> getAllRequests(final Ds3ApiSpec spec, final Ds3DocSpec docSpec) throws ParseException { final ImmutableList.Builder<Request> allRequestsBuilder = ImmutableList.builder(); if (ConverterUtil.hasContent(spec.getRequests())) { for (final Ds3Request ds3Request : spec.getRequests()) { allRequestsBuilder.add(RequestConverter.toRequest(ds3Request, docSpec)); } } return allRequestsBuilder.build(); } public void processTemplate(final Object obj, final String templateName, final OutputStream outputStream) throws IOException { final Template template = config.getTemplate(templateName); final Writer writer = new OutputStreamWriter(outputStream); try { template.process(obj, writer); } catch (final NullPointerException | TemplateException e) { LOG.error("Unable to process template " + templateName, e); } } }