summaryrefslogtreecommitdiffstats
path: root/src/com/google/android/libraries/backup/BackupKeyPredicates.java
blob: 56570ad34fef0189a69efb9d80c7ff86d9995396 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package com.google.android.libraries.backup;

import android.content.Context;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** Static utility methods returning {@link BackupKeyPredicate} instances. */
public class BackupKeyPredicates {

  /**
   * Returns a predicate that determines whether a key was defined as a field with the given
   * annotation in one of the given classes. Assumes that the given annotation and classes are
   * valid. You must ensure that proguard does not remove your annotation or any fields annotated
   * with it.
   *
   * @see Backup
   */
  public static BackupKeyPredicate buildPredicateFromAnnotatedFieldsIn(
      Class<? extends Annotation> annotation, Class<?>... klasses) {
    return in(getAnnotatedFieldValues(annotation, klasses));
  }

  /**
   * Returns a predicate that determines whether a key matches a regex that was defined as a field
   * with the given annotation in one of the given classes. The test used is equivalent to
   * {@link #containsPattern(String)} for each annotated field value. Assumes that the given
   * annotation and classes are valid. You must ensure that proguard does not remove your annotation
   * or any fields annotated with it.
   *
   * @see Backup
   */
  public static BackupKeyPredicate buildPredicateFromAnnotatedRegexFieldsIn(
      Class<? extends Annotation> annotation, Class<?>... klasses) {
    Set<String> patterns = getAnnotatedFieldValues(annotation, klasses);
    Set<BackupKeyPredicate> patternPredicates = new HashSet<>();
    for (String pattern : patterns) {
      patternPredicates.add(containsPattern(pattern));
    }
    return or(patternPredicates);
  }

  private static Set<String> getAnnotatedFieldValues(
      Class<? extends Annotation> annotation, Class<?>... klasses) {
    Set<String> values = new HashSet<>();
    for (Class<?> klass : klasses) {
      addAnnotatedFieldValues(annotation, klass, values);
    }
    return values;
  }

  private static void addAnnotatedFieldValues(
      Class<? extends Annotation> annotation, Class<?> klass, Set<String> values) {
    for (Field field : klass.getDeclaredFields()) {
      addFieldValueIfAnnotated(annotation, field, values);
    }
  }

  private static void addFieldValueIfAnnotated(
      Class<? extends Annotation> annotation, Field field, Set<String> values) {
    if (field.isAnnotationPresent(annotation) && field.getType().equals(String.class)) {
      try {
        values.add((String) field.get(null));
      } catch (IllegalAccessException e) {
        throw new IllegalArgumentException(e);
      }
    }
  }

  /**
   * Returns a predicate that determines whether a key is a member of the given collection. Changes
   * to the given collection will change the returned predicate.
   */
  public static BackupKeyPredicate in(final Collection<? extends String> collection) {
    if (collection == null) {
      throw new NullPointerException("Null collection given.");
    }
    return new BackupKeyPredicate() {
      @Override
      public boolean shouldBeBackedUp(String key) {
        return collection.contains(key);
      }
    };
  }

  /**
   * Returns a predicate that determines whether a key contains any match for the given regular
   * expression pattern. The test used is equivalent to {@link Matcher#find()}.
   */
  public static BackupKeyPredicate containsPattern(String pattern) {
    final Pattern compiledPattern = Pattern.compile(pattern);
    return new BackupKeyPredicate() {
      @Override
      public boolean shouldBeBackedUp(String key) {
        return compiledPattern.matcher(key).find();
      }
    };
  }

  /**
   * Returns a predicate that determines whether a key passes any of the given predicates. Each
   * predicate is evaluated in the order given, and the evaluation process stops as soon as an
   * accepting predicate is found. Changes to the given iterable will not change the returned
   * predicate. The returned predicate returns {@code false} for any key if the given iterable is
   * empty.
   */
  public static BackupKeyPredicate or(Iterable<BackupKeyPredicate> predicates) {
    final List<BackupKeyPredicate> copiedPredicates = new ArrayList<>();
    for (BackupKeyPredicate predicate : predicates) {
      copiedPredicates.add(predicate);
    }
    return orDefensivelyCopied(new ArrayList<>(copiedPredicates));
  }

  /**
   * Returns a predicate that determines whether a key passes any of the given predicates. Each
   * predicate is evaluated in the order given, and the evaluation process stops as soon as an
   * accepting predicate is found. The returned predicate returns {@code false} for any key if no
   * there are no given predicates.
   */
  public static BackupKeyPredicate or(BackupKeyPredicate... predicates) {
    return orDefensivelyCopied(Arrays.asList(predicates));
  }

  private static BackupKeyPredicate orDefensivelyCopied(
      final Iterable<BackupKeyPredicate> predicates) {
    return new BackupKeyPredicate() {
      @Override
      public boolean shouldBeBackedUp(String key) {
        for (BackupKeyPredicate predicate : predicates) {
          if (predicate.shouldBeBackedUp(key)) {
            return true;
          }
        }
        return false;
      }
    };
  }

  /**
   * Returns a predicate that determines whether a key is one of the resources from the provided
   * resource IDs. Assumes that all of the given resource IDs are valid.
   */
  public static BackupKeyPredicate buildPredicateFromResourceIds(
      Context context, Collection<Integer> ids) {
    Set<String> keys = new HashSet<>();
    for (Integer id : ids) {
      keys.add(context.getString(id));
    }
    return in(keys);
  }

  /** Returns a predicate that returns true for any key. */
  public static BackupKeyPredicate alwaysTrue() {
    return new BackupKeyPredicate() {
      @Override
      public boolean shouldBeBackedUp(String key) {
        return true;
      }
    };
  }
}