• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.android.libraries.backup;
2 
3 import android.content.Context;
4 import java.lang.annotation.Annotation;
5 import java.lang.reflect.Field;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Set;
12 import java.util.regex.Matcher;
13 import java.util.regex.Pattern;
14 
15 /** Static utility methods returning {@link BackupKeyPredicate} instances. */
16 public class BackupKeyPredicates {
17 
18   /**
19    * Returns a predicate that determines whether a key was defined as a field with the given
20    * annotation in one of the given classes. Assumes that the given annotation and classes are
21    * valid. You must ensure that proguard does not remove your annotation or any fields annotated
22    * with it.
23    *
24    * @see Backup
25    */
buildPredicateFromAnnotatedFieldsIn( Class<? extends Annotation> annotation, Class<?>... klasses)26   public static BackupKeyPredicate buildPredicateFromAnnotatedFieldsIn(
27       Class<? extends Annotation> annotation, Class<?>... klasses) {
28     return in(getAnnotatedFieldValues(annotation, klasses));
29   }
30 
31   /**
32    * Returns a predicate that determines whether a key matches a regex that was defined as a field
33    * with the given annotation in one of the given classes. The test used is equivalent to
34    * {@link #containsPattern(String)} for each annotated field value. Assumes that the given
35    * annotation and classes are valid. You must ensure that proguard does not remove your annotation
36    * or any fields annotated with it.
37    *
38    * @see Backup
39    */
buildPredicateFromAnnotatedRegexFieldsIn( Class<? extends Annotation> annotation, Class<?>... klasses)40   public static BackupKeyPredicate buildPredicateFromAnnotatedRegexFieldsIn(
41       Class<? extends Annotation> annotation, Class<?>... klasses) {
42     Set<String> patterns = getAnnotatedFieldValues(annotation, klasses);
43     Set<BackupKeyPredicate> patternPredicates = new HashSet<>();
44     for (String pattern : patterns) {
45       patternPredicates.add(containsPattern(pattern));
46     }
47     return or(patternPredicates);
48   }
49 
getAnnotatedFieldValues( Class<? extends Annotation> annotation, Class<?>... klasses)50   private static Set<String> getAnnotatedFieldValues(
51       Class<? extends Annotation> annotation, Class<?>... klasses) {
52     Set<String> values = new HashSet<>();
53     for (Class<?> klass : klasses) {
54       addAnnotatedFieldValues(annotation, klass, values);
55     }
56     return values;
57   }
58 
addAnnotatedFieldValues( Class<? extends Annotation> annotation, Class<?> klass, Set<String> values)59   private static void addAnnotatedFieldValues(
60       Class<? extends Annotation> annotation, Class<?> klass, Set<String> values) {
61     for (Field field : klass.getDeclaredFields()) {
62       addFieldValueIfAnnotated(annotation, field, values);
63     }
64   }
65 
addFieldValueIfAnnotated( Class<? extends Annotation> annotation, Field field, Set<String> values)66   private static void addFieldValueIfAnnotated(
67       Class<? extends Annotation> annotation, Field field, Set<String> values) {
68     if (field.isAnnotationPresent(annotation) && field.getType().equals(String.class)) {
69       try {
70         values.add((String) field.get(null));
71       } catch (IllegalAccessException e) {
72         throw new IllegalArgumentException(e);
73       }
74     }
75   }
76 
77   /**
78    * Returns a predicate that determines whether a key is a member of the given collection. Changes
79    * to the given collection will change the returned predicate.
80    */
in(final Collection<? extends String> collection)81   public static BackupKeyPredicate in(final Collection<? extends String> collection) {
82     if (collection == null) {
83       throw new NullPointerException("Null collection given.");
84     }
85     return new BackupKeyPredicate() {
86       @Override
87       public boolean shouldBeBackedUp(String key) {
88         return collection.contains(key);
89       }
90     };
91   }
92 
93   /**
94    * Returns a predicate that determines whether a key contains any match for the given regular
95    * expression pattern. The test used is equivalent to {@link Matcher#find()}.
96    */
97   public static BackupKeyPredicate containsPattern(String pattern) {
98     final Pattern compiledPattern = Pattern.compile(pattern);
99     return new BackupKeyPredicate() {
100       @Override
101       public boolean shouldBeBackedUp(String key) {
102         return compiledPattern.matcher(key).find();
103       }
104     };
105   }
106 
107   /**
108    * Returns a predicate that determines whether a key passes any of the given predicates. Each
109    * predicate is evaluated in the order given, and the evaluation process stops as soon as an
110    * accepting predicate is found. Changes to the given iterable will not change the returned
111    * predicate. The returned predicate returns {@code false} for any key if the given iterable is
112    * empty.
113    */
114   public static BackupKeyPredicate or(Iterable<BackupKeyPredicate> predicates) {
115     final List<BackupKeyPredicate> copiedPredicates = new ArrayList<>();
116     for (BackupKeyPredicate predicate : predicates) {
117       copiedPredicates.add(predicate);
118     }
119     return orDefensivelyCopied(new ArrayList<>(copiedPredicates));
120   }
121 
122   /**
123    * Returns a predicate that determines whether a key passes any of the given predicates. Each
124    * predicate is evaluated in the order given, and the evaluation process stops as soon as an
125    * accepting predicate is found. The returned predicate returns {@code false} for any key if no
126    * there are no given predicates.
127    */
128   public static BackupKeyPredicate or(BackupKeyPredicate... predicates) {
129     return orDefensivelyCopied(Arrays.asList(predicates));
130   }
131 
132   private static BackupKeyPredicate orDefensivelyCopied(
133       final Iterable<BackupKeyPredicate> predicates) {
134     return new BackupKeyPredicate() {
135       @Override
136       public boolean shouldBeBackedUp(String key) {
137         for (BackupKeyPredicate predicate : predicates) {
138           if (predicate.shouldBeBackedUp(key)) {
139             return true;
140           }
141         }
142         return false;
143       }
144     };
145   }
146 
147   /**
148    * Returns a predicate that determines whether a key is one of the resources from the provided
149    * resource IDs. Assumes that all of the given resource IDs are valid.
150    */
151   public static BackupKeyPredicate buildPredicateFromResourceIds(
152       Context context, Collection<Integer> ids) {
153     Set<String> keys = new HashSet<>();
154     for (Integer id : ids) {
155       keys.add(context.getString(id));
156     }
157     return in(keys);
158   }
159 
160   /** Returns a predicate that returns true for any key. */
161   public static BackupKeyPredicate alwaysTrue() {
162     return new BackupKeyPredicate() {
163       @Override
164       public boolean shouldBeBackedUp(String key) {
165         return true;
166       }
167     };
168   }
169 }
170