001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2025 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.proxy; 015 016import java.lang.reflect.Constructor; 017import java.lang.reflect.Executable; 018import java.lang.reflect.GenericArrayType; 019import java.lang.reflect.GenericDeclaration; 020import java.lang.reflect.Method; 021import java.lang.reflect.ParameterizedType; 022import java.lang.reflect.Type; 023import java.lang.reflect.TypeVariable; 024import java.lang.reflect.WildcardType; 025 026import java.util.List; 027 028import java.util.function.Supplier; 029 030import javax.lang.model.element.ExecutableElement; 031import javax.lang.model.element.Parameterizable; 032import javax.lang.model.element.TypeElement; 033 034import javax.lang.model.type.DeclaredType; 035import javax.lang.model.type.TypeKind; 036import javax.lang.model.type.TypeMirror; 037 038import org.microbean.construct.Domain; 039 040/** 041 * An {@link AbstractProxier} that helps subclasses create {@link Proxy proxies} using the {@link 042 * java.lang.reflect.Proxy java.lang.reflect.Proxy} machinery present in the Java Development Kit. 043 * 044 * <p>This class also contains various {@code protected} utility methods that help with converting reflective {@link 045 * Type}s and {@link Executable}s to their {@link TypeMirror} and {@link ExecutableElement} counterparts.</p> 046 * 047 * @param <PS> the {@link ProxySpecification} type 048 * 049 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 050 * 051 * @see #proxy(ProxySpecification, Supplier) 052 */ 053public abstract non-sealed class AbstractReflectiveProxier<PS extends ProxySpecification> extends AbstractProxier<PS> { 054 055 private static final TypeMirror[] EMPTY_TYPE_MIRROR_ARRAY = new TypeMirror[0]; 056 057 /** 058 * Creates a new {@link AbstractReflectiveProxier} implementation. 059 * 060 * @param domain a {@link Domain}; must not be {@code null} 061 * 062 * @exception NullPointerException if {@code domain} is {@code null} 063 */ 064 protected AbstractReflectiveProxier(final Domain domain) { 065 super(domain); 066 } 067 068 /** 069 * A convenience method that returns {@code true} if the supplied {@link Method} is the {@link Object#equals(Object) 070 * java.lang.Object#equals(java.lang.Object)} method. 071 * 072 * @param m a {@link Method}; must not be {@code null} 073 * 074 * @return {@code true} if the supplied {@link Method} is the {@link Object#equals(Object) 075 * java.lang.Object#equals(java.lang.Object)} method 076 * 077 * @exception NullPointerException if {@code m} is {@code null} 078 */ 079 // (Convenience.) 080 protected final boolean equalsMethod(final Method m) { 081 return 082 m.getDeclaringClass() == Object.class && 083 m.getReturnType() == boolean.class && 084 m.getParameterCount() == 1 && 085 m.getParameterTypes()[0] == Object.class && 086 m.getName().equals("equals"); 087 } 088 089 /** 090 * A convenience method that returns {@code true} if the supplied {@link Method} is the {@link Object#hashCode() 091 * java.lang.Object#hashCode()} method. 092 * 093 * @param m a {@link Method}; must not be {@code null} 094 * 095 * @return {@code true} if the supplied {@link Method} is the {@link Object#hashCode() java.lang.Object#hashCode()} 096 * method 097 * 098 * @exception NullPointerException if {@code m} is {@code null} 099 */ 100 // (Convenience.) 101 protected final boolean hashCodeMethod(final Method m) { 102 return 103 m.getDeclaringClass() == Object.class && 104 m.getReturnType() == int.class && 105 m.getParameterCount() == 0 && 106 m.getName().equals("hashCode"); 107 } 108 109 /** 110 * Returns a {@link Proxy} appropriate for the supplied specification and {@link Supplier} of instances. 111 * 112 * @param ps an appropriate proxy specification; must not be {@code null} 113 * 114 * @param instanceSupplier a {@link Supplier} of contextual instances; must not be {@code null}; may or may not create 115 * a new contextual instance each time it is invoked; may or may not be invoked multiple times depending on the 116 * subclass implementation 117 * 118 * @return a non-{@code null} {@link Proxy} 119 * 120 * @exception NullPointerException if any argument is {@code null} 121 * 122 * @exception IllegalArgumentException if the {@linkplain ProxySpecification#superclass() superclass} is not {@link 123 * Object java.lang.Object} (only interfaces may be proxied reflectively) 124 * 125 * @see #proxy(ProxySpecification, Class[], Supplier) 126 * 127 * @see #classLoader() 128 */ 129 @Override // AbstractProxier<PS> 130 public final <R> Proxy<R> proxy(final PS ps, final Supplier<? extends R> instanceSupplier) { 131 final Domain domain = this.domain(); 132 if (!domain.javaLangObject(ps.superclass())) { 133 throw new IllegalArgumentException("ps: " + ps); 134 } 135 final List<? extends TypeMirror> interfaceTypeMirrors = ps.interfaces(); 136 final int size = interfaceTypeMirrors.size(); 137 final Class<?>[] interfaces = new Class<?>[size]; 138 final ClassLoader classLoader = this.classLoader(); 139 try { 140 for (int i = 0; i < size; i++) { 141 final TypeElement e = (TypeElement)((DeclaredType)interfaceTypeMirrors.get(i)).asElement(); 142 final String binaryName = domain.toString(domain.binaryName(e)); 143 interfaces[i] = Class.forName(binaryName, false, classLoader); 144 } 145 } catch (final ClassNotFoundException cnfe) { 146 throw new IllegalArgumentException("ps: " + ps, cnfe); 147 } 148 return this.proxy(ps, interfaces, instanceSupplier); 149 } 150 151 /** 152 * Returns a {@link Proxy} appropriate for the supplied specification and {@link Supplier} of contextual instances. 153 * 154 * @param <R> the contextual instance type 155 * 156 * @param ps an appropriate proxy specification; must not be {@code null} 157 * 158 * @param interfaces the interfaces to implement; every element is guaranteed to {@linkplain Class#isInterface() be an interface} 159 * 160 * @param instanceSupplier a {@link Supplier} of contextual instances; must not be {@code null}; may or may not create 161 * a new contextual instance each time it is invoked; may or may not be invoked multiple times depending on the 162 * subclass implementation 163 * 164 * @return a non-{@code null} {@link Proxy} 165 * 166 * @exception NullPointerException if any argument is {@code null} 167 */ 168 protected abstract <R> Proxy<R> proxy(final PS ps, 169 final Class<?>[] interfaces, 170 final Supplier<? extends R> instanceSupplier); 171 172}