1919import java .util .Collections ;
2020import java .util .HashSet ;
2121import java .util .List ;
22+ import java .util .Map ;
2223import java .util .Objects ;
2324import java .util .Optional ;
2425import java .util .Set ;
26+ import java .util .concurrent .ConcurrentHashMap ;
2527import java .util .logging .Level ;
2628import java .util .logging .Logger ;
2729import java .util .stream .Collectors ;
@@ -48,9 +50,12 @@ public class Attribute<Owner, Type> {
4850 // Nop
4951 };
5052
51- // TODO: Concurrent cache?
52- // private static final HashMap<Class, Boolean> SECRET_ATTRIBUTE_CACHE =
53- // new HashMap<>();
53+ private static final ClassValue <Map <String , Boolean >> SECRET_ATTRIBUTE_CACHE = new ClassValue <>() {
54+ @ Override
55+ protected Map <String , Boolean > computeValue (@ NonNull Class <?> type ) {
56+ return new ConcurrentHashMap <>();
57+ }
58+ };
5459
5560 protected final String name ;
5661 protected final Class type ;
@@ -398,7 +403,6 @@ private static Field locatePrivateFieldInHierarchy(Class<?> clazz, @NonNull Stri
398403 return ExtraFieldUtils .getFieldNoForce (clazz , fieldName );
399404 }
400405
401- // TODO: consider Boolean and third condition
402406 /**
403407 * This is a method which tries to guess whether an attribute is {@link Secret}.
404408 * @param targetClass Class of the target object. {@code null} if unknown
@@ -416,48 +420,50 @@ public static boolean calculateIfSecret(@CheckForNull Class<?> targetClass, @Non
416420 }
417421
418422 if (targetClass == null ) {
419- LOGGER .log (
420- Level .FINER ,
421- "Attribute {0} is assumed to be non-secret, because there is no class instance in the call. "
422- + "This call is used only for fast-fetch caching, and the result may be adjusted later" ,
423- new Object [] {fieldName });
424423 return false ; // All methods below require a known target class
425424 }
426425
427- // TODO: Cache decisions?
426+ return SECRET_ATTRIBUTE_CACHE .get (targetClass ).computeIfAbsent (fieldName , key -> {
427+ Method m = locateGetter (targetClass , fieldName );
428+ if (m != null && m .getReturnType () == Secret .class ) {
429+ LOGGER .log (
430+ Level .FINER ,
431+ "Attribute {0}#{1} is secret, because there is a getter {2} which returns a Secret type" ,
432+ new Object [] {targetClass .getName (), fieldName , m });
433+ return true ;
434+ }
428435
429- Method m = locateGetter (targetClass , fieldName );
430- if (m != null && m . getReturnType () == Secret .class ) {
431- LOGGER .log (
432- Level .FINER ,
433- "Attribute {0}#{1} is secret, because there is a getter {2} which returns a Secret type" ,
434- new Object [] {targetClass .getName (), fieldName , m });
435- return true ;
436- }
436+ Field f = locatePublicField (targetClass , fieldName );
437+ if (f != null && f . getType () == Secret .class ) {
438+ LOGGER .log (
439+ Level .FINER ,
440+ "Attribute {0}#{1} is secret, because there is a public field {2} which has a Secret type" ,
441+ new Object [] {targetClass .getName (), fieldName , f });
442+ return true ;
443+ }
437444
438- Field f = locatePublicField (targetClass , fieldName );
439- if (f != null && f .getType () == Secret .class ) {
440- LOGGER .log (
441- Level .FINER ,
442- "Attribute {0}#{1} is secret, because there is a public field {2} which has a Secret type" ,
443- new Object [] {targetClass .getName (), fieldName , f });
444- return true ;
445- }
445+ f = locatePrivateFieldInHierarchy (targetClass , fieldName );
446+ if (f != null && f .getType () == Secret .class ) {
447+ LOGGER .log (
448+ Level .FINER ,
449+ "Attribute {0}#{1} is secret, because there is a private field {2} which has a Secret type" ,
450+ new Object [] {targetClass .getName (), fieldName , f });
451+ return true ;
452+ }
446453
447- f = locatePrivateFieldInHierarchy (targetClass , fieldName );
448- if (f != null && f .getType () == Secret .class ) {
449- LOGGER .log (
450- Level .FINER ,
451- "Attribute {0}#{1} is secret, because there is a private field {2} which has a Secret type" ,
452- new Object [] {targetClass .getName (), fieldName , f });
453- return true ;
454- }
454+ if (hasSecretSetter (targetClass , fieldName )) {
455+ LOGGER .log (
456+ Level .FINER ,
457+ "Attribute {0}#{1} is secret, because there is a setter which takes a Secret type" ,
458+ new Object [] {targetClass .getName (), fieldName });
459+ return true ;
460+ }
455461
456- // TODO(oleg_nenashev): Consider setters? Gonna be more interesting since there might be many of them
457- LOGGER .log (Level .FINER , "Attribute {0}#{1} is not a secret, because all checks have passed" , new Object [] {
458- targetClass .getName (), fieldName
462+ LOGGER .log (Level .FINER , "Attribute {0}#{1} is not a secret, because all checks have passed" , new Object [] {
463+ targetClass .getName (), fieldName
464+ });
465+ return false ;
459466 });
460- return false ;
461467 }
462468
463469 private Type _getValue (Owner target ) throws ConfiguratorException {
@@ -545,4 +551,16 @@ public int hashCode() {
545551 public static <T , V > Setter <T , V > noop () {
546552 return NOOP ;
547553 }
554+
555+ private static boolean hasSecretSetter (Class <?> clazz , @ NonNull String fieldName ) {
556+ final String setterName = "set" + StringUtils .capitalize (fieldName );
557+ for (Method method : clazz .getMethods ()) {
558+ if (method .getName ().equals (setterName ) && method .getParameterCount () == 1 ) {
559+ if (method .getParameterTypes ()[0 ] == Secret .class ) {
560+ return true ;
561+ }
562+ }
563+ }
564+ return false ;
565+ }
548566}
0 commit comments