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 org.eclipse.golo.runtime; 011 012import gololang.Union; 013import java.lang.invoke.MethodHandle; 014import java.lang.reflect.Method; 015import java.util.Objects; 016 017import static java.lang.invoke.MethodHandles.*; 018import static java.lang.invoke.MethodType.methodType; 019 020public class PropertyMethodFinder extends MethodFinder { 021 022 private static final MethodHandle FLUENT_SETTER; 023 024 static { 025 try { 026 FLUENT_SETTER = lookup().findStatic( 027 PropertyMethodFinder.class, 028 "fluentSetter", 029 methodType(Object.class, Object.class, Object.class)); 030 } catch (NoSuchMethodException | IllegalAccessException e) { 031 throw new Error("Could not bootstrap the required fluent method handles", e); 032 } 033 } 034 035 private static Object fluentSetter(Object o, Object notUsedSetterReturnValue) { 036 return o; 037 } 038 039 private String propertyName; 040 041 public PropertyMethodFinder(MethodInvocation invocation, Lookup lookup) { 042 super(invocation, lookup); 043 this.propertyName = capitalize(invocation.name()); 044 } 045 046 private MethodHandle findMethodForGetter() { 047 if (Union.class.isAssignableFrom(invocation.receiverClass())) { 048 return null; 049 } 050 MethodHandle target = new RegularMethodFinder( 051 invocation.withName("get" + propertyName), 052 lookup 053 ).find(); 054 055 if (target != null) { 056 return target; 057 } 058 return new RegularMethodFinder( 059 invocation.withName("is" + propertyName), 060 lookup 061 ).find(); 062 } 063 064 private MethodHandle fluentMethodHandle(Method candidate) { 065 Objects.requireNonNull(candidate); 066 MethodHandle target = toMethodHandle(candidate).orElse(null); 067 if (target != null) { 068 if (!TypeMatching.returnsValue(candidate)) { 069 Object receiver = invocation.arguments()[0]; 070 MethodHandle fluent = FLUENT_SETTER.bindTo(receiver); 071 target = filterReturnValue(target, fluent); 072 } 073 } 074 return target; 075 } 076 077 private MethodHandle findMethodForSetter() { 078 return new RegularMethodFinder( 079 invocation.withName("set" + propertyName), 080 lookup) 081 .findInMethods() 082 .filter(method -> !Union.class.isAssignableFrom(method.getDeclaringClass())) 083 .map(this::fluentMethodHandle) 084 .findFirst() 085 .orElse(null); 086 } 087 088 @Override 089 public MethodHandle find() { 090 if (invocation.arity() == 1) { 091 return findMethodForGetter(); 092 } 093 return findMethodForSetter(); 094 } 095 096 private static String capitalize(String word) { 097 return Character.toUpperCase(word.charAt(0)) + word.substring(1); 098 } 099}