View Javadoc

1   /**
2    * Copyright 2007 Google Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.tonicsystems.jarjar.util;
18  
19  import java.io.*;
20  import java.lang.reflect.Array;
21  import java.util.*;
22  
23  public class ClassHeaderReader
24  {
25      private int access;
26      private String thisClass;
27      private String superClass;
28      private String[] interfaces;
29  
30      private InputStream in;
31      private byte[] b = new byte[0x2000];
32      private int[] items = new int[1000];
33      private int bsize = 0;
34      private MyByteArrayInputStream bin = new MyByteArrayInputStream();
35      private DataInputStream data = new DataInputStream(bin);
36  
37      public int getAccess() {
38          return access;
39      }
40      
41      public String getClassName() {
42          return thisClass;
43      }
44  
45      public String getSuperName() {
46          return superClass;
47      }
48  
49      public String[] getInterfaces() {
50          return interfaces;
51      }
52  
53      public void read(InputStream in) throws IOException {
54          try {
55              this.in = in;
56              bsize = 0;
57              access = 0;
58              thisClass = superClass = null;
59              interfaces = null;
60  
61              try {
62                  buffer(4);
63              } catch (IOException e) {
64                  // ignore
65              }
66              if (b[0] != (byte)0xCA || b[1] != (byte)0xFE || b[2] != (byte)0xBA || b[3] != (byte)0xBE)
67                  throw new ClassFormatError("Bad magic number");
68  
69              buffer(6);
70              readUnsignedShort(4); // minorVersion
71              readUnsignedShort(6); // majorVersion
72              // TODO: check version
73              int constant_pool_count = readUnsignedShort(8);
74              items = (int[])resizeArray(items, constant_pool_count);
75  
76              int index = 10;
77              for (int i = 1; i < constant_pool_count; i++) {
78                  int size;
79                  buffer(index + 3); // TODO: reduce calls to buffer
80                  int tag = b[index];
81                  items[i] = index + 1;
82                  switch (tag) {
83                  case 9:  // Fieldref
84                  case 10: // Methodref
85                  case 11: // InterfaceMethodref
86                  case 3:  // Integer
87                  case 4:  // Float
88                  case 12: // NameAndType
89                      size = 4;
90                      break;
91                  case 5:  // Long
92                  case 6:  // Double
93                      size = 8;
94                      i++;
95                      break;
96                  case 1:  // Utf8
97                      size = 2 + readUnsignedShort(index + 1);
98                      break;
99                  case 7:  // Class
100                 case 8:  // String
101                     size = 2;
102                     break;
103                 default:
104                     throw new IllegalStateException("Unknown constant pool tag " + tag);
105                 }
106                 index += size + 1;
107             }
108             buffer(index + 8);
109             access = readUnsignedShort(index);
110             thisClass = readClass(index + 2);
111             superClass = readClass(index + 4);
112             int interfaces_count = readUnsignedShort(index + 6);
113         
114             index += 8;
115             buffer(index + interfaces_count * 2);
116             interfaces = new String[interfaces_count];
117             for (int i = 0; i < interfaces_count; i++) {
118                 interfaces[i] = readClass(index);
119                 index += 2;
120             }
121         } finally {
122             in.close();
123         }
124     }
125 
126     private String readClass(int index) throws IOException {
127         index = readUnsignedShort(index);
128         if (index == 0)
129             return null;
130         index = readUnsignedShort(items[index]);
131         bin.readFrom(b, items[index]);
132         return data.readUTF();
133     }
134 
135     private int readUnsignedShort(int index) {
136         byte[] b = this.b;
137         return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
138     }
139 
140     private static final int CHUNK = 2048;
141     private void buffer(int amount) throws IOException {
142         if (amount > b.length)
143             b = (byte[])resizeArray(b, b.length * 2);
144         if (amount > bsize) {
145             int rounded = (int)(CHUNK * Math.ceil((float)amount / CHUNK));
146             bsize += read(in, b, bsize, rounded - bsize);
147             if (amount > bsize)
148                 throw new EOFException();
149         }
150     }
151 
152     private static int read(InputStream in, byte[] b, int off, int len) throws IOException {
153         int total = 0;
154         while (total < len) {
155             int result = in.read(b, off + total, len - total);
156             if (result == -1)
157                 break;
158             total += result;
159         }
160         return total;
161     }
162 
163     private static Object resizeArray(Object array, int length)
164     {
165         if (Array.getLength(array) < length) {
166             Object newArray = Array.newInstance(array.getClass().getComponentType(), length);
167             System.arraycopy(array, 0, newArray, 0, Array.getLength(array));
168             return newArray;
169         } else {
170             return array;
171         }
172     }
173 
174     private static class MyByteArrayInputStream extends ByteArrayInputStream
175     {
176         public MyByteArrayInputStream() {
177             super(new byte[0]);
178         }
179 
180         public void readFrom(byte[] buf, int pos) {
181             this.buf = buf;
182             this.pos = pos;
183             count = buf.length;
184         }
185     }
186 }