Added support for discovering YangModuleInfo via ServiceLoader.
[yangtools.git] / code-generator / maven-sal-api-gen-plugin / src / main / java / org / opendaylight / yangtools / maven / sal / api / gen / plugin / CodeGeneratorImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. 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.yangtools.maven.sal.api.gen.plugin;
9
10 import java.io.BufferedWriter;
11 import java.io.File;
12 import java.io.IOException;
13 import java.io.OutputStream;
14 import java.io.OutputStreamWriter;
15 import java.io.Writer;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21
22 import org.apache.maven.plugin.logging.Log;
23 import org.apache.maven.project.MavenProject;
24 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
25 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
26 import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl;
27 import org.opendaylight.yangtools.sal.binding.model.api.Type;
28 import org.opendaylight.yangtools.sal.java.api.generator.GeneratorJavaFile;
29 import org.opendaylight.yangtools.sal.java.api.generator.YangModuleInfoTemplate;
30 import org.opendaylight.yangtools.yang.binding.BindingMapping;
31 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
32 import org.opendaylight.yangtools.yang.model.api.Module;
33 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
34 import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
35 import org.opendaylight.yangtools.yang2sources.spi.CodeGenerator;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.sonatype.plexus.build.incremental.BuildContext;
39
40 import com.google.common.base.Joiner;
41 import com.google.common.base.Preconditions;
42 import com.google.common.collect.ImmutableSet;
43 import com.google.common.collect.ImmutableSet.Builder;
44
45 public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware {
46     private static final String FS = File.separator;
47     private BuildContext buildContext;
48     private File projectBaseDir;
49     private Map<String, String> additionalConfig;
50
51     private static final Logger logger = LoggerFactory.getLogger(CodeGeneratorImpl.class);
52     private MavenProject mavenProject;
53     private File resourceBaseDir;
54
55     @Override
56     public Collection<File> generateSources(final SchemaContext context, final File outputDir,
57             final Set<Module> yangModules) throws IOException {
58         final File outputBaseDir;
59
60         outputBaseDir = outputDir == null ? getDefaultOutputBaseDir() : outputDir;
61
62         final BindingGenerator bindingGenerator = new BindingGeneratorImpl();
63         final List<Type> types = bindingGenerator.generateTypes(context, yangModules);
64         final GeneratorJavaFile generator = new GeneratorJavaFile(buildContext, new HashSet<>(types));
65
66         File persistentSourcesDir = null;
67         if (additionalConfig != null) {
68             String persistenSourcesPath = additionalConfig.get("persistentSourcesDir");
69             if (persistenSourcesPath != null) {
70                 persistentSourcesDir = new File(persistenSourcesPath);
71             }
72         }
73         if (persistentSourcesDir == null) {
74             persistentSourcesDir = new File(projectBaseDir, "src" + FS + "main" + FS + "java");
75         }
76
77         List<File> result = generator.generateToFile(outputBaseDir, persistentSourcesDir);
78
79         result.addAll(generateModuleInfos(outputBaseDir, yangModules, context));
80         return result;
81     }
82
83     private Collection<? extends File> generateModuleInfos(File outputBaseDir, Set<Module> yangModules,
84             SchemaContext context) {
85         Builder<File> result = ImmutableSet.builder();
86         Builder<String> bindingProviders = ImmutableSet.builder();
87         for (Module module : yangModules) {
88             result.addAll(generateYangModuleInfo(outputBaseDir, module, context, bindingProviders));
89         }
90
91         result.add(writeMetaInfServices(resourceBaseDir, YangModelBindingProvider.class, bindingProviders.build()));
92         return result.build();
93     }
94
95     private File writeMetaInfServices(File outputBaseDir, Class<YangModelBindingProvider> serviceClass,
96             ImmutableSet<String> services) {
97         File metainfServicesFolder = new File(outputBaseDir, "META-INF" + File.separator + "services");
98         metainfServicesFolder.mkdirs();
99         File serviceFile = new File(metainfServicesFolder, serviceClass.getName());
100
101         String src = Joiner.on('\n').join(services);
102
103         return writeFile(serviceFile, src);
104     }
105
106     public static final String DEFAULT_OUTPUT_BASE_DIR_PATH = "target" + File.separator + "generated-sources"
107             + File.separator + "maven-sal-api-gen";
108
109     private File getDefaultOutputBaseDir() {
110         File outputBaseDir;
111         outputBaseDir = new File(DEFAULT_OUTPUT_BASE_DIR_PATH);
112         setOutputBaseDirAsSourceFolder(outputBaseDir, mavenProject);
113         logger.debug("Adding " + outputBaseDir.getPath() + " as compile source root");
114         return outputBaseDir;
115     }
116
117     private static void setOutputBaseDirAsSourceFolder(File outputBaseDir, MavenProject mavenProject) {
118         Preconditions.checkNotNull(mavenProject, "Maven project needs to be set in this phase");
119         mavenProject.addCompileSourceRoot(outputBaseDir.getPath());
120     }
121
122     @Override
123     public void setLog(Log log) {
124     }
125
126     @Override
127     public void setAdditionalConfig(Map<String, String> additionalConfiguration) {
128         this.additionalConfig = additionalConfiguration;
129     }
130
131     @Override
132     public void setResourceBaseDir(File resourceBaseDir) {
133         this.resourceBaseDir = resourceBaseDir;
134     }
135
136     @Override
137     public void setMavenProject(MavenProject project) {
138         this.mavenProject = project;
139         this.projectBaseDir = project.getBasedir();
140     }
141
142     @Override
143     public void setBuildContext(BuildContext buildContext) {
144         this.buildContext = Preconditions.checkNotNull(buildContext);
145     }
146
147     private Set<File> generateYangModuleInfo(File outputBaseDir, Module module, SchemaContext ctx,
148             Builder<String> providerSourceSet) {
149         Builder<File> generatedFiles = ImmutableSet.<File> builder();
150
151         final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, ctx);
152         String moduleInfoSource = template.generate();
153         if (moduleInfoSource.isEmpty()) {
154             throw new IllegalStateException("Generated code should not be empty!");
155         }
156         String providerSource = template.generateModelProvider();
157
158         final File packageDir = GeneratorJavaFile.packageToDirectory(outputBaseDir,
159                 BindingGeneratorUtil.moduleNamespaceToPackageName(module));
160
161         generatedFiles.add(writeJavaSource(packageDir, BindingMapping.MODULE_INFO_CLASS_NAME, moduleInfoSource));
162         generatedFiles
163                 .add(writeJavaSource(packageDir, BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME, providerSource));
164         providerSourceSet.add(template.getModelBindingProviderName());
165
166         return generatedFiles.build();
167
168     }
169
170     private File writeJavaSource(File packageDir, String className, String source) {
171         final File file = new File(packageDir, className + ".java");
172         writeFile(file, source);
173         return file;
174     }
175
176     private File writeFile(File file, String source) {
177         try (final OutputStream stream = buildContext.newFileOutputStream(file)) {
178             try (final Writer fw = new OutputStreamWriter(stream)) {
179                 try (final BufferedWriter bw = new BufferedWriter(fw)) {
180                     bw.write(source);
181                 }
182             } catch (Exception e) {
183                 logger.error("Could not write file: {}",file,e);
184             }
185         } catch (Exception e) {
186             logger.error("Could not create file: {}",file,e);
187         }
188         return file;
189     }
190
191 }