mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-11 23:28:22 +00:00
DottedChains in identifier deobfuscation
This commit is contained in:
@@ -21,12 +21,13 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
|
||||
import com.jpexs.decompiler.graph.DottedChain;
|
||||
import com.jpexs.helpers.Cache;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -104,26 +105,27 @@ public class IdentifiersDeobfuscation {
|
||||
return false;
|
||||
}
|
||||
|
||||
private String fooString(boolean as3, HashMap<String, String> deobfuscated, String orig, boolean firstUppercase, int rndSize) {
|
||||
private String fooString(boolean as3, HashMap<DottedChain, DottedChain> deobfuscated, String orig, boolean firstUppercase, int rndSize) {
|
||||
boolean exists;
|
||||
String ret;
|
||||
loopfoo:
|
||||
do {
|
||||
exists = false;
|
||||
int len = 3 + rnd.nextInt(rndSize - 3);
|
||||
ret = "";
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
String c = "";
|
||||
char c;
|
||||
if ((i % 2) == 0) {
|
||||
c = "" + FOO_CHARACTERS.charAt(rnd.nextInt(FOO_CHARACTERS.length()));
|
||||
c = FOO_CHARACTERS.charAt(rnd.nextInt(FOO_CHARACTERS.length()));
|
||||
} else {
|
||||
c = "" + FOO_JOIN_CHARACTERS.charAt(rnd.nextInt(FOO_JOIN_CHARACTERS.length()));
|
||||
c = FOO_JOIN_CHARACTERS.charAt(rnd.nextInt(FOO_JOIN_CHARACTERS.length()));
|
||||
}
|
||||
if (i == 0 && firstUppercase) {
|
||||
c = c.toUpperCase(Locale.ENGLISH);
|
||||
c = Character.toUpperCase(c);
|
||||
}
|
||||
ret += c;
|
||||
sb.append(c);
|
||||
}
|
||||
ret = sb.toString();
|
||||
if (allVariableNamesStr.contains(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
@@ -134,7 +136,7 @@ public class IdentifiersDeobfuscation {
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
if (deobfuscated.containsValue(ret)) {
|
||||
if (deobfuscated.containsValue(DottedChain.parse(ret))) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
@@ -143,7 +145,7 @@ public class IdentifiersDeobfuscation {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void deobfuscateInstanceNames(boolean as3, HashMap<String, String> namesMap, RenameType renameType, List<Tag> tags, Map<String, String> selected) {
|
||||
public void deobfuscateInstanceNames(boolean as3, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, List<Tag> tags, Map<DottedChain, DottedChain> selected) {
|
||||
for (Tag t : tags) {
|
||||
if (t instanceof DefineSpriteTag) {
|
||||
deobfuscateInstanceNames(as3, namesMap, renameType, ((DefineSpriteTag) t).subTags, selected);
|
||||
@@ -170,50 +172,38 @@ public class IdentifiersDeobfuscation {
|
||||
}
|
||||
}
|
||||
|
||||
public String deobfuscatePackage(boolean as3, String pkg, HashMap<String, String> namesMap, RenameType renameType, Map<String, String> selected) {
|
||||
public DottedChain deobfuscatePackage(boolean as3, DottedChain pkg, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, Map<DottedChain, DottedChain> selected) {
|
||||
if (namesMap.containsKey(pkg)) {
|
||||
return namesMap.get(pkg);
|
||||
}
|
||||
String[] parts = null;
|
||||
if (pkg.contains(".")) {
|
||||
parts = pkg.split("\\.");
|
||||
} else {
|
||||
parts = new String[]{pkg};
|
||||
}
|
||||
StringBuilder ret = new StringBuilder();
|
||||
List<String> ret = new ArrayList<>(pkg.size());
|
||||
boolean isChanged = false;
|
||||
for (int p = 0; p < parts.length; p++) {
|
||||
if (p > 0) {
|
||||
ret.append(".");
|
||||
}
|
||||
String partChanged = deobfuscateName(as3, parts[p], false, "package", namesMap, renameType, selected);
|
||||
for (int p = 0; p < pkg.size(); p++) {
|
||||
String part = pkg.get(p);
|
||||
String partChanged = deobfuscateName(as3, part, false, "package", namesMap, renameType, selected);
|
||||
if (partChanged != null) {
|
||||
ret.append(partChanged);
|
||||
ret.add(partChanged);
|
||||
isChanged = true;
|
||||
} else {
|
||||
ret.append(parts[p]);
|
||||
ret.add(part);
|
||||
}
|
||||
}
|
||||
if (isChanged) {
|
||||
String retStr = ret.toString();
|
||||
namesMap.put(pkg, retStr);
|
||||
return retStr;
|
||||
DottedChain chain = new DottedChain(ret);
|
||||
namesMap.put(pkg, chain);
|
||||
return chain;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String deobfuscateNameWithPackage(boolean as3, String n, HashMap<String, String> namesMap, RenameType renameType, Map<String, String> selected) {
|
||||
String pkg = null;
|
||||
String name = "";
|
||||
if (n.contains(".")) {
|
||||
pkg = n.substring(0, n.lastIndexOf('.'));
|
||||
name = n.substring(n.lastIndexOf('.') + 1);
|
||||
} else {
|
||||
name = n;
|
||||
}
|
||||
public String deobfuscateNameWithPackage(boolean as3, String n, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, Map<DottedChain, DottedChain> selected) {
|
||||
DottedChain nChain = DottedChain.parse(n);
|
||||
DottedChain pkg = nChain.getWithoutLast();
|
||||
String name = nChain.getLast();
|
||||
|
||||
boolean changed = false;
|
||||
if ((pkg != null) && (!pkg.isEmpty())) {
|
||||
String changedPkg = deobfuscatePackage(as3, pkg, namesMap, renameType, selected);
|
||||
DottedChain changedPkg = deobfuscatePackage(as3, pkg, namesMap, renameType, selected);
|
||||
if (changedPkg != null) {
|
||||
changed = true;
|
||||
pkg = changedPkg;
|
||||
@@ -258,22 +248,23 @@ public class IdentifiersDeobfuscation {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String deobfuscateName(boolean as3, String s, boolean firstUppercase, String usageType, HashMap<String, String> namesMap, RenameType renameType, Map<String, String> selected) {
|
||||
public String deobfuscateName(boolean as3, String s, boolean firstUppercase, String usageType, HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, Map<DottedChain, DottedChain> selected) {
|
||||
boolean isValid = true;
|
||||
if (usageType == null) {
|
||||
usageType = "name";
|
||||
}
|
||||
|
||||
DottedChain sChain = DottedChain.parse(s);
|
||||
if (selected != null) {
|
||||
if (selected.containsKey(s)) {
|
||||
return selected.get(s);
|
||||
if (selected.containsKey(sChain)) {
|
||||
return selected.get(sChain).toRawString();
|
||||
}
|
||||
}
|
||||
|
||||
isValid = isValidName(as3, s);
|
||||
if (!isValid) {
|
||||
if (namesMap.containsKey(s)) {
|
||||
return namesMap.get(s);
|
||||
if (namesMap.containsKey(sChain)) {
|
||||
return namesMap.get(sChain).toRawString();
|
||||
} else {
|
||||
Integer cnt = typeCounts.get(usageType);
|
||||
if (cnt == null) {
|
||||
@@ -294,7 +285,7 @@ public class IdentifiersDeobfuscation {
|
||||
} else if (renameType == RenameType.RANDOMWORD) {
|
||||
ret = fooString(as3, namesMap, s, firstUppercase, DEFAULT_FOO_SIZE);
|
||||
}
|
||||
namesMap.put(s, ret);
|
||||
namesMap.put(DottedChain.parse(s), DottedChain.parse(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ import com.jpexs.decompiler.flash.types.filters.BlendComposite;
|
||||
import com.jpexs.decompiler.flash.types.filters.FILTER;
|
||||
import com.jpexs.decompiler.flash.xfl.FLAVersion;
|
||||
import com.jpexs.decompiler.flash.xfl.XFLConverter;
|
||||
import com.jpexs.decompiler.graph.DottedChain;
|
||||
import com.jpexs.decompiler.graph.Graph;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItem;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
|
||||
@@ -284,7 +285,7 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
public DefineBinaryDataTag binaryData;
|
||||
|
||||
@Internal
|
||||
private final HashMap<String, String> deobfuscated = new HashMap<>();
|
||||
private final HashMap<DottedChain, DottedChain> deobfuscated = new HashMap<>();
|
||||
|
||||
@Internal
|
||||
private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation();
|
||||
@@ -1886,8 +1887,8 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
}
|
||||
|
||||
public void renameAS2Identifier(String identifier, String newname) throws InterruptedException {
|
||||
Map<String, String> selected = new HashMap<>();
|
||||
selected.put(identifier, newname);
|
||||
Map<DottedChain, DottedChain> selected = new HashMap<>();
|
||||
selected.put(DottedChain.parse(identifier), DottedChain.parse(newname));
|
||||
renameAS2Identifiers(null, selected);
|
||||
}
|
||||
|
||||
@@ -1895,7 +1896,7 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
return renameAS2Identifiers(renameType, null);
|
||||
}
|
||||
|
||||
private int renameAS2Identifiers(RenameType renameType, Map<String, String> selected) throws InterruptedException {
|
||||
private int renameAS2Identifiers(RenameType renameType, Map<DottedChain, DottedChain> selected) throws InterruptedException {
|
||||
HashMap<ASMSource, ActionList> actionsMap = new HashMap<>();
|
||||
List<GraphSourceItem> allFunctions = new ArrayList<>();
|
||||
List<MyEntry<DirectValueActionItem, ConstantPool>> allVariableNames = new ArrayList<>();
|
||||
@@ -1959,7 +1960,7 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
String fname = dvf.toStringNoH(null);
|
||||
String changed = deobfuscation.deobfuscateName(false, fname, false, "method", deobfuscated, renameType, selected);
|
||||
if (changed != null) {
|
||||
deobfuscated.put(fname, changed);
|
||||
deobfuscated.put(DottedChain.parse(fname), DottedChain.parse(changed));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1978,7 +1979,7 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
String vname = dvf.toStringNoH(null);
|
||||
String changed = deobfuscation.deobfuscateName(false, vname, false, "attribute", deobfuscated, renameType, selected);
|
||||
if (changed != null) {
|
||||
deobfuscated.put(vname, changed);
|
||||
deobfuscated.put(DottedChain.parse(vname), DottedChain.parse(changed));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2014,7 +2015,7 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
changedNameStr = changedNameStr2;
|
||||
}
|
||||
ret++;
|
||||
deobfuscated.put(nameStr, changedNameStr);
|
||||
deobfuscated.put(DottedChain.parse(nameStr), DottedChain.parse(changedNameStr));
|
||||
pos++;
|
||||
}
|
||||
name = mem.object;
|
||||
@@ -2038,7 +2039,7 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
changedNameStr = changedNameStr2;
|
||||
}
|
||||
ret++;
|
||||
deobfuscated.put(nameStr, changedNameStr);
|
||||
deobfuscated.put(DottedChain.parse(nameStr), DottedChain.parse(changedNameStr));
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ public class ABC {
|
||||
}
|
||||
}
|
||||
|
||||
public void deobfuscateIdentifiers(HashMap<String, String> namesMap, RenameType renameType, boolean classesOnly) {
|
||||
public void deobfuscateIdentifiers(HashMap<DottedChain, DottedChain> namesMap, RenameType renameType, boolean classesOnly) {
|
||||
Set<Integer> stringUsages = getStringUsages();
|
||||
Set<Integer> namespaceUsages = getNsStringUsages();
|
||||
Map<Integer, String> stringUsageTypes = new HashMap<>();
|
||||
|
||||
@@ -30,13 +30,13 @@ public class ClassPath {
|
||||
public final String className;
|
||||
|
||||
public ClassPath(DottedChain packageStr, String className) {
|
||||
this.packageStr = packageStr;
|
||||
this.packageStr = packageStr == null ? DottedChain.EMPTY : packageStr;
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (packageStr == null || packageStr.isEmpty()) ? className : packageStr.toPrintableString(true) + "." + className;
|
||||
return packageStr.isEmpty() ? className : packageStr.toPrintableString(true) + "." + className;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -421,12 +421,7 @@ public class AVM2ConstantPool {
|
||||
String str = getString(index);
|
||||
DottedChain chain = dottedChainCache.get(str);
|
||||
if (chain == null) {
|
||||
if (str.isEmpty()) {
|
||||
chain = DottedChain.EMPTY;
|
||||
} else {
|
||||
chain = new DottedChain(str.split("\\."));
|
||||
}
|
||||
|
||||
chain = DottedChain.parse(str);
|
||||
dottedChainCache.put(str, chain);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,9 @@ package com.jpexs.decompiler.flash.abc.avm2;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.RenameType;
|
||||
import com.jpexs.decompiler.graph.DottedChain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
@@ -109,7 +110,7 @@ public class AVM2Deobfuscation {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String fooString(HashMap<String, String> deobfuscated, String orig, boolean firstUppercase, int rndSize, String usageType, RenameType renameType) {
|
||||
private String fooString(HashMap<DottedChain, DottedChain> deobfuscated, String orig, boolean firstUppercase, int rndSize, String usageType, RenameType renameType) {
|
||||
boolean exists;
|
||||
String ret;
|
||||
int pos = 0;
|
||||
@@ -131,18 +132,21 @@ public class AVM2Deobfuscation {
|
||||
} else if (renameType == RenameType.RANDOMWORD) {
|
||||
int len = 3 + rnd.nextInt(rndSize - 3);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < len; i++) {
|
||||
String c = "";
|
||||
char c;
|
||||
if ((i % 2) == 0) {
|
||||
c = "" + FOO_CHARACTERS.charAt(rnd.nextInt(FOO_CHARACTERS.length()));
|
||||
c = FOO_CHARACTERS.charAt(rnd.nextInt(FOO_CHARACTERS.length()));
|
||||
} else {
|
||||
c = "" + FOO_JOIN_CHARACTERS.charAt(rnd.nextInt(FOO_JOIN_CHARACTERS.length()));
|
||||
c = FOO_JOIN_CHARACTERS.charAt(rnd.nextInt(FOO_JOIN_CHARACTERS.length()));
|
||||
}
|
||||
if (i == 0 && firstUppercase) {
|
||||
c = c.toUpperCase(Locale.ENGLISH);
|
||||
c = Character.toUpperCase(c);
|
||||
}
|
||||
ret += c;
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
ret = sb.toString();
|
||||
}
|
||||
for (int i = 1; i < constants.getStringCount(); i++) {
|
||||
if (constants.getString(i).equals(ret)) {
|
||||
@@ -156,18 +160,18 @@ public class AVM2Deobfuscation {
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
if (deobfuscated.containsValue(ret)) {
|
||||
if (deobfuscated.containsValue(DottedChain.parse(ret))) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
} while (exists);
|
||||
usageTypesCount.put(usageType, pos);
|
||||
deobfuscated.put(orig, ret);
|
||||
deobfuscated.put(DottedChain.parse(orig), DottedChain.parse(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int deobfuscatePackageName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, HashMap<String, String> namesMap, int strIndex, RenameType renameType) {
|
||||
public int deobfuscatePackageName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, HashMap<DottedChain, DottedChain> namesMap, int strIndex, RenameType renameType) {
|
||||
if (strIndex <= 0) {
|
||||
return strIndex;
|
||||
}
|
||||
@@ -177,41 +181,35 @@ public class AVM2Deobfuscation {
|
||||
}
|
||||
boolean isValid = isValidNSPart(s);
|
||||
if (!isValid) {
|
||||
String newName;
|
||||
if (namesMap.containsKey(s)) {
|
||||
newName = constants.setString(strIndex, namesMap.get(s));
|
||||
DottedChain sChain = DottedChain.parse(s);
|
||||
DottedChain newName;
|
||||
if (namesMap.containsKey(sChain)) {
|
||||
newName = namesMap.get(sChain);
|
||||
constants.setString(strIndex, newName.toRawString());
|
||||
} else {
|
||||
String[] parts = null;
|
||||
if (s.contains(".")) {
|
||||
parts = s.split("\\.");
|
||||
} else {
|
||||
parts = new String[]{s};
|
||||
}
|
||||
StringBuilder ret = new StringBuilder();
|
||||
for (int p = 0; p < parts.length; p++) {
|
||||
if (p > 0) {
|
||||
ret.append(".");
|
||||
}
|
||||
if (!isValidNSPart(parts[p])) {
|
||||
ret.append(fooString(namesMap, parts[p], false, DEFAULT_FOO_SIZE, "package", renameType));
|
||||
List<String> ret = new ArrayList<>();
|
||||
for (int p = 0; p < sChain.size(); p++) {
|
||||
String part = sChain.get(p);
|
||||
if (!isValidNSPart(part)) {
|
||||
ret.add(fooString(namesMap, part, false, DEFAULT_FOO_SIZE, "package", renameType));
|
||||
} else {
|
||||
ret.append(parts[p]);
|
||||
ret.add(part);
|
||||
}
|
||||
}
|
||||
newName = ret.toString();
|
||||
namesMap.put(s, newName);
|
||||
newName = new DottedChain(ret);
|
||||
namesMap.put(sChain, newName);
|
||||
}
|
||||
if (stringUsages.contains(strIndex)) {
|
||||
strIndex = constants.addString(newName);
|
||||
strIndex = constants.addString(newName.toRawString());
|
||||
} else {
|
||||
constants.setString(strIndex, newName);
|
||||
constants.setString(strIndex, newName.toRawString());
|
||||
}
|
||||
|
||||
}
|
||||
return strIndex;
|
||||
}
|
||||
|
||||
public int deobfuscateName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, Set<Integer> namespaceUsages, HashMap<String, String> namesMap, int strIndex, boolean firstUppercase, RenameType renameType) {
|
||||
public int deobfuscateName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, Set<Integer> namespaceUsages, HashMap<DottedChain, DottedChain> namesMap, int strIndex, boolean firstUppercase, RenameType renameType) {
|
||||
if (strIndex <= 0) {
|
||||
return strIndex;
|
||||
}
|
||||
@@ -238,18 +236,20 @@ public class AVM2Deobfuscation {
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
String newname;
|
||||
if (namesMap.containsKey(s)) {
|
||||
newname = namesMap.get(s);
|
||||
DottedChain newname;
|
||||
DottedChain sChain = DottedChain.parse(s);
|
||||
if (namesMap.containsKey(sChain)) {
|
||||
newname = namesMap.get(sChain);
|
||||
} else {
|
||||
newname = fooString(namesMap, constants.getString(strIndex), firstUppercase, DEFAULT_FOO_SIZE, stringUsageTypes.get(strIndex), renameType);
|
||||
String str = fooString(namesMap, constants.getString(strIndex), firstUppercase, DEFAULT_FOO_SIZE, stringUsageTypes.get(strIndex), renameType);
|
||||
newname = DottedChain.parse(str);
|
||||
}
|
||||
if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { // this name is already referenced as String
|
||||
strIndex = constants.addString(s); // add new index
|
||||
}
|
||||
constants.setString(strIndex, newname);
|
||||
if (!namesMap.containsKey(s)) {
|
||||
namesMap.put(s, constants.getString(strIndex));
|
||||
constants.setString(strIndex, newname.toRawString());
|
||||
if (!namesMap.containsKey(sChain)) {
|
||||
namesMap.put(sChain, DottedChain.parse(constants.getString(strIndex)));
|
||||
}
|
||||
}
|
||||
return strIndex;
|
||||
|
||||
@@ -294,7 +294,7 @@ public class Multiname {
|
||||
return isAttribute() ? "@*" : "*";
|
||||
} else {
|
||||
String name = constants.getString(name_index);
|
||||
if (fullyQualifiedNames != null && fullyQualifiedNames.contains(name)) {
|
||||
if (fullyQualifiedNames != null && fullyQualifiedNames.contains(DottedChain.parse(name))) {
|
||||
DottedChain dc = getNameWithNamespace(constants);
|
||||
return raw ? dc.toRawString() : dc.toPrintableString(true);
|
||||
}
|
||||
|
||||
@@ -381,8 +381,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
|
||||
for (ScriptInfo si : tag.getABC().script_info) {
|
||||
for (Trait t : si.traits.traits) {
|
||||
ClassPath classPath = t.getPath(tag.getABC());
|
||||
DottedChain pkg = classPath.packageStr == null ? DottedChain.EMPTY : classPath.packageStr;
|
||||
if (pkg.equals(packageName)) {
|
||||
if (classPath.packageStr.equals(packageName)) {
|
||||
namesInThisPackage.add(classPath.className);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,14 @@ public class DottedChain implements Serializable {
|
||||
|
||||
private final int hash;
|
||||
|
||||
public static final DottedChain parse(String name) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
return DottedChain.EMPTY;
|
||||
} else {
|
||||
return new DottedChain(name.split("\\."));
|
||||
}
|
||||
}
|
||||
|
||||
public DottedChain(List<String> parts) {
|
||||
length = parts.size();
|
||||
this.parts = parts.toArray(new String[length]);
|
||||
|
||||
Reference in New Issue
Block a user