| /* |
| * Copyright 2007 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.tonicsystems.jarjar.util; |
| |
| import java.io.*; |
| import java.lang.reflect.Array; |
| import java.util.*; |
| |
| public class ClassHeaderReader { |
| private int access; |
| private String thisClass; |
| private String superClass; |
| private String[] interfaces; |
| |
| private InputStream in; |
| private byte[] b = new byte[0x2000]; |
| private int[] items = new int[1000]; |
| private int bsize = 0; |
| private MyByteArrayInputStream bin = new MyByteArrayInputStream(); |
| private DataInputStream data = new DataInputStream(bin); |
| |
| public int getAccess() { |
| return access; |
| } |
| |
| public String getClassName() { |
| return thisClass; |
| } |
| |
| public String getSuperName() { |
| return superClass; |
| } |
| |
| public String[] getInterfaces() { |
| return interfaces; |
| } |
| |
| public void read(InputStream in) throws IOException { |
| try { |
| this.in = in; |
| bsize = 0; |
| access = 0; |
| thisClass = superClass = null; |
| interfaces = null; |
| |
| try { |
| buffer(4); |
| } catch (IOException e) { |
| // ignore |
| } |
| if (b[0] != (byte) 0xCA |
| || b[1] != (byte) 0xFE |
| || b[2] != (byte) 0xBA |
| || b[3] != (byte) 0xBE) { |
| throw new ClassFormatError("Bad magic number"); |
| } |
| |
| buffer(6); |
| readUnsignedShort(4); // minorVersion |
| readUnsignedShort(6); // majorVersion |
| // TODO: check version |
| int constant_pool_count = readUnsignedShort(8); |
| items = (int[]) resizeArray(items, constant_pool_count); |
| |
| int index = 10; |
| for (int i = 1; i < constant_pool_count; i++) { |
| int size; |
| buffer(index + 3); // TODO: reduce calls to buffer |
| int tag = b[index]; |
| items[i] = index + 1; |
| switch (tag) { |
| case 9: // Fieldref |
| case 10: // Methodref |
| case 11: // InterfaceMethodref |
| case 3: // Integer |
| case 4: // Float |
| case 12: // NameAndType |
| size = 4; |
| break; |
| case 5: // Long |
| case 6: // Double |
| size = 8; |
| i++; |
| break; |
| case 1: // Utf8 |
| size = 2 + readUnsignedShort(index + 1); |
| break; |
| case 7: // Class |
| case 8: // String |
| size = 2; |
| break; |
| default: |
| throw new IllegalStateException("Unknown constant pool tag " + tag); |
| } |
| index += size + 1; |
| } |
| buffer(index + 8); |
| access = readUnsignedShort(index); |
| thisClass = readClass(index + 2); |
| superClass = readClass(index + 4); |
| int interfaces_count = readUnsignedShort(index + 6); |
| |
| index += 8; |
| buffer(index + interfaces_count * 2); |
| interfaces = new String[interfaces_count]; |
| for (int i = 0; i < interfaces_count; i++) { |
| interfaces[i] = readClass(index); |
| index += 2; |
| } |
| } finally { |
| in.close(); |
| } |
| } |
| |
| private String readClass(int index) throws IOException { |
| index = readUnsignedShort(index); |
| if (index == 0) { |
| return null; |
| } |
| index = readUnsignedShort(items[index]); |
| bin.readFrom(b, items[index]); |
| return data.readUTF(); |
| } |
| |
| private int readUnsignedShort(int index) { |
| byte[] b = this.b; |
| return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); |
| } |
| |
| private static final int CHUNK = 2048; |
| |
| private void buffer(int amount) throws IOException { |
| if (amount > b.length) { |
| b = (byte[]) resizeArray(b, b.length * 2); |
| } |
| if (amount > bsize) { |
| int rounded = (int) (CHUNK * Math.ceil((float) amount / CHUNK)); |
| bsize += read(in, b, bsize, rounded - bsize); |
| if (amount > bsize) { |
| throw new EOFException(); |
| } |
| } |
| } |
| |
| private static int read(InputStream in, byte[] b, int off, int len) throws IOException { |
| int total = 0; |
| while (total < len) { |
| int result = in.read(b, off + total, len - total); |
| if (result == -1) { |
| break; |
| } |
| total += result; |
| } |
| return total; |
| } |
| |
| private static Object resizeArray(Object array, int length) { |
| if (Array.getLength(array) < length) { |
| Object newArray = Array.newInstance(array.getClass().getComponentType(), length); |
| System.arraycopy(array, 0, newArray, 0, Array.getLength(array)); |
| return newArray; |
| } else { |
| return array; |
| } |
| } |
| |
| private static class MyByteArrayInputStream extends ByteArrayInputStream { |
| public MyByteArrayInputStream() { |
| super(new byte[0]); |
| } |
| |
| public void readFrom(byte[] buf, int pos) { |
| this.buf = buf; |
| this.pos = pos; |
| count = buf.length; |
| } |
| } |
| } |