
org.robotframework.remoteserver.keywords.OverloadedKeywordImpl Maven / Gradle / Ivy
The newest version!
/* Copyright 2014 Kevin Ormbrek
*
* 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.
*/
/* This code is derived from JavalibCore
* Copyright 2008 Nokia Siemens Networks Oyj
*/
package org.robotframework.remoteserver.keywords;
import com.google.common.collect.Iterables;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class providing implementation of {@link OverloadedKeyword}
*/
public class OverloadedKeywordImpl implements OverloadedKeyword {
protected static final Logger LOG = LoggerFactory.getLogger(OverloadedKeywordImpl.class.getName());
private final Map> keywordMap = new HashMap<>();
private final String keywordName;
private final Object keywordClass;
/**
* Constructor creating {@link OverloadedKeyword} providing {@link Object} and {@link Method},
* that are associated together
*
* @param keywordClass {@link Object} instance used for execution of {@link Method}
* @param method {@link Method} providing execution routine
*/
public OverloadedKeywordImpl(Object keywordClass, Method method) {
this(keywordClass, method, method.getName());
}
/**
* Constructor creating {@link OverloadedKeyword} providing {@link Object} and {@link Method},
* that are associated together
*
* @param keywordClass {@link Object} instance used for execution of {@link Method}
* @param method {@link Method} providing execution routine
* @param name Name of current {@link OverloadedKeyword}
*/
public OverloadedKeywordImpl(Object keywordClass, Method method, String name) {
this.keywordName = Objects.requireNonNull(name);
this.keywordClass = Objects.requireNonNull(keywordClass);
addOverload(method);
}
@Override public Object execute(Object[] arguments) {
final int argCount = arguments.length;
if (keywordMap.containsKey(argCount)) {
for (CheckedKeyword checkedKeyword : keywordMap.get(argCount)) {
if (checkedKeyword.canExecute(arguments)) {
LOG.debug("EXECUTED {} args{}", keywordName, checkedKeyword.getArgumentNames().length);
return checkedKeyword.execute(arguments);
}
LOG.debug("EXECUTION SKIPPED {} args {}", keywordName, checkedKeyword.getArgumentNames().length);
}
throw new IllegalArgumentException(
String.format("%s cannot be executed with args %s.", keywordName, Arrays.toString(arguments)));
} else if (keywordMap.size() == 1) {
throw new IllegalArgumentException(String.format("%s takes %d argument(s), received %d.", keywordName,
Iterables.get(keywordMap.keySet(), 0), argCount));
}
throw new IllegalArgumentException(
String.format("No overload of %s takes %d argument(s).", keywordName, argCount));
}
@Override public void addOverload(Method method) {
final int argCount = method.getParameterTypes().length;
if (hasVariableArgs(method)) {
LOG.warn("Overloads with variable arguments not supported. Ignoring overload {}", method);
} else if (!keywordMap.containsKey(argCount)) {
keywordMap.put(argCount, new ArrayList<>());
keywordMap.get(argCount).add(new CheckedKeywordImpl(keywordClass, method));
} else {
keywordMap.get(argCount).add(new CheckedKeywordImpl(keywordClass, method));
keywordMap.get(argCount).sort(Comparator.comparingLong(t -> computeKeywordRank(t.getArguments())));
}
}
/**
* Computes Rank of arguments used in {@link Method},
* the lower the rank is the better
*
* @param args {@link Method} arguments used in ranking
* @return Rank of arguments
*/
private long computeKeywordRank(final Class>[] args) {
// TODO: specify more complex ranking for method arguments that will consider prioritizing floats, doubles, etc.
long rank = 0;
for (int i = 0; i < args.length; i++) {
if (String.class.equals(args[i])) {
rank += Math.pow(2, args.length - i);
}
}
LOG.debug("{} {} rank {}", keywordName, Arrays.toString(args), rank);
return rank;
}
@Override public String[] getArgumentNames() {
final int min = Collections.min(keywordMap.keySet());
final int max = Collections.max(keywordMap.keySet());
final String[] arguments = new String[max], minNames = Iterables.get(keywordMap.get(min), 0).getArgumentNames(),
maxNames =
Iterables.get(keywordMap.get(max), 0).getArgumentNames();
for (int i = 0; i < max; i++) {
if (i < min)
arguments[i] = minNames[i];
else
arguments[i] = maxNames[i] + "=";
}
return arguments;
}
/**
* Tests if {@link Method} has defined VARARGS argument
*
* @param method {@link Method} that will be tested
* @return If {@link Method} arguments consists of VARARGS
*/
private boolean hasVariableArgs(Method method) {
final int argCount = method.getParameterTypes().length;
return (argCount > 0 && method.getParameterTypes()[argCount - 1].isArray());
}
@Override public String getDocumentation() {
for (Collection keywords : keywordMap.values()) {
for (CheckedKeyword keyword : keywords) {
if (!keyword.getDocumentation().isEmpty()) {
return keyword.getDocumentation();
}
}
}
return "";
}
@Override public String[] getTags() {
Set tags = new HashSet<>();
for (Collection keywords : keywordMap.values()) {
for (CheckedKeyword keyword : keywords) {
Arrays.stream(keyword.getTags()).filter(Objects::nonNull).collect(Collectors.toCollection(() -> tags));
}
}
return tags.toArray(new String[tags.size()]);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy