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 {@code char[]}s.
21   * 
22   * @since 4.0
23   * @version $Id: CharArrayKeyAnalyzer.java 1429905 2013-01-07 17:15:14Z ggregory $
24   */
25  public class CharArrayKeyAnalyzer extends AbstractKeyAnalyzer<char[]> {
26      
27      private static final long serialVersionUID = -8167897361549463457L;
28  
29      /**
30       * A singleton instance of {@link CharArrayKeyAnalyzer}
31       */
32      public static final CharArrayKeyAnalyzer INSTANCE = new CharArrayKeyAnalyzer();
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 char[] key) {
62          return key != null ? key.length * LENGTH : 0;
63      }
64  
65      /**
66       * {@inheritDoc}
67       */
68      public int bitIndex(final char[] key, final int offsetInBits, final int lengthInBits,
69              final char[] 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[index1];
99              }
100 
101             if (other == null || index2 >= endIndex2) {
102                 f = 0;
103             } else {
104                 f = other[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 char[] 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[index] & mask(bit)) != 0;
138     }
139 
140     /**
141      * {@inheritDoc}
142      */
143     public boolean isPrefix(final char[] prefix, final int offsetInBits,
144             final int lengthInBits, final char[] 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 int off = offsetInBits / LENGTH;
151         final int len = lengthInBits / LENGTH;
152         for (int i = 0; i < len; i ++) {
153             if (prefix[i + off] != key[i]) {
154                 return false;
155             }
156         }
157         return true;
158     }
159 }