/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.mdsal.binding.maven.api.gen.plugin; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import org.apache.maven.project.MavenProject; import org.opendaylight.mdsal.binding.generator.api.BindingGenerator; import org.opendaylight.mdsal.binding.generator.impl.BindingGeneratorImpl; import org.opendaylight.mdsal.binding.java.api.generator.GeneratorJavaFile; import org.opendaylight.mdsal.binding.java.api.generator.YangModuleInfoTemplate; import org.opendaylight.mdsal.binding.model.api.Type; import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil; import org.opendaylight.yangtools.yang.binding.BindingMapping; import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator; import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware; import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonatype.plexus.build.incremental.BuildContext; public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContextAware, MavenProjectAware { private static final Logger LOG = LoggerFactory.getLogger(CodeGeneratorImpl.class); private static final String FS = File.separator; private BuildContext buildContext; private File projectBaseDir; private Map additionalConfig; private MavenProject mavenProject; private File resourceBaseDir; @Override public Collection generateSources(final SchemaContext context, final File outputDir, final Set yangModules, final Function> moduleResourcePathResolver) throws IOException { final File outputBaseDir; outputBaseDir = outputDir == null ? getDefaultOutputBaseDir() : outputDir; final BindingGenerator bindingGenerator = new BindingGeneratorImpl(true); final List types = bindingGenerator.generateTypes(context, yangModules); final GeneratorJavaFile generator = new GeneratorJavaFile(buildContext, types); File persistentSourcesDir = null; if (additionalConfig != null) { String persistenSourcesPath = additionalConfig.get("persistentSourcesDir"); if (persistenSourcesPath != null) { persistentSourcesDir = new File(persistenSourcesPath); } } if (persistentSourcesDir == null) { persistentSourcesDir = new File(projectBaseDir, "src" + FS + "main" + FS + "java"); } List result = generator.generateToFile(outputBaseDir, persistentSourcesDir); result.addAll(generateModuleInfos(outputBaseDir, yangModules, context, moduleResourcePathResolver)); return result; } private Collection generateModuleInfos(final File outputBaseDir, final Set yangModules, final SchemaContext context, final Function> moduleResourcePathResolver) { Builder result = ImmutableSet.builder(); Builder bindingProviders = ImmutableSet.builder(); for (Module module : yangModules) { Builder currentProvidersBuilder = ImmutableSet.builder(); // TODO: do not mutate parameters, output of a method is defined by its return value Set moduleInfoProviders = generateYangModuleInfo(outputBaseDir, module, context, moduleResourcePathResolver, currentProvidersBuilder); ImmutableSet currentProviders = currentProvidersBuilder.build(); LOG.info("Adding ModuleInfo providers {}", currentProviders); bindingProviders.addAll(currentProviders); result.addAll(moduleInfoProviders); } result.add(writeMetaInfServices(resourceBaseDir, YangModelBindingProvider.class, bindingProviders.build())); return result.build(); } private File writeMetaInfServices(final File outputBaseDir, final Class serviceClass, final ImmutableSet services) { File metainfServicesFolder = new File(outputBaseDir, "META-INF" + File.separator + "services"); metainfServicesFolder.mkdirs(); File serviceFile = new File(metainfServicesFolder, serviceClass.getName()); String src = Joiner.on('\n').join(services); return writeFile(serviceFile, src); } public static final String DEFAULT_OUTPUT_BASE_DIR_PATH = "target" + File.separator + "generated-sources" + File.separator + "maven-sal-api-gen"; private File getDefaultOutputBaseDir() { File outputBaseDir; outputBaseDir = new File(DEFAULT_OUTPUT_BASE_DIR_PATH); setOutputBaseDirAsSourceFolder(outputBaseDir, mavenProject); LOG.debug("Adding " + outputBaseDir.getPath() + " as compile source root"); return outputBaseDir; } private static void setOutputBaseDirAsSourceFolder(final File outputBaseDir, final MavenProject mavenProject) { Preconditions.checkNotNull(mavenProject, "Maven project needs to be set in this phase"); mavenProject.addCompileSourceRoot(outputBaseDir.getPath()); } @Override public void setAdditionalConfig(final Map additionalConfiguration) { this.additionalConfig = additionalConfiguration; } @Override public void setResourceBaseDir(final File resourceBaseDir) { this.resourceBaseDir = resourceBaseDir; } @Override public void setMavenProject(final MavenProject project) { this.mavenProject = project; this.projectBaseDir = project.getBasedir(); } @Override public void setBuildContext(final BuildContext buildContext) { this.buildContext = Preconditions.checkNotNull(buildContext); } private Set generateYangModuleInfo(final File outputBaseDir, final Module module, final SchemaContext ctx, final Function> moduleResourcePathResolver, final Builder providerSourceSet) { Builder generatedFiles = ImmutableSet.builder(); final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, ctx, moduleResourcePathResolver); String moduleInfoSource = template.generate(); if (moduleInfoSource.isEmpty()) { throw new IllegalStateException("Generated code should not be empty!"); } String providerSource = template.generateModelProvider(); final File packageDir = GeneratorJavaFile.packageToDirectory(outputBaseDir, BindingGeneratorUtil.moduleNamespaceToPackageName(module)); generatedFiles.add(writeJavaSource(packageDir, BindingMapping.MODULE_INFO_CLASS_NAME, moduleInfoSource)); generatedFiles .add(writeJavaSource(packageDir, BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME, providerSource)); providerSourceSet.add(template.getModelBindingProviderName()); return generatedFiles.build(); } private File writeJavaSource(final File packageDir, final String className, final String source) { if (!packageDir.exists()) { packageDir.mkdirs(); } final File file = new File(packageDir, className + ".java"); writeFile(file, source); return file; } @SuppressWarnings("checkstyle:illegalCatch") private File writeFile(final File file, final String source) { try (OutputStream stream = buildContext.newFileOutputStream(file)) { try (Writer fw = new OutputStreamWriter(stream, StandardCharsets.UTF_8)) { try (BufferedWriter bw = new BufferedWriter(fw)) { bw.write(source); } } catch (Exception e) { LOG.error("Could not write file: {}",file,e); } } catch (Exception e) { LOG.error("Could not create file: {}",file,e); } return file; } }