blob: a143bd7c9b764f91775585054c485b8bbe02a973 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.config;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Holds a record of a configuration, its associated objects and their options.
*/
public class ConfigurationDef {
/**
* a map of object type names to config object class name(s). Use LinkedHashMap to keep objects
* in the same order they were added.
*/
private final Map<String, List<String>> mObjectClassMap = new LinkedHashMap<>();
/** a list of option name/value pairs. */
private final List<OptionDef> mOptionList = new ArrayList<>();
/** a cache of the frequency of every classname */
private final Map<String, Integer> mClassFrequency = new HashMap<>();
/** The set of files (and modification times) that were used to load this config */
private final Map<File, Long> mSourceFiles = new HashMap<>();
public static class OptionDef {
final String name;
final String key;
final String value;
final String source;
OptionDef(String optionName, String optionValue, String source) {
this(optionName, null, optionValue, source);
}
OptionDef(String optionName, String optionKey, String optionValue, String source) {
this.name = optionName;
this.key = optionKey;
this.value = optionValue;
this.source = source;
}
}
/** the unique name of the configuration definition */
private final String mName;
/** a short description of the configuration definition */
private String mDescription = "";
public ConfigurationDef(String name) {
mName = name;
}
/**
* Returns a short description of the configuration
*/
public String getDescription() {
return mDescription;
}
/**
* Sets the configuration definition description
*/
void setDescription(String description) {
mDescription = description;
}
/**
* Adds a config object to the definition
*
* @param typeName the config object type name
* @param className the class name of the config object
* @return the number of times this className has appeared in this {@link ConfigurationDef},
* including this time. Because all {@link ConfigurationDef} methods return these
* classes with a constant ordering, this index can serve as a unique identifier for the
* just-added instance of <code>clasName</code>.
*/
int addConfigObjectDef(String typeName, String className) {
List<String> classList = mObjectClassMap.get(typeName);
if (classList == null) {
classList = new ArrayList<String>();
mObjectClassMap.put(typeName, classList);
}
classList.add(className);
// Increment and store count for this className
Integer freq = mClassFrequency.get(className);
freq = freq == null ? 1 : freq + 1;
mClassFrequency.put(className, freq);
return freq;
}
/**
* Adds option to the definition
*
* @param optionName the name of the option
* @param optionValue the option value
*/
void addOptionDef(String optionName, String optionKey, String optionValue,
String optionSource) {
mOptionList.add(new OptionDef(optionName, optionKey, optionValue, optionSource));
}
/**
* Registers a source file that was used while loading this {@link ConfigurationDef}.
*/
void registerSource(File source) {
mSourceFiles.put(source, source.lastModified());
}
/**
* Determine whether any of the source files have changed since this {@link ConfigurationDef}
* was loaded.
*/
boolean isStale() {
for (Map.Entry<File, Long> entry : mSourceFiles.entrySet()) {
if (entry.getKey().lastModified() > entry.getValue()) {
return true;
}
}
return false;
}
/**
* Get the object type name-class map.
* <p/>
* Exposed for unit testing
*/
Map<String, List<String>> getObjectClassMap() {
return mObjectClassMap;
}
/**
* Get the option name-value map.
* <p/>
* Exposed for unit testing
*/
List<OptionDef> getOptionList() {
return mOptionList;
}
/**
* Creates a configuration from the info stored in this definition, and populates its fields
* with the provided option values.
*
* @return the created {@link IConfiguration}
* @throws ConfigurationException if configuration could not be created
*/
IConfiguration createConfiguration() throws ConfigurationException {
IConfiguration config = new Configuration(getName(), getDescription());
for (Map.Entry<String, List<String>> objClassEntry : mObjectClassMap.entrySet()) {
List<Object> objectList = new ArrayList<Object>(objClassEntry.getValue().size());
for (String className : objClassEntry.getValue()) {
Object configObject = createObject(objClassEntry.getKey(), className);
objectList.add(configObject);
}
config.setConfigurationObjectList(objClassEntry.getKey(), objectList);
}
config.injectOptionValues(mOptionList);
return config;
}
/**
* Creates a global configuration from the info stored in this definition, and populates its
* fields with the provided option values.
*
* @return the created {@link IGlobalConfiguration}
* @throws ConfigurationException if configuration could not be created
*/
IGlobalConfiguration createGlobalConfiguration() throws ConfigurationException {
IGlobalConfiguration config = new GlobalConfiguration(getName(), getDescription());
for (Map.Entry<String, List<String>> objClassEntry : mObjectClassMap.entrySet()) {
List<Object> objectList = new ArrayList<Object>(objClassEntry.getValue().size());
for (String className : objClassEntry.getValue()) {
Object configObject = createObject(objClassEntry.getKey(), className);
objectList.add(configObject);
}
config.setConfigurationObjectList(objClassEntry.getKey(), objectList);
}
for (OptionDef optionEntry : mOptionList) {
config.injectOptionValue(optionEntry.name, optionEntry.key, optionEntry.value);
}
return config;
}
/**
* Gets the name of this configuration definition
*
* @return
*/
public String getName() {
return mName;
}
/**
* Creates a config object associated with this definition.
*
* @param objectTypeName the name of the object. Used to generate more descriptive error
* messages
* @param className the class name of the object to load
* @return the config object
* @throws ConfigurationException if config object could not be created
*/
private Object createObject(String objectTypeName, String className)
throws ConfigurationException {
try {
Class<?> objectClass = getClassForObject(objectTypeName, className);
Object configObject = objectClass.newInstance();
return configObject;
} catch (InstantiationException e) {
throw new ConfigurationException(String.format(
"Could not instantiate class %s for config object type %s", className,
objectTypeName), e);
} catch (IllegalAccessException e) {
throw new ConfigurationException(String.format(
"Could not access class %s for config object type %s", className,
objectTypeName), e);
}
}
/**
* Loads the class for the given the config object associated with this definition.
*
* @param objectTypeName the name of the config object type. Used to generate more descriptive
* error messages
* @param className the class name of the object to load
* @return the config object populated with default option values
* @throws ConfigurationException if config object could not be created
*/
private Class<?> getClassForObject(String objectTypeName, String className)
throws ConfigurationException {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new ConfigurationException(
String.format("Could not find class %s for config object type %s", className,
objectTypeName), e);
}
}
}