Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/techtrader/modules/tools/bytecode/lowlevel/ConstantPool.java


1   package com.techtrader.modules.tools.bytecode.lowlevel;
2   
3   
4   import java.util.List;
5   import java.util.LinkedList;
6   import java.util.Iterator;
7   import java.util.Map;
8   import java.util.HashMap;
9   import java.io.IOException;
10  import java.io.DataInput;
11  import java.io.DataOutput;
12  
13  import com.techtrader.modules.tools.bytecode.BCClass;
14  import com.techtrader.modules.tools.bytecode.visitor.BCVisitor;
15  import com.techtrader.modules.tools.bytecode.visitor.VisitAcceptor;
16  
17  
18  /**
19   *  Represents a class constant pool, containing entries for all strings,
20   *  constants, classes, etc referenced in the class structure and opcodes.
21   *  In keeping with the low-level bytecode representation, all pool indexes 
22   *  are 1-based.  
23   *  <p>
24   *  NOTE: Entries are not meant to be manipulated manually.  If you change
25   *  entries by hand, make sure to call the {@link #rehash} method of the
26   *  ConstantPool so that the entry is hashed correctly for quick lookups and
27   *  to avoid duplicates.
28   *  <p>
29   *  NOTE: LongEntries and DoubleEntries are always followed by a 
30   *  PlaceHolderEntry in the pool, as they take up 2 pool indeces.
31   *  When manually adding a new entry of these types, it is <b>not</b>
32   *  necessary to insert the PlaceHolder as well; the system will do this
33   *  automatically.
34   *
35   *  @author    Abe White
36   */
37  public class ConstantPool
38    implements LowLevelConstants, VisitAcceptor
39  {
40    // map constant types to codes
41    private static final Map _consts = new HashMap ();
42    static
43    {
44      _consts.put (Integer.class, new Integer (ENTRY_INT));
45      _consts.put (Long.class, new Integer (ENTRY_LONG));
46      _consts.put (Double.class, new Integer (ENTRY_DOUBLE));
47      _consts.put (Float.class, new Integer (ENTRY_FLOAT));
48      _consts.put (String.class, new Integer (ENTRY_STRING));
49    }
50  
51    private List   _entries  = new LinkedList ();
52    private Map    _lookup    = new HashMap ();
53    private BCClass  _owner    = null;
54  
55  
56    /**
57     *  Create an Entry based on its one-byte code: one of the constants in
58      *  the LowLevelConstants class.
59     */
60    public static Entry createEntry (int type)
61    {
62      switch (type)
63      {
64      case ENTRY_PLACEHOLDER:
65        return new PlaceHolderEntry ();
66      case ENTRY_CLASS:
67        return new ClassEntry ();
68      case ENTRY_FIELD:
69        return new FieldEntry ();
70      case ENTRY_METHOD:
71        return new MethodEntry ();
72      case ENTRY_INTMETHOD:
73        return new InterfaceMethodEntry ();
74      case ENTRY_STRING:
75        return new StringEntry ();
76      case ENTRY_INT:
77        return new IntEntry ();
78      case ENTRY_FLOAT:
79        return new FloatEntry ();
80      case ENTRY_LONG:
81        return new LongEntry ();
82      case ENTRY_DOUBLE:
83        return new DoubleEntry ();
84      case ENTRY_NAME_AND_TYPE:
85        return new NameAndTypeEntry ();
86      case ENTRY_UTF8:
87        return new UTF8Entry ();
88      default:
89        return null;
90      }
91    }
92  
93  
94    public ConstantPool (BCClass owner)
95    {
96      _owner = owner;
97    }
98  
99   
100   /**
101     *  Get the entries in the pool.
102    */
103   public Entry[] getEntries ()
104   {
105     return (Entry[]) _entries.toArray (new Entry[_entries.size ()]);
106   }
107 
108 
109   /**
110    *  Retrieve the entry at the specified 1-based index.
111    *
112    *  @return    the specified entry, or null if invalid index
113    */
114   public Entry getEntry (int index)
115   {
116     if (index < 1 || index > _entries.size ())
117       return null;
118 
119     return (Entry) _entries.get (index - 1);
120   }
121 
122 
123   /**
124    *  Set the entry at the given 1-based index.
125    *
126    *  @return    the entry that was replaced
127    */
128   public Entry setEntry (int index, Entry entry)
129   {
130     if (entry == null)
131       throw new NullPointerException ();
132 
133     Entry old = (Entry) _entries.set (index - 1, entry);
134     removeHash (old);
135 
136     // remove placeholders
137     if (old != null && (old.getType () == ENTRY_LONG
138       || old.getType () == ENTRY_DOUBLE))
139       _entries.remove (index);
140 
141     // add placeholder
142     if (entry.getType () == ENTRY_LONG || entry.getType () == ENTRY_DOUBLE)
143       _entries.add (index, new PlaceHolderEntry ());
144 
145     hash (entry, index);
146 
147     return old;
148   }
149 
150 
151   /**
152    *  Set the entry at the given 1-based index.
153    */
154   public void addEntry (int index, Entry entry)
155   {
156     if (entry == null)
157       throw new NullPointerException ();
158 
159     _entries.add (index - 1, entry);
160     hash (entry, index);
161 
162     // add placeholder
163     if (entry.getType () == ENTRY_LONG || entry.getType () == ENTRY_DOUBLE)
164       _entries.add (index, new PlaceHolderEntry ());
165   }
166 
167 
168   /**
169    *  Add an entry to the pool.
170     *
171    *  @return    the index at which the entry was added
172    */
173   public int addEntry (Entry entry)
174   {
175     if (entry == null)
176       throw new NullPointerException ();
177 
178     _entries.add (entry);
179     int index = _entries.size ();
180     hash (entry, index);
181 
182     // add placeholder
183     if (entry.getType () == ENTRY_LONG || entry.getType () == ENTRY_DOUBLE)
184       _entries.add (new PlaceHolderEntry ());
185 
186     return index;
187   }
188 
189 
190   /**
191    *  Remove the entry at the given index.
192    *
193    *  @return    the removed entry, or null if none
194    */
195   public Entry removeEntry (int index)
196   {
197     if (index < 1 || index > _entries.size ())
198       return null;
199 
200     Entry old = (Entry) _entries.remove (index - 1);
201     removeHash (old);
202 
203     // remove placeholders
204     if (old != null && (old.getType () == ENTRY_LONG
205       || old.getType () == ENTRY_DOUBLE))
206       _entries.remove (index - 1);
207   
208     return old;
209   }
210 
211 
212   /**
213    *  Remove the given entry from the pool.
214    *
215    *  @return    false if the entry is not in the pool, true otherwise
216    */
217   public boolean removeEntry (Entry entry)
218   {
219     if (entry == null)
220       return false;
221 
222     int index = _entries.indexOf (entry);
223     if (index != -1)
224     {
225       _entries.remove (index);
226       removeHash (entry);
227 
228       // remove placeholders
229       if (entry.getType () == ENTRY_LONG 
230         || entry.getType () == ENTRY_DOUBLE)
231         _entries.remove (index);
232 
233       return true;
234     }
235     return false;  
236   }
237 
238 
239   /**
240    *  Return the number of entries in the pool, including placeholder 
241    *  entries.
242    */
243   public int size ()
244   {
245     return _entries.size ();
246   }
247 
248 
249   /**
250    *  Get the constant pool index of the entry for the given UTF value.
251    *
252    *  @return   the index of the matching entry, or 0 if no match
253     */
254   public int getUTFIndex (String name)
255   {
256     if (name == null)
257       name = "";
258 
259     return find (ENTRY_UTF8 + "|" + name);  
260   }
261 
262 
263   /**
264    *  Get the value of the entry at the given index.
265    *
266    *  @return    the value of the given entry, or empty string if
267    *        the entry does not exist
268    */
269   public String getUTF (int index)
270   {
271     UTF8Entry entry = (UTF8Entry) getEntry (index);
272     if (entry == null)
273       return "";
274 
275     return entry.getValue ();
276   }
277 
278 
279   /**
280    *  Set the entry at the given index; if the given index is &lt;= 0,
281    *  a search will be performed for an entry with the given value, and, if
282    *  it fails, a new entry will be added to the pool.
283    *
284    *  @return    the index of the entry with the given value, whether an
285    *        existing one was found/modified or a new one was added
286    */
287   public int setUTF (int index, String name)
288   {
289     if (name == null)
290       name = "";
291 
292     if (index <= 0)
293     {
294       index = getUTFIndex (name);
295       if (index > 0)
296         return index;
297     }
298 
299     UTF8Entry entry = (UTF8Entry) getEntry (index);
300     if (entry == null)
301     {
302       entry = new UTF8Entry ();
303       entry.setValue (name);
304       return addEntry (entry);
305     }
306     
307     entry.setValue (name);
308     rehash (entry, index);
309 
310     return index;
311   }
312 
313 
314   /**
315    *  Get the constant pool index of the entry for the given class name.
316    *
317    *  @return   the index of the matching entry, or 0 if no match
318     */
319   public int getClassIndex (String name)
320   {
321     int nameIdx = getUTFIndex (name);
322     if (nameIdx == 0)
323       return 0;
324 
325     return find (ENTRY_CLASS + "|" + nameIdx);  
326   }
327 
328 
329   /**
330    *  Get the value of the entry at the given index.
331    *
332    *  @return    the value of the given entry, or empty string if
333    *        the entry does not exist
334    */
335   public String getClassName (int index)
336   {
337     ClassEntry entry = (ClassEntry) getEntry (index);
338     if (entry == null)
339       return "";
340 
341     return getUTF (entry.getNameIndex ());
342   }
343 
344 
345   /**
346    *  Set the entry at the given index; if the given index is &lt;= 0,
347    *  a search will be performed for an entry with the given value, and, if
348    *  it fails, a new entry will be added to the pool.
349    *
350    *  @return    the index of the entry with the given value, whether an
351    *        existing one was found/modified or a new one was added
352    */
353   public int setClassName (int index, String name)
354   {
355     if (index <= 0)
356     {
357       index = getClassIndex (name);
358       if (index > 0)
359         return index;
360     }
361 
362     ClassEntry entry = (ClassEntry) getEntry (index);
363     if (entry == null)
364     {
365       entry = new ClassEntry ();
366       entry.setNameIndex (setUTF (0, name));
367       return addEntry (entry);
368     }
369     
370     entry.setNameIndex (setUTF (0, name));
371     rehash (entry, index);
372 
373     return index;
374   }
375 
376 
377   /**
378    *  Get the constant pool index of the entry for the given name+type.
379    *
380    *  @return   the index of the matching entry, or 0 if no match
381     */
382   public int getNameAndTypeIndex (String name, String desc)
383   {
384     int nameIdx = getUTFIndex (name);
385     if (nameIdx == 0)
386       return 0;
387 
388     int descIdx = getUTFIndex (desc);
389     if (descIdx == 0)
390       return 0;
391 
392     return find (ENTRY_NAME_AND_TYPE + "|" + nameIdx + "|" + descIdx);
393   }
394 
395 
396   /**
397    *  Set the entry at the given index; if the given index is &lt;= 0,
398    *  a search will be performed for an entry with the given value, and, if
399    *  it fails, a new entry will be added to the pool.
400    *
401    *  @return    the index of the entry with the given value, whether an
402    *        existing one was found/modified or a new one was added
403    */
404   public int setNameAndType (int index, String name, String desc)
405   {
406     if (index <= 0)
407     {
408       index = getNameAndTypeIndex (name, desc);
409       if (index > 0)
410         return index;
411     }
412 
413     NameAndTypeEntry entry = (NameAndTypeEntry) getEntry (index);
414     if (entry == null)
415     {
416       entry = new NameAndTypeEntry ();
417       entry.setNameIndex (setUTF (0, name));
418       entry.setDescriptorIndex (setUTF (0, desc));
419       return addEntry (entry);
420     }
421     
422     entry.setNameIndex (setUTF (0, name));
423     entry.setDescriptorIndex (setUTF (0, desc));
424     rehash (entry, index);
425 
426     return index;
427   }
428 
429 
430   /**
431    *  Get the constant pool index of the entry for the given complex entry.
432    *
433    *  @return   the index of the matching entry, or 0 if no match
434     */
435   public int getComplexIndex (String name, String desc, String owner,
436     Class type)
437   {
438     int classIdx = getClassIndex (owner);
439     if (classIdx == 0)
440       return 0;
441 
442     int descIdx = getNameAndTypeIndex (name, desc);
443     if (descIdx == 0)
444       return 0;
445 
446     int code;
447     if (type.equals (FieldEntry.class))
448       code = ENTRY_FIELD;
449     else if (type.equals (MethodEntry.class))
450       code = ENTRY_METHOD;
451     else
452       code = ENTRY_INTMETHOD;
453 
454     return find (code + "|" + classIdx + "|" + descIdx);
455   }
456 
457 
458   /**
459    *  Set the entry at the given index; if the given index is &lt;= 0,
460    *  a search will be performed for an entry with the given value, and, if
461    *  it fails, a new entry will be added to the pool.
462    *
463    *  @return    the index of the entry with the given value, whether an
464    *        existing one was found/modified or a new one was added
465    */
466   public int setComplex (int index, String name, String desc, 
467     String owner, Class entryType)
468   {
469     if (index <= 0)
470     {
471       index = getComplexIndex (name, desc, owner, entryType);
472       if (index > 0)
473         return index;
474     }
475 
476     ComplexEntry entry = (ComplexEntry) getEntry (index);
477     if (entry == null)
478     {
479       try
480       {
481         entry = (ComplexEntry) entryType.newInstance ();
482       }
483       catch (Throwable t)
484       {
485         throw new RuntimeException (t.getMessage ());
486       }
487 
488       entry.setClassIndex (setClassName (0, owner));
489       entry.setNameAndTypeIndex (setNameAndType (0, name, desc));
490       return addEntry (entry);
491     }
492     
493     entry.setClassIndex (setClassName (0, owner));
494     entry.setNameAndTypeIndex (setNameAndType (0, name, desc));
495     rehash (entry, index);
496 
497     return index;
498   }
499 
500 
501   /**
502    *  Get the value of the entry at the given index.
503    *
504    *  @return    the value of the given entry, or empty string if
505    *        the entry does not exist
506    */
507   public String getComplexName (int index)
508   {
509     ComplexEntry complex = (ComplexEntry) getEntry (index);
510     if (complex == null)
511       return "";
512 
513     NameAndTypeEntry nt = (NameAndTypeEntry) getEntry 
514       (complex.getNameAndTypeIndex ());
515     if (nt == null)
516       return "";
517 
518     return getUTF (nt.getNameIndex ()); 
519   }
520 
521 
522   /**
523    *  Get the value of the entry at the given index.
524    *
525    *  @return    the value of the given entry, or empty string if
526    *        the entry does not exist
527    */
528   public String getComplexTypeName (int index)
529   {
530     ComplexEntry complex = (ComplexEntry) getEntry (index);
531     if (complex == null)
532       return "";
533 
534     NameAndTypeEntry nt = (NameAndTypeEntry) getEntry 
535       (complex.getNameAndTypeIndex ());
536     if (nt == null)
537       return "";
538 
539     return getUTF (nt.getDescriptorIndex ()); 
540   }
541 
542 
543   /**
544    *  Get the value of the entry at the given index.
545    *
546    *  @return    the value of the given entry, or empty string if
547    *        the entry does not exist
548    */
549   public String getComplexOwnerTypeName (int index)
550   {
551     ComplexEntry complex = (ComplexEntry) getEntry (index);
552     if (complex == null)
553       return "";
554 
555     return getClassName (complex.getClassIndex ());
556   }
557 
558 
559   /**
560    *  Get the constant pool index of the entry for the given constant value.
561    *
562    *  @return   the index of the matching entry, or 0 if no match
563     */
564   public int getConstantIndex (Object value)
565   {
566     if (value == null)
567       return 0;
568 
569     // get the constant code for the given type  
570     int code = ((Integer) _consts.get (value.getClass ())).intValue ();
571     if (code == ENTRY_STRING)
572       return find (code + "|" + getUTFIndex (value.toString ()));
573 
574     return find (code + "|" + value);
575   }
576 
577 
578   /**
579    *  Get the value of the entry at the given index.
580    *
581    *  @return    the value of the given entry, or null if the entry
582    *        does not exist
583    */
584   public Object getConstant (int index)
585   {
586     ConstantEntry entry = (ConstantEntry) getEntry (index);
587     if (entry == null)
588       return null;
589 
590     if (entry instanceof StringEntry)
591       return getUTF (((StringEntry) entry).getStringIndex ());
592 
593     return ((ConstantEntry) entry).getConstantValue ();
594   }
595 
596 
597   /**
598    *  Set the entry at the given index; if the given index is &lt;= 0,
599    *  a search will be performed for an entry with the given value, and, if
600    *  it fails, a new entry will be added to the pool.
601    *
602    *  @return    the index of the entry with the given value, whether an
603    *        existing one was found/modified or a new one was added
604    */
605   public int setConstant (int index, Object value)
606   {
607     if (index <= 0)
608     {
609       index = getConstantIndex (value);
610       if (index > 0)
611         return index;
612     }
613 
614     ConstantEntry entry = (ConstantEntry) getEntry (index);
615     if (entry == null)
616     {
617       int code = ((Integer) _consts.get (value.getClass ())).intValue ();
618       entry = (ConstantEntry) createEntry (code);
619       if (entry instanceof StringEntry)
620         ((StringEntry) entry).setStringIndex 
621           (setUTF (0, value.toString ()));
622       else
623         entry.setConstantValue (value);
624       return addEntry (entry);
625     }
626     
627     if (entry instanceof StringEntry)
628       ((StringEntry) entry).setStringIndex (setUTF(0, value.toString ()));
629     else
630       entry.setConstantValue (value);
631     rehash (entry, index);
632 
633     return index;
634   }
635 
636 
637   /**
638    *  Rehash the given entry after modification; this allows for quick
639    *  lookups and guarantees that entries won't be repeated in the pool.
640    */
641   public void rehash (Entry entry, int poolIndex)
642   {
643     if (entry != null)
644     {
645       // remove the modified entry from the map
646       for (Iterator i = _lookup.values ().iterator (); i.hasNext ();)
647         if (((HashedEntry) i.next ()).entry.equals (entry))
648           i.remove ();
649 
650       // rehash with its new values
651       hash (entry, poolIndex);
652     }
653   }
654 
655 
656   public void readData (DataInput in)
657     throws IOException
658   {
659     _entries.clear ();
660     _lookup.clear ();
661 
662     int entryCount = in.readUnsignedShort ();
663     Entry entry;
664     for (int i = 1; i < entryCount; i++)
665     {
666       entry = createEntry (in.readUnsignedByte ());
667       entry.readData (in);
668       addEntry (entry);
669 
670       // bytecode 'feature': long and double entries
671       // are counted twice in the entryCount
672       if (entry.getType () == ENTRY_LONG
673         || entry.getType () == ENTRY_DOUBLE)
674         i++;
675     }
676   }
677 
678 
679   public void writeData (DataOutput out)
680     throws IOException
681   {
682     out.writeShort (_entries.size () + 1);
683     Entry entry;
684     for (Iterator i = _entries.iterator (); i.hasNext ();)
685     {
686       entry = (Entry) i.next ();
687 
688       // compensate for weird long/double entry behavior
689       if (entry.getType () == ENTRY_PLACEHOLDER)
690         continue;
691 
692       out.writeByte (entry.getType ());
693       entry.writeData (out);
694     }
695   }
696 
697 
698   private int find (String key)
699   {
700     HashedEntry he = (HashedEntry) _lookup.get (key);
701     if (he == null)
702       return 0;
703 
704     return he.index;  
705   }
706 
707 
708   private void hash (Entry entry, int index)
709   {
710     if (entry != null)
711       _lookup.put (entry.getKey (), new HashedEntry (entry, index));
712   }
713 
714 
715   private void removeHash (Entry entry)
716   {
717     if (entry != null)
718       _lookup.remove (entry.getKey ());
719   }
720 
721 
722   public void acceptVisit (BCVisitor visit)
723   {
724     visit.enterConstantPool (this);
725 
726     Entry next;
727     for (Iterator i = _entries.iterator (); i.hasNext ();)
728     {
729       next = (Entry) i.next ();
730 
731       visit.enterEntry (next);
732       next.acceptVisit (visit);
733       visit.exitEntry (next);
734     }
735 
736     visit.exitConstantPool (this);
737   }
738 
739 
740   private static class HashedEntry
741   {
742     public Entry   entry   = null;
743     public int     index  = 0;
744 
745 
746     public HashedEntry (Entry entry, int index)
747     {
748       this.entry = entry;
749       this.index = index;
750     }
751   }
752 }
753