mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Nicolas Capponi
04.01.2014 1f5674beec624d5524201b5fbac1aea036996bb5
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.opends.server.backends.jeb;
 
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.types.*;
import java.util.*;
import org.opends.server.api.SubstringMatchingRule;
 
/**
 * An implementation of an Indexer for attribute substrings.
 */
public class SubstringIndexer extends Indexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
 
 
  /**
   * The comparator for index keys generated by this class.
   */
  private static final Comparator<byte[]> comparator =
       new AttributeIndex.KeyComparator();
 
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
 
  /**
   * The substring length.
   */
  private int substrLength;
 
  /**
   * Create a new attribute substring indexer for the given index configuration.
   * @param attributeType The attribute type for which an indexer is
   * required.
   * @param substringLength The decomposed substring length.
   */
  public SubstringIndexer(AttributeType attributeType, int substringLength)
  {
    this.attributeType = attributeType;
    this.substrLength = substringLength;
  }
 
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  public String toString()
  {
    return attributeType.getNameOrOID() + ".substring";
  }
 
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  public Comparator<byte[]> getComparator()
  {
    return comparator;
  }
 
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
 
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
 
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
 
 
 
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
 
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
 
 
 
  /**
   * Generate the set of substring index keys for an attribute.
   * @param attrList The attribute for which substring keys are required.
   * @param keys The set into which the generated keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (attr.isVirtual())
      {
        continue;
      }
      //Get the substring matching rule.
      SubstringMatchingRule rule =
              attr.getAttributeType().getSubstringMatchingRule();
      for (AttributeValue value : attr)
      {
        try
        {
          byte[] normalizedBytes = rule.normalizeValue(value.getValue()).
                  toByteArray();
 
          substringKeys(normalizedBytes, keys);
        }
        catch (DirectoryException e)
        {
          logger.traceException(e);
        }
      }
    }
  }
 
  /**
   * Decompose an attribute value into a set of substring index keys.
   * The ID of the entry containing this value should be inserted
   * into the list of each of these keys.
   *
   * @param value A byte array containing the normalized attribute value
   * @param set A set into which the keys will be inserted.
   */
  private void substringKeys(byte[] value, Set<byte[]> set)
  {
    byte[] keyBytes;
 
    // Example: The value is ABCDE and the substring length is 3.
    // We produce the keys ABC BCD CDE DE E
    // To find values containing a short substring such as DE,
    // iterate through keys with prefix DE. To find values
    // containing a longer substring such as BCDE, read keys
    // BCD and CDE.
    for (int i = 0, remain = value.length; remain > 0; i++, remain--)
    {
      int len = Math.min(substrLength, remain);
      keyBytes = makeSubstringKey(value, i, len);
      set.add(keyBytes);
    }
  }
 
  /**
   * Makes a byte array representing a substring index key for
   * one substring of a value.
   *
   * @param bytes The byte array containing the value
   * @param pos The starting position of the substring
   * @param len The length of the substring
   * @return A byte array containing a substring key
   */
  private byte[] makeSubstringKey(byte[] bytes, int pos, int len)
  {
    byte[] keyBytes = new byte[len];
    System.arraycopy(bytes, pos, keyBytes, 0, len);
    return keyBytes;
  }
 
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
 
    for (Attribute attr : attrList)
    {
      if (attr.isVirtual())
      {
        continue;
      }
            //Get the substring matching rule.
      SubstringMatchingRule rule =
              attr.getAttributeType().getSubstringMatchingRule();
 
      for (AttributeValue value : attr)
      {
        try
        {
          byte[] normalizedBytes = rule.normalizeValue(value.getValue())
                  .toByteArray();
 
          substringKeys(normalizedBytes, modifiedKeys, insert);
        }
        catch (DirectoryException e)
        {
          logger.traceException(e);
        }
      }
    }
  }
 
  /**
   * Decompose an attribute value into a set of substring index keys.
   * The ID of the entry containing this value should be inserted
   * into the list of each of these keys.
   *
   * @param value A byte array containing the normalized attribute value
   * @param modifiedKeys The map into which the modified
   *  keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void substringKeys(byte[] value,
                             Map<byte[], Boolean> modifiedKeys,
                             Boolean insert)
  {
    byte[] keyBytes;
 
    // Example: The value is ABCDE and the substring length is 3.
    // We produce the keys ABC BCD CDE DE E
    // To find values containing a short substring such as DE,
    // iterate through keys with prefix DE. To find values
    // containing a longer substring such as BCDE, read keys
    // BCD and CDE.
    for (int i = 0, remain = value.length; remain > 0; i++, remain--)
    {
      int len = Math.min(substrLength, remain);
      keyBytes = makeSubstringKey(value, i, len);
      Boolean cInsert = modifiedKeys.get(keyBytes);
      if(cInsert == null)
      {
        modifiedKeys.put(keyBytes, insert);
      }
      else if(!cInsert.equals(insert))
      {
        modifiedKeys.remove(keyBytes);
      }
    }
  }
 
  /**
   * Return the substring length for an indexer.
   *
   * @return  The substring length configured for an sub string indexer.
   */
  public int getSubStringLen()
  {
    return substrLength;
  }
}