001/** 002 * Copyright (C) 2006-2024 Talend Inc. - www.talend.com 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 * http://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 */ 016package org.talend.sdk.component.runtime.manager.reflect.parameterenricher; 017 018import static java.util.Collections.emptyMap; 019import static java.util.Collections.singletonMap; 020import static java.util.Optional.ofNullable; 021import static java.util.stream.Collectors.joining; 022 023import java.lang.annotation.Annotation; 024import java.lang.reflect.InvocationTargetException; 025import java.lang.reflect.Method; 026import java.lang.reflect.Type; 027import java.util.HashMap; 028import java.util.Map; 029import java.util.concurrent.ConcurrentHashMap; 030import java.util.concurrent.ConcurrentMap; 031import java.util.stream.IntStream; 032import java.util.stream.Stream; 033 034import org.talend.sdk.component.api.configuration.action.meta.ActionRef; 035import org.talend.sdk.component.api.service.ActionType; 036 037public class ActionParameterEnricher extends BaseParameterEnricher { 038 039 public static final String META_PREFIX = "tcomp::action::"; 040 041 private final ConcurrentMap<String, String> clientActionsMapping = new ConcurrentHashMap<>(); 042 043 @Override 044 public Map<String, String> onParameterAnnotation(final String parameterName, final Type parameterType, 045 final Annotation annotation) { 046 final Class<? extends Annotation> annotationType = annotation.annotationType(); 047 final ActionRef ref = annotationType.getAnnotation(ActionRef.class); 048 if (ref == null) { 049 return emptyMap(); 050 } 051 final Class<?> actionType = ref.value(); 052 if (actionType == Object.class) { // client action 053 return singletonMap( 054 META_PREFIX 055 + clientActionsMapping.computeIfAbsent(annotationType.getSimpleName(), this::toSnakeCase), 056 getClientActionName(annotation)); 057 } 058 final String type = actionType.getAnnotation(ActionType.class).value(); 059 return new HashMap<String, String>() { 060 061 { 062 put(META_PREFIX + type, getValueString(ref.ref(), annotation)); 063 ofNullable(getParametersString(annotation)).ifPresent(v -> put(META_PREFIX + type + "::parameters", v)); 064 Stream 065 .of(annotationType.getMethods()) 066 .filter(it -> annotationType == it.getDeclaringClass() 067 && Stream.of("parameters", "value").noneMatch(v -> it.getName().equalsIgnoreCase(v))) 068 .forEach(m -> put(META_PREFIX + type + "::" + m.getName(), getString(m, annotation))); 069 } 070 }; 071 } 072 073 private String toSnakeCase(final String camelCaseName) { 074 final char[] chars = camelCaseName.substring(1).toCharArray(); 075 return Character.toLowerCase(camelCaseName.charAt(0)) 076 + IntStream.range(0, chars.length).mapToObj(i -> chars[i]).map(c -> { 077 if (Character.isUpperCase(c)) { 078 return "_" + Character.toLowerCase(c); 079 } 080 return Character.toString(c); 081 }).collect(joining()); 082 } 083 084 private String getClientActionName(final Annotation clientMeta) { 085 final String value = getValueString("value", clientMeta); 086 return "CUSTOM".equalsIgnoreCase(value) ? getValueString("name", clientMeta) : value; 087 } 088 089 private String getString(final Method method, final Annotation annotation) { 090 try { 091 return String.valueOf(method.invoke(annotation)); 092 } catch (final IllegalAccessException | InvocationTargetException e) { 093 throw new IllegalArgumentException("No valid " + method + " for " + annotation); 094 } 095 } 096 097 private String getValueString(final String method, final Annotation annotation) { 098 try { 099 return getString(annotation.annotationType().getMethod(method), annotation); 100 } catch (final NoSuchMethodException e) { 101 throw new IllegalArgumentException("No " + method + " for " + annotation); 102 } 103 } 104 105 private String getParametersString(final Annotation annotation) { 106 try { 107 return String 108 .join(",", String[].class 109 .cast(annotation.annotationType().getMethod("parameters").invoke(annotation))); 110 } catch (final IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 111 return null; 112 } 113 } 114}