View Javadoc

1   package org.lcsim.detector.identifier;
2   
3   import java.util.ArrayList;
4   import java.util.Arrays;
5   import java.util.HashMap;
6   import java.util.LinkedHashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  /**
11   * Implementation of {@link IIdentifierDictionary}. This class is declared final because overriding the methods could
12   * cause serious bugs. To provide specialized id functionality, the class {@link IdentifierHelper} can be subclassed
13   * instead.
14   * 
15   * @author Jeremy McCormick
16   * @version $Id: IdentifierDictionary.java,v 1.9 2013/01/24 22:26:35 jeremy Exp $
17   */
18  // TODO Add ctor checks on fields: no overlapping ranges, no overflow, w/in 64 bits, no fields crossing the 32-bit boundary, etc.
19  public final class IdentifierDictionary implements IIdentifierDictionary
20  {
21      // An array of field objects for fast access by index.
22      private IIdentifierField fieldArray[] = null;
23      
24      // A map of field names to field objects, maintaining creation order.
25      private Map<String, IIdentifierField> fieldMap = new LinkedHashMap<String, IIdentifierField>();
26  
27      // A map of field names to their indices in the array.
28      private Map<String, Integer> fieldIndices = new HashMap<String, Integer>();
29  
30      // The name of the dictionary.
31      private final String name;
32  
33      // The number of fields inthe dictionary.
34      protected int numberOfFields;
35      
36      // The maximum array index, which is cached here for performance.
37      protected int maxIndex;
38  
39      /**
40       * Create an identifier dictionary with fields from <code>fieldList</code>. A dictionary cannot be modified once it
41       * is created.
42       * @param name The name of the dictionary.
43       * @param fieldList The list of fields.
44       */
45      public IdentifierDictionary(String name, List<IIdentifierField> fieldList)
46      {
47          // Set the dictionary name.
48          this.name = name;
49  
50          // Add the fields to the dictionary.
51          addFields(fieldList);
52  
53          // Set the number of fields.
54          numberOfFields = fieldList.size();
55  
56          // Set the max index in the dictionary.
57          maxIndex = numberOfFields - 1;
58         
59          // TODO Register with global IdDictManager.
60      }
61  
62      /**
63       * Get the name of this dictionary.
64       * @return The dictionary's name.
65       */
66      public String getName()
67      {
68          return name;
69      }
70      
71      /**
72       * Get the list of fields.
73       * @return The list of fields.s
74       */
75      public List<IIdentifierField> getFieldList() {
76          return Arrays.asList(fieldArray);
77      }
78  
79      /**
80       * Get a field by name or <code>null</code> if it doesn't exist in this dictionary.
81       * @return The field called <code>fieldName</code> or null if doesn't exist.
82       */
83      public IIdentifierField getField(String fieldName)
84      {
85          return fieldMap.get(fieldName);
86      }
87  
88      /**
89       * Get a field by index.
90       * @return The field at <code>index</code> in the array.
91       * @throws ArrayOutOfBoundsException if index is not valid.
92       */
93      public IIdentifierField getField(int index)
94      {
95          return fieldArray[index];
96      }
97  
98      /**
99       * Get a field's index in the array or <code>null</code> if it doesn't exist.
100      * @return The field index for the field called <code>fieldName</code> or null if doesn't exist.
101      */
102     public int getFieldIndex(String fieldName)
103     {
104         return fieldIndices.get(fieldName);
105     }
106 
107     /**
108      * Get the list of fields in this dictionary, which is cloned in case the caller modifies it.
109      * This means that callers should take care not to put this method inside a loop unnecessarily.
110      * @return The list of fields.
111      */
112     public List<String> getFieldNames()
113     {
114         return new ArrayList<String>(fieldMap.keySet());
115     }
116 
117     /**
118      * Get the number of fields in the dictionary.
119      * @return The number of fields.
120      */
121     public int getNumberOfFields()
122     {
123         return numberOfFields;
124     }
125     
126     /**
127      * Get the maximum index in the field array.
128      * @return The max index.
129      */
130     public int getMaxIndex()
131     {
132         return maxIndex;
133     }
134 
135     /**
136      * True if the dictionary has a field called <code>fieldName</code>; false if not.
137      * @return True if <code>fieldName</code> exists in this dictionary; false if not.
138      */
139     public boolean hasField(String fieldName)
140     {
141         return fieldMap.containsKey(fieldName);
142     }
143 
144     /**
145      * Unpack a packed id.
146      * @return The expanded id, which is a list of field values.
147      */
148     public IExpandedIdentifier unpack(IIdentifier compact)
149     {
150         ExpandedIdentifier expId = new ExpandedIdentifier();
151         long id = compact.getValue();
152 
153         for (int i = 0; i < numberOfFields; i++ )
154         {
155             IIdentifierField field = getField(i);
156 
157             int start = field.getOffset();
158             int length = field.getNumberOfBits();
159             int mask = field.getIntegerMask();
160 
161             int result = (int)((id >> start) & mask);
162             if (field.isSigned())
163             {
164                 int signBit = 1 << (length - 1);
165                 if ((result & signBit) != 0)
166                     result -= (1 << length);
167             }
168 
169             expId.addValue(result);
170         }
171 
172         return expId;
173     }
174     
175     public IExpandedIdentifier unpack(IIdentifier compact, List<Integer> indices)
176     {        
177         ExpandedIdentifier buffer = new ExpandedIdentifier();
178         
179         long id = compact.getValue();
180                                                
181         for ( int i=0; i<numberOfFields; i++)
182         {            
183             if (indices.contains(i))
184             {                                                
185                 IIdentifierField field = getField(i);
186                 
187                 int start = field.getOffset();
188                 int length = field.getNumberOfBits();
189                 int mask = field.getIntegerMask();
190                 
191                 int result = (int) ((id >> start) & mask);
192                 if (field.isSigned())
193                 {
194                     int signBit = 1<<(length-1);
195                     if ((result & signBit) != 0) result -= (1<<length);
196                 }
197                 
198                 buffer.addValue(result);
199             }
200             else
201             {
202                 buffer.addValue(0);
203             }
204         }
205                
206         return buffer;
207     }
208     
209     
210 
211     /**
212      * Pack an expanded id.
213      * @return The packed id.
214      */
215     public IIdentifier pack(IExpandedIdentifier id)
216     {
217         long result = 0;
218 
219         for (int i = 0; i <= maxIndex; i++ )
220         {
221             IIdentifierField field = getField(i);
222 
223             int start = field.getOffset();
224             long mask = field.getLongMask();
225             result |= (mask & id.getValue(i)) << start;
226         }
227 
228         return new Identifier(result);
229     }
230     
231     /**
232      * Extract value of a named field from packed id.
233      * @param compact The packed id.
234      * @param field The field name.
235      * @return The field value.
236      */
237     public int getFieldValue(IIdentifier compact, String field)
238     {
239         return getField(field).unpack(compact);
240     }
241     
242     /**
243      * Extract value of a field by index.
244      * @param compact The packed id.
245      * @param field The field name.
246      * @return The field value.
247      */
248     public int getFieldValue(IIdentifier compact, int idx)
249     {
250         return getField(idx).unpack(compact);
251     }
252 
253     /**
254      * Add a field.
255      * @param field The field object.
256      * @param index The field index.
257      */
258     private void addField(IIdentifierField field, int index)
259     {
260         // Check for duplicate name.
261         if (fieldMap.containsKey(field.getLabel()))
262         {
263             throw new RuntimeException("Duplicate field name <" + field.getLabel() + "> in IdDict <" + getName() + ">.");
264         }
265 
266         // Put into the field map.
267         fieldMap.put(field.getLabel(), field);
268 
269         // Add field to array.
270         fieldArray[index] = field;
271 
272         // Store the order of this field by name.
273         fieldIndices.put(field.getLabel(), index);
274     }
275 
276     /**
277      * Add a list of fields.
278      * @param fields The list of fields.
279      */
280     // TODO Add sanity checks.
281     private void addFields(List<IIdentifierField> fields)
282     {
283         // Initialize the field array for fast indexed access.
284         fieldArray = new IIdentifierField[fields.size()];
285 
286         // Add the fields to the dictionary.
287         int fieldIndex = 0;
288         for (int i = 0, n = fields.size(); i < n; i++ )
289         {
290             IIdentifierField field = fields.get(i);
291             addField(field, i);
292             fieldArray[fieldIndex] = field;
293             ++fieldIndex;
294         }
295     }
296 
297     
298     /**
299      * Check that an {@link IExpandedIdentifier} is valid for this dictionary.
300      * This method checks that the sizes are the same and that the values
301      * are valid for their corresponding fields.
302      * @return True if valid; false if not.
303      */
304     public boolean isValid(IExpandedIdentifier id)
305     {
306         int size = id.size();
307         if (size != numberOfFields)
308         {
309             return false;
310         }
311         for (int i = 0, n = id.getMaxIndex(); i < n; i++ )
312         {
313             IIdentifierField field = getField(i);
314             if (!field.isValidValue(id.getValue(i)))
315                 return false;
316         }
317         return true;
318     }
319 
320     /**
321      * Convert the dictionary to a String.
322      * @return A string with the dictionary's field information.
323      */
324     public String toString()
325     {
326         StringBuffer str = new StringBuffer();
327         str.append(getName() + '\n');
328         for (IIdentifierField field : fieldMap.values())
329         {
330             str.append("    " + field.toString());
331         }
332         return str.toString();
333     }
334 
335 }