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