Added: Show api information of classes, methods and fields

This commit is contained in:
Akash Yadav
2021-09-16 09:11:34 +05:30
parent c5b1341c2b
commit 1476478e08
27 changed files with 9700 additions and 50 deletions
+85
View File
@@ -0,0 +1,85 @@
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
+29
View File
@@ -0,0 +1,29 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdk
buildToolsVersion rootProject.ext.buildTools
defaultConfig {
minSdkVersion rootProject.ext.minSdk
targetSdkVersion rootProject.ext.targetSdk
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled true
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.blankj:utilcodex:1.30.6'
}
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/bogdan/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
+1
View File
@@ -0,0 +1 @@
<manifest package="com.itsaky.apiinfo"/>
@@ -0,0 +1,160 @@
package com.itsaky.apiinfo;
import com.blankj.utilcode.util.FileIOUtils;
import com.itsaky.apiinfo.models.ClassInfo;
import com.itsaky.apiinfo.models.FieldInfo;
import com.itsaky.apiinfo.models.MethodInfo;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class ApiInfo {
private final File versionsXml;
private boolean read = false;
private final Map<String, ClassInfo> classInfos;
public static final String NAME = "name";
public static final String SINCE = "since";
public static final String DEPRECATED = "deprecated";
public static final String REMOVED = "removed";
public static final String EXTENDS = "extends";
public static final String IMPLEMENTS = "implements";
public static final String FIELD = "field";
public static final String METHOD = "method";
public ApiInfo(File versionsXml) {
this.versionsXml = versionsXml;
this.classInfos = new HashMap<String, ClassInfo>();
}
public boolean hasRead() {
return read;
}
public ClassInfo getClassByName(String qualifiedName) {
return classInfos.get(qualifiedName);
}
public void readAsync(Runnable onFinish) throws Exception{
new Thread(() -> {
String contents = FileIOUtils.readFile2String(versionsXml);
if(contents != null) {
contents = contents.substring(contents.indexOf("<api"));
Document doc = Jsoup.parse(contents);
Elements classes = doc.getElementsByTag("api").first().getElementsByTag("class");
if(classes == null || classes.size() <= 0) {
return;
}
for(Element clazz : classes) {
ClassInfo info = parseClassInfo(clazz);
if(info != null)
classInfos.put(info.name, info);
}
read = true;
if(onFinish != null)
onFinish.run();
}
}).start();
}
private ClassInfo parseClassInfo(Element clazz) {
if(!clazz.hasAttr(NAME)) return null;
ClassInfo info = new ClassInfo();
info.name = clazz.attr(NAME)
.replace("/", ".")
.replace("$", ".");
if(clazz.hasAttr(SINCE)) {
info.since = Integer.parseInt(clazz.attr(SINCE));
}
if(clazz.hasAttr(DEPRECATED)) {
info.deprecated = Integer.parseInt(clazz.attr(DEPRECATED));
}
if(clazz.hasAttr(REMOVED)) {
info.removed = Integer.parseInt(clazz.attr(REMOVED));
}
Elements superClass = clazz.getElementsByTag(EXTENDS);
if(superClass != null && superClass.size() == 1) {
info.superClass = superClass.first().attr(NAME);
}
Elements interfaces = clazz.getElementsByTag(IMPLEMENTS);
if(interfaces != null && interfaces.size() > 0) {
for(Element iface : interfaces) {
info.interfaces.add(iface.attr(NAME));
}
}
Elements fields = clazz.getElementsByTag(FIELD);
if(fields != null && fields.size() > 0) {
for(Element field : fields) {
putField(field, info);
}
}
Elements methods = clazz.getElementsByTag(METHOD);
if(methods != null && methods.size() > 0) {
for(Element method : methods) {
putMethod(method, info);
}
}
return info;
}
private void putField(Element field, ClassInfo clazz) {
if(!field.hasAttr(NAME)) return;
FieldInfo info = new FieldInfo();
info.name = field.attr(NAME);
if(field.hasAttr(SINCE)) {
info.since = Integer.parseInt(field.attr(SINCE));
}
if(field.hasAttr(DEPRECATED)) {
info.deprecated = Integer.parseInt(field.attr(DEPRECATED));
}
if(field.hasAttr(REMOVED)) {
info.removed = Integer.parseInt(field.attr(REMOVED));
}
clazz.fields.put(info.name, info);
}
private void putMethod(Element method, ClassInfo clazz) {
if(!method.hasAttr(NAME)) return;
MethodInfo info = new MethodInfo();
info.name = method.attr(NAME);
info.simpleName = info.name.substring(0, info.name.indexOf("("));
if(method.hasAttr(SINCE)) {
info.since = Integer.parseInt(method.attr(SINCE));
}
if(method.hasAttr(DEPRECATED)) {
info.deprecated = Integer.parseInt(method.attr(DEPRECATED));
}
if(method.hasAttr(REMOVED)) {
info.removed = Integer.parseInt(method.attr(REMOVED));
}
clazz.methods.put(info.name, info);
}
}
@@ -0,0 +1,117 @@
package com.itsaky.apiinfo.models;
import android.text.TextUtils;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.Signature;
import android.os.Environment;
import java.io.FileNotFoundException;
/**
* Holds information about a class
*/
public class ClassInfo extends Info {
/**
* Superclass of this class
*/
public String superClass;
/**
* Interfaces that this class implements
*/
public final List<String> interfaces = new ArrayList<>();
/**
* Fields of this class
*/
public final Map<String, FieldInfo> fields = new HashMap<>();
/**
* Methods of this class
*/
public final Map<String, MethodInfo> methods = new HashMap<>();
/**
* Get the field by its simple name
*
* @param name Name of the field to find
*/
public FieldInfo getFieldByName(String name) {
return fields.get(name);
}
/**
* Get method by its name and parameter types
*
* @param name Name of the method to find
* @param parameters Parameter types of the method
*/
public MethodInfo getMethod(String name, String[] parameters) {
/**
* Convert parameter types to their binary version
*/
final String[] parameterTypes = new String[parameters.length];
for(int i=0;i<parameters.length;i++) {
String paramType = parameters[i];
String binaryParamType = Signature.createTypeSignature(paramType, true /* isResolved */);
parameterTypes[i] = binaryParamType;
}
log("Original parameters: \n" + TextUtils.join(", ", parameters) + "\n\nNew parameters:\n" + TextUtils.join(", ", parameterTypes));
for(Map.Entry<String, MethodInfo> entry : methods.entrySet()) {
MethodInfo method = entry.getValue();
/**
* Check if this method's simple name is what we are looking for
*/
if(method != null
&& method.simpleName.trim().equals(name.trim())) {
/**
* Get parameter types of this method
* Takes binary representation, returns binary representation
*/
String[] paramTypes = Signature.getParameterTypes(method.name);
/**
* Join the parameters to check if they are equal
* This is just more efficient
*/
final String params1 = TextUtils.join(",", paramTypes);
final String params2 = TextUtils.join(",", parameterTypes);
if(params1.equals(params2)) {
/**
* We found what we were looking for!
*/
return method;
}
}
}
return null;
}
private BufferedOutputStream os;
private void log(String msg) {
try {
if(os == null)
os = new BufferedOutputStream(new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "ide_xlog/class_info.txt")));
os.write(msg.getBytes());
os.flush();
} catch (Throwable e) {}
}
}
@@ -0,0 +1,10 @@
package com.itsaky.apiinfo.models;
/**
* Holds information about a field
*
* No difference as compared to Info
*/
public class FieldInfo extends Info {
}
@@ -0,0 +1,27 @@
package com.itsaky.apiinfo.models;
/**
* Base class for information about a class, method, field
*/
public abstract class Info {
/**
* Name of this class, method or field
*/
public String name = "";
/**
* API level in which this info was deprecated
*/
public int deprecated = -1;
/**
* API level in which this was added
*/
public int since = -1;
/**
* API level in which this was removed
*/
public int removed = -1;
}
@@ -0,0 +1,21 @@
package com.itsaky.apiinfo.models;
import java.util.List;
import java.util.ArrayList;
/**
* Holds information about a method
*/
public class MethodInfo extends Info {
/**
* In case of a method, the {@link com.itsaky.apiinfo.models.Info#name} contains signature of the method
* This field contains the actual simple name
*/
public String simpleName;
/**
* Parameter types of this method
*/
public final List<String> parameters = new ArrayList<>();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
package org.eclipse.jdt.internal.compiler.ast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class ASTNode {
// storage for internal flags (32 bits) BIT USAGE
public final static int Bit1 = 0x1; // return type (operator) | name reference kind (name ref) | add assertion (type decl) | useful empty statement (empty statement)
public final static int Bit2 = 0x2; // return type (operator) | name reference kind (name ref) | has local type (type, method, field decl) | if type elided (local)
public final static int Bit3 = 0x4; // return type (operator) | name reference kind (name ref) | implicit this (this ref) | is argument(local)
public final static int Bit4 = 0x8; // return type (operator) | first assignment to local (name ref,local decl) | undocumented empty block (block, type and method decl)
public final static int Bit5 = 0x10; // value for return (expression) | has all method bodies (unit) | supertype ref (type ref) | resolved (field decl)| name ref (yield result value)
public final static int Bit6 = 0x20; // depth (name ref, msg) | ignore need cast check (cast expression) | error in signature (method declaration/ initializer) | is recovered (annotation reference)
public final static int Bit7 = 0x40; // depth (name ref, msg) | need runtime checkcast (cast expression) | label used (labelStatement) | needFreeReturn (AbstractMethodDeclaration)
public final static int Bit8 = 0x80; // depth (name ref, msg) | unsafe cast (cast expression) | is default constructor (constructor declaration) | isElseStatementUnreachable (if statement)
public final static int Bit9 = 0x100; // depth (name ref, msg) | operator (operator) | is local type (type decl) | isThenStatementUnreachable (if statement) | can be static
public final static int Bit10= 0x200; // depth (name ref, msg) | operator (operator) | is anonymous type (type decl) | is implicit constructor (constructor)
public final static int Bit11 = 0x400; // depth (name ref, msg) | operator (operator) | is member type (type decl)
public final static int Bit12 = 0x800; // depth (name ref, msg) | operator (operator) | has abstract methods (type decl)
public final static int Bit13 = 0x1000; // depth (name ref, msg) | operator (operator) | is secondary type (type decl)
public final static int Bit14 = 0x2000; // strictly assigned (reference lhs) | operator (operator) | discard enclosing instance (explicit constr call) | hasBeenGenerated (type decl)
public final static int Bit15 = 0x4000; // is unnecessary cast (expression) | is varargs (type ref) | isSubRoutineEscaping (try statement) | superAccess (javadoc allocation expression/javadoc message send/javadoc return statement)
public final static int Bit16 = 0x8000; // in javadoc comment (name ref, type ref, msg)
public final static int Bit17 = 0x10000; // compound assigned (reference lhs) | unchecked (msg, alloc, explicit constr call)
public final static int Bit18 = 0x20000; // non null (expression) | onDemand (import reference)
public final static int Bit19 = 0x40000; // didResolve (parameterized qualified type ref/parameterized single type ref) | empty (javadoc return statement) | needReceiverGenericCast (msg/fieldref)
public final static int Bit20 = 0x80000; // contains syntax errors (method declaration, type declaration, field declarations, initializer), typeref: <> name ref: lambda capture)
public final static int Bit21 = 0x100000; // for all declarations that can contain type references that have type annotations | insideExpressionStatement
public final static int Bit22 = 0x200000; // parenthesis count (expression) | used (import reference) shadows outer local (local declarations)
public final static int Bit23 = 0x400000; // parenthesis count (expression) | second or later declarator in declaration (local declarations)
public final static int Bit24 = 0x800000; // parenthesis count (expression)
public final static int Bit25 = 0x1000000; // parenthesis count (expression)
public final static int Bit26 = 0x2000000; // parenthesis count (expression)
public final static int Bit27 = 0x4000000; // parenthesis count (expression)
public final static int Bit28 = 0x8000000; // parenthesis count (expression)
public final static int Bit29 = 0x10000000; // parenthesis count (expression)
public final static int Bit30 = 0x20000000; // elseif (if statement) | try block exit (try statement) | fall-through (case statement) | ignore no effect assign (expression ref) | needScope (for statement) | isAnySubRoutineEscaping (return statement) | blockExit (synchronized statement)
public final static int Bit31 = 0x40000000; // local declaration reachable (local decl) | ignore raw type check (type ref) | discard entire assignment (assignment) | isSynchronized (return statement) | thenExit (if statement)
public final static int Bit32 = 0x80000000; // reachable (statement)
public final static long Bit32L = 0x80000000L;
public final static long Bit33L = 0x100000000L;
public final static long Bit34L = 0x200000000L;
public final static long Bit35L = 0x400000000L;
public final static long Bit36L = 0x800000000L;
public final static long Bit37L = 0x1000000000L;
public final static long Bit38L = 0x2000000000L;
public final static long Bit39L = 0x4000000000L;
public final static long Bit40L = 0x8000000000L;
public final static long Bit41L = 0x10000000000L;
public final static long Bit42L = 0x20000000000L;
public final static long Bit43L = 0x40000000000L;
public final static long Bit44L = 0x80000000000L;
public final static long Bit45L = 0x100000000000L;
public final static long Bit46L = 0x200000000000L;
public final static long Bit47L = 0x400000000000L;
public final static long Bit48L = 0x800000000000L;
public final static long Bit49L = 0x1000000000000L;
public final static long Bit50L = 0x2000000000000L;
public final static long Bit51L = 0x4000000000000L;
public final static long Bit52L = 0x8000000000000L;
public final static long Bit53L = 0x10000000000000L;
public final static long Bit54L = 0x20000000000000L;
public final static long Bit55L = 0x40000000000000L;
public final static long Bit56L = 0x80000000000000L;
public final static long Bit57L = 0x100000000000000L;
public final static long Bit58L = 0x200000000000000L;
public final static long Bit59L = 0x400000000000000L;
public final static long Bit60L = 0x800000000000000L;
public final static long Bit61L = 0x1000000000000000L;
public final static long Bit62L = 0x2000000000000000L;
public final static long Bit63L = 0x4000000000000000L;
public final static long Bit64L = 0x8000000000000000L;
}
@@ -0,0 +1,218 @@
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Jesper S Moller - Contributions for
* Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
* Bug 406982 - [1.8][compiler] Generation of MethodParameters Attribute in classfile
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
* Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.classfmt;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
public final class ClassFileConstants {
int AccDefault = 0;
/*
* Modifiers
*/
int AccPublic = 0x0001;
int AccPrivate = 0x0002;
int AccProtected = 0x0004;
int AccStatic = 0x0008;
int AccFinal = 0x0010;
int AccSynchronized = 0x0020;
int AccVolatile = 0x0040;
int AccBridge = 0x0040;
int AccTransient = 0x0080;
int AccVarargs = 0x0080;
int AccNative = 0x0100;
int AccInterface = 0x0200;
int AccAbstract = 0x0400;
int AccStrictfp = 0x0800;
int AccSynthetic = 0x1000;
int AccAnnotation = 0x2000;
int AccEnum = 0x4000;
int AccModule = 0x8000;
/**
* From classfile version 52 (compliance 1.8 up), meaning that a formal parameter is mandated
* by a language specification, so all compilers for the language must emit it.
*/
int AccMandated = 0x8000;
/**
* Flags in module declaration - since java9
*/
int ACC_OPEN = 0x0020;
int ACC_TRANSITIVE = 0x0020;
int ACC_STATIC_PHASE = 0x0040;
int ACC_SYNTHETIC = 0x1000;
/**
* Other VM flags.
*/
int AccSuper = 0x0020;
/**
* Extra flags for types and members attributes (not from the JVMS, should have been defined in ExtraCompilerModifiers).
*/
int AccAnnotationDefault = ASTNode.Bit18; // indicate presence of an attribute "DefaultValue" (annotation method)
int AccDeprecated = ASTNode.Bit21; // indicate presence of an attribute "Deprecated"
int Utf8Tag = 1;
int IntegerTag = 3;
int FloatTag = 4;
int LongTag = 5;
int DoubleTag = 6;
int ClassTag = 7;
int StringTag = 8;
int FieldRefTag = 9;
int MethodRefTag = 10;
int InterfaceMethodRefTag = 11;
int NameAndTypeTag = 12;
int MethodHandleTag = 15;
int MethodTypeTag = 16;
int DynamicTag = 17;
int InvokeDynamicTag = 18;
int ModuleTag = 19;
int PackageTag = 20;
int ConstantMethodRefFixedSize = 5;
int ConstantClassFixedSize = 3;
int ConstantDoubleFixedSize = 9;
int ConstantFieldRefFixedSize = 5;
int ConstantFloatFixedSize = 5;
int ConstantIntegerFixedSize = 5;
int ConstantInterfaceMethodRefFixedSize = 5;
int ConstantLongFixedSize = 9;
int ConstantStringFixedSize = 3;
int ConstantUtf8FixedSize = 3;
int ConstantNameAndTypeFixedSize = 5;
int ConstantMethodHandleFixedSize = 4;
int ConstantMethodTypeFixedSize = 3;
int ConstantDynamicFixedSize = 5;
int ConstantInvokeDynamicFixedSize = 5;
int ConstantModuleFixedSize = 3;
int ConstantPackageFixedSize = 3;
// JVMS 4.4.8
int MethodHandleRefKindGetField = 1;
int MethodHandleRefKindGetStatic = 2;
int MethodHandleRefKindPutField = 3;
int MethodHandleRefKindPutStatic = 4;
int MethodHandleRefKindInvokeVirtual = 5;
int MethodHandleRefKindInvokeStatic = 6;
int MethodHandleRefKindInvokeSpecial = 7;
int MethodHandleRefKindNewInvokeSpecial = 8;
int MethodHandleRefKindInvokeInterface = 9;
static int MAJOR_VERSION_1_1 = 45;
static int MAJOR_VERSION_1_2 = 46;
static int MAJOR_VERSION_1_3 = 47;
static int MAJOR_VERSION_1_4 = 48;
static int MAJOR_VERSION_1_5 = 49;
static int MAJOR_VERSION_1_6 = 50;
static int MAJOR_VERSION_1_7 = 51;
static int MAJOR_VERSION_1_8 = 52;
static int MAJOR_VERSION_9 = 53;
static int MAJOR_VERSION_10 = 54;
static int MAJOR_VERSION_11 = 55;
static int MAJOR_VERSION_12 = 56;
static int MAJOR_VERSION_13 = 57;
static int MAJOR_VERSION_14 = 58;
static int MAJOR_VERSION_15 = 59;
static int MAJOR_VERSION_16 = 60;
static int MAJOR_VERSION_0 = 44;
static int MAJOR_LATEST_VERSION = MAJOR_VERSION_16;
static int MINOR_VERSION_0 = 0;
static int MINOR_VERSION_1 = 1;
static int MINOR_VERSION_2 = 2;
static int MINOR_VERSION_3 = 3;
static int MINOR_VERSION_4 = 4;
static int MINOR_VERSION_PREVIEW = 0xffff;
// JDK 1.1 -> 9, comparable value allowing to check both major/minor version at once 1.4.1 > 1.4.0
// 16 unsigned bits for major, then 16 bits for minor
public static long JDK1_1 = ((long)MAJOR_VERSION_1_1 << 16) + MINOR_VERSION_3; // 1.1. is 45.3
public static long JDK1_2 = ((long)MAJOR_VERSION_1_2 << 16) + MINOR_VERSION_0;
public static long JDK1_3 = ((long)MAJOR_VERSION_1_3 << 16) + MINOR_VERSION_0;
public static long JDK1_4 = ((long)MAJOR_VERSION_1_4 << 16) + MINOR_VERSION_0;
public static long JDK1_5 = ((long)MAJOR_VERSION_1_5 << 16) + MINOR_VERSION_0;
public static long JDK1_6 = ((long)MAJOR_VERSION_1_6 << 16) + MINOR_VERSION_0;
public static long JDK1_7 = ((long)MAJOR_VERSION_1_7 << 16) + MINOR_VERSION_0;
public static long JDK1_8 = ((long)MAJOR_VERSION_1_8 << 16) + MINOR_VERSION_0;
public static long JDK9 = ((long)MAJOR_VERSION_9 << 16) + MINOR_VERSION_0;
public static long JDK10 = ((long)MAJOR_VERSION_10 << 16) + MINOR_VERSION_0;
public static long JDK11 = ((long)MAJOR_VERSION_11 << 16) + MINOR_VERSION_0;
public static long JDK12 = ((long)MAJOR_VERSION_12 << 16) + MINOR_VERSION_0;
public static long JDK13 = ((long)MAJOR_VERSION_13 << 16) + MINOR_VERSION_0;
public static long JDK14 = ((long)MAJOR_VERSION_14 << 16) + MINOR_VERSION_0;
public static long JDK15 = ((long)MAJOR_VERSION_15 << 16) + MINOR_VERSION_0;
public static long JDK16 = ((long)MAJOR_VERSION_16 << 16) + MINOR_VERSION_0;
public static long getLatestJDKLevel() {
return ((long)MAJOR_LATEST_VERSION << 16) + MINOR_VERSION_0;
}
/**
* As we move away from declaring every compliance level explicitly (such as JDK11, JDK12 etc.),
* this method can be used to compute the compliance level on the fly for a given Java major version.
*
* @param major Java major version
* @return the compliance level for the given Java version
*/
public static long getComplianceLevelForJavaVersion(int major) {
switch(major) {
case 45: // MAJOR_VERSION_1_1
return ((long)MAJOR_VERSION_1_1 << 16) + MINOR_VERSION_3;
default:
major = Math.min(major, MAJOR_LATEST_VERSION);
return ((long)major << 16) + MINOR_VERSION_0;
}
}
/*
* cldc1.1 is 45.3, but we modify it to be different from JDK1_1.
* In the code gen, we will generate the same target value as JDK1_1
*/
long CLDC_1_1 = ((long)MAJOR_VERSION_1_1 << 16) + MINOR_VERSION_4;
// jdk level used to denote future releases: optional behavior is not enabled for now, but may become so. In order to enable these,
// search for references to this constant, and change it to one of the official JDT constants above.
public static long JDK_DEFERRED = Long.MAX_VALUE;
int INT_ARRAY = 10;
int BYTE_ARRAY = 8;
int BOOLEAN_ARRAY = 4;
int SHORT_ARRAY = 9;
int CHAR_ARRAY = 5;
int LONG_ARRAY = 11;
int FLOAT_ARRAY = 6;
int DOUBLE_ARRAY = 7;
// Debug attributes
int ATTR_SOURCE = 0x1; // SourceFileAttribute
int ATTR_LINES = 0x2; // LineNumberAttribute
int ATTR_VARS = 0x4; // LocalVariableTableAttribute
int ATTR_STACK_MAP_TABLE = 0x8; // Stack map table attribute
int ATTR_STACK_MAP = 0x10; // Stack map attribute: cldc
int ATTR_TYPE_ANNOTATION = 0x20; // type annotation attribute (jsr 308)
int ATTR_METHOD_PARAMETERS = 0x40; // method parameters attribute (jep 118)
// See java.lang.invoke.LambdaMetafactory constants - option bitflags when calling altMetaFactory()
int FLAG_SERIALIZABLE = 0x01;
int FLAG_MARKERS = 0x02;
int FLAG_BRIDGES = 0x04;
}
@@ -0,0 +1,646 @@
/*******************************************************************************
* Copyright (c) 2005, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* David Foerster - patch for toUpperCase as described in https://bugs.eclipse.org/bugs/show_bug.cgi?id=153125
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
public class ScannerHelper {
public final static long[] Bits = {
ASTNode.Bit1, ASTNode.Bit2, ASTNode.Bit3, ASTNode.Bit4, ASTNode.Bit5, ASTNode.Bit6,
ASTNode.Bit7, ASTNode.Bit8, ASTNode.Bit9, ASTNode.Bit10, ASTNode.Bit11, ASTNode.Bit12,
ASTNode.Bit13, ASTNode.Bit14, ASTNode.Bit15, ASTNode.Bit16, ASTNode.Bit17, ASTNode.Bit18,
ASTNode.Bit19, ASTNode.Bit20, ASTNode.Bit21, ASTNode.Bit22, ASTNode.Bit23, ASTNode.Bit24,
ASTNode.Bit25, ASTNode.Bit26, ASTNode.Bit27, ASTNode.Bit28, ASTNode.Bit29, ASTNode.Bit30,
ASTNode.Bit31, ASTNode.Bit32L, ASTNode.Bit33L, ASTNode.Bit34L, ASTNode.Bit35L, ASTNode.Bit36L,
ASTNode.Bit37L, ASTNode.Bit38L, ASTNode.Bit39L, ASTNode.Bit40L, ASTNode.Bit41L, ASTNode.Bit42L,
ASTNode.Bit43L, ASTNode.Bit44L, ASTNode.Bit45L, ASTNode.Bit46L, ASTNode.Bit47L, ASTNode.Bit48L,
ASTNode.Bit49L, ASTNode.Bit50L, ASTNode.Bit51L, ASTNode.Bit52L, ASTNode.Bit53L, ASTNode.Bit54L,
ASTNode.Bit55L, ASTNode.Bit56L, ASTNode.Bit57L, ASTNode.Bit58L, ASTNode.Bit59L, ASTNode.Bit60L,
ASTNode.Bit61L, ASTNode.Bit62L, ASTNode.Bit63L, ASTNode.Bit64L,
};
private static final int START_INDEX = 0;
private static final int PART_INDEX = 1;
private static long[][][] Tables;
private static long[][][] Tables7;
private static long[][][] Tables8;
private static long[][][] Tables9;
private static long[][][] Tables11;
private static long[][][] Tables12;
private static long[][][] Tables13;
private static long[][][] Tables15;
public final static int MAX_OBVIOUS = 128;
public final static int[] OBVIOUS_IDENT_CHAR_NATURES = new int[MAX_OBVIOUS];
public final static int C_JLS_SPACE = ASTNode.Bit9;
public final static int C_SPECIAL = ASTNode.Bit8;
public final static int C_IDENT_START = ASTNode.Bit7;
public final static int C_UPPER_LETTER = ASTNode.Bit6;
public final static int C_LOWER_LETTER = ASTNode.Bit5;
public final static int C_IDENT_PART = ASTNode.Bit4;
public final static int C_DIGIT = ASTNode.Bit3;
public final static int C_SEPARATOR = ASTNode.Bit2;
public final static int C_SPACE = ASTNode.Bit1;
static {
OBVIOUS_IDENT_CHAR_NATURES[0] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[1] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[2] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[3] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[4] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[5] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[6] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[7] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[8] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[14] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[15] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[16] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[17] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[18] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[19] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[20] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[21] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[22] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[23] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[24] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[25] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[26] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[27] = C_IDENT_PART;
OBVIOUS_IDENT_CHAR_NATURES[127] = C_IDENT_PART;
for (int i = '0'; i <= '9'; i++)
OBVIOUS_IDENT_CHAR_NATURES[i] = C_DIGIT | C_IDENT_PART;
for (int i = 'a'; i <= 'z'; i++)
OBVIOUS_IDENT_CHAR_NATURES[i] = C_LOWER_LETTER | C_IDENT_PART | C_IDENT_START;
for (int i = 'A'; i <= 'Z'; i++)
OBVIOUS_IDENT_CHAR_NATURES[i] = C_UPPER_LETTER | C_IDENT_PART | C_IDENT_START;
OBVIOUS_IDENT_CHAR_NATURES['_'] = C_SPECIAL | C_IDENT_PART | C_IDENT_START;
OBVIOUS_IDENT_CHAR_NATURES['$'] = C_SPECIAL | C_IDENT_PART | C_IDENT_START;
OBVIOUS_IDENT_CHAR_NATURES[9] = C_SPACE | C_JLS_SPACE; // \ u0009: HORIZONTAL TABULATION
OBVIOUS_IDENT_CHAR_NATURES[10] = C_SPACE | C_JLS_SPACE; // \ u000a: LINE FEED
OBVIOUS_IDENT_CHAR_NATURES[11] = C_SPACE;
OBVIOUS_IDENT_CHAR_NATURES[12] = C_SPACE | C_JLS_SPACE; // \ u000c: FORM FEED
OBVIOUS_IDENT_CHAR_NATURES[13] = C_SPACE | C_JLS_SPACE; // \ u000d: CARRIAGE RETURN
OBVIOUS_IDENT_CHAR_NATURES[28] = C_SPACE;
OBVIOUS_IDENT_CHAR_NATURES[29] = C_SPACE;
OBVIOUS_IDENT_CHAR_NATURES[30] = C_SPACE;
OBVIOUS_IDENT_CHAR_NATURES[31] = C_SPACE;
OBVIOUS_IDENT_CHAR_NATURES[32] = C_SPACE | C_JLS_SPACE; // \ u0020: SPACE
OBVIOUS_IDENT_CHAR_NATURES['.'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES[':'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES[';'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES[','] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['['] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES[']'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['('] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES[')'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['{'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['}'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['+'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['-'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['*'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['/'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['='] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['&'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['|'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['?'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['<'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['>'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['!'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['%'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['^'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['~'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['"'] = C_SEPARATOR;
OBVIOUS_IDENT_CHAR_NATURES['\''] = C_SEPARATOR;
}
static void initializeTable() {
Tables = initializeTables("unicode"); //$NON-NLS-1$
}
static void initializeTable17() {
Tables7 = initializeTables("unicode6"); //$NON-NLS-1$
}
static void initializeTable18() {
Tables8 = initializeTables("unicode6_2"); //$NON-NLS-1$
}
static void initializeTable19() {
Tables9 = initializeTables("unicode8"); //$NON-NLS-1$
}
static void initializeTableJava11() {
Tables11 = initializeTables("unicode10"); //$NON-NLS-1$
}
static void initializeTableJava12() {
Tables12 = initializeTables("unicode11"); //$NON-NLS-1$
}
static void initializeTableJava13() {
Tables13 = initializeTables("unicode12_1"); //$NON-NLS-1$
}
static void initializeTableJava15() {
Tables15 = initializeTables13andPlus("unicode13"); //$NON-NLS-1$
}
static long[][][] initializeTables(String unicode_path) {
long[][][] tempTable = new long[2][][];
tempTable[START_INDEX] = new long[3][];
tempTable[PART_INDEX] = new long[4][];
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/start0.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[START_INDEX][0] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/start1.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[START_INDEX][1] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/start2.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[START_INDEX][2] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part0.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][0] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part1.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][1] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part2.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][2] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part14.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][3] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
return tempTable;
}
static long[][][] initializeTables13andPlus(String unicode_path) {
long[][][] tempTable = new long[2][][];
tempTable[START_INDEX] = new long[4][];
tempTable[PART_INDEX] = new long[5][];
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/start0.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[START_INDEX][0] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/start1.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[START_INDEX][1] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/start2.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[START_INDEX][2] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/start3.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[START_INDEX][3] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part0.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][0] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part1.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][1] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part2.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][2] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part3.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][3] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(ScannerHelper.class.getResourceAsStream(unicode_path + "/part14.rsc")))) { //$NON-NLS-1$
long[] readValues = new long[1024];
for (int i = 0; i < 1024; i++) {
readValues[i] = inputStream.readLong();
}
tempTable[PART_INDEX][4] = readValues;
} catch (IOException e) {
e.printStackTrace();
}
return tempTable;
}
private final static boolean isBitSet(long[] values, int i) {
try {
return (values[i / 64] & Bits[i % 64]) != 0;
} catch (NullPointerException e) {
return false;
}
}
public static boolean isJavaIdentifierPart(char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_PART) != 0;
}
return Character.isJavaIdentifierPart(c);
}
public static boolean isJavaIdentifierPart(long complianceLevel, char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_PART) != 0;
}
return isJavaIdentifierPart(complianceLevel, (int) c);
}
private static boolean isJavaIdentifierPart0(int codePoint, long[][][] tables) {
return isJavaIdentifierPart0(codePoint, tables, false);
}
private static boolean isJavaIdentifierPart0(int codePoint, long[][][] tables, boolean isJava15orAbove) {
switch((codePoint & 0x1F0000) >> 16) {
case 0 :
return isBitSet(tables[PART_INDEX][0], codePoint & 0xFFFF);
case 1 :
return isBitSet(tables[PART_INDEX][1], codePoint & 0xFFFF);
case 2 :
return isBitSet(tables[PART_INDEX][2], codePoint & 0xFFFF);
case 3 :
if (isJava15orAbove) {
return isBitSet(tables[PART_INDEX][3], codePoint & 0xFFFF);
}
return false;
case 14 :
if (isJava15orAbove) {
return isBitSet(tables[PART_INDEX][4], codePoint & 0xFFFF);
}
return isBitSet(tables[PART_INDEX][3], codePoint & 0xFFFF);
}
return false;
}
public static boolean isJavaIdentifierPart(long complianceLevel, int codePoint) {
if (complianceLevel <= ClassFileConstants.JDK1_6) {
if (Tables == null) {
initializeTable();
}
return isJavaIdentifierPart0(codePoint, Tables);
} else if (complianceLevel <= ClassFileConstants.JDK1_7) {
// java 7 supports Unicode 6
if (Tables7 == null) {
initializeTable17();
}
return isJavaIdentifierPart0(codePoint, Tables7);
} else if (complianceLevel <= ClassFileConstants.JDK1_8) {
// java 8 supports Unicode 6.2
if (Tables8 == null) {
initializeTable18();
}
return isJavaIdentifierPart0(codePoint, Tables8);
} else if (complianceLevel <= ClassFileConstants.JDK10) {
// java 9/10 supports Unicode 8
if (Tables9 == null) {
initializeTable19();
}
return isJavaIdentifierPart0(codePoint, Tables9);
} else if (complianceLevel <= ClassFileConstants.JDK11) {
// java 11 supports Unicode 10
if (Tables11 == null) {
initializeTableJava11();
}
return isJavaIdentifierPart0(codePoint, Tables11);
} else if (complianceLevel <= ClassFileConstants.JDK12) {
// java 12 supports Unicode 11
if (Tables12 == null) {
initializeTableJava12();
}
return isJavaIdentifierPart0(codePoint, Tables12);
} else if (complianceLevel <= ClassFileConstants.JDK14) {
// java 13 and 14 support Unicode 12.1
if (Tables13 == null) {
initializeTableJava13();
}
return isJavaIdentifierPart0(codePoint, Tables13);
} else {
// java 15 supports Unicode 13
if (Tables15 == null) {
initializeTableJava15();
}
return isJavaIdentifierPart0(codePoint, Tables15, true);
}
}
public static boolean isJavaIdentifierStart(char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0;
}
return Character.isJavaIdentifierStart(c);
}
public static boolean isJavaIdentifierStart(long complianceLevel, char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0;
}
return ScannerHelper.isJavaIdentifierStart(complianceLevel, (int) c);
}
private static boolean isJavaIdentifierStart0(int codePoint, long[][][] tables) {
return isJavaIdentifierStart0(codePoint, tables, false);
}
private static boolean isJavaIdentifierStart0(int codePoint, long[][][] tables, boolean isJava15orAbove) {
switch((codePoint & 0x1F0000) >> 16) {
case 0 :
return isBitSet(tables[START_INDEX][0], codePoint & 0xFFFF);
case 1 :
return isBitSet(tables[START_INDEX][1], codePoint & 0xFFFF);
case 2 :
return isBitSet(tables[START_INDEX][2], codePoint & 0xFFFF);
case 3 :
if (isJava15orAbove)
return isBitSet(tables[START_INDEX][3], codePoint & 0xFFFF);
return false;
}
return false;
}
public static boolean isJavaIdentifierStart(long complianceLevel, int codePoint) {
if (complianceLevel <= ClassFileConstants.JDK1_6) {
if (Tables == null) {
initializeTable();
}
return isJavaIdentifierStart0(codePoint, Tables);
} else if (complianceLevel <= ClassFileConstants.JDK1_7) {
// java 7 supports Unicode 6
if (Tables7 == null) {
initializeTable17();
}
return isJavaIdentifierStart0(codePoint, Tables7);
} else if (complianceLevel <= ClassFileConstants.JDK1_8) {
// java 8 supports Unicode 6.2
if (Tables8 == null) {
initializeTable18();
}
return isJavaIdentifierStart0(codePoint, Tables8);
} else if (complianceLevel <= ClassFileConstants.JDK10) {
// java 9/10 supports Unicode 8
if (Tables9 == null) {
initializeTable19();
}
return isJavaIdentifierStart0(codePoint, Tables9);
} else if (complianceLevel <= ClassFileConstants.JDK11) {
// java 11 supports Unicode 10
if (Tables11 == null) {
initializeTableJava11();
}
return isJavaIdentifierStart0(codePoint, Tables11);
} else if (complianceLevel <= ClassFileConstants.JDK12) {
// java 12 supports Unicode 11
if (Tables12 == null) {
initializeTableJava12();
}
return isJavaIdentifierStart0(codePoint, Tables12);
} else if (complianceLevel <= ClassFileConstants.JDK14) {
// java 13 and 14 support Unicode 12.1
if (Tables13 == null) {
initializeTableJava13();
}
return isJavaIdentifierStart0(codePoint, Tables13);
} else {
// java 15 supports Unicode 13
if (Tables15 == null) {
initializeTableJava15();
}
return isJavaIdentifierStart0(codePoint, Tables15, true);
}
}
public static boolean isDigit(char c) throws InvalidInputException {
if(c < ScannerHelper.MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_DIGIT) != 0;
}
return Character.isDigit(c);
}
public static int digit(char c, int radix) {
if (c < ScannerHelper.MAX_OBVIOUS) {
switch(radix) {
case 8 :
if (c >= 48 && c <= 55) {
return c - 48;
}
return -1;
case 10 :
if (c >= 48 && c <= 57) {
return c - 48;
}
return -1;
case 16 :
if (c >= 48 && c <= 57) {
return c - 48;
}
if (c >= 65 && c <= 70) {
return c - 65 + 10;
}
if (c >= 97 && c <= 102) {
return c - 97 + 10;
}
return -1;
}
}
return Character.digit(c, radix);
}
public static int getNumericValue(char c) {
if (c < ScannerHelper.MAX_OBVIOUS) {
switch(ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c]) {
case C_DIGIT :
return c - '0';
case C_LOWER_LETTER :
return 10 + c - 'a';
case C_UPPER_LETTER :
return 10 + c - 'A';
}
}
return Character.getNumericValue(c);
}
public static int getHexadecimalValue(char c) {
switch(c) {
case '0' :
return 0;
case '1' :
return 1;
case '2' :
return 2;
case '3' :
return 3;
case '4' :
return 4;
case '5' :
return 5;
case '6' :
return 6;
case '7' :
return 7;
case '8' :
return 8;
case '9' :
return 9;
case 'A' :
case 'a' :
return 10;
case 'B' :
case 'b' :
return 11;
case 'C' :
case 'c' :
return 12;
case 'D' :
case 'd' :
return 13;
case 'E' :
case 'e' :
return 14;
case 'F' :
case 'f' :
return 15;
default:
return -1;
}
}
public static char toUpperCase(char c) {
if (c < MAX_OBVIOUS) {
if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_UPPER_LETTER) != 0) {
return c;
} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_LOWER_LETTER) != 0) {
return (char) (c - 32);
}
}
return Character.toUpperCase(c);
}
public static char toLowerCase(char c) {
if (c < MAX_OBVIOUS) {
if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_LOWER_LETTER) != 0) {
return c;
} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_UPPER_LETTER) != 0) {
return (char) (32 + c);
}
}
return Character.toLowerCase(c);
}
public static boolean isLowerCase(char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_LOWER_LETTER) != 0;
}
return Character.isLowerCase(c);
}
public static boolean isUpperCase(char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_UPPER_LETTER) != 0;
}
return Character.isUpperCase(c);
}
/**
* Include also non JLS whitespaces.
*
* return true if Character.isWhitespace(c) would return true
*/
public static boolean isWhitespace(char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_SPACE) != 0;
}
return Character.isWhitespace(c);
}
public static boolean isLetter(char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER)) != 0;
}
return Character.isLetter(c);
}
public static boolean isLetterOrDigit(char c) {
if (c < MAX_OBVIOUS) {
return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_DIGIT)) != 0;
}
return Character.isLetterOrDigit(c);
}
public static class InvalidInputException extends RuntimeException {}
}
@@ -0,0 +1,653 @@
package org.eclipse.jdt.internal.compiler.util;
import org.eclipse.jdt.core.compiler.CharOperation;
public class Util {
/**
* Character constant indicating the primitive type boolean in a signature.
* Value is <code>'Z'</code>.
*/
public static final char C_BOOLEAN = 'Z';
/**
* Character constant indicating the primitive type byte in a signature.
* Value is <code>'B'</code>.
*/
public static final char C_BYTE = 'B';
/**
* Character constant indicating the primitive type char in a signature.
* Value is <code>'C'</code>.
*/
public static final char C_CHAR = 'C';
/**
* Character constant indicating the primitive type double in a signature.
* Value is <code>'D'</code>.
*/
public static final char C_DOUBLE = 'D';
/**
* Character constant indicating the primitive type float in a signature.
* Value is <code>'F'</code>.
*/
public static final char C_FLOAT = 'F';
/**
* Character constant indicating the primitive type int in a signature.
* Value is <code>'I'</code>.
*/
public static final char C_INT = 'I';
/**
* Character constant indicating the semicolon in a signature.
* Value is <code>';'</code>.
*/
public static final char C_SEMICOLON = ';';
/**
* Character constant indicating the colon in a signature.
* Value is <code>':'</code>.
* @since 3.0
*/
public static final char C_COLON = ':';
/**
* Character constant indicating the primitive type long in a signature.
* Value is <code>'J'</code>.
*/
public static final char C_LONG = 'J';
/**
* Character constant indicating the primitive type short in a signature.
* Value is <code>'S'</code>.
*/
public static final char C_SHORT = 'S';
/**
* Character constant indicating result type void in a signature.
* Value is <code>'V'</code>.
*/
public static final char C_VOID = 'V';
/**
* Character constant indicating the start of a resolved type variable in a
* signature. Value is <code>'T'</code>.
* @since 3.0
*/
public static final char C_TYPE_VARIABLE = 'T';
/**
* Character constant indicating an unbound wildcard type argument
* in a signature.
* Value is <code>'*'</code>.
* @since 3.0
*/
public static final char C_STAR = '*';
/**
* Character constant indicating an exception in a signature.
* Value is <code>'^'</code>.
* @since 3.1
*/
public static final char C_EXCEPTION_START = '^';
/**
* Character constant indicating a bound wildcard type argument
* in a signature with extends clause.
* Value is <code>'+'</code>.
* @since 3.1
*/
public static final char C_EXTENDS = '+';
/**
* Character constant indicating a bound wildcard type argument
* in a signature with super clause.
* Value is <code>'-'</code>.
* @since 3.1
*/
public static final char C_SUPER = '-';
/**
* Character constant indicating the dot in a signature.
* Value is <code>'.'</code>.
*/
public static final char C_DOT = '.';
/**
* Character constant indicating the dollar in a signature.
* Value is <code>'$'</code>.
*/
public static final char C_DOLLAR = '$';
/**
* Character constant indicating an array type in a signature.
* Value is <code>'['</code>.
*/
public static final char C_ARRAY = '[';
/**
* Character constant indicating the start of a resolved, named type in a
* signature. Value is <code>'L'</code>.
*/
public static final char C_RESOLVED = 'L';
/**
* Character constant indicating the start of an unresolved, named type in a
* signature. Value is <code>'Q'</code>.
*/
public static final char C_UNRESOLVED = 'Q';
/**
* Character constant indicating the end of a named type in a signature.
* Value is <code>';'</code>.
*/
public static final char C_NAME_END = ';';
/**
* Character constant indicating the start of a parameter type list in a
* signature. Value is <code>'('</code>.
*/
public static final char C_PARAM_START = '(';
/**
* Character constant indicating the end of a parameter type list in a
* signature. Value is <code>')'</code>.
*/
public static final char C_PARAM_END = ')';
/**
* Character constant indicating the start of a formal type parameter
* (or type argument) list in a signature. Value is <code>'&lt;'</code>.
* @since 3.0
*/
public static final char C_GENERIC_START = '<';
/**
* Character constant indicating the end of a generic type list in a
* signature. Value is <code>'&gt;'</code>.
* @since 3.0
*/
public static final char C_GENERIC_END = '>';
/**
* Character constant indicating a capture of a wildcard type in a
* signature. Value is <code>'!'</code>.
* @since 3.1
*/
public static final char C_CAPTURE = '!';
public static final String EMPTY_STRING = new String(CharOperation.NO_CHAR);
public static int getParameterCount(char[] methodSignature) {
try {
int count = 0;
int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
if (i < 0) {
throw new IllegalArgumentException(String.valueOf(methodSignature));
} else {
i++;
}
for (;;) {
if (methodSignature[i] == C_PARAM_END) {
return count;
}
int e= Util.scanTypeSignature(methodSignature, i);
if (e < 0) {
throw new IllegalArgumentException(String.valueOf(methodSignature));
} else {
i = e + 1;
}
count++;
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(String.valueOf(methodSignature), e);
}
}
/**
* Scans the given string for a type signature starting at the given index
* and returns the index of the last character.
* <pre>
* TypeSignature:
* | BaseTypeSignature
* | ArrayTypeSignature
* | ClassTypeSignature
* | TypeVariableSignature
* </pre>
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a type signature
*/
public static int scanTypeSignature(char[] string, int start) {
// need a minimum 1 char
if (start >= string.length) {
throw newIllegalArgumentException(string, start);
}
char c = string[start];
switch (c) {
case C_ARRAY :
return scanArrayTypeSignature(string, start);
case C_RESOLVED :
case C_UNRESOLVED :
return scanClassTypeSignature(string, start);
case C_TYPE_VARIABLE :
return scanTypeVariableSignature(string, start);
case C_BOOLEAN :
case C_BYTE :
case C_CHAR :
case C_DOUBLE :
case C_FLOAT :
case C_INT :
case C_LONG :
case C_SHORT :
case C_VOID :
return scanBaseTypeSignature(string, start);
case C_CAPTURE :
return scanCaptureTypeSignature(string, start);
case C_EXTENDS:
case C_SUPER:
case C_STAR:
return scanTypeBoundSignature(string, start);
default :
throw newIllegalArgumentException(string, start);
}
}
/**
* Scans the given string for a base type signature starting at the given index
* and returns the index of the last character.
* <pre>
* BaseTypeSignature:
* <b>B</b> | <b>C</b> | <b>D</b> | <b>F</b> | <b>I</b>
* | <b>J</b> | <b>S</b> | <b>V</b> | <b>Z</b>
* </pre>
* Note that although the base type "V" is only allowed in method return types,
* there is no syntactic ambiguity. This method will accept them anywhere
* without complaint.
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a base type signature
*/
public static int scanBaseTypeSignature(char[] string, int start) {
// need a minimum 1 char
if (start >= string.length) {
throw newIllegalArgumentException(string, start);
}
char c = string[start];
if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$
return start;
} else {
throw newIllegalArgumentException(string, start);
}
}
/**
* Scans the given string for an array type signature starting at the given
* index and returns the index of the last character.
* <pre>
* ArrayTypeSignature:
* <b>[</b> TypeSignature
* </pre>
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not an array type signature
*/
public static int scanArrayTypeSignature(char[] string, int start) {
int length = string.length;
// need a minimum 2 char
if (start >= length - 1) {
throw newIllegalArgumentException(string, start);
}
char c = string[start];
if (c != C_ARRAY) {
throw newIllegalArgumentException(string, start);
}
c = string[++start];
while(c == C_ARRAY) {
// need a minimum 2 char
if (start >= length - 1) {
throw newIllegalArgumentException(string, start);
}
c = string[++start];
}
return scanTypeSignature(string, start);
}
/**
* Scans the given string for a capture of a wildcard type signature starting at the given
* index and returns the index of the last character.
* <pre>
* CaptureTypeSignature:
* <b>!</b> TypeBoundSignature
* </pre>
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a capture type signature
*/
public static int scanCaptureTypeSignature(char[] string, int start) {
// need a minimum 2 char
if (start >= string.length - 1) {
throw newIllegalArgumentException(string, start);
}
char c = string[start];
if (c != C_CAPTURE) {
throw newIllegalArgumentException(string, start);
}
return scanTypeBoundSignature(string, start + 1);
}
/**
* Scans the given string for a type variable signature starting at the given
* index and returns the index of the last character.
* <pre>
* TypeVariableSignature:
* <b>T</b> Identifier <b>;</b>
* </pre>
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a type variable signature
*/
public static int scanTypeVariableSignature(char[] string, int start) {
// need a minimum 3 chars "Tx;"
if (start >= string.length - 2) {
throw newIllegalArgumentException(string, start);
}
// must start in "T"
char c = string[start];
if (c != C_TYPE_VARIABLE) {
throw newIllegalArgumentException(string, start);
}
int id = scanIdentifier(string, start + 1);
c = string[id + 1];
if (c == C_SEMICOLON) {
return id + 1;
} else {
throw newIllegalArgumentException(string, start);
}
}
/**
* Scans the given string for an identifier starting at the given
* index and returns the index of the last character.
* Stop characters are: ";", ":", "&lt;", "&gt;", "/", ".".
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not an identifier
*/
public static int scanIdentifier(char[] string, int start) {
// need a minimum 1 char
if (start >= string.length) {
throw newIllegalArgumentException(string, start);
}
int p = start;
while (true) {
char c = string[p];
if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') {
return p - 1;
}
p++;
if (p == string.length) {
return p - 1;
}
}
}
/**
* Scans the given string for a class type signature starting at the given
* index and returns the index of the last character.
* <pre>
* ClassTypeSignature:
* { <b>L</b> | <b>Q</b> } Identifier
* { { <b>/</b> | <b>.</b> Identifier [ <b>&lt;</b> TypeArgumentSignature* <b>&gt;</b> ] }
* <b>;</b>
* </pre>
* Note that although all "/"-identifiers most come before "."-identifiers,
* there is no syntactic ambiguity. This method will accept them without
* complaint.
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a class type signature
*/
public static int scanClassTypeSignature(char[] string, int start) {
// need a minimum 3 chars "Lx;"
if (start >= string.length - 2) {
throw newIllegalArgumentException(string, start);
}
// must start in "L" or "Q"
char c = string[start];
if (c != C_RESOLVED && c != C_UNRESOLVED) {
return -1;
}
int p = start + 1;
while (true) {
if (p >= string.length) {
throw newIllegalArgumentException(string, start);
}
c = string[p];
if (c == C_SEMICOLON) {
// all done
return p;
} else if (c == C_GENERIC_START) {
int e = scanTypeArgumentSignatures(string, p);
p = e;
} else if (c == C_DOT || c == '/') {
int id = scanIdentifier(string, p + 1);
p = id;
}
p++;
}
}
/**
* Scans the given string for a type bound signature starting at the given
* index and returns the index of the last character.
* <pre>
* TypeBoundSignature:
* <b>[-+]</b> TypeSignature <b>;</b>
* <b>*</b></b>
* </pre>
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a type variable signature
*/
public static int scanTypeBoundSignature(char[] string, int start) {
// need a minimum 1 char for wildcard
if (start >= string.length) {
throw newIllegalArgumentException(string, start);
}
char c = string[start];
switch (c) {
case C_STAR :
return start;
case C_SUPER :
case C_EXTENDS :
break;
default :
// must start in "+/-"
throw newIllegalArgumentException(string, start);
}
c = string[++start];
if (c != C_STAR && start >= string.length -1) { // unless "-*" we need at least one more char, e.g. after "+[", other variants are even longer
throw new IllegalArgumentException();
}
switch (c) {
case C_CAPTURE :
return scanCaptureTypeSignature(string, start);
case C_SUPER :
case C_EXTENDS :
return scanTypeBoundSignature(string, start);
case C_RESOLVED :
case C_UNRESOLVED :
return scanClassTypeSignature(string, start);
case C_TYPE_VARIABLE :
return scanTypeVariableSignature(string, start);
case C_ARRAY :
return scanArrayTypeSignature(string, start);
case C_STAR:
return start;
default:
throw newIllegalArgumentException(string, start);
}
}
/**
* Scans the given string for a list of type argument signatures starting at
* the given index and returns the index of the last character.
* <pre>
* TypeArgumentSignatures:
* <b>&lt;</b> TypeArgumentSignature* <b>&gt;</b>
* </pre>
* Note that although there is supposed to be at least one type argument, there
* is no syntactic ambiguity if there are none. This method will accept zero
* type argument signatures without complaint.
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a list of type arguments
* signatures
*/
public static int scanTypeArgumentSignatures(char[] string, int start) {
// need a minimum 2 char "<>"
if (start >= string.length - 1) {
throw newIllegalArgumentException(string, start);
}
char c = string[start];
if (c != C_GENERIC_START) {
throw newIllegalArgumentException(string, start);
}
int p = start + 1;
while (true) {
if (p >= string.length) {
throw newIllegalArgumentException(string, start);
}
c = string[p];
if (c == C_GENERIC_END) {
return p;
}
int e = scanTypeArgumentSignature(string, p);
p = e + 1;
}
}
/**
* Scans the given string for a type argument signature starting at the given
* index and returns the index of the last character.
* <pre>
* TypeArgumentSignature:
* <b>&#42;</b>
* | <b>+</b> TypeSignature
* | <b>-</b> TypeSignature
* | TypeSignature
* </pre>
* Note that although base types are not allowed in type arguments, there is
* no syntactic ambiguity. This method will accept them without complaint.
*
* @param string the signature string
* @param start the 0-based character index of the first character
* @return the 0-based character index of the last character
* @exception IllegalArgumentException if this is not a type argument signature
*/
public static int scanTypeArgumentSignature(char[] string, int start) {
// need a minimum 1 char
if (start >= string.length) {
throw newIllegalArgumentException(string, start);
}
char c = string[start];
switch (c) {
case C_STAR :
return start;
case C_EXTENDS :
case C_SUPER :
return scanTypeBoundSignature(string, start);
default :
return scanTypeSignature(string, start);
}
}
public static boolean effectivelyEqual(Object [] one, Object [] two) {
if (one == two)
return true;
int oneLength = one == null ? 0 : one.length;
int twoLength = two == null ? 0 : two.length;
if (oneLength != twoLength)
return false;
if (oneLength == 0)
return true;
for (int i = 0; i < one.length; i++) {
if (one[i] != two[i])
return false;
}
return true;
}
public static void appendEscapedChar(StringBuffer buffer, char c, boolean stringLiteral) {
switch (c) {
case '\b' :
buffer.append("\\b"); //$NON-NLS-1$
break;
case '\t' :
buffer.append("\\t"); //$NON-NLS-1$
break;
case '\n' :
buffer.append("\\n"); //$NON-NLS-1$
break;
case '\f' :
buffer.append("\\f"); //$NON-NLS-1$
break;
case '\r' :
buffer.append("\\r"); //$NON-NLS-1$
break;
case '\"':
if (stringLiteral) {
buffer.append("\\\""); //$NON-NLS-1$
} else {
buffer.append(c);
}
break;
case '\'':
if (stringLiteral) {
buffer.append(c);
} else {
buffer.append("\\\'"); //$NON-NLS-1$
}
break;
case '\\':
buffer.append("\\\\"); //$NON-NLS-1$
break;
default:
if (c >= 0x20) {
buffer.append(c);
} else if (c >= 0x10) {
buffer.append("\\u00").append(Integer.toHexString(c)); //$NON-NLS-1$
} else if (c >= 0) {
buffer.append("\\u000").append(Integer.toHexString(c)); //$NON-NLS-1$
} else {
buffer.append(c);
}
}
}
private static IllegalArgumentException newIllegalArgumentException(char[] string, int start) {
return new IllegalArgumentException("\"" + String.valueOf(string) + "\" at " + start); //$NON-NLS-1$ //$NON-NLS-2$
}
}
+1
View File
@@ -130,6 +130,7 @@ dependencies {
implementation 'com.google.firebase:firebase-messaging'
// Local projects
implementation project(':api-info')
implementation project(':treeview')
implementation project(':lsp')
implementation project(':emulator')
@@ -705,28 +705,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
invalidateOptionsMenu();
}
@Override
public void onGetDependencies(List<String> dependencies) {
setStatus(getString(R.string.msg_starting_completion));
mProject.setClassPaths(dependencies);
createServices();
}
@Override
public void onGetDependenciesFailed() {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog);
builder.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, (p1, p2) -> {
p1.dismiss();
if(getBuildService() != null)
getBuildService().initProject();
})
.setTitle(R.string.failed)
.setMessage(R.string.msg_first_prepare_failed)
.create().show();
}
@Override
public void onProjectLoaded(IDEProject project, Optional<IDEModule> appModule) {
project.iconPath = mProject.getIconPath();
@@ -751,8 +730,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
@Override
public void notifyBuildModified() {
public void onBuildModified() {
// Does nothing currently
}
private boolean isClasspathValid(String path) {
@@ -7,7 +7,9 @@ import android.view.View;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivitySplashBinding;
import com.itsaky.androidide.models.ConstantsBridge;
import com.itsaky.androidide.models.ConstantsBridge;
import android.text.TextUtils;
import org.eclipse.jdt.core.Signature;
public class SplashActivity extends StudioActivity
{
@@ -6,18 +6,39 @@ import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutCompletionItemBinding;
import com.itsaky.androidide.models.CompletionItemWrapper;
import com.itsaky.androidide.models.CompletionListItem;
import com.itsaky.androidide.models.SuggestItem;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.utils.Either;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.apiinfo.ApiInfo;
import com.itsaky.apiinfo.models.ClassInfo;
import com.itsaky.apiinfo.models.MethodInfo;
import com.itsaky.lsp.CompletionItemKind;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.widget.EditorCompletionAdapter;
import java.util.Locale;
import com.google.gson.JsonArray;
import com.itsaky.apiinfo.models.FieldInfo;
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.utils.Logger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.itsaky.apiinfo.models.Info;
import android.text.TextUtils;
public class CompletionListAdapter extends EditorCompletionAdapter {
private static final Logger LOG = Logger.instance("CompletionListAdapter");
@Override
public int getItemHeight() {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, Resources.getSystem().getDisplayMetrics());
@@ -43,19 +64,139 @@ public class CompletionListAdapter extends EditorCompletionAdapter {
type = item.getItemType().contains(".") ? item.getItemType().substring(item.getItemType().lastIndexOf(".") + 1) : item.getItemType();
desc = item.getType() != CompletionListItem.Type.NOT_IMPORTED_CLASS ? item.getDetail() : getContext().getString(R.string.label_not_imported, item.getDetail());
}
binding.completionIconText.setText(header);
binding.completionLabel.setText(label);
binding.completionType.setText(type);
binding.completionDetail.setText(desc);
binding.completionIconText.setTypeface(TypefaceUtils.jetbrainsMono(), Typeface.BOLD);
if(desc == null || desc.isEmpty())
if (desc == null || desc.isEmpty())
binding.completionDetail.setVisibility(View.GONE);
if (isCurrentCursorPosition)
binding.getRoot().setBackgroundColor(ContextCompat.getColor(getContext(), R.color.completionList_backgroundSelected));
else binding.getRoot().setBackgroundColor(ContextCompat.getColor(getContext(), R.color.completionList_background));
binding.completionApiInfo.setVisibility(View.GONE);
if (e != null && e.isLeft() && e.getLeft() instanceof CompletionItemWrapper) {
showApiInfoIfNeeded((CompletionItemWrapper) e.getLeft(), binding.completionApiInfo);
}
binding.getRoot().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
ViewGroup.LayoutParams p = binding.completionIconText.getLayoutParams();
p.height = binding.getRoot().getHeight();
binding.completionIconText.setLayoutParams(p);
});
return binding.getRoot();
}
private void showApiInfoIfNeeded(final CompletionItemWrapper item, final TextView completionApiInfo) {
new Thread(() -> {
final ApiInfo info = StudioApp.getInstance().getApiInfo();
boolean hasRead = info != null && info.hasRead();
boolean isValid = isValidForApiVersion(item.getItem());
if (hasRead && isValid) {
JsonElement element = item.getItem().data;
if (element == null || !element.isJsonObject()) return;
JsonObject data = element.getAsJsonObject();
if (!data.has("className")) return;
final String className = data.get("className").getAsString();
int kind = item.getItem().kind;
ClassInfo clazz = info.getClassByName(className);
if(clazz == null) return;
Info apiInfo = clazz;
/**
* If this Info is not a class info, find the right member
*/
if (kind == CompletionItemKind.Method
&& data.has("erasedParameterTypes")
&& data.has("memberName")) {
JsonElement erasedParameterTypesElement = data.get("erasedParameterTypes");
if(erasedParameterTypesElement.isJsonArray()) {
String simpleName = data.get("memberName").getAsString();
JsonArray erasedParameterTypes = erasedParameterTypesElement.getAsJsonArray();
String[] paramTypes = new String[erasedParameterTypes.size()];
for(int i=0;i<erasedParameterTypes.size();i++) {
paramTypes[i] = erasedParameterTypes.get(i).getAsString();
}
MethodInfo method = clazz.getMethod(simpleName, paramTypes);
if(method != null) {
apiInfo = method;
}
}
} else if(kind == CompletionItemKind.Field
&& data.has("memberName")) {
String simpleName = data.get("memberName").getAsString();
FieldInfo field = clazz.getFieldByName(simpleName);
if(field != null) {
apiInfo = field;
}
}
final StringBuilder infoBuilder = new StringBuilder();
if (apiInfo != null && apiInfo.since > 1) {
infoBuilder.append(completionApiInfo.getContext().getString(R.string.msg_api_info_since, apiInfo.since));
}
if (apiInfo != null && apiInfo.removed > 0) {
infoBuilder.append(completionApiInfo.getContext().getString(R.string.msg_api_info_removed, apiInfo.removed));
}
if (apiInfo != null && apiInfo.deprecated > 0) {
infoBuilder.append(completionApiInfo.getContext().getString(R.string.msg_api_info_deprecated, apiInfo.deprecated));
}
ThreadUtils.runOnUiThread(() -> {
if(infoBuilder.toString().trim().length() > 0) {
completionApiInfo.setText(infoBuilder.toString().trim());
completionApiInfo.setVisibility(View.VISIBLE);
} else completionApiInfo.setVisibility(View.GONE);
});
}
}).start();
}
private boolean isValidForApiVersion(com.itsaky.lsp.CompletionItem item) {
if (item == null) return false;
final int type = item.kind;
JsonElement element = item.data;
if (
/**
* These represent a class type
*/
(type == CompletionItemKind.Class
|| type == CompletionItemKind.Interface
|| type == CompletionItemKind.Enum
/**
* These represent a method type
*/
|| type == CompletionItemKind.Method
|| type == CompletionItemKind.Constructor
/**
* A field type
*/
|| type == CompletionItemKind.Field)
&& element != null && element.isJsonObject()) {
JsonObject data = element.getAsJsonObject();
return data.has("className");
}
return false;
}
}
@@ -42,6 +42,7 @@ import java.util.Locale;
import com.blankj.utilcode.util.FileUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import com.itsaky.apiinfo.ApiInfo;
public class StudioApp extends MultiDexApplication
{
@@ -56,6 +57,8 @@ public class StudioApp extends MultiDexApplication
private boolean stopGradleDaemon = true;
private String sArch = "";
private ApiInfo mApiInfo;
private List<LanguageServerListener> serveralisteners = new ArrayList<>();
public static final boolean DEBUG = com.itsaky.androidide.BuildConfig.DEBUG;
@@ -81,7 +84,6 @@ public class StudioApp extends MultiDexApplication
this.instance = this;
this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> handleCrash(thread, ex));
initLogger();
super.onCreate();
mPrefsManager = new PreferenceManager(this);
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
@@ -143,10 +145,23 @@ public class StudioApp extends MultiDexApplication
private String getDevNotificationChannelName() {
return getString(R.string.cms_channel_id_devs);
}
private void initLogger() {
}
/**
* Reads API version information from api-versions.xml
*/
public void initializeApiInformation() {
if(mApiInfo == null || !mApiInfo.hasRead()) {
File apiVersions = new File(Environment.BOOTCLASSPATH.getParentFile(), "data/api-versions.xml");
if(apiVersions.exists() && apiVersions.isFile()) {
mApiInfo = new ApiInfo(apiVersions);
try {
mApiInfo.readAsync(null);
} catch (Exception e) {
LOG.error("Failed to read API version information", e);
}
}
}
}
public void stopAllDaemons() {
newShell(null).bgAppend("gradle --stop");
@@ -177,6 +192,10 @@ public class StudioApp extends MultiDexApplication
public XMLCompletionService getXmlCompletionService() {
return mXmlCompletionService;
}
public ApiInfo getApiInfo() {
return mApiInfo;
}
public boolean areCompletorsStarted() {
return mXmlCompletionService != null
@@ -106,7 +106,6 @@ public class CompletionItemWrapper implements SuggestItem, Comparable {
CharPosition startPos = content.getIndexer().getCharPosition(start);
CharPosition endPos = content.getIndexer().getCharPosition(end);
Logger.instance("CompletionItemWrapper").debug("PartialIdentifer Range: " + start + "," + end + ", text: " + content.subSequence(start, end));
return new Range(new Position(startPos.line, startPos.column), new Position(endPos.line, endPos.column));
}
@@ -130,9 +129,7 @@ public class CompletionItemWrapper implements SuggestItem, Comparable {
return 1;
}
private int getInsertLength() {
if (prefix.endsWith(".")) return 0;
else if (prefix.contains(".")) return prefix.substring(prefix.lastIndexOf(".") + 1).length();
else return prefix.length();
public CompletionItem getItem() {
return item;
}
}
@@ -127,11 +127,7 @@ public class IDEService implements ShellServer.Callback {
isBuilding = false;
if(recreateShellOnDone)
createShell();
if(currentTask != null) {
if(currentTask.getTaskID() == TASK_SHOW_DEPENDENCIES)
listener.onGetDependenciesFailed();
else listener.onBuildFailed(currentTask, line.trim());
}
listener.onBuildFailed(currentTask, line.trim());
appendOutputSeparator();
execTask(BaseGradleTasks.TASKS);
@@ -211,6 +207,10 @@ public class IDEService implements ShellServer.Callback {
server.didChangeWatchedFiles(new DidChangeWatchedFilesParams(createSourceChangeEvents()));
server.buildModified();
if(listener != null) {
listener.onBuildModified();
}
}
/**
@@ -295,6 +295,17 @@ public class IDEService implements ShellServer.Callback {
}
}
/**
* @return An int[] containing minimumSdk at index 0 and targetSdk at index 1 or {1, 1}
*/
public int[] getSdkInfo() {
if(mAppModule != null) {
return new int[]{mAppModule.minSdk.apiLevel, mAppModule.targetSdk.apiLevel};
}
return new int[]{1, 1};
}
public void initProject() {
execTask(BaseGradleTasks.INITIALIZE_IDE_PROJECT);
}
@@ -379,20 +390,71 @@ public class IDEService implements ShellServer.Callback {
public boolean isBuilding() {
return isBuilding;
}
/**
* A client should implement this interface to get callbacks about a build
*/
public static interface BuildListener {
void notifyBuildModified();
void onProjectLoaded(IDEProject project, Optional<IDEModule> appModule);
void onGetDependencies(List<String> dependencies);
void onGetDependenciesFailed();
/**
* Called to notify the client about the modification of build/generated files
*/
void onBuildModified();
/**
* Called when the project has been loaded and its data has been read
*
* @param project The loaded project
* @param appModule The main application module
*/
void onProjectLoaded(IDEProject project, Optional<IDEModule> appModule);
/**
* Called when a Gradle Daemon is being started
*
* @param task Task that is currently running
*/
void onStartingGradleDaemon(GradleTask task);
/**
* Called when a task is started
*
* @param task Task that is currently running
* @param name Name of this task that should be shown to user
*/
void onRunTask(GradleTask task, String name);
/**
* Notifys about a successful build
*
* @param task Task that succeeded
* @param msg Successful message
*/
void onBuildSuccessful(GradleTask task, String msg);
/**
* Notifys about a failed build
*
* @param task Task that failed
* @param msg Failure message
*/
void onBuildFailed(GradleTask task, String msg);
/**
* Called to notify client to save any opened files
*/
void saveFiles();
/**
* Notifys the client to append the build output that should be shown to user
*
* @param task Task that is currently running
* @param text Output to append
*/
void appendOutput(GradleTask task, CharSequence text);
/**
* Called to notify the client to prepare for a build
*/
void prepare();
}
@@ -12,6 +12,11 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import android.os.Build;
import com.itsaky.apiinfo.ApiInfo;
import com.itsaky.apiinfo.models.ClassInfo;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.itsaky.apiinfo.models.MethodInfo;
public final class Environment {
@@ -105,6 +110,8 @@ public final class Environment {
public static void setBootClasspath(File file) {
BOOTCLASSPATH = new File(file.getAbsolutePath());
StudioApp.getInstance().initializeApiInformation();
}
public static Map<String, String> getEnvironment(boolean publicUse) {
@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="SmallSp"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="@color/primaryLightColor">
@@ -47,12 +47,26 @@
android:ellipsize="end"
android:singleLine="true"
android:layout_toEndOf="@id/completion_iconText"
android:layout_marginBottom="2dp"
android:layout_marginEnd="4dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/completion_detail"
android:id="@+id/completion_api_info"
android:textColor="@color/primaryTextColor"
android:textSize="11sp"
android:gravity="center_vertical"
android:ellipsize="end"
android:singleLine="true"
android:layout_toEndOf="@id/completion_iconText"
android:layout_marginBottom="2dp"
android:layout_marginEnd="4dp"/>
<TextView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:id="@+id/completion_iconText"
android:background="@color/secondaryColor"
+3
View File
@@ -1,4 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="msg_api_info_since">Since API %d\n</string>
<string name="msg_api_info_removed">Removed in API %d\n</string>
<string name="msg_api_info_deprecated">Deprecated in API %d\n</string>
<string name="title_confirm_open_project">Open last project?</string>
<string name="msg_confirm_open_project">Do you want me to open last opened project? Opened project was:\n%s</string>
<string name="title_open_projects">Open last project</string>
+1
View File
@@ -4,5 +4,6 @@ include ':lsp'
include ':emulatorview'
include ':emulator'
include ':javapoet'
include ':api-info'
rootProject.name = "Android IDE"