/******************************************************************************* * Copyright (c) 2013, 2015 Spring IDE Developers * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.core.java.typehierarchy; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import org.eclipse.core.resources.IProject; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.springframework.ide.eclipse.core.SpringCore; /** * @author Martin Lippert * @since 3.3.0 */ @SuppressWarnings("restriction") public class BytecodeTypeHierarchyClassReader implements TypeHierarchyClassReader { private ClasspathLookup lookup; public BytecodeTypeHierarchyClassReader(ClasspathLookup lookup) { this.lookup = lookup; } public TypeHierarchyElement readTypeHierarchyInformation(char[] fullyQualifiedClassName, IProject project) { String fullyQualifiedClassFileName = new String(fullyQualifiedClassName) + ".class"; String packageName = ""; String className = fullyQualifiedClassFileName; int lastIndexOf = fullyQualifiedClassFileName.lastIndexOf('/'); if (lastIndexOf > -1) { packageName = fullyQualifiedClassFileName.substring(0, lastIndexOf); className = fullyQualifiedClassFileName.substring(lastIndexOf + 1); } InputStream stream = null; try { stream = lookup.getStream(fullyQualifiedClassFileName, packageName, className); if (stream != null) { return readTypeHierarchy(stream); } } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { SpringCore.log(e); } } } return null; } public void cleanup() { lookup.close(); } public TypeHierarchyElement readTypeHierarchy(InputStream stream) { try { DataInputStream dis = new DataInputStream(new BufferedInputStream(stream)); int magic = dis.readInt(); // magic 0xCAFEBABE if (magic != 0xCAFEBABE) { throw new IllegalStateException("not bytecode, magic was 0x" + Integer.toString(magic, 16)); } skip(dis, 4); int constantPoolCount = dis.readShort(); Object[] constantPoolData = new Object[constantPoolCount]; for (int i = 1; i < constantPoolCount; i++) { int tag = dis.readByte(); switch (tag) { case ClassFileConstants.Utf8Tag : constantPoolData[i] = dis.readUTF(); break; case ClassFileConstants.IntegerTag : skip(dis, 4); break; case ClassFileConstants.FloatTag : skip(dis, 4); break; case ClassFileConstants.LongTag : skip(dis, 8); i++; break; case ClassFileConstants.DoubleTag : skip(dis, 8); i++; break; case ClassFileConstants.ClassTag : constantPoolData[i] = dis.readShort(); break; case ClassFileConstants.StringTag : skip(dis, 2); break; case ClassFileConstants.FieldRefTag : skip(dis, 4); break; case ClassFileConstants.MethodRefTag : skip(dis, 4); break; case ClassFileConstants.InterfaceMethodRefTag : skip(dis, 4); break; case ClassFileConstants.NameAndTypeTag : skip(dis, 4); break; case 15 : // ClassFileConstants.MethodHandleTag skip(dis, 3); break; case 16 : // ClassFileConstants.MethodTypeTag skip(dis, 2); break; case 18 : // ClassFileConstants.InvokeDynamicTag skip(dis, 4); break; } } skip(dis, 2); // classname short classNameIndex = dis.readShort(); short classNameUTF8index = (Short) constantPoolData[classNameIndex]; char[] className = ((String) constantPoolData[classNameUTF8index]).toCharArray(); // superclass name short superclassNameIndex = dis.readShort(); char[] superclassName = null; if (superclassNameIndex != 0) { short superclassNameUTF8index = (Short) constantPoolData[superclassNameIndex]; superclassName = ((String) constantPoolData[superclassNameUTF8index]).toCharArray(); } // interfaces short interfacesCount = dis.readShort(); char[][] interfaceNames = null; if (interfacesCount != 0) { interfaceNames = new char[interfacesCount][]; for (int i = 0; i < interfacesCount; i++) { short interfaceNameIndex = dis.readShort(); short interfaceNameUTF8index = (Short) constantPoolData[interfaceNameIndex]; interfaceNames[i] = ((String) constantPoolData[interfaceNameUTF8index]).toCharArray(); } } return new TypeHierarchyElement(className, superclassName, interfaceNames); } catch(Exception e) { SpringCore.log(e); } return null; } private void skip(InputStream stream, long n) throws IOException { long bytesToSkip = n; do { long skipped = stream.skip(bytesToSkip); bytesToSkip = bytesToSkip - skipped; } while (bytesToSkip > 0); } }