001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core; 018 019import java.lang.reflect.Constructor; 020import java.lang.reflect.Method; 021import java.util.List; 022import java.util.stream.Collectors; 023 024import kotlin.reflect.KFunction; 025import kotlin.reflect.KParameter; 026import kotlin.reflect.jvm.ReflectJvmMapping; 027 028import org.springframework.lang.Nullable; 029 030/** 031 * {@link ParameterNameDiscoverer} implementation which uses Kotlin's reflection facilities 032 * for introspecting parameter names. 033 * 034 * Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to 035 * determine interface parameter names without requiring Java 8 -parameters compiler flag. 036 * 037 * @author Sebastien Deleuze 038 * @since 5.0 039 */ 040public class KotlinReflectionParameterNameDiscoverer implements ParameterNameDiscoverer { 041 042 @Override 043 @Nullable 044 public String[] getParameterNames(Method method) { 045 if (!KotlinDetector.isKotlinType(method.getDeclaringClass())) { 046 return null; 047 } 048 049 try { 050 KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); 051 return (function != null ? getParameterNames(function.getParameters()) : null); 052 } 053 catch (UnsupportedOperationException ex) { 054 return null; 055 } 056 } 057 058 @Override 059 @Nullable 060 public String[] getParameterNames(Constructor<?> ctor) { 061 if (ctor.getDeclaringClass().isEnum() || !KotlinDetector.isKotlinType(ctor.getDeclaringClass())) { 062 return null; 063 } 064 065 try { 066 KFunction<?> function = ReflectJvmMapping.getKotlinFunction(ctor); 067 return (function != null ? getParameterNames(function.getParameters()) : null); 068 } 069 catch (UnsupportedOperationException ex) { 070 return null; 071 } 072 } 073 074 @Nullable 075 private String[] getParameterNames(List<KParameter> parameters) { 076 List<KParameter> filteredParameters = parameters 077 .stream() 078 // Extension receivers of extension methods must be included as they appear as normal method parameters in Java 079 .filter(p -> KParameter.Kind.VALUE.equals(p.getKind()) || KParameter.Kind.EXTENSION_RECEIVER.equals(p.getKind())) 080 .collect(Collectors.toList()); 081 String[] parameterNames = new String[filteredParameters.size()]; 082 for (int i = 0; i < filteredParameters.size(); i++) { 083 KParameter parameter = filteredParameters.get(i); 084 // extension receivers are not explicitly named, but require a name for Java interoperability 085 // $receiver is not a valid Kotlin identifier, but valid in Java, so it can be used here 086 String name = KParameter.Kind.EXTENSION_RECEIVER.equals(parameter.getKind()) ? "$receiver" : parameter.getName(); 087 if (name == null) { 088 return null; 089 } 090 parameterNames[i] = name; 091 } 092 return parameterNames; 093 } 094 095}