com.reandroid.dex.common.DexUtils Maven / Gradle / Ivy
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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.reandroid.dex.common;
import com.reandroid.dex.key.TypeKey;
import com.reandroid.utils.NumbersUtil;
import com.reandroid.utils.StringsUtil;
import com.reandroid.utils.collection.ArrayCollection;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
public class DexUtils {
public static List listDexFiles(File dir){
ArrayCollection results = new ArrayCollection<>();
if(!dir.isDirectory()){
return results;
}
File[] files = dir.listFiles();
if(files == null){
return results;
}
for(File file : files){
if(file.isFile() && file.getName().endsWith(".dex")){
results.add(file);
}
}
results.sort(getDexPathComparator());
return results;
}
public static Comparator getDexPathComparator(){
return DexUtils::compareDex;
}
public static Comparator getDexPathComparator(Function function){
return (dex1, dex2) -> compareDex(function, dex1, dex2);
}
public static int compareDex(Function function, T dexPath1, T dexPath2){
return compareDex(function.apply(dexPath1), function.apply(dexPath2));
}
public static int compareDex(Object dexPath1, Object dexPath2){
if(dexPath1 == dexPath2){
return 0;
}
if(dexPath1 == null){
return 1;
}
if(dexPath2 == null){
return -1;
}
return compareDexPath(dexPath1.toString(), dexPath2.toString());
}
public static int compareDexPath(String path1, String path2){
if(path1 == null){
return 1;
}
if(path2 == null){
return -1;
}
if(path1.equals(path2)){
return 0;
}
return Integer.compare(getDexNumber(path1), getDexNumber(path2));
}
private static int getDexNumber(String path){
int i = path.lastIndexOf('/');
if(i < 0){
i = path.lastIndexOf(File.separatorChar);
}
if(i >= 0){
path = path.substring(i + 1);
}
String name = "classes";
if(path.equals(name)){
return 0;
}
if(!path.startsWith(name)){
return 0xffff;
}
path = path.substring(name.length());
i = path.indexOf('.');
if(i == 0){
return 0;
}else if(i < 0){
return 0xffff;
}
path = path.substring(0, i);
try{
return Integer.parseInt(path);
}catch (NumberFormatException ignored){
return 0xffff;
}
}
public static String[] splitParameters(String parameters) {
if(StringsUtil.isEmpty(parameters)){
return null;
}
int length = parameters.length();
String[] results = new String[length];
int count = 0;
boolean array = false;
int start = 0;
for(int i = 0; i < length; i++){
boolean pop = false;
char ch = parameters.charAt(i);
if(ch == '[') {
array = true;
}else if(ch == ';'){
pop = true;
}else if((array || (i - start) == 0) && isPrimitive(ch)){
pop = true;
array = false;
}else {
array = false;
}
if(pop) {
results[count] = parameters.substring(start, i + 1);
count ++;
start = i + 1;
}
}
if(count == 0){
return null;
}
if(count == length){
return results;
}
String[] tmp = new String[count];
System.arraycopy(results, 0, tmp, 0, count);
return tmp;
}
public static String quoteString(String text){
StringBuilder builder = new StringBuilder(text.length() + 2);
try {
appendQuotedString(builder, text);
} catch (IOException ignored) {
}
return builder.toString();
}
public static String encodeString(String text){
StringBuilder builder = new StringBuilder(text.length());
try {
encodeString(builder, text);
} catch (IOException ignored) {
}
return builder.toString();
}
public static void appendQuotedString(Appendable appendable, String text) throws IOException {
appendable.append('"');
encodeString(appendable, text);
appendable.append('"');
}
public static boolean encodeString(Appendable appendable, String text) throws IOException {
boolean unicodeDetected = false;
int length = text.length();
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
if ((c >= ' ') && (c < 0x7f)) {
if ((c == '\'') || (c == '\"') || (c == '\\')) {
appendable.append('\\');
}
appendable.append(c);
continue;
} else if (c <= 0x7f) {
switch (c) {
case '\n':
appendable.append("\\n");
continue;
case '\r':
appendable.append("\\r");
continue;
case '\t':
appendable.append("\\t");
continue;
}
}
escapeChar(appendable, c);
if(!unicodeDetected && c != '…' && c > 0xff) {
unicodeDetected = true;
}
}
return unicodeDetected;
}
public static void appendCommentString(int maxLength, Appendable appendable, String text) throws IOException {
int length = NumbersUtil.min(maxLength, text.length());
appendable.append('\'');
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
if ((c >= ' ') && (c < 0x7f)) {
appendable.append(c);
continue;
} else if (c <= 0x7f) {
switch (c) {
case '\n':
appendable.append("\\n");
continue;
case '\r':
appendable.append("\\r");
continue;
case '\t':
appendable.append("\\t");
continue;
}
}
if(Character.isDefined(c) && !Character.isWhitespace(c)) {
appendable.append(c);
} else {
escapeChar(appendable, c);
}
}
appendable.append('\'');
}
public static String quoteChar(char ch){
StringBuilder builder = new StringBuilder();
try {
appendSingleQuotedChar(builder, ch);
} catch (IOException ignored) {
}
return builder.toString();
}
public static void appendSingleQuotedChar(Appendable appendable, char ch) throws IOException {
if ((ch >= ' ') && (ch < 0x7f)) {
appendable.append('\'');
if ((ch == '\'') || (ch == '\"') || (ch == '\\')) {
appendable.append('\\');
}
appendable.append(ch);
appendable.append('\'');
return;
} else if (ch <= 0x7f) {
switch (ch) {
case '\b':
appendable.append("'\\b'");
return;
case '\f':
appendable.append("'\\f'");
return;
case '\n':
appendable.append("'\\n'");
return;
case '\r':
appendable.append("'\\r'");
return;
case '\t':
appendable.append("'\\t'");
return;
}
}
appendable.append('\'');
escapeChar(appendable, ch);
appendable.append('\'');
}
private static void escapeChar(Appendable appendable, char c) throws IOException {
appendable.append("\\u");
appendable.append(Character.forDigit(c >> 12, 16));
appendable.append(Character.forDigit((c >> 8) & 0x0f, 16));
appendable.append(Character.forDigit((c >> 4) & 0x0f, 16));
appendable.append(Character.forDigit(c & 0x0f, 16));
}
public static String decodeString(String text) {
if(text.indexOf('\\') < 0){
return text;
}
StringBuilder sb = new StringBuilder();
int length = text.length();
boolean escaped = false;
for(int i = 0; i < length; i++){
char ch = text.charAt(i);
if(escaped){
escaped = false;
if(ch == 'u'){
Character character = nextHex(text, i + 1);
if(character != null){
sb.append(character);
i = i + 4;
continue;
}
}
sb.append(getEscaped(ch));
}else if(ch == '\\'){
escaped = true;
}else {
sb.append(ch);
}
}
return sb.toString();
}
private static char getEscaped(char ch){
switch(ch) {
case 'b':
return '\b';
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
default:
return ch;
}
}
private static Character nextHex(String text, int start){
int length = text.length();
int end = start + 4;
if(end > length){
return null;
}
StringBuilder builder = new StringBuilder(4);
for(int i = start; i < end; i++){
builder.append(text.charAt(i));
}
try{
int i = Integer.parseInt(builder.toString(), 16);
return (char) i;
}catch (NumberFormatException ignored){
return null;
}
}
public static boolean isJavaFramework(String name){
return name.startsWith("Ljava/");
}
public static String toSignatureType(String type){
if(type == null){
return null;
}
int length = type.length();
if(length == 0){
return type;
}
int i = 0;
while (type.charAt(i) == '['){
i++;
}
length = length - 1;
if(i == length || type.charAt(length) == '<' || type.charAt(length) != ';'){
if(i != 0){
return type.substring(i);
}
return type;
}
StringBuilder builder = new StringBuilder(length - i);
builder.append(type, i, length);
builder.append('<');
return builder.toString();
}
public static String toDeclaringType(String type){
if(type == null){
return null;
}
int length = type.length();
if(length == 0){
return type;
}
int i = 0;
while (type.charAt(i) == '['){
i++;
}
length = length - 1;
if(i == length || type.charAt(length) == ';' || type.charAt(length) != '<'){
if(i != 0){
return type.substring(i);
}
return type;
}
StringBuilder builder = new StringBuilder(length - i);
builder.append(type, i, length);
builder.append(';');
return builder.toString();
}
public static String makeArrayType(String type, int dimension){
if(type == null){
return null;
}
int length = type.length();
if(length == 0){
return type;
}
int i = 0;
while (type.charAt(i) == '['){
i++;
}
if(i == dimension){
return type;
}
if(i > dimension){
i = i - dimension;
return type.substring(i);
}
i = dimension - i;
StringBuilder builder = new StringBuilder(length + i);
while (i > 0){
builder.append('[');
i--;
}
builder.append(type);
return builder.toString();
}
public static int countArrayPrefix(String type){
if(type == null){
return 0;
}
int length = type.length();
if(length < 2){
return 0;
}
int i = 0;
while (type.charAt(i) == '['){
i++;
}
return i;
}
public static boolean isTypeArray(String type){
if(type == null){
return false;
}
int length = type.length();
if(length < 2){
return false;
}
int i = 0;
while (type.charAt(i) == '['){
i++;
}
if(i == 0){
return false;
}
length = length - 1;
if(i == length){
return isPrimitive(type.charAt(i));
}
return type.charAt(i) == 'L' && type.charAt(length) == ';';
}
public static boolean isTypeSignature(String type){
if(type == null){
return false;
}
int length = type.length();
if(length < 3){
return false;
}
return type.charAt(0) == 'L' && type.charAt(length - 1) == '<';
}
public static boolean isTypeOrSignature(String type){
if(type == null){
return false;
}
int length = type.length();
if(length < 3){
return false;
}
char last = type.charAt(length - 1);
return type.charAt(0) == 'L' && (last == '<' || last == ';');
}
public static boolean isTypeObject(String type){
if(type == null || type.length() < 2){
return false;
}
char ch = type.charAt(0);
return ch == 'L' || ch == '[';
}
public static boolean isPlatform(TypeKey typeKey){
if(typeKey == null){
return false;
}
return isPlatform(typeKey.getTypeName());
}
public static boolean isPlatform(String type){
if(type == null){
return false;
}
int length = type.length();
if(length == 0){
return false;
}
int i = 0;
while (i < length && type.charAt(i) == '['){
i++;
}
if(i >= length){
return false;
}
if(i != 0){
type = type.substring(i);
}
for(String platformPackage : PLATFORM_PACKAGES){
if(type.startsWith(platformPackage)){
return true;
}
}
return false;
}
public static boolean isPrimitive(String type){
if(type == null || type.length() != 1){
return false;
}
return isPrimitive(type.charAt(0));
}
public static boolean isPrimitive(char ch){
switch (ch){
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'V':
case 'Z':
return true;
default:
return false;
}
}
public static boolean looksSignatureType(String name){
int length = name.length();
if(length < 3){
return false;
}
return name.charAt(0) == 'L' && name.charAt(length - 1) == '<';
}
public static String toSourceName(String binaryName){
int i = binaryName.indexOf('L');
binaryName = binaryName.substring(i + 1);
i = binaryName.indexOf(';');
if(i < 0){
i = binaryName.indexOf('<');
}
if(i > 0){
binaryName = binaryName.substring(0, i);
}
return binaryName.replace('/', '.');
}
public static String toBinaryName(String sourceName){
return 'L' + sourceName.replace('.', '/') + ';';
}
public static String toBinaryPackageName(String sourceName) {
if(sourceName.indexOf('/') > 0) {
return sourceName;
}
String binName = 'L' + sourceName.replace('.', '/');
if(binName.charAt(sourceName.length() - 1) != '/') {
binName = binName + '/';
}
return binName;
}
public static String getPackageName(String className) {
if(className.length() < 3){
return StringsUtil.EMPTY;
}
int start = 0;
while (className.charAt(start) == '['){
start ++;
}
int i = className.lastIndexOf('/');
if(i > start){
return className.substring(start, i + 1);
}
i = start;
if(className.charAt(i) == 'L'){
i++;
}
return className.substring(start, i);
}
public static String toSourceFileName(String className){
String simple = getSimpleName(className);
int i = simple.indexOf('$');
if(i > 0){
simple = simple.substring(0, i);
}
return simple + ".java";
}
public static String getSimpleName(String className) {
className = trimArrayPrefix(className);
if(className.length() < 2){
return className;
}
int i = className.lastIndexOf('/');
if (i < 0){
i = 0;
}
i++;
className = className.substring(i);
i = className.length() - 1;
if(className.charAt(i) == ';' || className.charAt(i) == '<'){
className = className.substring(0, i);
}
return className;
}
public static String getSimpleInnerName(String className) {
className = trimArrayPrefix(className);
if(className.length() < 2){
return className;
}
int i = className.lastIndexOf('/');
if (i < 0){
i = 0;
}
i++;
className = className.substring(i);
i = className.length() - 1;
if(className.charAt(i) == ';' || className.charAt(i) == '<'){
className = className.substring(0, i);
}
i = className.indexOf('$');
if(i < 0){
return className;
}
String[] split = StringsUtil.split(className, '$', true);
split = StringsUtil.removeEmpty(split);
i = split.length;
if(i < 2){
return className;
}
return split[i - 1];
}
public static String getParentClassName(String className) {
className = trimArrayPrefix(className);
if(className.length() < 2){
return className;
}
int i = className.lastIndexOf('/');
if (i < 0){
i = 0;
}
i++;
int j = className.lastIndexOf('$');
if(j <= i){
return className;
}
className = className.substring(0, j);
while (className.length() > 0 && className.charAt(className.length() - 1) == '$'){
className = className.substring(0, className.length() - 1);
}
return className + ";";
}
public static String createChildClass(String type, String simpleName){
int i = type.length();
if(i < 2){
return type;
}
i = i - 1;
char ch = type.charAt(i);
if(ch == ';' || ch == '<'){
type = type.substring(0, i);
}
return type + "$" + simpleName + ";";
}
public static String trimArrayPrefix(String className) {
int i = 0;
while (i < className.length() && className.charAt(i) == '['){
i++;
}
if(i == 0){
return className;
}
return className.substring(i);
}
public static String[] splitSignatures(String text){
ArrayCollection results = new ArrayCollection<>(5);
StringBuilder builder = new StringBuilder();
int length = text.length();
for(int i = 0; i < length; i++){
char ch = text.charAt(i);
builder.append(ch);
if(isSignatureSymbol(ch)){
results.add(builder.toString());
builder = new StringBuilder();
}
}
if(builder.length() != 0){
results.add(builder.toString());
}
return results.toArray(new String[results.size()]);
}
private static boolean isSignatureSymbol(char ch){
switch (ch){
case '+':
case '-':
case '*':
case ':':
case ';':
case '<':
case '>':
case '?':
return true;
default:
return false;
}
}
public static final String[] PLATFORM_PACKAGES = new String[]{
"Ljava/",
"Landroid/",
"Ldalvik/",
"Lorg/json/",
"Lorg/xmlpull/",
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy