Mirror of the JODE repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
jode/jode/src/net/sf/jode/obfuscator/PackageIdentifier.java

499 lines
15 KiB

/* PackageIdentifier Copyright (C) 1999-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package net.sf.jode.obfuscator;
import net.sf.jode.GlobalOptions;
import java.io.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
///#def COLLECTIONS java.util
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
import java.util.Arrays;
import java.util.Collections;
///#enddef
public class PackageIdentifier extends Identifier {
ClassBundle bundle;
PackageIdentifier parent;
String name;
String fullName;
boolean loadOnDemand;
boolean initialized = false;
Map loadedClasses;
List swappedClasses;
Random rand = new Random();
public PackageIdentifier(ClassBundle bundle,
PackageIdentifier parent,
String fullName, String name) {
super(name);
this.bundle = bundle;
this.parent = parent;
this.fullName = fullName;
this.name = name;
this.loadedClasses = new HashMap();
}
/**
* Marks the parent package as preserved, too.
*/
protected void setSinglePreserved() {
if (parent != null)
parent.setPreserved();
}
public void setLoadOnDemand() {
if (loadOnDemand)
return;
loadOnDemand = true;
if ((Main.stripping & Main.STRIP_UNREACH) == 0) {
String fullNamePrefix =
(fullName.length() > 0) ? fullName + "." : "";
// Load all classes and packages now, so they don't get stripped
Enumeration enumeration =
bundle.getClassPath().listClassesAndPackages(getFullName());
while (enumeration.hasMoreElements()) {
String subclazz = ((String)enumeration.nextElement()).intern();
if (loadedClasses.containsKey(subclazz))
continue;
String subFull = (fullNamePrefix + subclazz).intern();
if (bundle.getClassPath().isPackage(subFull)) {
PackageIdentifier ident = new PackageIdentifier
(bundle, this, subFull, subclazz);
loadedClasses.put(subclazz, ident);
swappedClasses = null;
ident.setLoadOnDemand();
} else {
ClassIdentifier ident = new ClassIdentifier
(this, subFull, subclazz,
bundle.getClassPath().getClassInfo(subFull));
if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("preloading Class "
+ subFull);
loadedClasses.put(subclazz, ident);
swappedClasses = null;
bundle.addClassIdentifier(ident);
}
}
// Everything is loaded, we don't need to load on demand anymore.
loadOnDemand = false;
}
}
public void loadMatchingClasses(IdentifierMatcher matcher) {
String component = matcher.getNextComponent(this);
if (component != null) {
Identifier ident = (Identifier) loadedClasses.get(component);
if (ident == null) {
component = component.intern();
String subFull = (fullName.length() > 0)
? fullName + "."+ component : component;
subFull = subFull.intern();
if (bundle.getClassPath().isPackage(subFull)) {
ident = new PackageIdentifier(bundle, this,
subFull, component);
loadedClasses.put(component, ident);
swappedClasses = null;
if (loadOnDemand)
((PackageIdentifier) ident).setLoadOnDemand();
if (initialized)
((PackageIdentifier) ident).initialize();
} else if (bundle.getClassPath().existsClass(subFull)) {
if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("loading Class " +subFull);
ident = new ClassIdentifier(this, subFull, component,
bundle.getClassPath()
.getClassInfo(subFull));
if (loadOnDemand || matcher.matches(ident)) {
loadedClasses.put(component, ident);
if (initialized)
((ClassIdentifier) ident).initClass();
swappedClasses = null;
bundle.addClassIdentifier(ident);
}
} else {
GlobalOptions.err.println
("Warning: Can't find class/package " + subFull);
}
}
if (ident instanceof PackageIdentifier) {
if (matcher.matches(ident)) {
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("loading Package "
+ident.getFullName());
((PackageIdentifier) ident).setLoadOnDemand();
}
if (matcher.matchesSub(ident, null))
((PackageIdentifier) ident).loadMatchingClasses(matcher);
}
} else {
String fullNamePrefix =
(fullName.length() > 0) ? fullName + "." : "";
/* Load all matching classes and packages */
Enumeration enumeration =
bundle.getClassPath().listClassesAndPackages(getFullName());
while (enumeration.hasMoreElements()) {
String subclazz = ((String)enumeration.nextElement()).intern();
if (loadedClasses.containsKey(subclazz))
continue;
String subFull = (fullNamePrefix + subclazz).intern();
if (matcher.matchesSub(this, subclazz)) {
if (bundle.getClassPath().isPackage(subFull)) {
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("loading Package "
+ subFull);
PackageIdentifier ident = new PackageIdentifier
(bundle, this, subFull, subclazz);
loadedClasses.put(subclazz, ident);
swappedClasses = null;
if (loadOnDemand || matcher.matches(ident))
ident.setLoadOnDemand();
if (initialized)
ident.initialize();
} else {
ClassIdentifier ident = new ClassIdentifier
(this, subFull, subclazz,
bundle.getClassPath().getClassInfo(subFull));
if (loadOnDemand || matcher.matches(ident)) {
if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("loading Class "
+ subFull);
loadedClasses.put(subclazz, ident);
swappedClasses = null;
bundle.addClassIdentifier(ident);
if (initialized)
ident.initClass();
}
}
}
}
List list = new ArrayList();
list.addAll(loadedClasses.values());
for (Iterator i = list.iterator(); i.hasNext(); ) {
Identifier ident = (Identifier) i.next();
if (ident instanceof PackageIdentifier) {
if (matcher.matches(ident))
((PackageIdentifier) ident).setLoadOnDemand();
if (matcher.matchesSub(ident, null))
((PackageIdentifier) ident)
.loadMatchingClasses(matcher);
}
}
}
}
public void initialize() {
for (Iterator i = getChilds(); i.hasNext(); ) {
Identifier ident = (Identifier) i.next();
if (ident instanceof ClassIdentifier)
((ClassIdentifier) ident).initClass();
else
((PackageIdentifier) ident).initialize();
}
initialized = true;
}
public Identifier getIdentifier(String name) {
if (loadOnDemand) {
return loadClass(name);
}
int index = name.indexOf('.');
if (index == -1) {
return (Identifier) loadedClasses.get(name);
} else {
PackageIdentifier pack = (PackageIdentifier)
loadedClasses.get(name.substring(0, index));
if (pack != null)
return pack.getIdentifier(name.substring(index+1));
else
return null;
}
}
private Identifier loadClass(String name) {
int index = name.indexOf('.');
if (index == -1) {
Identifier ident = (Identifier) loadedClasses.get(name);
if (ident == null) {
String subFull =
(fullName.length() > 0) ? fullName + "."+ name : name;
subFull = subFull.intern();
if (bundle.getClassPath().isPackage(subFull)) {
PackageIdentifier pack
= new PackageIdentifier(bundle, this, subFull, name);
loadedClasses.put(name, pack);
swappedClasses = null;
pack.setLoadOnDemand();
ident = pack;
} else if (!bundle.getClassPath().existsClass(subFull)) {
GlobalOptions.err.println("Warning: Can't find class "
+ subFull);
Thread.dumpStack();
} else {
ident = new ClassIdentifier(this, subFull, name,
bundle.getClassPath()
.getClassInfo(subFull));
loadedClasses.put(name, ident);
((ClassIdentifier) ident).initClass();
swappedClasses = null;
bundle.addClassIdentifier(ident);
}
}
return ident;
} else {
String subpack = name.substring(0, index);
PackageIdentifier pack =
(PackageIdentifier) loadedClasses.get(subpack);
if (pack == null) {
String subFull = (fullName.length() > 0)
? fullName + "."+ subpack : subpack;
subFull = subFull.intern();
if (bundle.getClassPath().isPackage(subFull)) {
pack = new PackageIdentifier(bundle, this,
subFull, subpack);
loadedClasses.put(subpack, pack);
swappedClasses = null;
if (loadOnDemand)
pack.setLoadOnDemand();
}
}
if (pack != null)
return pack.loadClass(name.substring(index+1));
else
return null;
}
}
public void applyPreserveRule(IdentifierMatcher preserveRule) {
if (loadOnDemand)
loadMatchingClasses(preserveRule);
super.applyPreserveRule(preserveRule);
}
/**
* @return the full qualified name.
*/
public String getFullName() {
return fullName;
}
/**
* @return the full qualified alias.
*/
public String getFullAlias() {
if (parent != null) {
String parentAlias = parent.getFullAlias();
String alias = getAlias();
if (alias.length() == 0)
return parentAlias;
else if (parentAlias.length() == 0)
return alias;
else
return parentAlias + "." + alias;
}
return "";
}
public String findAlias(String className) {
int index = className.indexOf('.');
if (index == -1) {
Identifier ident = getIdentifier(className);
if (ident != null)
return ident.getFullAlias();
} else {
Identifier pack = getIdentifier(className.substring(0, index));
if (pack != null)
return ((PackageIdentifier)pack)
.findAlias(className.substring(index+1));
}
return className;
}
public void buildTable(Renamer renameRule) {
loadOnDemand = false;
super.buildTable(renameRule);
}
public void doTransformations() {
for (Iterator i = getChilds(); i.hasNext(); ) {
Identifier ident = (Identifier) i.next();
if (ident instanceof ClassIdentifier) {
((ClassIdentifier) ident).doTransformations();
} else
((PackageIdentifier) ident).doTransformations();
}
}
public void readTable(Map table) {
if (parent != null)
setAlias((String) table.get(getFullName()));
for (Iterator i = loadedClasses.values().iterator(); i.hasNext(); ) {
Identifier ident = (Identifier) i.next();
if ((Main.stripping & Main.STRIP_UNREACH) == 0
|| ident.isReachable())
ident.readTable(table);
}
}
public Identifier getParent() {
return parent;
}
public String getName() {
return name;
}
public String getType() {
return "package";
}
public Iterator getChilds() {
/* Since loadedClasses is somewhat sorted by the hashcode
* of the _original_ names, swap it here to prevent to guess
* even parts of the names.
*/
if (swappedClasses == null) {
swappedClasses = Arrays.asList(loadedClasses.values().toArray());
Collections.shuffle(swappedClasses, rand);
}
return swappedClasses.iterator();
}
public void storeClasses(ZipOutputStream zip) {
for (Iterator i = getChilds(); i.hasNext(); ) {
Identifier ident = (Identifier) i.next();
if ((Main.stripping & Main.STRIP_UNREACH) != 0
&& !ident.isReachable()) {
if (GlobalOptions.verboseLevel > 4)
GlobalOptions.err.println("Class/Package "
+ ident.getFullName()
+ " is not reachable");
continue;
}
if (ident instanceof PackageIdentifier)
((PackageIdentifier) ident).storeClasses(zip);
else {
try {
String filename = ident.getFullAlias().replace('.','/')
+ ".class";
zip.putNextEntry(new ZipEntry(filename));
DataOutputStream out = new DataOutputStream
(new BufferedOutputStream(zip));
((ClassIdentifier) ident).storeClass(out);
out.flush();
zip.closeEntry();
} catch (java.io.IOException ex) {
GlobalOptions.err.println("Can't write Class "
+ ident.getName());
ex.printStackTrace(GlobalOptions.err);
}
}
}
}
public void storeClasses(File destination) {
File newDest = (parent == null) ? destination
: new File(destination, getAlias());
if (!newDest.exists() && !newDest.mkdir()) {
GlobalOptions.err.println("Could not create directory "
+newDest.getPath()+", check permissions.");
}
for (Iterator i = getChilds(); i.hasNext(); ) {
Identifier ident = (Identifier) i.next();
if ((Main.stripping & Main.STRIP_UNREACH) != 0
&& !ident.isReachable()) {
if (GlobalOptions.verboseLevel > 4)
GlobalOptions.err.println("Class/Package "
+ ident.getFullName()
+ " is not reachable");
continue;
}
if (ident instanceof PackageIdentifier)
((PackageIdentifier) ident)
.storeClasses(newDest);
else {
try {
File file = new File(newDest, ident.getAlias()+".class");
// if (file.exists()) {
// GlobalOptions.err.println
// ("Refuse to overwrite existing class file "
// +file.getPath()+". Remove it first.");
// return;
// }
DataOutputStream out = new DataOutputStream
(new BufferedOutputStream
(new FileOutputStream(file)));
((ClassIdentifier) ident).storeClass(out);
out.close();
} catch (java.io.IOException ex) {
GlobalOptions.err.println("Can't write Class "
+ ident.getName());
ex.printStackTrace(GlobalOptions.err);
}
}
}
}
public String toString() {
return (parent == null) ? "base package" : getFullName();
}
public boolean contains(String newAlias, Identifier except) {
for (Iterator i = loadedClasses.values().iterator(); i.hasNext(); ) {
Identifier ident = (Identifier)i.next();
if (ident != except) {
if (((Main.stripping & Main.STRIP_UNREACH) == 0
|| ident.isReachable())
&& ident.getAlias().equalsIgnoreCase(newAlias))
return true;
if (ident instanceof PackageIdentifier
&& ident.getAlias().length() == 0
&& (((PackageIdentifier) ident)
.contains(newAlias, this)))
return true;
}
}
if (getAlias().length() == 0
&& parent != null
&& parent != except
&& parent.contains(newAlias, this))
return true;
return false;
}
public boolean conflicting(String newAlias) {
return parent.contains(newAlias, this);
}
}