All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openqa.selenium.grid.config.AnnotatedConfig Maven / Gradle / Ivy

// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you 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 org.openqa.selenium.grid.config;

import com.google.common.collect.ImmutableMap;

import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * A form of {@link Config} that is generated by looking at fields in the constructor arg that are
 * annotated with {@link ConfigValue}. The class hierarchy is walked from closest to Object to the
 * constructor argument's type, null values are ignored, and the order in which fields are read is
 * not stable (meaning duplicate config values may give different values each time).
 * 

* The main use of this class is to allow an object configured using (for example) jcommander to be * used directly within the app, without requiring intermediate support classes to convert flags to * config values. */ public class AnnotatedConfig implements Config { private final Map> config; public AnnotatedConfig(Object obj) { Map> values = new HashMap<>(); Deque allConfigValues = findConfigFields(obj.getClass()); for (Field field : allConfigValues) { if (Collection.class.isAssignableFrom(field.getType())) { throw new ConfigException("Collection fields may not be used for configuration: " + field); } if (Map.class.isAssignableFrom(field.getType())) { throw new ConfigException("Map fields may not be used for configuration: " + field); } field.setAccessible(true); Object value; try { value = field.get(obj); } catch (IllegalAccessException e) { throw new ConfigException("Unable to read field: " + field); } if (value == null) { continue; } ConfigValue annotation = field.getAnnotation(ConfigValue.class); Map section = values.getOrDefault(annotation.section(), new HashMap<>()); section.put(annotation.name(), String.valueOf(value)); values.put(annotation.section(), section); } config = ImmutableMap.copyOf(values); } private Deque findConfigFields(Class clazz) { Deque toSet = new ArrayDeque<>(); Set> toVisit = new HashSet<>(); toVisit.add(clazz); Set> seen = new HashSet<>(); while (!toVisit.isEmpty()) { clazz = toVisit.iterator().next(); toVisit.remove(clazz); seen.add(clazz); Arrays.stream(clazz.getDeclaredFields()) .filter(field -> field.getAnnotation(ConfigValue.class) != null) .forEach(toSet::addFirst); Class toAdd = clazz.getSuperclass(); if (!Object.class.equals(toAdd) && !seen.contains(toAdd)) { toVisit.add(toAdd); } Arrays.stream(clazz.getInterfaces()) .filter(face -> !seen.contains(face)) .forEach(toVisit::add); } return toSet; } @Override public Optional get(String section, String option) { Objects.requireNonNull(section, "Section name not set"); Objects.requireNonNull(option, "Option name not set"); Map sec = config.get(section); if (sec == null) { return Optional.empty(); } return Optional.ofNullable(sec.get(option)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy