1 /*
2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * This file is available under and governed by the GNU General Public
28  * License version 2 only, as published by the Free Software Foundation.
29  * However, the following notice accompanied the original version of this
30  * file, and Oracle licenses the original version of this file under the BSD
31  * license:
32  */
33 /*
34    Copyright 2009-2013 Attila Szegedi
35 
36    Redistribution and use in source and binary forms, with or without
37    modification, are permitted provided that the following conditions are
38    met:
39    * Redistributions of source code must retain the above copyright
40      notice, this list of conditions and the following disclaimer.
41    * Redistributions in binary form must reproduce the above copyright
42      notice, this list of conditions and the following disclaimer in the
43      documentation and/or other materials provided with the distribution.
44    * Neither the name of the copyright holder nor the names of
45      contributors may be used to endorse or promote products derived from
46      this software without specific prior written permission.
47 
48    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
49    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
51    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
52    BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
55    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
56    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
57    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
58    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 */
60 
61 package jdk.dynalink.beans;
62 
63 import java.lang.invoke.MethodHandles.Lookup;
64 import java.util.Collections;
65 import java.util.Set;
66 import jdk.dynalink.DynamicLinkerFactory;
67 import jdk.dynalink.StandardNamespace;
68 import jdk.dynalink.StandardOperation;
69 import jdk.dynalink.linker.GuardedInvocation;
70 import jdk.dynalink.linker.GuardingDynamicLinker;
71 import jdk.dynalink.linker.LinkRequest;
72 import jdk.dynalink.linker.LinkerServices;
73 import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
74 
75 /**
76  * A linker for ordinary Java objects. Normally used as the ultimate fallback
77  * linker by the {@link DynamicLinkerFactory} so it is given the chance to link
78  * calls to all objects that no other linker recognized. Specifically, this
79  * linker will:
80  * <ul>
81  * <li>expose all public methods of form {@code setXxx()}, {@code getXxx()},
82  * and {@code isXxx()} as property setters and getters for
83  * {@link StandardOperation#SET} and {@link StandardOperation#GET} operations in the
84  * {@link StandardNamespace#PROPERTY} namespace;</li>
85  * <li>expose all public methods for retrieval for
86  * {@link StandardOperation#GET} operation in the {@link StandardNamespace#METHOD} namespace;
87  * the methods thus retrieved can then be invoked using {@link StandardOperation#CALL}.</li>
88  * <li>expose all public fields as properties, unless there are getters or
89  * setters for the properties of the same name;</li>
90  * <li> expose elements of native Java arrays, {@link java.util.List} and {@link java.util.Map} objects as
91  * {@link StandardOperation#GET} and {@link StandardOperation#SET} operations in the
92  * {@link StandardNamespace#ELEMENT} namespace;</li>
93  * <li> expose removal of elements of {@link java.util.List} and {@link java.util.Map} objects as
94  * {@link StandardOperation#REMOVE} operation in the {@link StandardNamespace#ELEMENT} namespace;</li>
95  * <li>expose a virtual property named {@code length} on Java arrays, {@link java.util.Collection} and
96  * {@link java.util.Map} objects;</li>
97  * <li>expose {@link StandardOperation#NEW} on instances of {@link StaticClass}
98  * as calls to constructors, including those static class objects that represent
99  * Java arrays (their constructors take a single {@code int} parameter
100  * representing the length of the array to create);</li>
101  * <li>expose static methods, fields, and properties of classes in a similar
102  * manner to how instance method, fields, and properties are exposed, on
103  * {@link StaticClass} objects.</li>
104  * <li>expose a virtual property named {@code static} on instances of
105  * {@link java.lang.Class} to access their {@link StaticClass}.</li>
106  * </ul>
107  * <p><strong>Overloaded method resolution</strong> is performed automatically
108  * for property setters, methods, and constructors. Additionally, manual
109  * overloaded method selection is supported by having a call site specify a name
110  * for a method that contains an explicit signature, e.g.
111  * {@code StandardOperation.GET.withNamespace(METHOD).named("parseInt(String,int)")}
112  * You can use non-qualified class names in such signatures regardless of those
113  * classes' packages, they will match any class with the same non-qualified name. You
114  * only have to use a fully qualified class name in case non-qualified class
115  * names would cause selection ambiguity (that is extremely rare). Overloaded
116  * resolution for constructors is not automatic as there is no logical place to
117  * attach that functionality to but if a language wishes to provide this
118  * functionality, it can use {@link #getConstructorMethod(Class, String)} as a
119  * useful building block for it.</p>
120  * <p><strong>Variable argument invocation</strong> is handled for both methods
121  * and constructors.</p>
122  * <p><strong>Caller sensitive methods</strong> can be linked as long as they
123  * are otherwise public and link requests have call site descriptors carrying
124  * full-strength {@link Lookup} objects and not weakened lookups or the public
125  * lookup.</p>
126  * <p><strong>The behavior for handling missing members</strong> can be
127  * customized by passing a {@link MissingMemberHandlerFactory} to the
128  * {@link BeansLinker#BeansLinker(MissingMemberHandlerFactory) constructor}.
129  * </p>
130  * <p>The class also exposes various methods for discovery of available
131  * property and method names on classes and class instances, as well as access
132  * to per-class linkers using the {@link #getLinkerForClass(Class)}
133  * method.</p>
134  */
135 public class BeansLinker implements GuardingDynamicLinker {
136     private static final ClassValue<TypeBasedGuardingDynamicLinker> linkers = new ClassValue<TypeBasedGuardingDynamicLinker>() {
137         @Override
138         protected TypeBasedGuardingDynamicLinker computeValue(final Class<?> clazz) {
139             // If ClassValue.put() were public, we could just pre-populate with these known mappings...
140             return
141                 clazz == Class.class ? new ClassLinker() :
142                 clazz == StaticClass.class ? new StaticClassLinker() :
143                 DynamicMethod.class.isAssignableFrom(clazz) ? new DynamicMethodLinker() :
144                 new BeanLinker(clazz);
145         }
146     };
147 
148     private final MissingMemberHandlerFactory missingMemberHandlerFactory;
149 
150     /**
151      * Creates a new beans linker. Equivalent to
152      * {@link BeansLinker#BeansLinker(MissingMemberHandlerFactory)} with
153      * {@code null} passed as the missing member handler factory, resulting in
154      * the default behavior for linking and evaluating missing members.
155      */
BeansLinker()156     public BeansLinker() {
157         this(null);
158     }
159 
160     /**
161      * Creates a new beans linker with the specified factory for creating
162      * missing member handlers. The passed factory can be null if the default
163      * behavior is adequate. See {@link MissingMemberHandlerFactory} for details.
164      * @param missingMemberHandlerFactory a factory for creating handlers for
165      * operations on missing members.
166      */
BeansLinker(final MissingMemberHandlerFactory missingMemberHandlerFactory)167     public BeansLinker(final MissingMemberHandlerFactory missingMemberHandlerFactory) {
168         this.missingMemberHandlerFactory = missingMemberHandlerFactory;
169     }
170 
171     /**
172      * Returns a bean linker for a particular single class. Useful when you need
173      * to override or extend the behavior of linking for some classes in your
174      * language runtime's linker, but still want to delegate to the default
175      * behavior in some cases.
176      * @param clazz the class
177      * @return a bean linker for that class
178      */
getLinkerForClass(final Class<?> clazz)179     public TypeBasedGuardingDynamicLinker getLinkerForClass(final Class<?> clazz) {
180         final TypeBasedGuardingDynamicLinker staticLinker = getStaticLinkerForClass(clazz);
181         if (missingMemberHandlerFactory == null) {
182             return staticLinker;
183         }
184         return new NoSuchMemberHandlerBindingLinker(staticLinker, missingMemberHandlerFactory);
185     }
186 
187     private static class NoSuchMemberHandlerBindingLinker implements TypeBasedGuardingDynamicLinker {
188         private final TypeBasedGuardingDynamicLinker linker;
189         private final MissingMemberHandlerFactory missingMemberHandlerFactory;
190 
NoSuchMemberHandlerBindingLinker(final TypeBasedGuardingDynamicLinker linker, final MissingMemberHandlerFactory missingMemberHandlerFactory)191         NoSuchMemberHandlerBindingLinker(final TypeBasedGuardingDynamicLinker linker, final MissingMemberHandlerFactory missingMemberHandlerFactory) {
192             this.linker = linker;
193             this.missingMemberHandlerFactory = missingMemberHandlerFactory;
194         }
195 
196         @Override
canLinkType(final Class<?> type)197         public boolean canLinkType(final Class<?> type) {
198             return linker.canLinkType(type);
199         }
200 
201         @Override
getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)202         public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
203             return linker.getGuardedInvocation(linkRequest,
204                     LinkerServicesWithMissingMemberHandlerFactory.get(
205                             linkerServices, missingMemberHandlerFactory));
206         }
207     }
208 
getStaticLinkerForClass(final Class<?> clazz)209     static TypeBasedGuardingDynamicLinker getStaticLinkerForClass(final Class<?> clazz) {
210         return linkers.get(clazz);
211     }
212 
213     /**
214      * Returns true if the object is a Java dynamic method (e.g., one
215      * obtained through a {@code GET:METHOD} operation on a Java object or
216      * {@link StaticClass} or through
217      * {@link #getConstructorMethod(Class, String)}.
218      *
219      * @param obj the object we want to test for being a Java dynamic method.
220      * @return true if it is a dynamic method, false otherwise.
221      */
isDynamicMethod(final Object obj)222     public static boolean isDynamicMethod(final Object obj) {
223         return obj instanceof DynamicMethod;
224     }
225 
226     /**
227      * Returns true if the object is a Java constructor (obtained through
228      * {@link #getConstructorMethod(Class, String)}}.
229      *
230      * @param obj the object we want to test for being a Java constructor.
231      * @return true if it is a constructor, false otherwise.
232      */
isDynamicConstructor(final Object obj)233     public static boolean isDynamicConstructor(final Object obj) {
234         return obj instanceof DynamicMethod && ((DynamicMethod)obj).isConstructor();
235     }
236 
237     /**
238      * Return the dynamic method of constructor of the given class and the given
239      * signature. This method is useful for exposing a functionality for
240      * selecting an overloaded constructor based on an explicit signature, as
241      * this functionality is not otherwise exposed by Dynalink as
242      * {@link StaticClass} objects act as overloaded constructors without
243      * explicit signature selection. Example usage would be:
244      * {@code getConstructorMethod(java.awt.Color.class, "int, int, int")}.
245      * @param clazz the class
246      * @param signature full signature of the constructor. Note how you can use
247      * names of primitive types, array names with normal Java notation (e.g.
248      * {@code "int[]"}), and normally you can even use unqualified class names
249      * (e.g. {@code "String, List"} instead of
250      * {@code "java.lang.String, java.util.List"} as long as they don't cause
251      * ambiguity in the specific parameter position.
252      * @return dynamic method for the constructor or null if no constructor with
253      * the specified signature exists.
254      */
getConstructorMethod(final Class<?> clazz, final String signature)255     public static Object getConstructorMethod(final Class<?> clazz, final String signature) {
256         return StaticClassLinker.getConstructorMethod(clazz, signature);
257     }
258 
259     /**
260      * Returns a set of names of all readable instance properties of a class.
261      * @param clazz the class
262      * @return a set of names of all readable instance properties of a class.
263      */
getReadableInstancePropertyNames(final Class<?> clazz)264     public static Set<String> getReadableInstancePropertyNames(final Class<?> clazz) {
265         final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);
266         if(linker instanceof BeanLinker) {
267             return ((BeanLinker)linker).getReadablePropertyNames();
268         }
269         return Collections.emptySet();
270     }
271 
272     /**
273      * Returns a set of names of all writable instance properties of a class.
274      * @param clazz the class
275      * @return a set of names of all writable instance properties of a class.
276      */
getWritableInstancePropertyNames(final Class<?> clazz)277     public static Set<String> getWritableInstancePropertyNames(final Class<?> clazz) {
278         final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);
279         if(linker instanceof BeanLinker) {
280             return ((BeanLinker)linker).getWritablePropertyNames();
281         }
282         return Collections.emptySet();
283     }
284 
285     /**
286      * Returns a set of names of all instance methods of a class.
287      * @param clazz the class
288      * @return a set of names of all instance methods of a class.
289      */
getInstanceMethodNames(final Class<?> clazz)290     public static Set<String> getInstanceMethodNames(final Class<?> clazz) {
291         final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);
292         if(linker instanceof BeanLinker) {
293             return ((BeanLinker)linker).getMethodNames();
294         }
295         return Collections.emptySet();
296     }
297 
298     /**
299      * Returns a set of names of all readable static properties of a class.
300      * @param clazz the class
301      * @return a set of names of all readable static properties of a class.
302      */
getReadableStaticPropertyNames(final Class<?> clazz)303     public static Set<String> getReadableStaticPropertyNames(final Class<?> clazz) {
304         return StaticClassLinker.getReadableStaticPropertyNames(clazz);
305     }
306 
307     /**
308      * Returns a set of names of all writable static properties of a class.
309      * @param clazz the class
310      * @return a set of names of all writable static properties of a class.
311      */
getWritableStaticPropertyNames(final Class<?> clazz)312     public static Set<String> getWritableStaticPropertyNames(final Class<?> clazz) {
313         return StaticClassLinker.getWritableStaticPropertyNames(clazz);
314     }
315 
316     /**
317      * Returns a set of names of all static methods of a class.
318      * @param clazz the class
319      * @return a set of names of all static methods of a class.
320      */
getStaticMethodNames(final Class<?> clazz)321     public static Set<String> getStaticMethodNames(final Class<?> clazz) {
322         return StaticClassLinker.getStaticMethodNames(clazz);
323     }
324 
325     @Override
getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)326     public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
327             throws Exception {
328         final Object receiver = request.getReceiver();
329         if(receiver == null) {
330             // Can't operate on null
331             return null;
332         }
333         return getLinkerForClass(receiver.getClass()).getGuardedInvocation(request,
334                 LinkerServicesWithMissingMemberHandlerFactory.get(linkerServices,
335                         missingMemberHandlerFactory));
336     }
337 }
338