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