• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.tradefed.config;
18 
19 import com.android.tradefed.log.LogUtil.CLog;
20 import com.android.tradefed.util.FileUtil;
21 import com.android.tradefed.util.MultiMap;
22 
23 import org.kxml2.io.KXmlSerializer;
24 
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import java.lang.reflect.Field;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.HashSet;
32 import java.util.LinkedHashMap;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Map.Entry;
37 import java.util.Set;
38 
39 /** Utility functions to handle configuration files. */
40 public class ConfigurationUtil {
41 
42     // Element names used for emitting the configuration XML.
43     public static final String CONFIGURATION_NAME = "configuration";
44     public static final String OPTION_NAME = "option";
45     public static final String CLASS_NAME = "class";
46     public static final String NAME_NAME = "name";
47     public static final String KEY_NAME = "key";
48     public static final String VALUE_NAME = "value";
49 
50     /**
51      * Create a serializer to be used to create a new configuration file.
52      *
53      * @param outputXml the XML file to write to
54      * @return a {@link KXmlSerializer}
55      */
createSerializer(File outputXml)56     static KXmlSerializer createSerializer(File outputXml) throws IOException {
57         PrintWriter output = new PrintWriter(outputXml);
58         KXmlSerializer serializer = new KXmlSerializer();
59         serializer.setOutput(output);
60         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
61         serializer.startDocument("UTF-8", null);
62         return serializer;
63     }
64 
65     /**
66      * Add a class to the configuration XML dump.
67      *
68      * @param serializer a {@link KXmlSerializer} to create the XML dump
69      * @param classTypeName a {@link String} of the class type's name
70      * @param obj {@link Object} to be added to the XML dump
71      * @param excludeClassFilter list of object configuration type or fully qualified class names to
72      *     be excluded from the dump. for example: {@link Configuration#TARGET_PREPARER_TYPE_NAME}.
73      *     com.android.tradefed.testtype.StubTest
74      * @param printDeprecatedOptions whether or not to print deprecated options
75      */
dumpClassToXml( KXmlSerializer serializer, String classTypeName, Object obj, List<String> excludeClassFilter, boolean printDeprecatedOptions)76     static void dumpClassToXml(
77             KXmlSerializer serializer,
78             String classTypeName,
79             Object obj,
80             List<String> excludeClassFilter,
81             boolean printDeprecatedOptions)
82             throws IOException {
83         dumpClassToXml(
84                 serializer, classTypeName, obj, false, excludeClassFilter, printDeprecatedOptions);
85     }
86 
87     /**
88      * Add a class to the configuration XML dump.
89      *
90      * @param serializer a {@link KXmlSerializer} to create the XML dump
91      * @param classTypeName a {@link String} of the class type's name
92      * @param obj {@link Object} to be added to the XML dump
93      * @param isGenericObject Whether or not the object is specified as <object> in the xml
94      * @param excludeClassFilter list of object configuration type or fully qualified class names to
95      *     be excluded from the dump. for example: {@link Configuration#TARGET_PREPARER_TYPE_NAME}.
96      *     com.android.tradefed.testtype.StubTest
97      * @param printDeprecatedOptions whether or not to print deprecated options
98      */
dumpClassToXml( KXmlSerializer serializer, String classTypeName, Object obj, boolean isGenericObject, List<String> excludeClassFilter, boolean printDeprecatedOptions)99     static void dumpClassToXml(
100             KXmlSerializer serializer,
101             String classTypeName,
102             Object obj,
103             boolean isGenericObject,
104             List<String> excludeClassFilter,
105             boolean printDeprecatedOptions)
106             throws IOException {
107         if (excludeClassFilter.contains(classTypeName)) {
108             return;
109         }
110         if (excludeClassFilter.contains(obj.getClass().getName())) {
111             return;
112         }
113         if (isGenericObject) {
114             serializer.startTag(null, "object");
115             serializer.attribute(null, "type", classTypeName);
116             serializer.attribute(null, CLASS_NAME, obj.getClass().getName());
117             dumpOptionsToXml(serializer, obj, printDeprecatedOptions);
118             serializer.endTag(null, "object");
119         } else {
120             serializer.startTag(null, classTypeName);
121             serializer.attribute(null, CLASS_NAME, obj.getClass().getName());
122             dumpOptionsToXml(serializer, obj, printDeprecatedOptions);
123             serializer.endTag(null, classTypeName);
124         }
125     }
126 
127     /**
128      * Add all the options of class to the command XML dump.
129      *
130      * @param serializer a {@link KXmlSerializer} to create the XML dump
131      * @param obj {@link Object} to be added to the XML dump
132      * @param printDeprecatedOptions whether or not to skip the deprecated options
133      */
134     @SuppressWarnings({"rawtypes", "unchecked"})
dumpOptionsToXml( KXmlSerializer serializer, Object obj, boolean printDeprecatedOptions)135     private static void dumpOptionsToXml(
136             KXmlSerializer serializer, Object obj, boolean printDeprecatedOptions)
137             throws IOException {
138         for (Field field : OptionSetter.getOptionFieldsForClass(obj.getClass())) {
139             Option option = field.getAnnotation(Option.class);
140             Deprecated deprecatedAnnotation = field.getAnnotation(Deprecated.class);
141             // If enabled, skip @Deprecated options
142             if (!printDeprecatedOptions && deprecatedAnnotation != null) {
143                 continue;
144             }
145             Object fieldVal = OptionSetter.getFieldValue(field, obj);
146             if (fieldVal == null) {
147                 continue;
148             } else if (fieldVal instanceof Collection) {
149                 for (Object entry : (Collection) fieldVal) {
150                     dumpOptionToXml(serializer, option.name(), null, entry.toString());
151                 }
152             } else if (fieldVal instanceof Map) {
153                 Map map = (Map) fieldVal;
154                 for (Object entryObj : map.entrySet()) {
155                     Map.Entry entry = (Entry) entryObj;
156                     dumpOptionToXml(
157                             serializer,
158                             option.name(),
159                             entry.getKey().toString(),
160                             entry.getValue().toString());
161                 }
162             } else if (fieldVal instanceof MultiMap) {
163                 MultiMap multimap = (MultiMap) fieldVal;
164                 for (Object keyObj : multimap.keySet()) {
165                     for (Object valueObj : multimap.get(keyObj)) {
166                         dumpOptionToXml(
167                                 serializer, option.name(), keyObj.toString(), valueObj.toString());
168                     }
169                 }
170             } else {
171                 dumpOptionToXml(serializer, option.name(), null, fieldVal.toString());
172             }
173         }
174     }
175 
176     /**
177      * Add a single option to the command XML dump.
178      *
179      * @param serializer a {@link KXmlSerializer} to create the XML dump
180      * @param name a {@link String} of the option's name
181      * @param key a {@link String} of the option's key, used as name if param name is null
182      * @param value a {@link String} of the option's value
183      */
dumpOptionToXml( KXmlSerializer serializer, String name, String key, String value)184     private static void dumpOptionToXml(
185             KXmlSerializer serializer, String name, String key, String value) throws IOException {
186         serializer.startTag(null, OPTION_NAME);
187         serializer.attribute(null, NAME_NAME, name);
188         if (key != null) {
189             serializer.attribute(null, KEY_NAME, key);
190         }
191         serializer.attribute(null, VALUE_NAME, value);
192         serializer.endTag(null, OPTION_NAME);
193     }
194 
195     /**
196      * Helper to get the test config files from given directories.
197      *
198      * @param subPath where to look for configuration. Can be null.
199      * @param dirs a list of {@link File} of extra directories to search for test configs
200      */
getConfigNamesFromDirs(String subPath, List<File> dirs)201     public static Set<String> getConfigNamesFromDirs(String subPath, List<File> dirs) {
202         Set<File> res = getConfigNamesFileFromDirs(subPath, dirs);
203         if (res.isEmpty()) {
204             return new HashSet<>();
205         }
206         Set<String> files = new HashSet<>();
207         res.forEach(file -> files.add(file.getAbsolutePath()));
208         return files;
209     }
210 
211     /**
212      * Helper to get the test config files from given directories.
213      *
214      * @param subPath The location where to look for configuration. Can be null.
215      * @param dirs A list of {@link File} of extra directories to search for test configs
216      * @return the set of {@link File} that were found.
217      */
getConfigNamesFileFromDirs(String subPath, List<File> dirs)218     public static Set<File> getConfigNamesFileFromDirs(String subPath, List<File> dirs) {
219         List<String> patterns = new ArrayList<>();
220         patterns.add(".*\\.config$");
221         patterns.add(".*\\.xml$");
222         return getConfigNamesFileFromDirs(subPath, dirs, patterns);
223     }
224 
225     /**
226      * Search a particular pattern of in the given directories.
227      *
228      * @param subPath The location where to look for configuration. Can be null.
229      * @param dirs A list of {@link File} of extra directories to search for test configs
230      * @param configNamePatterns the list of patterns for files to be found.
231      * @return the set of {@link File} that were found.
232      */
getConfigNamesFileFromDirs( String subPath, List<File> dirs, List<String> configNamePatterns)233     public static Set<File> getConfigNamesFileFromDirs(
234             String subPath, List<File> dirs, List<String> configNamePatterns) {
235         Set<File> configNames = new LinkedHashSet<>();
236         for (File dir : dirs) {
237             if (subPath != null) {
238                 dir = new File(dir, subPath);
239             }
240             if (!dir.isDirectory()) {
241                 CLog.d("%s doesn't exist or is not a directory.", dir.getAbsolutePath());
242                 continue;
243             }
244             try {
245                 for (String configNamePattern : configNamePatterns) {
246                     configNames.addAll(FileUtil.findFilesObject(dir, configNamePattern));
247                 }
248             } catch (IOException e) {
249                 CLog.w("Failed to get test config files from directory %s", dir.getAbsolutePath());
250             }
251         }
252         return dedupFiles(configNames);
253     }
254 
255     /**
256      * From a same tests dir we only expect a single instance of each names, so we dedup the files
257      * if that happens.
258      */
dedupFiles(Set<File> origSet)259     private static Set<File> dedupFiles(Set<File> origSet) {
260         Map<String, File> newMap = new LinkedHashMap<>();
261         for (File f : origSet) {
262             // Always keep the first found
263             if (!newMap.keySet().contains(f.getName())) {
264                 newMap.put(f.getName(), f);
265             }
266         }
267         return new LinkedHashSet<>(newMap.values());
268     }
269 }
270