001/* 002 * Copyright (c) 2012-2018 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.adapters; 012 013import gololang.FunctionReference; 014 015import java.lang.reflect.Method; 016import java.util.*; 017 018import static java.lang.invoke.MethodType.genericMethodType; 019import static java.lang.reflect.Modifier.*; 020import static java.util.Collections.unmodifiableMap; 021import static java.util.Collections.unmodifiableSet; 022 023public final class AdapterDefinition { 024 025 private final ClassLoader classLoader; 026 private final String name; 027 private final String parent; 028 private final TreeSet<String> interfaces = new TreeSet<>(); 029 private final LinkedHashMap<String, FunctionReference> implementations = new LinkedHashMap<>(); 030 private final LinkedHashMap<String, FunctionReference> overrides = new LinkedHashMap<>(); 031 032 public AdapterDefinition(ClassLoader classLoader, String name, String parent) { 033 this.classLoader = classLoader; 034 this.name = name; 035 this.parent = parent; 036 } 037 038 public ClassLoader getClassLoader() { 039 return classLoader; 040 } 041 042 public String getName() { 043 return name; 044 } 045 046 public String getParent() { 047 return parent; 048 } 049 050 public Set<String> getInterfaces() { 051 return unmodifiableSet(interfaces); 052 } 053 054 public Map<String, FunctionReference> getImplementations() { 055 return unmodifiableMap(implementations); 056 } 057 058 public Map<String, FunctionReference> getOverrides() { 059 return unmodifiableMap(overrides); 060 } 061 062 public AdapterDefinition implementsInterface(String iface) { 063 interfaces.add(iface); 064 return this; 065 } 066 067 public AdapterDefinition implementsMethod(String name, FunctionReference target) throws AdapterDefinitionProblem { 068 checkForImplementation(target); 069 checkStarImplementationType(name, target); 070 implementations.put(name, target); 071 return this; 072 } 073 074 public AdapterDefinition overridesMethod(String name, FunctionReference target) throws AdapterDefinitionProblem { 075 checkForOverriding(target); 076 checkStarOverrideType(name, target); 077 overrides.put(name, target); 078 return this; 079 } 080 081 public boolean hasStarImplementation() { 082 return implementations.containsKey("*"); 083 } 084 085 public boolean hasStarOverride() { 086 return overrides.containsKey("*"); 087 } 088 089 public AdapterDefinition validate() throws AdapterDefinitionProblem { 090 checkSuperTypesExistence(); 091 checkStarConflict(); 092 checkMethodsToBeImplemented(); 093 checkOverridesImplementationsConflict(); 094 checkAllOverridesAndImplementationsExist(); 095 return this; 096 } 097 098 private void checkOverridesImplementationsConflict() { 099 for (String key : implementations.keySet()) { 100 if (!"*".equals(key) && overrides.containsKey(key)) { 101 throw new AdapterDefinitionProblem("Conflict: there is both an implementation and an override for method " + key); 102 } 103 } 104 } 105 106 private void checkStarImplementationType(String name, FunctionReference target) { 107 if ("*".equals(name) && !target.type().equals(genericMethodType(2))) { 108 throw new AdapterDefinitionProblem("A * implementation must be of type (Object methodName, Object args)Object: " + target); 109 } 110 } 111 112 private void checkStarOverrideType(String name, FunctionReference target) { 113 if ("*".equals(name) && !target.type().equals(genericMethodType(3))) { 114 throw new AdapterDefinitionProblem("A * override must be of type (Object superHandle, Object methodName, Object args)Object: " + target); 115 } 116 } 117 118 private void checkAllOverridesAndImplementationsExist() { 119 try { 120 Class<?> parentClass = Class.forName(parent, true, classLoader); 121 HashSet<String> canBeOverridden = new HashSet<>(); 122 for (Method method : parentClass.getMethods()) { 123 if (!isStatic(method.getModifiers())) { 124 canBeOverridden.add(method.getName()); 125 } 126 } 127 for (Method method : parentClass.getDeclaredMethods()) { 128 if (!isStatic(method.getModifiers())) { 129 canBeOverridden.add(method.getName()); 130 } 131 } 132 for (Method method : parentClass.getMethods()) { 133 if (!isStatic(method.getModifiers())) { 134 canBeOverridden.add(method.getName()); 135 } 136 } 137 for (String key : overrides.keySet()) { 138 if (!"*".equals(key) && !canBeOverridden.contains(key)) { 139 throw new AdapterDefinitionProblem("There is no method named " + key + " to be overridden in " + parentClass); 140 } 141 } 142 for (String iface : interfaces) { 143 for (Method method : Class.forName(iface, true, classLoader).getMethods()) { 144 canBeOverridden.add(method.getName()); 145 } 146 } 147 for (String key : implementations.keySet()) { 148 if (!"*".equals(key) && !canBeOverridden.contains(key)) { 149 throw new AdapterDefinitionProblem("There is no method named " + key + " to be implemented in " + parentClass + " or interfaces " + interfaces); 150 } 151 } 152 } catch (ClassNotFoundException e) { 153 throw new AdapterDefinitionProblem(e); 154 } 155 } 156 157 private Set<Method> abstractMethodsIn(Class<?> klass) { 158 LinkedHashSet<Method> abstractMethods = new LinkedHashSet<>(); 159 for (Method method : klass.getMethods()) { 160 if (isAbstract(method.getModifiers())) { 161 abstractMethods.add(method); 162 } 163 } 164 for (Method method : klass.getDeclaredMethods()) { 165 if (isAbstract(method.getModifiers())) { 166 abstractMethods.add(method); 167 } 168 } 169 return abstractMethods; 170 } 171 172 private void checkMethodsToBeImplemented() { 173 try { 174 LinkedHashSet<Method> abstractMethods = new LinkedHashSet<>(); 175 abstractMethods.addAll(abstractMethodsIn(Class.forName(parent, true, classLoader))); 176 for (String iface : interfaces) { 177 abstractMethods.addAll(abstractMethodsIn(Class.forName(iface, true, classLoader))); 178 } 179 for (Method abstractMethod : abstractMethods) { 180 String name = abstractMethod.getName(); 181 if (!implementations.containsKey(name) && !hasStarImplementation()) { 182 throw new AdapterDefinitionProblem("There is no implementation or override for: " + abstractMethod); 183 } 184 if (implementations.containsKey(name)) { 185 FunctionReference target = implementations.get(name); 186 if (argsDifferForImplementation(abstractMethod, target) || varargsMismatch(abstractMethod, target)) { 187 throw new AdapterDefinitionProblem("Types do not match to implement " + abstractMethod + " with " + target); 188 } 189 } 190 if (overrides.containsKey(name)) { 191 FunctionReference target = overrides.get(name); 192 if (argsDifferForOverride(abstractMethod, target) || varargsMismatch(abstractMethod, target)) { 193 throw new AdapterDefinitionProblem("Types do not match to implement " + abstractMethod + " with " + target); 194 } 195 } 196 } 197 } catch (ClassNotFoundException e) { 198 throw new AdapterDefinitionProblem(e); 199 } 200 } 201 202 private boolean varargsMismatch(Method abstractMethod, FunctionReference target) { 203 return abstractMethod.isVarArgs() != target.isVarargsCollector(); 204 } 205 206 private boolean argsDifferForImplementation(Method abstractMethod, FunctionReference target) { 207 return (target.type().parameterCount() - 1 != abstractMethod.getParameterTypes().length); 208 } 209 210 private boolean argsDifferForOverride(Method abstractMethod, FunctionReference target) { 211 return (target.type().parameterCount() - 2 != abstractMethod.getParameterTypes().length); 212 } 213 214 private void checkStarConflict() { 215 if (hasStarImplementation() && hasStarOverride()) { 216 throw new AdapterDefinitionProblem("Having both a star implementation and a star override is forbidden."); 217 } 218 } 219 220 private void checkSuperTypesExistence() { 221 try { 222 Class<?> parentClass = Class.forName(parent, true, classLoader); 223 if (parentClass.isInterface()) { 224 throw new AdapterDefinitionProblem("The parent class cannot be an interface: " + parentClass.getName()); 225 } 226 if (isFinal(parentClass.getModifiers())) { 227 throw new AdapterDefinitionProblem("The parent class is final: " + parentClass.getName()); 228 } 229 for (String iface : interfaces) { 230 Class.forName(iface, true, classLoader); 231 } 232 } catch (ClassNotFoundException e) { 233 throw new AdapterDefinitionProblem(e); 234 } 235 } 236 237 private void checkForImplementation(FunctionReference target) throws AdapterDefinitionProblem { 238 if (target.type().parameterCount() < 1) { 239 throw new AdapterDefinitionProblem("An implemented method target must take at least 1 argument (the receiver): " + target); 240 } 241 } 242 243 private void checkForOverriding(FunctionReference target) throws AdapterDefinitionProblem { 244 if (target.type().parameterCount() < 2) { 245 throw new AdapterDefinitionProblem("An overriden method target must take at least 2 arguments (the 'super' function reference followed by the receiver): " + target); 246 } 247 } 248}