/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* 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 io.sarl.maven.docs.testing;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.collect.Iterables;
import org.arakhne.afc.vmutil.FileSystem;
import org.arakhne.afc.vmutil.ReflectionUtil;
import org.eclipse.jdt.core.Flags;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.xbase.XBinaryOperation;
import org.eclipse.xtext.xbase.XBooleanLiteral;
import org.eclipse.xtext.xbase.XCollectionLiteral;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XNullLiteral;
import org.eclipse.xtext.xbase.XNumberLiteral;
import org.eclipse.xtext.xbase.XSetLiteral;
import org.eclipse.xtext.xbase.XStringLiteral;
import org.eclipse.xtext.xbase.XTypeLiteral;
import org.eclipse.xtext.xbase.lib.Pair;
import org.osgi.framework.Version;
/** Shoulds functions.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.6
*/
@SuppressWarnings({"checkstyle:methodname"})
public final class ShouldExtensions {
private static final int HEX_RADIX = 16;
private ShouldExtensions() {
//
}
/** Ensure that the string has the format of a date.
*
* @param actual - the string to parse.
* @param dateFormat - the expected format of the date, as
* described in {@link SimpleDateFormat}. If <code>null</code>, the
* default date format is considered.
* @return the validation status
*/
public static boolean shouldBeDate(String actual, String dateFormat) {
if (actual == null || actual.isEmpty()) {
return false;
}
try {
final DateFormat format;
if (dateFormat == null || dateFormat.isEmpty()) {
format = DateFormat.getDateInstance();
} else {
format = new SimpleDateFormat(dateFormat);
}
return format.parse(actual) != null;
} catch (Throwable exception) {
//
}
return false;
}
/** Ensure that the string has the format of a date.
*
* @param actual - the string to parse.
* @return the validation status
*/
public static boolean shouldBeDate(String actual) {
return shouldBeDate(actual, null);
}
/** Ensure that the string has the format of a number.
*
* @param actual - the string to parse.
* @param numberFormat - the expected format of the number, as
* described in {@link DecimalFormat}. If <code>null</code>, the
* default date format is considered.
* @return the validation status
*/
public static boolean shouldBeNumber(String actual, String numberFormat) {
if (actual == null || actual.isEmpty()) {
return false;
}
try {
final NumberFormat format;
if (numberFormat == null || numberFormat.isEmpty()) {
format = NumberFormat.getNumberInstance();
} else {
format = new DecimalFormat(numberFormat);
}
return format.parse(actual) != null;
} catch (Throwable exception) {
//
}
return false;
}
/** Ensure that the string has the format of a number.
*
* @param actual - the string to parse.
* @return the validation status
*/
public static boolean shouldBeNumber(String actual) {
return shouldBeNumber(actual);
}
/** Ensure that the given string is a valid Maven version number.
*
* @param actual - the string to test.
* @param allowSnapshot - indicates if the <code>-SNAPSHOT</code> postfix
* is considered as valid.
* @return the validation status.
*/
public static boolean shouldBeMavenVersion(String actual, boolean allowSnapshot) {
if (actual == null) {
return false;
}
final StringBuilder pattern = new StringBuilder("^"); //$NON-NLS-1$
pattern.append("[0-9a-zA-Z_-]+(\\.[0-9a-zA-Z_-]+)*"); //$NON-NLS-1$
if (allowSnapshot) {
pattern.append("(?:"); //$NON-NLS-1$
pattern.append(Matcher.quoteReplacement("-SNAPSHOT")); //$NON-NLS-1$
pattern.append(")?"); //$NON-NLS-1$
}
pattern.append("$"); //$NON-NLS-1$
return Pattern.matches(pattern.toString(), actual);
}
/** Ensure that the given string is a valid Maven version number, including snapshot.
*
* @param actual - the string to test.
* @return the validation status.
*/
public static boolean shouldBeMavenVersion(String actual) {
return shouldBeMavenVersion(actual, true);
}
private static Version parseJavaVersion(String version, Version defaultVersion) {
try {
final Pattern pattern = Pattern.compile("^([0-9]+)(?:\\.([0-9]+)(?:\\.([0-9]+))?)?"); //$NON-NLS-1$
final Matcher matcher = pattern.matcher(version);
if (matcher.find()) {
int minor = 0;
String group = matcher.group(2);
if (group != null && !group.isEmpty()) {
try {
minor = Integer.parseInt(group);
} catch (Exception exception) {
//
}
}
int micro = 0;
group = matcher.group(3);
if (group != null && !group.isEmpty()) {
try {
micro = Integer.parseInt(group);
} catch (Exception exception) {
//
}
}
final int major = Integer.parseInt(matcher.group(1));
return new Version(major, minor, micro);
}
if (version != null && !version.isEmpty()) {
final Version v = Version.valueOf(version);
if (v != null) {
return v;
}
}
} catch (Exception exception) {
//
}
return defaultVersion;
}
/** Ensure that the version of the current Java specification is in
* the range given by the minVersion (inclusive) and maxVersion (exclusive).
* If the maxVersion is not given or is not a properly formated version
* number, then all versions after the minVersion are valid.
*
* @param minVersion - the minimal version.
* @param maxVersion - the maximal version.
* @return the validation status.
*/
public static boolean shouldBeJavaRange(String minVersion, String maxVersion) {
final Version jreV = parseJavaVersion(System.getProperty("java.version"), null); //$NON-NLS-1$
if (jreV != null && minVersion != null) {
final Version minV = parseJavaVersion(minVersion, null);
if (minV != null) {
Version maxV = null;
if (maxVersion != null) {
maxV = parseJavaVersion(maxVersion, null);
}
if (maxV == null) {
return jreV.compareTo(minV) >= 0;
}
return jreV.compareTo(minV) >= 0 && jreV.compareTo(maxV) < 0;
}
}
return false;
}
/** Ensure that the version of the current Java specification is at
* least the one given by minVersion.
*
* @param minVersion - the minimal version.
* @return the validation status.
*/
public static boolean shouldBeAtLeastJava(String minVersion) {
return shouldBeJavaRange(minVersion, null);
}
/** Ensure that the iterator replies the expected values in the given order.
*
* @param actual - the iterator to test.
* @param expected - the expected values.
* @return the validation status
*/
public static boolean shouldIterate(Iterator<?> actual, Object expected) {
return shouldIterate(actual, expected, true);
}
/** Ensure that the iterator replies the expected values in the given order.
*
* @param actual - the iterator to test.
* @param expected - the expected values.
* @param significantOrder - indicates if the order of the elements is significant.
* @return the validation status
*/
@SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"})
public static boolean shouldIterate(Iterator<?> actual, Object expected, boolean significantOrder) {
Object obj;
final Iterator<?> it;
if (expected instanceof Iterable<?>) {
it = ((Iterable<?>) expected).iterator();
} else if (expected instanceof Array) {
final Array array = (Array) expected;
it = new ArrayIterator(array);
} else if (expected instanceof Map<?, ?>) {
it = ((Map<?, ?>) expected).entrySet().iterator();
} else {
it = Collections.singleton(expected).iterator();
}
if (significantOrder) {
// Significant order
Object eObj;
while (actual.hasNext()) {
obj = actual.next();
if (!it.hasNext()) {
return false;
}
eObj = it.next();
if (obj instanceof XExpression) {
if (!shouldBeLiteral((XExpression) obj, eObj)) {
return false;
}
} else if (!Objects.equals(obj, eObj)) {
return false;
}
}
return !it.hasNext();
}
// Unsignificant order
final List<Object> expectedElements = new LinkedList<>();
while (it.hasNext()) {
expectedElements.add(it.next());
}
boolean found;
while (actual.hasNext()) {
obj = actual.next();
final Iterator<Object> i = expectedElements.iterator();
found = false;
while (!found && i.hasNext()) {
final Object eObj = i.next();
if (obj instanceof XExpression) {
if (shouldBeLiteral((XExpression) obj, eObj)) {
i.remove();
found = true;
}
} else if (obj instanceof JvmIdentifiableElement
&& Objects.equals(((JvmIdentifiableElement) obj).getQualifiedName(), eObj)) {
i.remove();
found = true;
} else if (obj instanceof JvmTypeReference
&& Objects.equals(((JvmTypeReference) obj).getQualifiedName(), eObj)) {
i.remove();
found = true;
} else if (Objects.equals(obj, eObj)) {
i.remove();
found = true;
}
}
if (!found) {
return false;
}
}
return expectedElements.isEmpty();
}
/** Ensure that the string has the format of an URL to the SARL API.
*
* @param actual - the string to parse.
* @param allowedAPIhostname - is a list of API base hostname to consider as valid.
* If not given, only "www.sarl.io" is allowed.
* @return the validation status
*/
public static boolean shouldBeApiURL(String actual, String allowedAPIhostname) {
if (actual == null || actual.isEmpty()) {
return false;
}
try {
final URL u = FileSystem.convertStringToURL(actual, true);
if (u == null) {
return false;
}
String[] validHostnames = {"www.sarl.io"}; //$NON-NLS-1$
if (allowedAPIhostname != null && !allowedAPIhostname.isEmpty()) {
validHostnames = allowedAPIhostname.split("[ \t]*[,;][ \t]*"); //$NON-NLS-1$
}
final List<String> hosts = Arrays.asList(validHostnames);
if (!hosts.contains(u.getHost())
|| !u.getQuery().endsWith(".html") //$NON-NLS-1$
|| !u.getPath().endsWith("index.html")) { //$NON-NLS-1$
return false;
}
return true;
} catch (Throwable exception) {
//
}
return false;
}
private static Number cleanNumber(String stringRepresentation) {
if (stringRepresentation == null) {
return null;
}
if (stringRepresentation.startsWith("0x") || stringRepresentation.startsWith("0X")) { //$NON-NLS-1$//$NON-NLS-2$
return new BigInteger(stringRepresentation.substring(2), HEX_RADIX);
}
String literal = stringRepresentation.replace("_", ""); //$NON-NLS-1$//$NON-NLS-2$
literal = literal.toLowerCase().replaceFirst("l|f|d|(bi)|(bd)$", ""); //$NON-NLS-1$ //$NON-NLS-2$
return new BigDecimal(literal);
}
/** Ensure that the given string literal is equal to the given value.
*
* @param actual - the string literal to test.
* @param expected - the expected value.
* @return the validation status
*/
public static boolean shouldBe(XStringLiteral actual, Object expected) {
if (actual == null) {
return false;
}
final String string = (expected == null) ? null : expected.toString();
return Objects.equals(string, actual.getValue());
}
/** Ensure that the given boolean literal is equal to the given value.
*
* @param actual - the boolean literal to test.
* @param expected - the expected value.
* @return the validation status
*/
public static boolean shouldBe(XBooleanLiteral actual, Object expected) {
if (actual == null) {
return false;
}
final Boolean b;
if (expected instanceof Boolean) {
b = (Boolean) expected;
} else {
try {
b = new Boolean(expected.toString());
} catch (Throwable exception) {
return false;
}
}
return b.booleanValue() == actual.isIsTrue();
}
/** Ensure that the given number literal is equal to the given value.
*
* @param actual - the number literal to test.
* @param expected - the expected value.
* @return the validation status
*/
public static boolean shouldBe(XNumberLiteral actual, Object expected) {
if (actual == null) {
return false;
}
final Number number;
if (expected instanceof Number) {
number = (Number) expected;
} else {
try {
number = NumberFormat.getInstance().parse(expected.toString());
} catch (Throwable exception) {
return false;
}
}
final Number anumber = cleanNumber(actual.getValue());
return number.doubleValue() == anumber.doubleValue();
}
/** Ensure that the given type literal is equal to the given type.
*
* @param actual - the type literal to test.
* @param expected - the name of the expected type.
* @return the validation status
*/
public static boolean shouldBe(XTypeLiteral actual, Object expected) {
if (actual == null) {
return false;
}
final String fqn;
if (expected instanceof Class) {
fqn = ((Class<?>) expected).getName();
} else {
fqn = expected.toString();
}
return actual.getType() != null
&& Objects.equals(fqn, actual.getType().getQualifiedName());
}
/** Ensure that the given type literal is equal to the given list.
*
* @param actual - the type literal to test.
* @param expected - the name of the expected type.
* @return the validation status
*/
public static boolean shouldBe(XCollectionLiteral actual, Object expected) {
if (actual == null || actual.getElements() == null) {
return false;
}
return shouldIterate(
actual.getElements().iterator(),
expected,
!(actual instanceof XSetLiteral));
}
/** Ensure that the given type literal is equal to the given type.
*
* @param actual - the type literal to test.
* @param expected - the name of the expected type.
* @return the validation status
*/
@SuppressWarnings({"checkstyle:returncount", "checkstyle:npathcomplexity"})
public static boolean shouldBeLiteral(XExpression actual, Object expected) {
if (actual instanceof XNumberLiteral) {
return shouldBe((XNumberLiteral) actual, expected);
}
if (actual instanceof XBooleanLiteral) {
return shouldBe((XBooleanLiteral) actual, expected);
}
if (actual instanceof XStringLiteral) {
return shouldBe((XStringLiteral) actual, expected);
}
if (actual instanceof XTypeLiteral) {
return shouldBe((XTypeLiteral) actual, expected);
}
if (actual instanceof XNullLiteral) {
return Objects.equals("null", expected); //$NON-NLS-1$
}
if (actual instanceof XCollectionLiteral) {
return shouldBe((XCollectionLiteral) actual, expected);
}
if (actual instanceof XBinaryOperation) {
final XBinaryOperation op = (XBinaryOperation) actual;
if ("operator_mappedTo".equals(op.getFeature().getSimpleName())) { //$NON-NLS-1$
final Object key;
final Object value;
if (expected instanceof Pair<?, ?>) {
key = ((Pair<?, ?>) expected).getKey();
value = ((Pair<?, ?>) expected).getValue();
} else if (expected instanceof Entry<?, ?>) {
key = ((Entry<?, ?>) expected).getKey();
value = ((Entry<?, ?>) expected).getValue();
} else {
return false;
}
return shouldBeLiteral(op.getLeftOperand(), key)
&& shouldBeLiteral(op.getRightOperand(), value);
}
}
return false;
}
/** Ensure that the given type has the given deprecated method.
* The format of the name may be: <ul>
* <li>ID</li>
* <li>ID(TYPE, TYPE...)</li>
* <li>ID : TYPE</li>
* <li>ID(TYPE, TYPE...) : TYPE</li>
* </ul>
*
* @param type - the type to check.
* @param name - the name and prototype, e.g. <code>fct(java.lang.String):int</code>.
* @return the method.
*/
public static Method shouldHaveDeprecatedMethod(Class<?> type, String name) {
return shouldHaveMethod(type, name, true);
}
/** Ensure that the given type has the given method.
* The format of the name may be: <ul>
* <li>ID</li>
* <li>ID(TYPE, TYPE...)</li>
* <li>ID : TYPE</li>
* <li>ID(TYPE, TYPE...) : TYPE</li>
* </ul>
*
* @param type - the type to check.
* @param name - the name and prototype, e.g. <code>fct(java.lang.String):int</code>.
* @return the method.
*/
public static Method shouldHaveMethod(Class<?> type, String name) {
return shouldHaveMethod(type, name, false);
}
/** Ensure that the given type has the given method.
* The format of the name may be: <ul>
* <li>ID</li>
* <li>ID(TYPE, TYPE...)</li>
* <li>ID : TYPE</li>
* <li>ID(TYPE, TYPE...) : TYPE</li>
* </ul>
*
* @param type - the type to check.
* @param name - the name and prototype, e.g. <code>fct(java.lang.String):int</code>.
* @param deprecated indicates if the field must be deprecated.
* @return the method.
*/
@SuppressWarnings({"rawtypes", "checkstyle:npathcomplexity"})
protected static Method shouldHaveMethod(Class<?> type, String name, boolean deprecated) {
final Pattern pattern = Pattern.compile(
"^([_a-zA-Z0-9]+)\\s*" //$NON-NLS-1$
+ "(?:\\(\\s*([_a-zA-Z0-9.\\$]+(?:\\[\\])?\\s*" //$NON-NLS-1$
+ "(?:,\\s*[_a-zA-Z0-9.\\$]+(?:\\[\\])?\\s*)*)\\))?" //$NON-NLS-1$
+ "(?:\\s*:\\s*([_a-zA-Z0-9.\\$]+(?:\\[\\])?))?$"); //$NON-NLS-1$
final Matcher matcher = pattern.matcher(name);
if (matcher.matches()) {
String paramText;
try {
paramText = matcher.group(2).trim();
} catch (Throwable exception) {
paramText = ""; //$NON-NLS-1$
}
String returnText;
try {
returnText = matcher.group(3).trim();
} catch (Throwable exception) {
returnText = ""; //$NON-NLS-1$
}
final String[] params;
if (paramText.isEmpty()) {
params = new String[0];
} else {
params = paramText.split("\\s*,\\s*"); //$NON-NLS-1$
}
final Class[] types = new Class[params.length];
for (int i = 0; i < params.length; ++i) {
if (params[i].endsWith("[]")) { //$NON-NLS-1$
Class<?> paramType;
final String typeName = params[i].substring(0, params[i].length() - 2);
try {
paramType = ReflectionUtil.forName(typeName);
} catch (ClassNotFoundException e) {
throw new ShouldException("Type not found: " + typeName); //$NON-NLS-1$
}
final Object tmpArray = Array.newInstance(paramType, 0);
types[i] = tmpArray.getClass();
} else {
try {
types[i] = ReflectionUtil.forName(params[i]);
} catch (ClassNotFoundException e) {
throw new ShouldException("Type not found: " + params[i]); //$NON-NLS-1$
}
}
}
final String fctName = matcher.group(1);
Method method;
try {
method = type.getDeclaredMethod(fctName, types);
} catch (NoSuchMethodException | SecurityException e) {
method = null;
}
if (method == null) {
throw new ShouldException("Unable to find a function with the prototype: " + name); //$NON-NLS-1$
}
final boolean validReturnType;
if (returnText == null || returnText.isEmpty()) {
validReturnType = void.class.equals(method.getReturnType())
|| Void.class.equals(method.getReturnType());
} else {
final Class<?> rtype;
try {
rtype = ReflectionUtil.forName(returnText);
} catch (ClassNotFoundException exception) {
throw new ShouldException("Type not found: " + returnText); //$NON-NLS-1$
}
validReturnType = rtype.equals(method.getReturnType());
}
if (validReturnType) {
final Deprecated deprecatedAnnotation = method.getAnnotation(Deprecated.class);
if ((deprecated && deprecatedAnnotation != null) || (!deprecated && deprecatedAnnotation == null)) {
return method;
}
throw new ShouldException("Deprecated method for the prototype: " + name); //$NON-NLS-1$
}
throw new ShouldException("Invalid return type for the prototype: " + name //$NON-NLS-1$
+ "\nactual: " + method.getReturnType().getName()); //$NON-NLS-1$
}
return null;
}
/** Ensure that the given type has the given methods.
* The format of the prototypes may be: <ul>
* <li>ID</li>
* <li>ID(TYPE, TYPE...)</li>
* <li>ID : TYPE</li>
* <li>ID(TYPE, TYPE...) : TYPE</li>
* </ul>
*
* @param type - the type to check.
* @param prototypes - the prototypes, e.g. <code>fct(java.lang.String):int</code>.
* @return the validation status.
*/
public static boolean shouldHaveMethods(Class<?> type, String... prototypes) {
List<Method> methods = new ArrayList<>();
for (final Method method : type.getDeclaredMethods()) {
if (Flags.isPublic(method.getModifiers())) {
final Deprecated deprecatedAnnotation = method.getAnnotation(Deprecated.class);
if (deprecatedAnnotation == null) {
methods.add(method);
}
}
}
for (final String prototype : prototypes) {
final Method method = shouldHaveMethod(type, prototype, false);
if (method != null) {
methods.remove(method);
} else {
throw new ShouldException(MessageFormat.format(Messages.ShouldExtensions_0,
prototypes, Iterables.toString(methods)));
}
}
if (!methods.isEmpty()) {
throw new ShouldException(MessageFormat.format(Messages.ShouldExtensions_1,
type.getName(), Iterables.toString(methods)));
}
return true;
}
/** Ensure that the given type has the given type has the given deprecated field.
* The format of the name may be: <ul>
* <li>ID</li>
* <li>ID : TYPE</li>
* </ul>
*
* @param type - the type to check.
* @param name - the name and prototype, e.g. <code>x:int</code>.
* @return the field.
*/
public static Field shouldHaveDeprecatedField(Class<?> type, String name) {
return shouldHaveField(type, name, true);
}
/** Ensure that the given type has the given type has the given field.
* The format of the name may be: <ul>
* <li>ID</li>
* <li>ID : TYPE</li>
* </ul>
*
* @param type - the type to check.
* @param name - the name and prototype, e.g. <code>x:int</code>.
* @return the field.
*/
public static Field shouldHaveField(Class<?> type, String name) {
return shouldHaveField(type, name, false);
}
/** Ensure that the given type has the given type has the given method.
* The format of the name may be: <ul>
* <li>ID</li>
* <li>ID : TYPE</li>
* </ul>
*
* @param type - the type to check.
* @param name - the name and prototype, e.g. <code>x:int</code>.
* @param deprecated indicates if the field must be deprecated.
* @return the field.
*/
protected static Field shouldHaveField(Class<?> type, String name, boolean deprecated) {
try {
final Pattern pattern = Pattern.compile(
"^([_a-zA-Z0-9]+)\\s*" //$NON-NLS-1$
+ "(?:\\s*:\\s*([_a-zA-Z0-9.\\$]+(?:\\[\\])?))?$"); //$NON-NLS-1$
final Matcher matcher = pattern.matcher(name);
if (matcher.matches()) {
final String fieldName = matcher.group(1);
String fieldType;
try {
fieldType = matcher.group(2).trim();
} catch (Throwable exception) {
fieldType = ""; //$NON-NLS-1$
}
final Field field = type.getDeclaredField(fieldName);
if (field == null) {
return null;
}
final boolean validType;
if (fieldType != null && !fieldType.isEmpty()) {
Class<?> rtype;
if (fieldType.endsWith("[]")) { //$NON-NLS-1$
final Class<?> paramType = ReflectionUtil.forName(fieldType.substring(0, fieldType.length() - 2));
final Object tmpArray = Array.newInstance(paramType, 0);
rtype = tmpArray.getClass();
} else {
rtype = ReflectionUtil.forName(fieldType);
}
validType = rtype.equals(field.getType());
} else {
validType = true;
}
if (validType) {
final Deprecated deprecatedAnnotation = field.getAnnotation(Deprecated.class);
if ((deprecated && deprecatedAnnotation != null) || (!deprecated && deprecatedAnnotation == null)) {
return field;
}
}
}
} catch (Throwable e) {
//
}
return null;
}
/** Ensure that the given type extends specific types.
*
* @param type - the type to check.
* @param expectedTypes - the qualified names of the expected types, separated by comas.
* @return the validation status.
*/
public static boolean shouldExtend(Class<?> type, String expectedTypes) {
if (type == null) {
return false;
}
try {
final Class<?> st = type.getSuperclass();
final List<Class<?>> types = new LinkedList<>();
if (st == null || Object.class.equals(st)) {
if (type.isInterface()) {
types.addAll(Arrays.asList(type.getInterfaces()));
}
} else {
types.add(st);
}
//
if (expectedTypes == null || expectedTypes.isEmpty()) {
return types.isEmpty();
}
for (final String expectedType : expectedTypes.split("\\s*,\\s*")) { //$NON-NLS-1$
final Class<?> et = ReflectionUtil.forName(expectedType);
if (!types.remove(et)) {
return false;
}
}
return types.isEmpty();
} catch (Throwable e) {
//
}
return false;
}
/** Ensure that the given type has the number of members.
*
* @param type - the type to check.
* @param expectedNbOfElements - the expected number of elements.
* @return the validation status.
*/
public static boolean shouldHaveNbMembers(Class<?> type, int expectedNbOfElements) {
if (type == null) {
return false;
}
try {
final int nb = type.getDeclaredConstructors().length
+ type.getDeclaredFields().length
+ type.getDeclaredMethods().length
+ type.getDeclaredAnnotations().length
+ type.getDeclaredClasses().length;
return nb == expectedNbOfElements;
} catch (Throwable e) {
//
}
return false;
}
/** Ensure that the given map contains the elements.
*
* @param <K> type of the keys.
* @param <V> type of the values.
* @param map - the map to check.
* @param reference - the expected elements in the map.
* @return the validation status.
*/
public static <K, V> boolean shouldBe(Map<K, V> map, Map<? super K, ? super V> reference) {
if (map == null) {
return false;
}
if (reference == null || reference.isEmpty()) {
return map.isEmpty();
}
for (final Entry<? super K, ? super V> entry : reference.entrySet()) {
if (!map.containsKey(entry.getKey())) {
return false;
}
final V currentValue = map.get(entry.getKey());
if (!Objects.equals(currentValue, entry.getValue())) {
return false;
}
}
return map.size() == reference.size();
}
/** Ensure that the given URL is a property file with the given property name.
*
* @param propertyFile - the name of the property file.
* @param propertyName - the name of the property name.
* @return the validation status.
*/
public static boolean shouldHaveProperty(URL propertyFile, String propertyName) {
try {
final Properties props = new Properties();
try (InputStream is = propertyFile.openStream()) {
props.load(is);
final String value = props.getProperty(propertyName, null);
return value != null;
}
} catch (Throwable exception) {
//
}
return false;
}
/** Ensure that the given URL is a property file with the given property.
*
* @param propertyFile - the name of the property file.
* @param property - the property.
* @return the validation status.
*/
public static boolean shouldHaveProperty(URL propertyFile, Pair<String, String> property) {
if (propertyFile != null && property != null) {
try {
final Properties props = new Properties();
try (InputStream is = propertyFile.openStream()) {
props.load(is);
final String value = props.getProperty(property.getKey(), null);
return Objects.equals(value, property.getValue());
}
} catch (Throwable exception) {
//
}
}
return false;
}
/** Iterator on array.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class ArrayIterator implements Iterator<Object> {
private final Array array;
private int index;
private Object obj;
/** Construct the iterator on array.
*
* @param array - the array to iterate on.
*/
ArrayIterator(Array array) {
this.array = array;
searchNext();
}
private void searchNext() {
try {
this.obj = Array.get(this.array, this.index);
++this.index;
} catch (Throwable exception) {
this.obj = null;
}
}
@Override
public boolean hasNext() {
return this.obj != null;
}
@Override
public Object next() {
if (this.obj == null) {
throw new NoSuchElementException();
}
final Object object = this.obj;
searchNext();
return object;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/** Major exception in a should function.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public static class ShouldException extends RuntimeException {
private static final long serialVersionUID = 3129485320879065188L;
/** Constructor.
*
* @param message the message.
*/
public ShouldException(String message) {
super(message);
}
}
}