001/*
002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package org.eclipse.golo.runtime;
012
013import org.eclipse.golo.compiler.macro.Macro;
014
015import java.lang.reflect.*;
016import java.util.function.Predicate;
017import java.util.stream.Stream;
018
019import static org.eclipse.golo.runtime.TypeMatching.compareTypes;
020import static org.eclipse.golo.runtime.DecoratorsHelper.isMethodDecorated;
021import static org.eclipse.golo.runtime.TypeMatching.argumentsNumberMatches;
022
023public final class Extractors {
024  private Extractors() {
025    throw new UnsupportedOperationException("don't instantiate");
026  }
027
028  public static Stream<Constructor<?>> getConstructors(Class<?> klass) {
029    if (klass == null) {
030      return Stream.empty();
031    }
032    return Stream.of(klass.getConstructors());
033  }
034
035  public static Stream<Method> getFunctions(Class<?> klass) {
036    return getMethods(klass).filter(Extractors::isFunction);
037  }
038
039  public static Stream<Method> getMacros(Class<?> klass) {
040    return getMethods(klass).filter(Extractors::isMacro);
041  }
042
043  public static Stream<Method> getMethods(Class<?> klass) {
044    if (klass == null) {
045      return Stream.empty();
046    }
047    return Stream.concat(
048        Stream.of(klass.getDeclaredMethods()),
049        Stream.of(klass.getMethods()))
050      .distinct()
051      .sorted((m1, m2) -> {
052        if (m1.isVarArgs() && !m2.isVarArgs()) {
053          return 1;
054        }
055        if (m2.isVarArgs() && !m1.isVarArgs()) {
056          return -1;
057        }
058        return compareTypes(m1.getParameterTypes(), m2.getParameterTypes());
059      });
060  }
061
062  public static Stream<Field> getFields(Class<?> klass) {
063    if (klass == null) {
064      return Stream.empty();
065    }
066    return Stream.concat(
067        Stream.of(klass.getDeclaredFields()),
068        Stream.of(klass.getFields()))
069      .distinct();
070  }
071
072  public static Stream<String> getImportedNames(Class<?> klass) {
073    if (klass == null) {
074      return Stream.empty();
075    }
076    return Stream.of(Module.imports(klass));
077  }
078
079  public static Stream<Member> getMembers(Class<?> klass) {
080    if (klass == null) {
081      return Stream.empty();
082    }
083    return Stream.concat(getMethods(klass), getFields(klass));
084  }
085
086  public static boolean isPublic(Member m) {
087    return Modifier.isPublic(m.getModifiers());
088  }
089
090  public static boolean isStatic(Member m) {
091    return Modifier.isStatic(m.getModifiers());
092  }
093
094  public static boolean isConcrete(Member m) {
095    return !Modifier.isAbstract(m.getModifiers());
096  }
097
098  public static boolean isFunction(Method m) {
099    return isConcrete(m) && isPublic(m) && isStatic(m) && !m.isAnnotationPresent(Macro.class);
100  }
101
102  public static boolean isMacro(Method m) {
103    return isConcrete(m) && isPublic(m) && isStatic(m) && m.isAnnotationPresent(Macro.class);
104  }
105
106  public static Predicate<Member> isNamed(String name) {
107    return m -> m.getName().equals(name);
108  }
109
110  public static Predicate<Method> matchFunctionReference(String name, int arity, boolean varargs) {
111    return m ->
112      m.getName().equals(name)
113      && (isMethodDecorated(m) || argumentsNumberMatches(m.getParameterCount(), arity, varargs))
114      && (arity < 0 || m.isVarArgs() == varargs);
115  }
116
117  public static <T extends AnnotatedElement & Member> T checkDeprecation(Class<?> caller, T object) {
118    if (object.isAnnotationPresent(Deprecated.class)) {
119      Warnings.deprecatedElement(
120          (object instanceof Executable ? ((Executable) object).toGenericString()
121           : object instanceof Field ? ((Field) object).toGenericString()
122           : object.getName()),
123          caller.getName());
124    }
125    return object;
126  }
127
128}