summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/mapping/Ancestries.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/mapping/Ancestries.java')
-rw-r--r--src/cuchaz/enigma/mapping/Ancestries.java134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/mapping/Ancestries.java b/src/cuchaz/enigma/mapping/Ancestries.java
new file mode 100644
index 0000000..b7a5e24
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Ancestries.java
@@ -0,0 +1,134 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.Serializable;
17import java.util.ArrayList;
18import java.util.List;
19import java.util.Map;
20import java.util.zip.ZipEntry;
21import java.util.zip.ZipInputStream;
22
23import javassist.ByteArrayClassPath;
24import javassist.ClassPool;
25import javassist.CtClass;
26import javassist.NotFoundException;
27import javassist.bytecode.Descriptor;
28
29import com.google.common.collect.Maps;
30
31import cuchaz.enigma.Constants;
32
33public class Ancestries implements Serializable
34{
35 private static final long serialVersionUID = 738687982126844179L;
36
37 private Map<String,String> m_superclasses;
38
39 public Ancestries( )
40 {
41 m_superclasses = Maps.newHashMap();
42 }
43
44 public void readFromJar( InputStream in )
45 throws IOException
46 {
47 ClassPool classPool = new ClassPool();
48
49 ZipInputStream zin = new ZipInputStream( in );
50 ZipEntry entry;
51 while( ( entry = zin.getNextEntry() ) != null )
52 {
53 // filter out non-classes
54 if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) )
55 {
56 continue;
57 }
58
59 // read the class into a buffer
60 ByteArrayOutputStream bos = new ByteArrayOutputStream();
61 byte[] buf = new byte[Constants.KiB];
62 int totalNumBytesRead = 0;
63 while( zin.available() > 0 )
64 {
65 int numBytesRead = zin.read( buf );
66 if( numBytesRead < 0 )
67 {
68 break;
69 }
70 bos.write( buf, 0, numBytesRead );
71
72 // sanity checking
73 totalNumBytesRead += numBytesRead;
74 if( totalNumBytesRead > Constants.MiB )
75 {
76 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
77 }
78 }
79
80 // determine the class name (ie chop off the ".class")
81 String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
82
83 // get a javassist handle for the class
84 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
85 try
86 {
87 CtClass c = classPool.get( className );
88 addSuperclass( c.getName(), c.getClassFile().getSuperclass() );
89 }
90 catch( NotFoundException ex )
91 {
92 throw new Error( "Unable to load class: " + className );
93 }
94 }
95 }
96
97 public void addSuperclass( String className, String superclassName )
98 {
99 className = Descriptor.toJvmName( className );
100 superclassName = Descriptor.toJvmName( superclassName );
101
102 if( className.equals( superclassName ) )
103 {
104 throw new IllegalArgumentException( "Class cannot be its own superclass! " + className );
105 }
106
107 if( !isJre( className ) && !isJre( superclassName ) )
108 {
109 m_superclasses.put( className, superclassName );
110 }
111 }
112
113 public String getSuperclassName( String className )
114 {
115 return m_superclasses.get( className );
116 }
117
118 public List<String> getAncestry( String className )
119 {
120 List<String> ancestors = new ArrayList<String>();
121 while( className != null )
122 {
123 className = getSuperclassName( className );
124 ancestors.add( className );
125 }
126 return ancestors;
127 }
128
129 private boolean isJre( String className )
130 {
131 return className.startsWith( "java/" )
132 || className.startsWith( "javax/" );
133 }
134}