Path: blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeansLinker.java
41161 views
/*1* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file, and Oracle licenses the original version of this file under the BSD30* license:31*/32/*33Copyright 2009-2013 Attila Szegedi3435Redistribution and use in source and binary forms, with or without36modification, are permitted provided that the following conditions are37met:38* Redistributions of source code must retain the above copyright39notice, this list of conditions and the following disclaimer.40* Redistributions in binary form must reproduce the above copyright41notice, this list of conditions and the following disclaimer in the42documentation and/or other materials provided with the distribution.43* Neither the name of the copyright holder nor the names of44contributors may be used to endorse or promote products derived from45this software without specific prior written permission.4647THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS48IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED49TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A50PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER51BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR52CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF53SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR54BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,55WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR56OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF57ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.58*/5960package jdk.dynalink.beans;6162import java.lang.invoke.MethodHandles.Lookup;63import java.util.Collections;64import java.util.Set;65import jdk.dynalink.DynamicLinkerFactory;66import jdk.dynalink.StandardNamespace;67import jdk.dynalink.StandardOperation;68import jdk.dynalink.linker.GuardedInvocation;69import jdk.dynalink.linker.GuardingDynamicLinker;70import jdk.dynalink.linker.LinkRequest;71import jdk.dynalink.linker.LinkerServices;72import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;7374/**75* A linker for ordinary Java objects. Normally used as the ultimate fallback76* linker by the {@link DynamicLinkerFactory} so it is given the chance to link77* calls to all objects that no other linker recognized. Specifically, this78* linker will:79* <ul>80* <li>if the object is a {@link java.lang.Record record}, expose all public accessors of81* record components as property getters for {@link StandardOperation#GET} operations82* in the {@link StandardNamespace#PROPERTY} namespace;</li>83* <li>expose all public methods of form {@code setXxx()}, {@code getXxx()},84* and {@code isXxx()} as property setters and getters for85* {@link StandardOperation#SET} and {@link StandardOperation#GET} operations in the86* {@link StandardNamespace#PROPERTY} namespace, except for getters for properties87* with names already handled by record component getters;</li>88* <li>expose all public methods for retrieval for89* {@link StandardOperation#GET} operation in the {@link StandardNamespace#METHOD} namespace;90* the methods thus retrieved can then be invoked using {@link StandardOperation#CALL}.</li>91* <li>expose all public fields as properties, unless there are getters or92* setters for the properties of the same name;</li>93* <li> expose elements of native Java arrays, {@link java.util.List} and {@link java.util.Map} objects as94* {@link StandardOperation#GET} and {@link StandardOperation#SET} operations in the95* {@link StandardNamespace#ELEMENT} namespace;</li>96* <li> expose removal of elements of {@link java.util.List} and {@link java.util.Map} objects as97* {@link StandardOperation#REMOVE} operation in the {@link StandardNamespace#ELEMENT} namespace;</li>98* <li>expose a virtual property named {@code length} on Java arrays, {@link java.util.Collection} and99* {@link java.util.Map} objects;</li>100* <li>expose {@link StandardOperation#NEW} on instances of {@link StaticClass}101* as calls to constructors, including those static class objects that represent102* Java arrays (their constructors take a single {@code int} parameter103* representing the length of the array to create);</li>104* <li>expose static methods, fields, and properties of classes in a similar105* manner to how instance method, fields, and properties are exposed, on106* {@link StaticClass} objects.</li>107* <li>expose a virtual property named {@code static} on instances of108* {@link java.lang.Class} to access their {@link StaticClass}.</li>109* </ul>110* <p><strong>Overloaded method resolution</strong> is performed automatically111* for property setters, methods, and constructors. Additionally, manual112* overloaded method selection is supported by having a call site specify a name113* for a method that contains an explicit signature, e.g.114* {@code StandardOperation.GET.withNamespace(METHOD).named("parseInt(String,int)")}115* You can use non-qualified class names in such signatures regardless of those116* classes' packages, they will match any class with the same non-qualified name. You117* only have to use a fully qualified class name in case non-qualified class118* names would cause selection ambiguity (that is extremely rare). Overloaded119* resolution for constructors is not automatic as there is no logical place to120* attach that functionality to but if a language wishes to provide this121* functionality, it can use {@link #getConstructorMethod(Class, String)} as a122* useful building block for it.</p>123* <p><strong>Variable argument invocation</strong> is handled for both methods124* and constructors.</p>125* <p><strong>Caller sensitive methods</strong> can be linked as long as they126* are otherwise public and link requests have call site descriptors carrying127* full-strength {@link Lookup} objects and not weakened lookups or the public128* lookup.</p>129* <p><strong>The behavior for handling missing members</strong> can be130* customized by passing a {@link MissingMemberHandlerFactory} to the131* {@link BeansLinker#BeansLinker(MissingMemberHandlerFactory) constructor}.132* </p>133* <p>The class also exposes various methods for discovery of available134* property and method names on classes and class instances, as well as access135* to per-class linkers using the {@link #getLinkerForClass(Class)}136* method.</p>137*/138public class BeansLinker implements GuardingDynamicLinker {139private static final ClassValue<TypeBasedGuardingDynamicLinker> linkers = new ClassValue<>() {140@Override141protected TypeBasedGuardingDynamicLinker computeValue(final Class<?> clazz) {142// If ClassValue.put() were public, we could just pre-populate with these known mappings...143return144clazz == Class.class ? new ClassLinker() :145clazz == StaticClass.class ? new StaticClassLinker() :146DynamicMethod.class.isAssignableFrom(clazz) ? new DynamicMethodLinker() :147new BeanLinker(clazz);148}149};150151private final MissingMemberHandlerFactory missingMemberHandlerFactory;152153/**154* Creates a new beans linker. Equivalent to155* {@link BeansLinker#BeansLinker(MissingMemberHandlerFactory)} with156* {@code null} passed as the missing member handler factory, resulting in157* the default behavior for linking and evaluating missing members.158*/159public BeansLinker() {160this(null);161}162163/**164* Creates a new beans linker with the specified factory for creating165* missing member handlers. The passed factory can be null if the default166* behavior is adequate. See {@link MissingMemberHandlerFactory} for details.167* @param missingMemberHandlerFactory a factory for creating handlers for168* operations on missing members.169*/170public BeansLinker(final MissingMemberHandlerFactory missingMemberHandlerFactory) {171this.missingMemberHandlerFactory = missingMemberHandlerFactory;172}173174/**175* Returns a bean linker for a particular single class. Useful when you need176* to override or extend the behavior of linking for some classes in your177* language runtime's linker, but still want to delegate to the default178* behavior in some cases.179* @param clazz the class180* @return a bean linker for that class181*/182public TypeBasedGuardingDynamicLinker getLinkerForClass(final Class<?> clazz) {183final TypeBasedGuardingDynamicLinker staticLinker = getStaticLinkerForClass(clazz);184if (missingMemberHandlerFactory == null) {185return staticLinker;186}187return new NoSuchMemberHandlerBindingLinker(staticLinker, missingMemberHandlerFactory);188}189190private static class NoSuchMemberHandlerBindingLinker implements TypeBasedGuardingDynamicLinker {191private final TypeBasedGuardingDynamicLinker linker;192private final MissingMemberHandlerFactory missingMemberHandlerFactory;193194NoSuchMemberHandlerBindingLinker(final TypeBasedGuardingDynamicLinker linker, final MissingMemberHandlerFactory missingMemberHandlerFactory) {195this.linker = linker;196this.missingMemberHandlerFactory = missingMemberHandlerFactory;197}198199@Override200public boolean canLinkType(final Class<?> type) {201return linker.canLinkType(type);202}203204@Override205public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {206return linker.getGuardedInvocation(linkRequest,207LinkerServicesWithMissingMemberHandlerFactory.get(208linkerServices, missingMemberHandlerFactory));209}210}211212static TypeBasedGuardingDynamicLinker getStaticLinkerForClass(final Class<?> clazz) {213return linkers.get(clazz);214}215216/**217* Returns true if the object is a Java dynamic method (e.g., one218* obtained through a {@code GET:METHOD} operation on a Java object or219* {@link StaticClass} or through220* {@link #getConstructorMethod(Class, String)}.221*222* @param obj the object we want to test for being a Java dynamic method.223* @return true if it is a dynamic method, false otherwise.224*/225public static boolean isDynamicMethod(final Object obj) {226return obj instanceof DynamicMethod;227}228229/**230* Returns true if the object is a Java constructor (obtained through231* {@link #getConstructorMethod(Class, String)}}.232*233* @param obj the object we want to test for being a Java constructor.234* @return true if it is a constructor, false otherwise.235*/236public static boolean isDynamicConstructor(final Object obj) {237return obj instanceof DynamicMethod && ((DynamicMethod)obj).isConstructor();238}239240/**241* Return the dynamic method of constructor of the given class and the given242* signature. This method is useful for exposing a functionality for243* selecting an overloaded constructor based on an explicit signature, as244* this functionality is not otherwise exposed by Dynalink as245* {@link StaticClass} objects act as overloaded constructors without246* explicit signature selection. Example usage would be:247* {@code getConstructorMethod(java.awt.Color.class, "int, int, int")}.248* @param clazz the class249* @param signature full signature of the constructor. Note how you can use250* names of primitive types, array names with normal Java notation (e.g.251* {@code "int[]"}), and normally you can even use unqualified class names252* (e.g. {@code "String, List"} instead of253* {@code "java.lang.String, java.util.List"} as long as they don't cause254* ambiguity in the specific parameter position.255* @return dynamic method for the constructor or null if no constructor with256* the specified signature exists.257*/258public static Object getConstructorMethod(final Class<?> clazz, final String signature) {259return StaticClassLinker.getConstructorMethod(clazz, signature);260}261262/**263* Returns a set of names of all readable instance properties of a class.264* @param clazz the class265* @return a set of names of all readable instance properties of a class.266*/267public static Set<String> getReadableInstancePropertyNames(final Class<?> clazz) {268final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);269if(linker instanceof BeanLinker) {270return ((BeanLinker)linker).getReadablePropertyNames();271}272return Collections.emptySet();273}274275/**276* Returns a set of names of all writable instance properties of a class.277* @param clazz the class278* @return a set of names of all writable instance properties of a class.279*/280public static Set<String> getWritableInstancePropertyNames(final Class<?> clazz) {281final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);282if(linker instanceof BeanLinker) {283return ((BeanLinker)linker).getWritablePropertyNames();284}285return Collections.emptySet();286}287288/**289* Returns a set of names of all instance methods of a class.290* @param clazz the class291* @return a set of names of all instance methods of a class.292*/293public static Set<String> getInstanceMethodNames(final Class<?> clazz) {294final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);295if(linker instanceof BeanLinker) {296return ((BeanLinker)linker).getMethodNames();297}298return Collections.emptySet();299}300301/**302* Returns a set of names of all readable static properties of a class.303* @param clazz the class304* @return a set of names of all readable static properties of a class.305*/306public static Set<String> getReadableStaticPropertyNames(final Class<?> clazz) {307return StaticClassLinker.getReadableStaticPropertyNames(clazz);308}309310/**311* Returns a set of names of all writable static properties of a class.312* @param clazz the class313* @return a set of names of all writable static properties of a class.314*/315public static Set<String> getWritableStaticPropertyNames(final Class<?> clazz) {316return StaticClassLinker.getWritableStaticPropertyNames(clazz);317}318319/**320* Returns a set of names of all static methods of a class.321* @param clazz the class322* @return a set of names of all static methods of a class.323*/324public static Set<String> getStaticMethodNames(final Class<?> clazz) {325return StaticClassLinker.getStaticMethodNames(clazz);326}327328@Override329public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)330throws Exception {331final Object receiver = request.getReceiver();332if(receiver == null) {333// Can't operate on null334return null;335}336return getLinkerForClass(receiver.getClass()).getGuardedInvocation(request,337LinkerServicesWithMissingMemberHandlerFactory.get(linkerServices,338missingMemberHandlerFactory));339}340}341342343