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

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

Go to download

Selenium automates browsers. That's it! What you do with that power is entirely up to you.

There is a newer version: 4.26.0
Show newest version
// 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.beust.jcommander.Parameter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.primitives.Primitives;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.openqa.selenium.internal.Require;

/**
 * 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 transform * flags to config values. */ public class AnnotatedConfig implements Config { private final Map>> config; public AnnotatedConfig(Object obj) { this(obj, Collections.emptySet(), false); } public AnnotatedConfig(Object obj, Set cliArgs, boolean includeCliArgs) { Map>> values = new HashMap<>(); Deque allConfigValues = findConfigFields(obj.getClass()); for (Field field : allConfigValues) { 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); } ConfigValue annotation = field.getAnnotation(ConfigValue.class); Parameter cliAnnotation = field.getAnnotation(Parameter.class); boolean containsCliArg = cliAnnotation != null && Arrays.stream(cliAnnotation.names()).anyMatch(cliArgs::contains); if (cliArgs.size() > 0 && !containsCliArg && includeCliArgs) { // Only getting config values for args entered by the user. continue; } if (cliArgs.size() > 0 && containsCliArg && !includeCliArgs) { // Excluding config values for args entered by the user. continue; } Map> section = values.computeIfAbsent(annotation.section(), str -> new HashMap<>()); List all = section.computeIfAbsent(annotation.name(), str -> new LinkedList<>()); if (value instanceof Collection) { for (Object o : ((Collection) value)) { String singleValue = getSingleValue(o); if (singleValue != null) { all.add(singleValue); } } } else { String singleValue = getSingleValue(value); if (singleValue != null) { all.add(singleValue); } } } // Now make the config immutable. this.config = values; } private String getSingleValue(Object value) { if (value == null) { return null; } if (value instanceof Map) { throw new ConfigException("Map fields may not be used for configuration: " + value); } if (value instanceof Collection) { throw new ConfigException("Collection fields may not be used for configuration: " + value); } if (Boolean.FALSE.equals(value) && !Primitives.isWrapperType(value.getClass())) { return null; } if (value instanceof Number && ((Number) value).floatValue() == 0f) { return null; } return String.valueOf(value); } 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::addLast); Class toAdd = clazz.getSuperclass(); if (toAdd != null && !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> getAll(String section, String option) { Require.nonNull("Section name", section); Require.nonNull("Option name", option); Map> sec = config.get(section); if (sec == null || sec.isEmpty()) { return Optional.empty(); } List values = sec.get(option); if (values == null || values.isEmpty()) { return Optional.empty(); } return Optional.of(ImmutableList.copyOf(values)); } @Override public Set getSectionNames() { return ImmutableSortedSet.copyOf(config.keySet()); } @Override public Set getOptions(String section) { Require.nonNull("Section name to get options for", section); return ImmutableSortedSet.copyOf(config.getOrDefault(section, ImmutableMap.of()).keySet()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy