1/* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5package org.mockito.internal.util.reflection; 6 7import org.mockito.Incubating; 8import org.mockito.internal.util.Checks; 9import org.mockito.internal.util.collections.ListUtil.Filter; 10import org.mockito.internal.util.collections.ListUtil; 11 12import java.lang.annotation.Annotation; 13import java.lang.reflect.Field; 14import java.util.ArrayList; 15import java.util.List; 16 17/** 18 * Small fluent reflection tools to work with fields. 19 * 20 * Code is very new and might need rework. 21 */ 22@Incubating 23public abstract class Fields { 24 25 /** 26 * Instance fields declared in the class and superclasses of the given instance. 27 * 28 * @param instance Instance from which declared fields will be retrieved. 29 * @return InstanceFields of this object instance. 30 */ 31 public static InstanceFields allDeclaredFieldsOf(Object instance) { 32 List<InstanceField> instanceFields = new ArrayList<InstanceField>(); 33 for (Class<?> clazz = instance.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) { 34 instanceFields.addAll(instanceFieldsIn(instance, clazz.getDeclaredFields())); 35 } 36 return new InstanceFields(instance, instanceFields); 37 } 38 39 /** 40 * Instance fields declared in the class of the given instance. 41 * 42 * @param instance Instance from which declared fields will be retrieved. 43 * @return InstanceFields of this object instance. 44 */ 45 public static InstanceFields declaredFieldsOf(Object instance) { 46 List<InstanceField> instanceFields = new ArrayList<InstanceField>(); 47 instanceFields.addAll(instanceFieldsIn(instance, instance.getClass().getDeclaredFields())); 48 return new InstanceFields(instance, instanceFields); 49 } 50 51 private static List<InstanceField> instanceFieldsIn(Object instance, Field[] fields) { 52 List<InstanceField> instanceDeclaredFields = new ArrayList<InstanceField>(); 53 for (Field field : fields) { 54 InstanceField instanceField = new InstanceField(field, instance); 55 instanceDeclaredFields.add(instanceField); 56 } 57 return instanceDeclaredFields; 58 } 59 60 /** 61 * Accept fields annotated by the given annotations. 62 * 63 * @param annotations Annotation types to check. 64 * @return The filter. 65 */ 66 public static Filter<InstanceField> annotatedBy(final Class<? extends Annotation>... annotations) { 67 return new Filter<InstanceField>() { 68 public boolean isOut(InstanceField instanceField) { 69 Checks.checkNotNull(annotations, "Provide at least one annotation class"); 70 71 for (Class<? extends Annotation> annotation : annotations) { 72 if(instanceField.isAnnotatedBy(annotation)) { 73 return false; 74 } 75 } 76 return true; 77 } 78 }; 79 } 80 81 /** 82 * Accept fields with non null value. 83 * 84 * @return The filter. 85 */ 86 private static Filter<InstanceField> nullField() { 87 return new Filter<InstanceField>() { 88 public boolean isOut(InstanceField instanceField) { 89 return instanceField.isNull(); 90 } 91 }; 92 } 93 94 public static class InstanceFields { 95 private final Object instance; 96 97 private final List<InstanceField> instanceFields; 98 99 public InstanceFields(Object instance, List<InstanceField> instanceFields) { 100 this.instance = instance; 101 this.instanceFields = instanceFields; 102 } 103 104 public InstanceFields filter(Filter<InstanceField> withFilter) { 105 return new InstanceFields(instance, ListUtil.filter(instanceFields, withFilter)); 106 } 107 108 public InstanceFields notNull() { 109 return filter(nullField()); 110 } 111 112 public List<InstanceField> instanceFields() { 113 return new ArrayList<InstanceField>(instanceFields); 114 } 115 116 public List<Object> assignedValues() { 117 List<Object> values = new ArrayList<Object>(instanceFields.size()); 118 for (InstanceField instanceField : instanceFields) { 119 values.add(instanceField.read()); 120 } 121 return values; 122 } 123 124 public List<String> names() { 125 List<String> fieldNames = new ArrayList<String>(instanceFields.size()); 126 for (InstanceField instanceField : instanceFields) { 127 fieldNames.add(instanceField.name()); 128 } 129 return fieldNames; 130 } 131 } 132} 133