Bump yangtools to 13.0.0
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / JavaFileGenerator.java
1 /*
2  * Copyright (c) 2020 PATHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.java.api.generator;
9
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;
17 import java.io.File;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.ServiceLoader;
22 import java.util.Set;
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.GeneratedType;
26 import org.opendaylight.mdsal.binding.model.api.Type;
27 import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
28 import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
29 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFile;
30 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileLifecycle;
31 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFilePath;
32 import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileType;
33 import org.opendaylight.yangtools.plugin.generator.api.ModuleResourceResolver;
34 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
35 import org.opendaylight.yangtools.yang.binding.contract.Naming;
36 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
37 import org.opendaylight.yangtools.yang.model.api.Module;
38 import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 final class JavaFileGenerator implements FileGenerator {
43     public static final String CONFIG_IGNORE_DUPLICATE_FILES = "ignoreDuplicateFiles";
44
45     private static final Logger LOG = LoggerFactory.getLogger(JavaFileGenerator.class);
46     private static final CharMatcher DOT_MATCHER = CharMatcher.is('.');
47     private static final String MODULE_INFO = Naming.MODULE_INFO_CLASS_NAME + ".java";
48     private static final String MODEL_BINDING_PROVIDER = Naming.MODEL_BINDING_PROVIDER_CLASS_NAME + ".java";
49     private static final GeneratedFilePath MODEL_BINDING_PROVIDER_SERVICE =
50         GeneratedFilePath.ofPath("META-INF/services/" + YangModelBindingProvider.class.getName());
51     private static final List<CodeGenerator> GENERATORS = List.of(
52         new InterfaceGenerator(), new TOGenerator(), new EnumGenerator(), new BuilderGenerator());
53
54     private final BindingGenerator bindingGenerator;
55     private final boolean ignoreDuplicateFiles;
56
57     JavaFileGenerator(final Map<String, String> configuration) {
58         final String ignoreDuplicateFilesString = configuration.get(CONFIG_IGNORE_DUPLICATE_FILES);
59         if (ignoreDuplicateFilesString != null) {
60             ignoreDuplicateFiles = Boolean.parseBoolean(ignoreDuplicateFilesString);
61         } else {
62             ignoreDuplicateFiles = true;
63         }
64         bindingGenerator = ServiceLoader.load(BindingGenerator.class).findFirst()
65             .orElseThrow(() -> new IllegalStateException("No BindingGenerator implementation found"));
66     }
67
68     @Override
69     public Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generateFiles(final EffectiveModelContext context,
70             final Set<Module> localModules, final ModuleResourceResolver moduleResourcePathResolver)
71                 throws FileGeneratorException {
72         final Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> result =
73             generateFiles(bindingGenerator.generateTypes(context, localModules), ignoreDuplicateFiles);
74
75         // YangModuleInfo files
76         final Builder<String> bindingProviders = ImmutableSet.builder();
77         for (Module module : localModules) {
78             final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, context,
79                 mod -> moduleResourcePathResolver.findModuleResourcePath(mod, YangTextSource.class));
80             final String path = DOT_MATCHER.replaceFrom(template.getPackageName(), '/') + "/";
81
82             result.put(GeneratedFileType.SOURCE, GeneratedFilePath.ofPath(path + MODULE_INFO),
83                 new SupplierGeneratedFile(GeneratedFileLifecycle.TRANSIENT, template::generate));
84             result.put(GeneratedFileType.SOURCE, GeneratedFilePath.ofPath(path + MODEL_BINDING_PROVIDER),
85                 new SupplierGeneratedFile(GeneratedFileLifecycle.TRANSIENT, template::generateModelProvider));
86
87             bindingProviders.add(template.getModelBindingProviderName());
88         }
89
90         // META-INF/services entries, sorted to make the build predictable
91         final List<String> sorted = new ArrayList<>(bindingProviders.build());
92         sorted.sort(String::compareTo);
93
94         result.put(GeneratedFileType.RESOURCE, MODEL_BINDING_PROVIDER_SERVICE,
95             GeneratedFile.of(GeneratedFileLifecycle.TRANSIENT, String.join("\n", sorted)));
96
97         return ImmutableTable.copyOf(result);
98     }
99
100     @VisibleForTesting
101     static Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generateFiles(final List<GeneratedType> types,
102             final boolean ignoreDuplicateFiles) {
103         final Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> result = HashBasedTable.create();
104
105         for (Type type : types) {
106             for (CodeGenerator generator : GENERATORS) {
107                 if (!generator.isAcceptable(type)) {
108                     continue;
109                 }
110
111                 final GeneratedFilePath file =  GeneratedFilePath.ofFilePath(
112                     type.getPackageName().replace('.', File.separatorChar)
113                     + File.separator + generator.getUnitName(type) + ".java");
114
115                 if (result.contains(GeneratedFileType.SOURCE, file)) {
116                     if (ignoreDuplicateFiles) {
117                         LOG.warn("Naming conflict for type '{}': file with same name already exists and will not be "
118                                 + "generated.", type.getFullyQualifiedName());
119                         continue;
120                     }
121                     throw new IllegalStateException("Duplicate file '" + file.getPath() + "' for "
122                         + type.getFullyQualifiedName());
123                 }
124
125                 result.put(GeneratedFileType.SOURCE, file,
126                     new CodeGeneratorGeneratedFile(GeneratedFileLifecycle.TRANSIENT, generator, type));
127             }
128         }
129
130         return result;
131     }
132 }