• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.csuite.core;
18 
19 import com.android.tradefed.config.IConfiguration;
20 import com.android.tradefed.config.Option;
21 import com.android.tradefed.config.Option.Importance;
22 
23 import com.google.common.annotations.VisibleForTesting;
24 import com.google.common.base.Preconditions;
25 import com.google.common.io.Resources;
26 
27 import java.io.IOException;
28 import java.nio.charset.StandardCharsets;
29 import java.nio.file.Path;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 
35 public final class ModuleTemplate {
36     @VisibleForTesting static final String XML_FILE_EXTENSION = ".xml";
37     @VisibleForTesting static final String TEMPLATE_FILE_EXTENSION = ".xml.template";
38 
39     @VisibleForTesting
40     static final String MODULE_TEMPLATE_PROVIDER_OBJECT_TYPE = "MODULE_TEMPLATE_PROVIDER";
41 
42     @VisibleForTesting static final String DEFAULT_TEMPLATE_OPTION = "default-template";
43     @VisibleForTesting static final String EXTRA_TEMPLATES_OPTION = "extra-templates";
44     @VisibleForTesting static final String TEMPLATE_ROOT_OPTION = "template-root";
45 
46     @Option(
47             name = DEFAULT_TEMPLATE_OPTION,
48             description = "The default module config template resource path.",
49             importance = Importance.ALWAYS)
50     private String mDefaultTemplate;
51 
52     @Option(
53             name = TEMPLATE_ROOT_OPTION,
54             description = "The root path of the template files.",
55             importance = Importance.ALWAYS)
56     private String mTemplateRoot;
57 
58     @Option(
59             name = EXTRA_TEMPLATES_OPTION,
60             description = "Extra module config template resource paths.",
61             importance = Importance.NEVER)
62     private List<String> mExtraTemplates = new ArrayList<>();
63 
64     private final ResourceLoader mResourceLoader;
65     private String mDefaultTemplateContent;
66     private Map<String, String> mTemplateContentMap;
67     private Map<String, String> mTemplateMapping;
68 
69     /**
70      * Load the ModuleTemplate object from a suite configuration.
71      *
72      * <p>An error will be thrown if there's no such objects or more than one objects.
73      *
74      * @param configuration The suite configuration.
75      * @return A ModuleTemplate object.
76      * @throws IOException
77      */
loadFrom(IConfiguration configuration)78     public static ModuleTemplate loadFrom(IConfiguration configuration) throws IOException {
79         List<?> moduleTemplates =
80                 configuration.getConfigurationObjectList(MODULE_TEMPLATE_PROVIDER_OBJECT_TYPE);
81         Preconditions.checkNotNull(
82                 moduleTemplates, "Missing " + MODULE_TEMPLATE_PROVIDER_OBJECT_TYPE);
83         Preconditions.checkArgument(
84                 moduleTemplates.size() == 1,
85                 "Only one module template object is expected. Found " + moduleTemplates.size());
86         ModuleTemplate moduleTemplate = (ModuleTemplate) moduleTemplates.get(0);
87         moduleTemplate.init(configuration);
88         return moduleTemplate;
89     }
90 
ModuleTemplate()91     public ModuleTemplate() {
92         this(new ClassResourceLoader());
93     }
94 
95     @VisibleForTesting
ModuleTemplate(ResourceLoader resourceLoader)96     ModuleTemplate(ResourceLoader resourceLoader) {
97         mResourceLoader = resourceLoader;
98     }
99 
100     @SuppressWarnings("MustBeClosedChecker")
init(IConfiguration configuration)101     private void init(IConfiguration configuration) throws IOException {
102         if (mDefaultTemplateContent != null) { // Already loaded.
103             return;
104         }
105 
106         mTemplateContentMap = new HashMap<>();
107 
108         String defaultTemplateContent = mResourceLoader.load(mDefaultTemplate);
109         mDefaultTemplateContent = defaultTemplateContent;
110         mTemplateContentMap.put(
111                 getTemplateNameFromTemplateFile(mDefaultTemplate), defaultTemplateContent);
112 
113         for (String extraTemplate : mExtraTemplates) {
114             mTemplateContentMap.put(
115                     getTemplateNameFromTemplateFile(extraTemplate),
116                     mResourceLoader.load(extraTemplate));
117         }
118 
119         mTemplateMapping = new HashMap<>();
120 
121         List<?> templateMappingObjects =
122                 configuration.getConfigurationObjectList(
123                         TemplateMappingProvider.TEMPLATE_MAPPING_PROVIDER_OBJECT_TYPE);
124 
125         if (templateMappingObjects == null) { // No mapping objects found.
126             return;
127         }
128 
129         for (Object provider : templateMappingObjects) {
130             ((TemplateMappingProvider) provider)
131                     .get()
132                     .forEach(
133                             entry -> {
134                                 String moduleName = entry.getKey();
135                                 String templateName =
136                                         getTemplateNameFromTemplateMapping(entry.getValue());
137 
138                                 Preconditions.checkArgument(
139                                         !mTemplateMapping.containsKey(moduleName),
140                                         "Duplicated module template map key: " + moduleName);
141                                 Preconditions.checkArgument(
142                                         mTemplateContentMap.containsKey(templateName),
143                                         "The template specified in module template map does not"
144                                                 + " exist: "
145                                                 + templateName);
146 
147                                 mTemplateMapping.put(moduleName, templateName);
148                             });
149         }
150     }
151 
getTemplateNameFromTemplateMapping(String name)152     private String getTemplateNameFromTemplateMapping(String name) {
153         String fileName = Path.of(name).toString();
154         if (fileName.toLowerCase().endsWith(XML_FILE_EXTENSION)) {
155             return fileName.substring(0, fileName.length() - XML_FILE_EXTENSION.length());
156         }
157         return fileName;
158     }
159 
getTemplateNameFromTemplateFile(String path)160     private String getTemplateNameFromTemplateFile(String path) {
161         Preconditions.checkArgument(
162                 path.endsWith(TEMPLATE_FILE_EXTENSION),
163                 "Unexpected file extension for template path: " + path);
164         String fileName = Path.of(mTemplateRoot).relativize(Path.of(path)).toString();
165         return fileName.substring(0, fileName.length() - TEMPLATE_FILE_EXTENSION.length());
166     }
167 
substitute(String moduleName, Map<String, String> replacementPairs)168     public String substitute(String moduleName, Map<String, String> replacementPairs) {
169         Preconditions.checkNotNull(
170                 mDefaultTemplateContent, "The module template object is not fully loaded.");
171         return replacementPairs.keySet().stream()
172                 .reduce(
173                         getTemplateContent(moduleName),
174                         (res, placeholder) ->
175                                 res.replace(placeholder, replacementPairs.get(placeholder)));
176     }
177 
getTemplateContent(String moduleName)178     private String getTemplateContent(String moduleName) {
179         if (!mTemplateMapping.containsKey(moduleName)) {
180             return mDefaultTemplateContent;
181         }
182 
183         return mTemplateContentMap.get(mTemplateMapping.get(moduleName));
184     }
185 
186     public interface ResourceLoader {
load(String resourceName)187         String load(String resourceName) throws IOException;
188     }
189 
190     public static final class ClassResourceLoader implements ResourceLoader {
191         @Override
load(String resourceName)192         public String load(String resourceName) throws IOException {
193             return Resources.toString(
194                     getClass().getClassLoader().getResource(resourceName), StandardCharsets.UTF_8);
195         }
196     }
197 }
198