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 gololang; 011 012import org.eclipse.golo.runtime.TypeMatching; 013import org.eclipse.golo.runtime.adapters.AdapterDefinition; 014import org.eclipse.golo.runtime.adapters.JavaBytecodeAdapterGenerator; 015 016import java.lang.reflect.Constructor; 017import java.util.Arrays; 018import java.util.Map; 019import java.util.concurrent.atomic.AtomicLong; 020 021/** 022 * An adapter fabric can provide instance makers of adapter objects defined at runtime. 023 * <p> 024 * An adapter object inherits from a parent class or {@code java.lang.Object}, implements a set of specified 025 * interfaces, and provides method implementation / overrides defined by Golo closures. 026 * <p> 027 * Adapter instance makers are created based on a configuration that is defined by a simple collections-based 028 * representation where the root is a {@code java.util.Map} instance with keys: 029 * <ul> 030 * <li>{@code extends}: a string for the parent class, {@code java.lang.Object} if not specified,</li> 031 * <li>{@code interfaces}: a {@code java.lang.Iterable} of strings specifying which interfaces to implement,</li> 032 * <li>{@code implements}: points to a map where keys are strings of method names to implement, and values are the 033 * implementation closures, </li> 034 * <li>{@code overrides}: same as a {@code implements} to provides overrides.</li> 035 * </ul> 036 * <p> 037 * The signature for closures is as follows: 038 * <ul> 039 * <li>implementations: {@code |receiver, argument1, argument2|} (and so on),</li> 040 * <li>overrides: {@code |handle_in_superclass, receiver, argument1, argument2|} (and so on).</li> 041 * </ul> 042 * <p>Star implementations or overrides can also be provided, in which case any unimplemented or not overridden method 043 * is dispatched to the provided closure. Note that both a star implementation and a star override cannot be defined. 044 * The signatures are as follows. 045 * <ul> 046 * <li>*-implementation: {@code |method_name, arguments_array|},</li> 047 * <li>*-overrides: {@code |handle_in_superclass, method_name, arguments_array|}.</li> 048 * </ul> 049 * <p> 050 * It is important to note that adapters are useful for interoperability with 3rd-party Java code, as that allow 051 * passing adequate objects from Golo to such libraries. Their usage for pure Golo code is discouraged. 052 */ 053public final class AdapterFabric { 054 055 /** 056 * An adapter maker can produce instances of Golo adapter objects. 057 */ 058 public static final class Maker { 059 060 private final AdapterDefinition adapterDefinition; 061 private final Class<?> adapterClass; 062 063 private Maker(AdapterDefinition adapterDefinition, Class<?> adapterClass) { 064 this.adapterDefinition = adapterDefinition; 065 this.adapterClass = adapterClass; 066 } 067 068 /** 069 * Creates a new instance, calling the right constructor based on the adapter super class. 070 * 071 * @param args the constructor arguments. 072 * @return an adapter instance. 073 * @throws ReflectiveOperationException thrown when no constructor can be found based on the argument types. 074 */ 075 public Object newInstance(Object... args) throws ReflectiveOperationException { 076 Object[] cargs = new Object[args.length + 1]; 077 cargs[0] = adapterDefinition; 078 System.arraycopy(args, 0, cargs, 1, args.length); 079 for (Constructor constructor : adapterClass.getConstructors()) { 080 Class[] parameterTypes = constructor.getParameterTypes(); 081 if ((cargs.length == parameterTypes.length) || (constructor.isVarArgs() && (cargs.length >= parameterTypes.length))) { 082 if (TypeMatching.canAssign(parameterTypes, cargs, constructor.isVarArgs())) { 083 return constructor.newInstance(cargs); 084 } 085 } 086 } 087 throw new IllegalArgumentException("Could not create an instance for arguments " + Arrays.toString(cargs)); 088 } 089 } 090 091 private final ClassLoader classLoader; 092 private final AtomicLong nextId = new AtomicLong(); 093 private final JavaBytecodeAdapterGenerator adapterGenerator = new JavaBytecodeAdapterGenerator(); 094 095 /** 096 * Makes an adapter fabric using a classloader. 097 * 098 * @param classLoader the classloader to use. 099 */ 100 public AdapterFabric(ClassLoader classLoader) { 101 this.classLoader = classLoader; 102 } 103 104 /** 105 * Makes an adapter fabric whose parent is the current thread context classloader. 106 */ 107 public AdapterFabric() { 108 this(new ClassLoader(Thread.currentThread().getContextClassLoader()) { 109 }); 110 } 111 112 /** 113 * Makes an adapter fabric whose parent classloader is provided. 114 * 115 * @param parentClassLoader the parent classloader. 116 * @return an adapter fabric. 117 */ 118 public static AdapterFabric withParentClassLoader(ClassLoader parentClassLoader) { 119 return new AdapterFabric(new ClassLoader(parentClassLoader) { 120 }); 121 } 122 123 /** 124 * Provides an instance maker based on an adapter definition. 125 * 126 * @param configuration the adapter configuration. 127 * @return an adapter maker for that configuration. 128 */ 129 public Maker maker(Map<String, Object> configuration) { 130 String parent = "java.lang.Object"; 131 if (configuration.containsKey("extends")) { 132 parent = (String) configuration.get("extends"); 133 } 134 String name = "$Golo$Adapter$" + nextId.getAndIncrement(); 135 AdapterDefinition definition = new AdapterDefinition(classLoader, name, parent); 136 137 if (configuration.containsKey("interfaces")) { 138 @SuppressWarnings("unchecked") 139 Iterable<String> interfaces = (Iterable<String>) configuration.get("interfaces"); 140 for (String iface : interfaces) { 141 definition.implementsInterface(iface); 142 } 143 } 144 if (configuration.containsKey("implements")) { 145 @SuppressWarnings("unchecked") 146 Map<String, FunctionReference> implementations = (Map<String, FunctionReference>) configuration.get("implements"); 147 for (Map.Entry<String, FunctionReference> implementation : implementations.entrySet()) { 148 definition.implementsMethod(implementation.getKey(), implementation.getValue()); 149 } 150 } 151 if (configuration.containsKey("overrides")) { 152 @SuppressWarnings("unchecked") 153 Map<String, FunctionReference> overrides = (Map<String, FunctionReference>) configuration.get("overrides"); 154 for (Map.Entry<String, FunctionReference> override : overrides.entrySet()) { 155 definition.overridesMethod(override.getKey(), override.getValue()); 156 } 157 } 158 definition.validate(); 159 Class<?> adapterClass = adapterGenerator.generateIntoDefinitionClassloader(definition); 160 return new Maker(definition, adapterClass); 161 } 162}