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; 017 018import static java.util.Comparator.comparing; 019import static java.util.Optional.of; 020import static java.util.Optional.ofNullable; 021import static org.talend.sdk.component.api.component.Icon.IconType.CUSTOM; 022 023import java.lang.annotation.Annotation; 024import java.lang.reflect.AnnotatedElement; 025import java.lang.reflect.InvocationTargetException; 026import java.util.Optional; 027import java.util.stream.Stream; 028 029import org.talend.sdk.component.api.component.Icon; 030 031public class IconFinder { 032 033 public String findIcon(final AnnotatedElement type) { 034 return findDirectIcon(type).orElseGet(() -> findIndirectIcon(type).orElse("default")); 035 } 036 037 public Optional<String> findIndirectIcon(final AnnotatedElement type) { 038 // first try an annotation decorated with @Icon 039 return ofNullable(findMetaIconAnnotation(type) 040 .map(this::getMetaIcon) 041 .orElseGet(() -> findImplicitIcon(type).orElse(null))); 042 } 043 044 private Optional<Annotation> findMetaIconAnnotation(final AnnotatedElement type) { 045 return Stream 046 .of(type.getAnnotations()) 047 .filter(this::isMetaIcon) 048 .min(comparing(it -> it.annotationType().getName())); 049 } 050 051 private boolean isMetaIcon(final Annotation it) { 052 return it.annotationType().isAnnotationPresent(Icon.class) && hasMethod(it.annotationType(), "value"); 053 } 054 055 private Optional<String> findImplicitIcon(final AnnotatedElement type) { 056 return findImplicitIconAnnotation(type).map(it -> String.valueOf(invoke(it, it.annotationType(), "value"))); 057 } 058 059 private Optional<Annotation> findImplicitIconAnnotation(final AnnotatedElement type) { 060 return Stream 061 .of(type.getAnnotations()) 062 .filter(it -> it.annotationType().getSimpleName().endsWith("Icon") 063 && hasMethod(it.annotationType(), "value")) 064 .findFirst(); 065 } 066 067 private String getMetaIcon(final Annotation it) { 068 // extract type and value (by convention), type can be custom to use value 069 if (hasMethod(it.annotationType(), "type")) { 070 final Object enumValue = invoke(it, it.annotationType(), "type"); 071 if (!"custom".equalsIgnoreCase(String.valueOf(enumValue))) { 072 return getEnumKey(enumValue).orElseGet(() -> String.valueOf(invoke(it, it.annotationType(), "value"))); 073 } 074 } 075 final Object value = invoke(it, it.annotationType(), "value"); 076 if (value.getClass().isEnum()) { 077 return getEnumKey(value).orElseGet(() -> String.valueOf(value)); 078 } 079 return String.valueOf(value); 080 } 081 082 private Optional<String> getEnumKey(final Object enumValue) { 083 return Stream 084 .of("getKey", "getValue", "name") 085 .filter(getter -> hasMethod(enumValue.getClass(), getter)) 086 .map(name -> String.valueOf(invoke(enumValue, enumValue.getClass(), name))) 087 .findFirst(); 088 } 089 090 private boolean hasMethod(final Class<?> clazz, final String method) { 091 try { 092 return clazz.getMethod(method) != null; 093 } catch (final NoSuchMethodException e) { 094 return false; 095 } 096 } 097 098 private Object invoke(final Object instance, final Class<?> type, final String method) { 099 try { 100 return type.getMethod(method).invoke(instance); 101 } catch (final IllegalAccessException | NoSuchMethodException e) { 102 throw new IllegalStateException(e); 103 } catch (final InvocationTargetException e) { 104 throw new IllegalStateException(e.getTargetException()); 105 } 106 } 107 108 public Optional<String> findDirectIcon(final AnnotatedElement type) { 109 return ofNullable(type.getAnnotation(Icon.class)) 110 .map(i -> i.value() == Icon.IconType.CUSTOM ? of(i.custom()).filter(s -> !s.isEmpty()).orElse("default") 111 : i.value().getKey()); 112 } 113 114 public boolean isCustom(final Annotation icon) { 115 if (Icon.class == icon.annotationType()) { 116 return Icon.class.cast(icon).value() == CUSTOM; 117 } 118 if (hasMethod(icon.annotationType(), "type")) { 119 return "custom".equalsIgnoreCase(String.valueOf(invoke(icon, icon.annotationType(), "type"))); 120 } 121 return false; 122 } 123 124 public Annotation extractIcon(final AnnotatedElement annotatedElement) { 125 return ofNullable(annotatedElement.getAnnotation(Icon.class)) 126 .map(Annotation.class::cast) 127 .orElseGet(() -> findMetaIconAnnotation(annotatedElement) 128 .orElseGet(() -> findImplicitIconAnnotation(annotatedElement).orElse(null))); 129 } 130}