/** * 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; } } }