X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmaven-sal-api-gen-plugin%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fmaven%2Fapi%2Fgen%2Fplugin%2FCodeGeneratorImpl.java;h=499e030836b7297eb60a0fb9487f4bcb9cbb7672;hb=c0c76a53d25048d86fcd60e16b68fe942672b85a;hp=0eaa68d00b439e1c81f44608db2f38432b662b01;hpb=2119f6e6919b1766ea4b3121c77f36e72196ad26;p=mdsal.git diff --git a/binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/maven/api/gen/plugin/CodeGeneratorImpl.java b/binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/maven/api/gen/plugin/CodeGeneratorImpl.java index 0eaa68d00b..499e030836 100644 --- a/binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/maven/api/gen/plugin/CodeGeneratorImpl.java +++ b/binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/maven/api/gen/plugin/CodeGeneratorImpl.java @@ -7,10 +7,18 @@ */ package org.opendaylight.mdsal.binding.maven.api.gen.plugin; +import static java.util.Objects.requireNonNull; + import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.MultimapBuilder; +import com.google.common.collect.Table; +import com.google.common.collect.Table.Cell; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; @@ -18,23 +26,27 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ForkJoinPool; import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; 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.generator.util.BindingGeneratorUtil; import org.opendaylight.mdsal.binding.java.api.generator.GeneratorJavaFile; +import org.opendaylight.mdsal.binding.java.api.generator.GeneratorJavaFile.FileKind; import org.opendaylight.mdsal.binding.java.api.generator.YangModuleInfoTemplate; import org.opendaylight.mdsal.binding.model.api.Type; -import org.opendaylight.yangtools.yang.binding.BindingMapping; +import org.opendaylight.mdsal.binding.spec.naming.BindingMapping; import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; 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; @@ -43,6 +55,9 @@ import org.slf4j.LoggerFactory; import org.sonatype.plexus.build.incremental.BuildContext; public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContextAware, MavenProjectAware { + public static final String CONFIG_PERSISTENT_SOURCES_DIR = "persistentSourcesDir"; + public static final String CONFIG_IGNORE_DUPLICATE_FILES = "ignoreDuplicateFiles"; + private static final Logger LOG = LoggerFactory.getLogger(CodeGeneratorImpl.class); private static final String FS = File.separator; private BuildContext buildContext; @@ -52,36 +67,88 @@ public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContext private File resourceBaseDir; @Override - public Collection generateSources(final SchemaContext context, final File outputDir, + public Collection generateSources(final EffectiveModelContext 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); + // Step one: determine binding types which we are generating + final Stopwatch sw = Stopwatch.createStarted(); + final List types = new BindingGeneratorImpl().generateTypes(context, yangModules); + LOG.info("Found {} Binding types in {}", types.size(), sw); + + final GeneratorJavaFile generator = new GeneratorJavaFile(types); File persistentSourcesDir = null; + boolean ignoreDuplicateFiles = true; if (additionalConfig != null) { - String persistenSourcesPath = additionalConfig.get("persistentSourcesDir"); + String persistenSourcesPath = additionalConfig.get(CONFIG_PERSISTENT_SOURCES_DIR); if (persistenSourcesPath != null) { persistentSourcesDir = new File(persistenSourcesPath); } + String ignoreDuplicateFilesString = additionalConfig.get(CONFIG_IGNORE_DUPLICATE_FILES); + if (ignoreDuplicateFilesString != null) { + ignoreDuplicateFiles = Boolean.parseBoolean(ignoreDuplicateFilesString); + } } if (persistentSourcesDir == null) { persistentSourcesDir = new File(projectBaseDir, "src" + FS + "main" + FS + "java"); } - List result = generator.generateToFile(outputBaseDir, persistentSourcesDir); + final Table> generatedFiles = generator.generateFileContent( + ignoreDuplicateFiles); + + // Step two: create generation tasks for each target file and group them by parent directory + final ListMultimap dirs = MultimapBuilder.hashKeys().arrayListValues().build(); + for (Cell> cell : generatedFiles.cellSet()) { + final File target; + switch (cell.getRowKey()) { + case PERSISTENT: + target = new File(persistentSourcesDir, cell.getColumnKey()); + if (target.exists()) { + LOG.debug("Skipping existing persistent {}", target); + continue; + } + break; + case TRANSIENT: + target = new File(outputBaseDir, cell.getColumnKey()); + break; + default: + throw new IllegalStateException("Unsupported file type in " + cell); + } + + dirs.put(target.getParentFile().toPath(), new GenerationTask(buildContext, target, cell.getValue())); + } + LOG.info("Generating {} Binding source files into {} directories", dirs.size(), dirs.keySet().size()); + + // Step three: submit parent directory creation tasks (via parallelStream()) and wait for them to complete + sw.reset().start(); + dirs.keySet().parallelStream().forEach(path -> { + try { + Files.createDirectories(path); + } catch (IOException e) { + throw new IllegalStateException("Failed to create " + path, e); + } + }); + LOG.debug("Parent directories created in {}", sw); + // Step four: submit all code generation tasks (via parallelStream()) and wait for them to complete + final ListeningExecutorService service = MoreExecutors.listeningDecorator(ForkJoinPool.commonPool()); + sw.reset().start(); + final List result = dirs.values().parallelStream() + .map(GenerationTask::generateFile) + .collect(Collectors.toList()); + LOG.debug("{} Binding source type files generated in {}", result.size(), sw); + + // Step five: generate auxiliary files result.addAll(generateModuleInfos(outputBaseDir, yangModules, context, moduleResourcePathResolver)); return result; } private Collection generateModuleInfos(final File outputBaseDir, final Set yangModules, - final SchemaContext context, final Function> moduleResourcePathResolver) { + final EffectiveModelContext context, final Function> moduleResourcePathResolver) { Builder result = ImmutableSet.builder(); Builder bindingProviders = ImmutableSet.builder(); for (Module module : yangModules) { @@ -90,7 +157,7 @@ public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContext Set moduleInfoProviders = generateYangModuleInfo(outputBaseDir, module, context, moduleResourcePathResolver, currentProvidersBuilder); ImmutableSet currentProviders = currentProvidersBuilder.build(); - LOG.info("Adding ModuleInfo providers {}", currentProviders); + LOG.debug("Adding ModuleInfo providers {}", currentProviders); bindingProviders.addAll(currentProviders); result.addAll(moduleInfoProviders); } @@ -117,12 +184,12 @@ public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContext File outputBaseDir; outputBaseDir = new File(DEFAULT_OUTPUT_BASE_DIR_PATH); setOutputBaseDirAsSourceFolder(outputBaseDir, mavenProject); - LOG.debug("Adding " + outputBaseDir.getPath() + " as compile source root"); + LOG.debug("Adding {} as compile source root", outputBaseDir.getPath()); return outputBaseDir; } private static void setOutputBaseDirAsSourceFolder(final File outputBaseDir, final MavenProject mavenProject) { - Preconditions.checkNotNull(mavenProject, "Maven project needs to be set in this phase"); + requireNonNull(mavenProject, "Maven project needs to be set in this phase"); mavenProject.addCompileSourceRoot(outputBaseDir.getPath()); } @@ -144,13 +211,13 @@ public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContext @Override public void setBuildContext(final BuildContext buildContext) { - this.buildContext = Preconditions.checkNotNull(buildContext); + this.buildContext = requireNonNull(buildContext); } - private Set generateYangModuleInfo(final File outputBaseDir, final Module module, final SchemaContext ctx, - final Function> moduleResourcePathResolver, + private Set generateYangModuleInfo(final File outputBaseDir, final Module module, + final EffectiveModelContext ctx, final Function> moduleResourcePathResolver, final Builder providerSourceSet) { - Builder generatedFiles = ImmutableSet. builder(); + Builder generatedFiles = ImmutableSet.builder(); final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, ctx, moduleResourcePathResolver); String moduleInfoSource = template.generate(); @@ -160,7 +227,7 @@ public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContext String providerSource = template.generateModelProvider(); final File packageDir = GeneratorJavaFile.packageToDirectory(outputBaseDir, - BindingGeneratorUtil.moduleNamespaceToPackageName(module)); + BindingMapping.getRootPackageName(module.getQNameModule())); generatedFiles.add(writeJavaSource(packageDir, BindingMapping.MODULE_INFO_CLASS_NAME, moduleInfoSource)); generatedFiles @@ -180,26 +247,46 @@ public final class CodeGeneratorImpl implements BasicCodeGenerator, BuildContext return file; } + @SuppressWarnings("checkstyle:illegalCatch") private File writeFile(final File file, final String source) { - try (final OutputStream stream = buildContext.newFileOutputStream(file)) { - try (final Writer fw = new OutputStreamWriter(stream, StandardCharsets.UTF_8)) { - try (final BufferedWriter bw = new BufferedWriter(fw)) { + 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); + LOG.error("Could not write file: {}", file, e); } } catch (Exception e) { - LOG.error("Could not create file: {}",file,e); + LOG.error("Could not create file: {}", file, e); } return file; } - @Override - public Collection generateSources(final SchemaContext context, final File outputBaseDir, final Set currentModules) - throws IOException { - return generateSources(context, outputBaseDir, currentModules, - m -> Optional.of("/" + m.getModuleSourcePath().replace(File.separator, "/"))); - } + private static final class GenerationTask { + private final BuildContext buildContext; + private final Supplier contentSupplier; + private final File target; + + GenerationTask(final BuildContext buildContext, final File target, final Supplier contentSupplier) { + this.buildContext = requireNonNull(buildContext); + this.target = requireNonNull(target); + this.contentSupplier = requireNonNull(contentSupplier); + } + File generateFile() { + try { + try (OutputStream stream = buildContext.newFileOutputStream(target)) { + try (Writer fw = new OutputStreamWriter(stream, StandardCharsets.UTF_8)) { + try (BufferedWriter bw = new BufferedWriter(fw)) { + bw.write(contentSupplier.get()); + } + } + } + } catch (IOException e) { + throw new IllegalStateException("Failed to generate file " + target, e); + } + return target; + } + } }