View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections.trie;
18  
19  /**
20   * An {@link KeyAnalyzer} for {@link String}s.
21   * 
22   * @since 4.0
23   * @version $Id: StringKeyAnalyzer.java 1429905 2013-01-07 17:15:14Z ggregory $
24   */
25  public class StringKeyAnalyzer extends AbstractKeyAnalyzer<String> {
26      
27      private static final long serialVersionUID = -7032449491269434877L;
28      
29      /**
30       * A singleton instance of {@link StringKeyAnalyzer}
31       */
32      public static final StringKeyAnalyzer INSTANCE = new StringKeyAnalyzer();
33      
34      /**
35       * The number of bits per {@link Character}
36       */
37      public static final int LENGTH = Character.SIZE;
38      
39      /**
40       * A bit mask where the first bit is 1 and the others are zero
41       */
42      private static final int MSB = 0x8000;
43      
44      /**
45       * Returns a bit mask where the given bit is set
46       */
47      private static int mask(final int bit) {
48          return MSB >>> bit;
49      }
50      
51      /**
52       * {@inheritDoc}
53       */
54      public int bitsPerElement() {
55          return LENGTH;
56      }
57      
58      /**
59       * {@inheritDoc}
60       */
61      public int lengthInBits(final String key) {
62          return key != null ? key.length() * LENGTH : 0;
63      }
64      
65      /**
66       * {@inheritDoc}
67       */
68      public int bitIndex(final String key, final int offsetInBits, final int lengthInBits,
69              final String other, final int otherOffsetInBits, final int otherLengthInBits) {
70          boolean allNull = true;
71          
72          if (offsetInBits % LENGTH != 0 || otherOffsetInBits % LENGTH != 0 
73                  || lengthInBits % LENGTH != 0 || otherLengthInBits % LENGTH != 0) {
74              throw new IllegalArgumentException(
75                      "The offsets and lengths must be at Character boundaries");
76          }
77          
78          
79          final int beginIndex1 = offsetInBits / LENGTH;
80          final int beginIndex2 = otherOffsetInBits / LENGTH;
81          
82          final int endIndex1 = beginIndex1 + lengthInBits / LENGTH;
83          final int endIndex2 = beginIndex2 + otherLengthInBits / LENGTH;
84          
85          final int length = Math.max(endIndex1, endIndex2);
86          
87          // Look at each character, and if they're different
88          // then figure out which bit makes the difference
89          // and return it.
90          char k = 0, f = 0;
91          for(int i = 0; i < length; i++) {
92              final int index1 = beginIndex1 + i;
93              final int index2 = beginIndex2 + i;
94              
95              if (index1 >= endIndex1) {
96                  k = 0;
97              } else {
98                  k = key.charAt(index1);
99              }
100             
101             if (other == null || index2 >= endIndex2) {
102                 f = 0;
103             } else {
104                 f = other.charAt(index2);
105             }
106             
107             if (k != f) {
108                final int x = k ^ f;
109                return i * LENGTH + Integer.numberOfLeadingZeros(x) - LENGTH;
110             }
111             
112             if (k != 0) {
113                 allNull = false;
114             }
115         }
116         
117         // All bits are 0
118         if (allNull) {
119             return KeyAnalyzer.NULL_BIT_KEY;
120         }
121         
122         // Both keys are equal
123         return KeyAnalyzer.EQUAL_BIT_KEY;
124     }
125     
126     /**
127      * {@inheritDoc}
128      */
129     public boolean isBitSet(final String key, final int bitIndex, final int lengthInBits) {
130         if (key == null || bitIndex >= lengthInBits) {
131             return false;
132         }
133         
134         final int index = bitIndex / LENGTH;
135         final int bit = bitIndex % LENGTH;
136         
137         return (key.charAt(index) & mask(bit)) != 0;
138     }
139     
140     /**
141      * {@inheritDoc}
142      */
143     public boolean isPrefix(final String prefix, final int offsetInBits, 
144             final int lengthInBits, final String key) {
145         if (offsetInBits % LENGTH != 0 || lengthInBits % LENGTH != 0) {
146             throw new IllegalArgumentException(
147                     "Cannot determine prefix outside of Character boundaries");
148         }
149     
150         final String s1 = prefix.substring(offsetInBits / LENGTH, lengthInBits / LENGTH);
151         return key.startsWith(s1);
152     }
153 }