2 * Copyright (c) 2020 PATHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.java.api.generator;
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.CharMatcher;
12 import com.google.common.collect.HashBasedTable;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.ImmutableSet.Builder;
15 import com.google.common.collect.ImmutableTable;
16 import com.google.common.collect.Table;
18 import java.util.ArrayList;
19 import java.util.List;
21 import java.util.ServiceLoader;
23 import org.opendaylight.mdsal.binding.generator.BindingGenerator;
24 import org.opendaylight.mdsal.binding.model.api.CodeGenerator;
25 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
26 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
27 import org.opendaylight.mdsal.binding.model.api.Type;
28 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
29 import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
30 import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
31 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFile;
32 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileLifecycle;
33 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFilePath;
34 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileType;
35 import org.opendaylight.yangtools.plugin.generator.api.ModuleResourceResolver;
36 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
37 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
38 import org.opendaylight.yangtools.yang.model.api.Module;
39 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 final class JavaFileGenerator implements FileGenerator {
44 public static final String CONFIG_IGNORE_DUPLICATE_FILES = "ignoreDuplicateFiles";
46 private static final Logger LOG = LoggerFactory.getLogger(JavaFileGenerator.class);
47 private static final CharMatcher DOT_MATCHER = CharMatcher.is('.');
48 private static final String MODULE_INFO = BindingMapping.MODULE_INFO_CLASS_NAME + ".java";
49 private static final String MODEL_BINDING_PROVIDER = BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME + ".java";
50 private static final GeneratedFilePath MODEL_BINDING_PROVIDER_SERVICE =
51 GeneratedFilePath.ofPath("META-INF/services/" + YangModelBindingProvider.class.getName());
52 private static final List<CodeGenerator> GENERATORS = List.of(
53 new InterfaceGenerator(), new TOGenerator(), new EnumGenerator(), new BuilderGenerator());
55 private final BindingGenerator bindingGenerator;
56 private final boolean ignoreDuplicateFiles;
58 JavaFileGenerator(final Map<String, String> configuration) {
59 final String ignoreDuplicateFilesString = configuration.get(CONFIG_IGNORE_DUPLICATE_FILES);
60 if (ignoreDuplicateFilesString != null) {
61 ignoreDuplicateFiles = Boolean.parseBoolean(ignoreDuplicateFilesString);
63 ignoreDuplicateFiles = true;
65 bindingGenerator = ServiceLoader.load(BindingGenerator.class).findFirst()
66 .orElseThrow(() -> new IllegalStateException("No BindingGenerator implementation found"));
70 public Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generateFiles(final EffectiveModelContext context,
71 final Set<Module> localModules, final ModuleResourceResolver moduleResourcePathResolver)
72 throws FileGeneratorException {
73 final Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> result =
74 generateFiles(bindingGenerator.generateTypes(context, localModules), ignoreDuplicateFiles);
76 // YangModuleInfo files
77 final Builder<String> bindingProviders = ImmutableSet.builder();
78 for (Module module : localModules) {
79 final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, context,
80 mod -> moduleResourcePathResolver.findModuleResourcePath(mod, YangTextSchemaSource.class));
81 final String path = DOT_MATCHER.replaceFrom(template.getPackageName(), '/') + "/";
83 result.put(GeneratedFileType.SOURCE, GeneratedFilePath.ofPath(path + MODULE_INFO),
84 new SupplierGeneratedFile(GeneratedFileLifecycle.TRANSIENT, template::generate));
85 result.put(GeneratedFileType.SOURCE, GeneratedFilePath.ofPath(path + MODEL_BINDING_PROVIDER),
86 new SupplierGeneratedFile(GeneratedFileLifecycle.TRANSIENT, template::generateModelProvider));
88 bindingProviders.add(template.getModelBindingProviderName());
91 // META-INF/services entries, sorted to make the build predictable
92 final List<String> sorted = new ArrayList<>(bindingProviders.build());
93 sorted.sort(String::compareTo);
95 result.put(GeneratedFileType.RESOURCE, MODEL_BINDING_PROVIDER_SERVICE,
96 GeneratedFile.of(GeneratedFileLifecycle.TRANSIENT, String.join("\n", sorted)));
98 return ImmutableTable.copyOf(result);
102 static Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generateFiles(final List<GeneratedType> types,
103 final boolean ignoreDuplicateFiles) {
104 final Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> result = HashBasedTable.create();
106 for (Type type : types) {
107 for (CodeGenerator generator : GENERATORS) {
108 if (!generator.isAcceptable(type)) {
112 final GeneratedFileLifecycle kind = type instanceof GeneratedTransferObject
113 && ((GeneratedTransferObject) type).isUnionTypeBuilder()
114 ? GeneratedFileLifecycle.PERSISTENT : GeneratedFileLifecycle.TRANSIENT;
115 final GeneratedFilePath file = GeneratedFilePath.ofFilePath(
116 type.getPackageName().replace('.', File.separatorChar)
117 + File.separator + generator.getUnitName(type) + ".java");
119 if (result.contains(GeneratedFileType.SOURCE, file)) {
120 if (ignoreDuplicateFiles) {
121 LOG.warn("Naming conflict for type '{}': file with same name already exists and will not be "
122 + "generated.", type.getFullyQualifiedName());
125 throw new IllegalStateException("Duplicate " + kind + " file '" + file.getPath() + "' for "
126 + type.getFullyQualifiedName());
129 result.put(GeneratedFileType.SOURCE, file, new CodeGeneratorGeneratedFile(kind, generator, type));