001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.reflect; 018 019import static org.apache.juneau.internal.ConsumerUtils.*; 020 021import java.lang.annotation.*; 022import java.lang.reflect.*; 023import java.util.function.*; 024 025import org.apache.juneau.*; 026import org.apache.juneau.internal.*; 027 028/** 029 * Lightweight utility class for introspecting information about a constructor. 030 * 031 * <h5 class='section'>See Also:</h5><ul> 032 * </ul> 033 */ 034public class ConstructorInfo extends ExecutableInfo implements Comparable<ConstructorInfo> { 035 036 //----------------------------------------------------------------------------------------------------------------- 037 // Static 038 //----------------------------------------------------------------------------------------------------------------- 039 040 /** 041 * Convenience method for instantiating a {@link ConstructorInfo}; 042 * 043 * @param declaringClass The class that declares this method. 044 * @param c The constructor being wrapped. 045 * @return A new {@link ConstructorInfo} object, or <jk>null</jk> if the method was null; 046 */ 047 public static ConstructorInfo of(ClassInfo declaringClass, Constructor<?> c) { 048 if (c == null) 049 return null; 050 return ClassInfo.of(declaringClass).getConstructorInfo(c); 051 } 052 053 /** 054 * Convenience method for instantiating a {@link ConstructorInfo}; 055 * 056 * @param c The constructor being wrapped. 057 * @return A new {@link ConstructorInfo} object, or <jk>null</jk> if the method was null; 058 */ 059 public static ConstructorInfo of(Constructor<?> c) { 060 if (c == null) 061 return null; 062 return ClassInfo.of(c.getDeclaringClass()).getConstructorInfo(c); 063 } 064 065 //----------------------------------------------------------------------------------------------------------------- 066 // Instance 067 //----------------------------------------------------------------------------------------------------------------- 068 069 private final Constructor<?> c; 070 071 /** 072 * Constructor. 073 * 074 * @param declaringClass The class that declares this method. 075 * @param c The constructor being wrapped. 076 */ 077 protected ConstructorInfo(ClassInfo declaringClass, Constructor<?> c) { 078 super(declaringClass, c); 079 this.c = c; 080 } 081 082 /** 083 * Returns the wrapped method. 084 * 085 * @param <T> The inner class type. 086 * @return The wrapped method. 087 */ 088 @SuppressWarnings("unchecked") 089 public <T> Constructor<T> inner() { 090 return (Constructor<T>)c; 091 } 092 093 //----------------------------------------------------------------------------------------------------------------- 094 // Annotations 095 //----------------------------------------------------------------------------------------------------------------- 096 097 /** 098 * Finds the annotation of the specified type defined on this constructor. 099 * 100 * @param <A> The annotation type to look for. 101 * @param type The annotation to look for. 102 * @return The annotation if found, or <jk>null</jk> if not. 103 */ 104 public <A extends Annotation> A getAnnotation(Class<A> type) { 105 return getAnnotation(AnnotationProvider.DEFAULT, type); 106 } 107 108 /** 109 * Finds the annotation of the specified type defined on this constructor. 110 * 111 * @param <A> The annotation type to look for. 112 * @param annotationProvider The annotation provider. 113 * @param type The annotation to look for. 114 * @return The first annotation found, or <jk>null</jk> if it doesn't exist. 115 */ 116 public <A extends Annotation> A getAnnotation(AnnotationProvider annotationProvider, Class<A> type) { 117 Value<A> t = Value.empty(); 118 annotationProvider.forEachAnnotation(type, c, x -> true, x -> t.set(x)); 119 return t.orElse(null); 120 } 121 122 /** 123 * Returns <jk>true</jk> if the specified annotation is present on this constructor. 124 * 125 * @param <A> The annotation type to look for. 126 * @param type The annotation to look for. 127 * @return <jk>true</jk> if the specified annotation is present on this constructor. 128 */ 129 public <A extends Annotation> boolean hasAnnotation(Class<A> type) { 130 return hasAnnotation(AnnotationProvider.DEFAULT, type); 131 } 132 133 /** 134 * Returns <jk>true</jk> if the specified annotation is present on this constructor. 135 * 136 * @param <A> The annotation type to look for. 137 * @param annotationProvider The annotation provider. 138 * @param type The annotation to look for. 139 * @return <jk>true</jk> if the specified annotation is present on this constructor. 140 */ 141 public <A extends Annotation> boolean hasAnnotation(AnnotationProvider annotationProvider, Class<A> type) { 142 return annotationProvider.firstAnnotation(type, c, x -> true) != null; 143 } 144 145 /** 146 * Returns <jk>true</jk> if the specified annotation is not present on this constructor. 147 * 148 * @param <A> The annotation type to look for. 149 * @param annotationProvider The annotation provider. 150 * @param type The annotation to look for. 151 * @return <jk>true</jk> if the specified annotation is not present on this constructor. 152 */ 153 public <A extends Annotation> boolean hasNoAnnotation(AnnotationProvider annotationProvider, Class<A> type) { 154 return ! hasAnnotation(annotationProvider, type); 155 } 156 157 //----------------------------------------------------------------------------------------------------------------- 158 // Other methods 159 //----------------------------------------------------------------------------------------------------------------- 160 161 /** 162 * Returns <jk>true</jk> if this object passes the specified predicate test. 163 * 164 * @param test The test to perform. 165 * @return <jk>true</jk> if this object passes the specified predicate test. 166 */ 167 public boolean matches(Predicate<ConstructorInfo> test) { 168 return test(test, this); 169 } 170 171 /** 172 * Performs an action on this object if the specified predicate test passes. 173 * 174 * @param test A test to apply to determine if action should be executed. Can be <jk>null</jk>. 175 * @param action An action to perform on this object. 176 * @return This object. 177 */ 178 public ConstructorInfo accept(Predicate<ConstructorInfo> test, Consumer<ConstructorInfo> action) { 179 if (matches(test)) 180 action.accept(this); 181 return this; 182 } 183 184 /** 185 * Returns <jk>true</jk> if this constructor can accept the specified arguments in the specified order. 186 * 187 * @param args The arguments to check. 188 * @return <jk>true</jk> if this constructor can accept the specified arguments in the specified order. 189 */ 190 public boolean canAccept(Object...args) { 191 Class<?>[] pt = c.getParameterTypes(); 192 if (pt.length != args.length) 193 return false; 194 for (int i = 0; i < pt.length; i++) 195 if (! pt[i].isInstance(args[i])) 196 return false; 197 return true; 198 } 199 200 /** 201 * Shortcut for calling the new-instance method on the underlying constructor. 202 * 203 * @param <T> The constructor class type. 204 * @param args the arguments used for the method call. 205 * <br>Extra parameters are ignored. 206 * <br>Missing parameters are set to null. 207 * @return The object returned from the constructor. 208 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 209 */ 210 public <T> T invokeFuzzy(Object...args) throws ExecutableException { 211 return invoke(ClassUtils.getMatchingArgs(c.getParameterTypes(), args)); 212 } 213 214 /** 215 * Shortcut for calling the new-instance method on the underlying constructor. 216 * 217 * @param <T> The constructor class type. 218 * @param args the arguments used for the method call. 219 * @return The object returned from the constructor. 220 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 221 */ 222 @SuppressWarnings("unchecked") 223 public <T> T invoke(Object...args) throws ExecutableException { 224 try { 225 return (T)c.newInstance(args); 226 } catch (InvocationTargetException e) { 227 throw new ExecutableException(e.getTargetException()); 228 } catch (Exception e) { 229 throw new ExecutableException(e); 230 } 231 } 232 233 /** 234 * Makes constructor accessible if it matches the visibility requirements, or returns <jk>null</jk> if it doesn't. 235 * 236 * <p> 237 * Security exceptions thrown on the call to {@link Constructor#setAccessible(boolean)} are quietly ignored. 238 * 239 * @param v The minimum visibility. 240 * @return 241 * The same constructor if visibility requirements met, or <jk>null</jk> if visibility requirement not 242 * met or call to {@link Constructor#setAccessible(boolean)} throws a security exception. 243 */ 244 public ConstructorInfo accessible(Visibility v) { 245 if (v.transform(c) == null) 246 return null; 247 return this; 248 } 249 250 @Override 251 public int compareTo(ConstructorInfo o) { 252 int i = getSimpleName().compareTo(o.getSimpleName()); 253 if (i == 0) { 254 i = getParamCount() - o.getParamCount(); 255 if (i == 0) { 256 for (int j = 0; j < getParamCount() && i == 0; j++) { 257 Class<?>[] tpt = _getRawParamTypes(), opt = o._getRawParamTypes(); 258 i = tpt[j].getName().compareTo(opt[j].getName()); 259 } 260 } 261 } 262 return i; 263 } 264 @Override /* Overridden from ExecutableInfo */ 265 public ConstructorInfo accessible() { 266 super.accessible(); 267 return this; 268 } 269}