-Updated JDK and backup before implementing JVM's ClassLoader

This commit is contained in:
itsaky
2021-07-22 19:40:52 +05:30
commit e5cdf40c2a
511 changed files with 60582 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
# 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
# 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/
+2
View File
@@ -0,0 +1,2 @@
# AndroidIDE
An IDE to develop Android Apps on Android Smartphones. Supports Gradle Build System and OpenJDK 11.0.1
+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/
+130
View File
@@ -0,0 +1,130 @@
apply plugin: 'com.android.application'
apply plugin: 'com.itsaky.androidide'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'stringfog'
android {
compileSdkVersion rootProject.ext.compileSdk
buildToolsVersion rootProject.ext.buildTools
defaultConfig {
applicationId rootProject.ext.packageName
minSdkVersion rootProject.ext.minSdk
targetSdkVersion rootProject.ext.targetSdk
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
abortOnError false
disable "VectorPath",
"NestedWeights",
"UnusedResources",
"ContentDescription"
}
buildFeatures.viewBinding true
signingConfigs {
debug {
storeFile file("/sdcard/Android App Projects/publishKey.keystore")
storePassword "itsaky"
keyAlias "itsaky"
keyPassword "itsaky"
}
release {
storeFile file("/sdcard/Android App Projects/publishKey.keystore")
storePassword "itsaky"
keyAlias "itsaky"
keyPassword "itsaky"
}
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
stringfog.enable false
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
stringfog.enable true
}
}
packagingOptions {
exclude 'META-INF/eclipse.inf'
exclude 'about_files/LICENSE-2.0.txt'
exclude 'plugin.xml'
exclude 'plugin.properties'
}
}
stringfog {
key 'WVV0aGMwaEFZVzVFVW05SlJFbGtaUT09'
enable true
implementation 'com.github.megatronking.stringfog.xor.StringFogImpl'
}
dependencies {
// Annotation processors
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.blankj:utilcodex:1.30.6'
implementation 'com.elvishew:xlog:1.8.0'
implementation 'com.github.abhishekti7:UnicornFilePicker:1.0.2'
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation 'com.github.megatronking.stringfog:xor:1.1.0'
implementation 'com.github.piruin:quickaction:2.4.3'
implementation 'com.github.smart-fun:XmlToJson:1.5.1'
// Dependencies for java completion
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.google.guava:guava:30.1.1-android'
implementation 'org.antlr:antlr4-runtime:4.9.2'
// AndroidX
implementation 'androidx.annotation:annotation:1.2.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
implementation 'androidx.drawerlayout:drawerlayout:1.1.1'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
implementation 'com.google.android.material:material:1.4.0'
// Firebase
implementation platform('com.google.firebase:firebase-bom:28.1.0')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-messaging'
// Local projects
implementation project(':treeview')
implementation project(':editor')
implementation project(':spinkit')
implementation project(':slidr')
implementation project(':javadocparser')
implementation fileTree(dir: 'libs', include: ['*.jar'])
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}
+47
View File
@@ -0,0 +1,47 @@
{
"project_info": {
"project_number": "675638215311",
"project_id": "android--ide",
"storage_bucket": "android--ide.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:675638215311:android:0db45fd3cbd65abe71e6fa",
"android_client_info": {
"package_name": "com.itsaky.androidide"
}
},
"oauth_client": [
{
"client_id": "675638215311-33ngqr6dgg2ujbto9u70nt94scnpm89t.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.itsaky.androidide",
"certificate_hash": "0d182e8f8cf2e1fc702b0f56f81732e21c8b6908"
}
},
{
"client_id": "675638215311-hgg79t4vd0a6ff1epl56lfqm2ccdu66l.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBVog9KolXaH95_0C41P8o5QPFYFOqaR10"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "675638215311-hgg79t4vd0a6ff1epl56lfqm2ccdu66l.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+30
View File
@@ -0,0 +1,30 @@
-keepattributes SourceFile, LineNumberTable
-renamesourcefileattribute FuckYou
-ignorewarnings
-dontwarn
-dontnote
-dontwarn android.arch.**
-dontwarn android.lifecycle.**
-keep class android.arch.** { *; }
-keep class android.lifecycle.** { *; }
-dontwarn androidx.arch.**
-dontwarn androidx.lifecycle.**
-keep class androidx.arch.** { *; }
-keep class androidx.lifecycle.** { *; }
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class * extends com.bumptech.glide.module.AppGlideModule {
<init>(...);
}
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
*** rewind();
}
+76
View File
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.itsaky.androidide">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:name=".app.StudioApp"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:allowBackup="false"
android:largeHeap="true"
android:allowNativeHeapPointerTagging="false"
android:hardwareAccelerated="true">
<activity android:name=".SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|uiMode">
</activity>
<activity
android:name=".DownloadActivity"
android:configChanges="orientation|screenSize|uiMode">
</activity>
<activity
android:name=".EditorActivity"
android:configChanges="orientation|screenSize|uiMode"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name=".PreferencesActivity"
android:configChanges="orientation|screenSize">
</activity>
<service
android:name="com.itsaky.androidide.services.IDEService"
android:exported="false">
</service>
<service
android:name=".services.MessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<receiver
android:name="com.itsaky.androidide.receivers.LogReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.itsaky.androidide.logs.APPEND_LOG" />
</intent-filter>
</receiver>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/ic_launcher" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/secondaryColor" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/cms_channel_id_devs" />
</application>
</manifest>
+28
View File
@@ -0,0 +1,28 @@
[
{
"version": "1.0.1-alpha",
"changes": [
"Added: Option to choose custom build tools",
"Added: Build output is now shown while starting completion services",
"Added: A preference screen with some basic preferences",
"Added: \u0027Stop all Daemons\u0027 option now force stops a build if it already running",
"Added: Option which shows the status of Gradle Daemons",
"Added: Syntax highlighting and basic (reserved) completion for Gradle files.",
"Added: Option to recreate completion services",
"Added: Basic options while creating a new file",
"Added: You can now invoke the selection mode without selecting a word",
"Fixed: Bug which caused files from previously created project to be copied to new project",
"Fixed: Auto import issue when a completion item is selected",
"Fixed: Orientation issue in downloads activity",
"Fixed: Sometimes, auto closing tags won\u0027t work as expected",
"Enhanced: Speed of Java completion has been improved. But still it needs a lot of work.",
"Enhanced: Starting of completion services is a bit faster as compared to the previous version. But takes more time on first startup."
]
},
{
"version": "1.0.0-alpha",
"changes": [
"First alpha release"
]
}
]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@@ -0,0 +1,195 @@
package com.itsaky.androidide;
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import androidx.core.content.ContextCompat;
import androidx.transition.TransitionManager;
import com.blankj.utilcode.util.SizeUtils;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityDownloadBinding;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.ListDirectoryCallable;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
public class DownloadActivity extends StudioActivity {
private ActivityDownloadBinding binding;
private ProgressSheet progressSheet;
private File choosenDir = null;
@Override
protected View bindLayout() {
binding = ActivityDownloadBinding.inflate(getLayoutInflater());
return binding.getRoot();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OnClickListener clickListener = v -> handleClick(v);
binding.dismiss.setOnClickListener(clickListener);
binding.choose.setOnClickListener(clickListener);
binding.install.setOnClickListener(clickListener);
binding.install.setEnabled(false);
}
private void handleClick(View v) {
final int id = v.getId();
if(id == binding.dismiss.getId()) {
TransitionManager.beginDelayedTransition(binding.getRoot());
binding.card.setVisibility(View.GONE);
} else if(id == binding.choose.getId()) {
try {
binding.install.setEnabled(false);
UnicornFilePicker.from(this)
.addConfigBuilder()
.addItemDivider(false)
.selectMultipleFiles(false)
.setRootDirectory(FileUtil.getExternalStorageDir())
.showHiddenFiles(true)
.showOnlyDirectory(true)
.theme(R.style.AppTheme_FilePicker)
.build()
.forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
} catch (Throwable th){
getApp().writeException(th);
}
} else if(id == binding.install.getId()) {
if(choosenDir == null) {
getApp().toast(R.string.msg_pick_files, Toaster.Type.ERROR);
return;
}
if(!choosenDir.exists()) {
getApp().toast(R.string.msg_file_doesnt_exist, Toaster.Type.ERROR);
return;
}
installAll();
}
}
private final String DONE = "DONE";
private void installAll() {
showProgress();
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true);
final ShellServer server = getApp().newShell(__ -> checkInstalled(__));
final StringBuilder sb = new StringBuilder();
sb.append("cd $HOME && ");
sb.append("echo 'Installing...' && ");
File[] files = choosenDir.listFiles(ARCHIVE_FILTER);
if(files != null)
for(File f : files) {
if(f.getName().endsWith(".tar.xz")) {
sb.append("$BUSYBOX tar xvJf '" + f.getAbsolutePath() + "' && ");
} else if(f.getName().endsWith(".zip")) {
sb.append("$BUSYBOX unzip '" + f.getAbsolutePath() + "'");
}
}
sb.append("rm -rf '" + getApp().getToolsDownloadDirectory().getAbsolutePath() + "' && ");
sb.append("echo " + DONE);
server.bgAppend(sb.toString());
}
private void checkInstalled(CharSequence out) {
if(out != null) {
final String line = out.toString().trim();
runOnUiThread(() -> {
getProgressSheet().setSubMessage(line);
if(line.contains(DONE)) {
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_INSTALLED, true);
showRestartNeeded();
}
});
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE && resultCode == RESULT_OK) {
ArrayList<String> files = data.getStringArrayListExtra("filePaths");
if(files != null) {
if(files.size() == 1) {
File choosenDir = new File(files.get(0));
if(choosenDir.exists() && choosenDir.isDirectory()) {
this.choosenDir = choosenDir;
setDone(binding.choose);
binding.install.setEnabled(true);
new TaskExecutor().executeAsync(new ListDirectoryCallable(choosenDir), __ -> {
binding.filesText.setText(getString(R.string.msg_installable_files, __));
});
} else {
getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
choosenDir = null;
}
} else {
getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
}
}
}
}
private void setDone(MaterialButton button) {
final int dp8 = SizeUtils.dp2px(8);
final int dp4 = SizeUtils.dp2px(4);
final int dp24 = SizeUtils.dp2px(24);
button.setText("");
button.setIconResource(R.drawable.ic_ok);
button.setIconTint(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.bg_green)));
button.setBackgroundColor(android.graphics.Color.TRANSPARENT);
button.setPaddingRelative(dp8, dp4, dp8, dp4);
button.setStrokeWidth(0);
button.setGravity(Gravity.CENTER);
button.setIconPadding(0);
button.setInsetTop(0);
button.setInsetBottom(0);
button.setPaddingRelative(dp24, dp4, dp24, dp4);
}
private void showProgress() {
getProgressSheet().setCancelable(false);
getProgressSheet().setShowShadow(false);
getProgressSheet().setSubMessageEnabled(true)
.setWelcomeTextEnabled(true)
.show(getSupportFragmentManager(), "progress_sheet");
}
private void showRestartNeeded() {
setDone(binding.install);
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.title_restart);
builder.setMessage(R.string.msg_restart);
builder.setCancelable(false);
builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
builder.create().show();
}
private ProgressSheet getProgressSheet() {
return progressSheet == null ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait)) : progressSheet;
}
private final FileFilter ARCHIVE_FILTER = new FileFilter(){
@Override
public boolean accept(File p1) {
return p1.isFile() && (p1.getName().endsWith(".tar.xz") || p1.getName().endsWith(".zip"));
}
};
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,103 @@
package com.itsaky.androidide;
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.blankj.utilcode.util.SizeUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.adapters.MainPagerAdapter;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityMainBinding;
import com.itsaky.androidide.fragments.MainFragment;
import com.itsaky.androidide.fragments.ProjectsFragment;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.views.VerticalViewPager;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.util.ArrayList;
import com.itsaky.androidide.utils.Environment;
public class MainActivity extends StudioActivity
{
private ActivityMainBinding binding;
private MainFragment mMainFragment;
private ProjectsFragment mProjectsFragment;
private MainPagerAdapter mPagerAdapter;
private int currentItem = 0;
public static final String TAG = "MainActivity";
@Override
protected View bindLayout() {
binding = ActivityMainBinding.inflate(getLayoutInflater());
return binding.getRoot();
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mPagerAdapter = new MainPagerAdapter(getSupportFragmentManager())
.addFragment(getMainFragment())
.addFragment(getProjectsFragment());
binding.mainViewPager.setAdapter(mPagerAdapter);
binding.mainMoveDown.hide();
binding.mainMoveDown.setOnClickListener(v -> showFirst());
binding.settings.setOnClickListener(v -> gotoSettings());
binding.mainViewPager.setOnPageChangeListener(new VerticalViewPager.OnPageChangeListener(){
@Override
public void onPageScrolled(int p1, float p2, int p3) {
float elevation = SizeUtils.dp2px(8) * (currentItem == 0 ? p2 : -p2);
binding.activitymainTextView1.setElevation(elevation);
}
@Override
public void onPageSelected(int p1) {
currentItem = p1;
if(p1 == 1)
binding.mainMoveDown.show();
else binding.mainMoveDown.hide();
}
@Override
public void onPageScrollStateChanged(int p1) {
}
});
}
private void gotoSettings() {
startActivity(new Intent(this, PreferencesActivity.class));
}
private void showFirst() {
if(binding.mainViewPager.getCurrentItem() != 0)
binding.mainViewPager.setCurrentItem(0, true);
}
@Override
protected void onStorageGranted()
{
getProjectsFragment().loadProjects();
}
@Override
protected void onStorageDenied()
{
getApp().toast(R.string.msg_storage_denied, Toaster.Type.ERROR);
finishAffinity();
}
private MainFragment getMainFragment() {
return mMainFragment == null ? mMainFragment = new MainFragment().setCreateProjectListener(() -> getProjectsFragment().loadProjects()).setOnCreateProjectVisibilityListener((isVisible) -> binding.mainViewPager.setScrollEnabled(!isVisible)) : mMainFragment;
}
private ProjectsFragment getProjectsFragment() {
return mProjectsFragment == null ? mProjectsFragment = new ProjectsFragment() : mProjectsFragment;
}
}
@@ -0,0 +1,48 @@
package com.itsaky.androidide;
import android.os.Bundle;
import android.view.View;
import androidx.fragment.app.Fragment;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityPreferencesBinding;
import com.itsaky.androidide.fragments.preferences.AppearancePreferences;
import com.itsaky.androidide.fragments.preferences.BuildPreferences;
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
import com.itsaky.androidide.fragments.preferences.IDEPreferences;
public class PreferencesActivity extends StudioActivity {
private ActivityPreferencesBinding binding;
private IDEPreferences mPref;
@Override
protected View bindLayout() {
binding = ActivityPreferencesBinding.inflate(getLayoutInflater());
return binding.getRoot();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSupportActionBar(binding.toolbar);
getSupportActionBar().setTitle(R.string.ide_preferences);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
loadFragment(getPrefsFragment());
}
private IDEPreferences getPrefsFragment() {
return mPref == null ? mPref = new IDEPreferences() : mPref;
}
private void loadFragment(Fragment frag) {
super.loadFragment(frag, binding.fragmentContainer.getId());
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
}
@@ -0,0 +1,82 @@
package com.itsaky.androidide;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
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.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.utils.Environment;
import java.io.File;
public class SplashActivity extends StudioActivity
{
private ActivitySplashBinding binding;
private static final Handler mHandler = new Handler();
private final Runnable mRunnable = new Runnable(){
@Override
public void run()
{
startActivity(new Intent(SplashActivity.this, MainActivity.class));
finish();
}
};
@Override
protected View bindLayout() {
binding = ActivitySplashBinding.inflate(getLayoutInflater());
return binding.getRoot();
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
binding.splashText.setText(getString(R.string.msg_checking_storage_permissions));
}
@Override
protected void onStorageGranted()
{
super.onStorageGranted();
proceed();
}
@Override
protected void onStorageAlreadyGranted()
{
super.onStorageAlreadyGranted();
proceed();
}
private void proceed()
{
if(false && !getApp().is64Bit()) {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.title_device_not_supported);
builder.setMessage(R.string.msg_device_not_supported);
builder.setCancelable(false);
builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
builder.create().show();
} else {
binding.splashText.setText(getString(R.string.msg_storage_granted));
if (getApp().isFrameworkInstalled()) {
goToMain();
} else {
startActivity(new Intent(this, DownloadActivity.class));
finish();
}
}
}
private void goToMain() {
getApp().checkAndUpdateGradle();
mHandler.removeCallbacks(mRunnable);
mHandler.postDelayed(mRunnable, 1000);
}
}
@@ -0,0 +1,76 @@
package com.itsaky.androidide.adapters;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.content.ContextCompat;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutCompletionItemBinding;
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.models.CompletionListItem;
import com.itsaky.androidide.utils.Either;
import com.itsaky.androidide.utils.TypefaceUtils;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.widget.EditorCompletionAdapter;
import java.util.Locale;
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
public class CompletionListAdapter extends EditorCompletionAdapter {
@Override
public int getItemHeight() {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, Resources.getSystem().getDisplayMetrics());
}
@Override
protected View getView(int position, View convertView, ViewGroup parent, boolean isCurrentCursorPosition) {
LayoutCompletionItemBinding binding = LayoutCompletionItemBinding.inflate(LayoutInflater.from(getContext()), parent, false);
Either<SuggestItem, CompletionItem> e = getItem(position);
String label = "", desc = "", type = "";
String header = "";
if (e != null && e.isLeft()) {
SuggestItem s = e.getLeft();
label = s.getName();
desc = s.getDescription();
type = s.getReturnType();
header = String.valueOf(s.getTypeHeader()).toUpperCase(Locale.US);
} else if (e != null && e.isRight()) {
CompletionListItem item = (CompletionListItem) e.getRight();
header = item.getType() == null ? CompletionListItem.Type.OBJECT.getAsString() : item.getType().getAsString();
label = item.getLabel();
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());
}
type = JavaUtil.getArrayTypeIfNeeded(type);
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())
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));
return binding.getRoot();
}
@Override
protected String getHtmlJavadocAt(int index) {
Either<SuggestItem, CompletionItem> item = getItem(index);
if(item.isLeft() && item.getLeft() instanceof IJavaDocCommentable) {
IJavaDocCommentable commentable = (IJavaDocCommentable) item;
String doc = commentable.getJavaDoc();
if(doc != null && doc.trim().length() > 0) {
return doc;
}
}
return "";
}
}
@@ -0,0 +1,108 @@
package com.itsaky.androidide.adapters;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import com.itsaky.androidide.fragments.EditorFragment;
import com.itsaky.androidide.models.AndroidProject;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class EditorPagerAdapter extends FragmentStatePagerAdapter
{
private ArrayList<File> mOpenedFiles;
private ArrayList<Fragment> mFragments;
private AndroidProject project;
public EditorPagerAdapter(FragmentManager manager, AndroidProject project)
{
super(manager);
this.project = project;
this.mOpenedFiles = new ArrayList<>();
this.mFragments = new ArrayList<>();
}
public EditorPagerAdapter(FragmentManager manager, AndroidProject project, List<Fragment> fragments, List<File> files)
{
super(manager);
this.project = project;
this.mOpenedFiles = new ArrayList<>();
this.mFragments = new ArrayList<>();
mOpenedFiles.addAll(files);
mFragments.addAll(fragments);
}
public int openFile(File file)
{
int openedFileIndex = -1;
for(int i=0;i<mOpenedFiles.size();i++)
{
File f = mOpenedFiles.get(i);
if(f.getAbsolutePath().equals(file.getAbsolutePath()))
{
openedFileIndex = i;
break;
}
}
if(openedFileIndex == -1)
{
mFragments.add(EditorFragment.newInstance(file, project));
mOpenedFiles.add(file);
notifyDataSetChanged();
return mFragments.size() - 1;
} else return openedFileIndex;
}
public boolean saveAll() {
boolean hasGradle = false;
for(int i=0;i<mFragments.size();i++) {
hasGradle = hasGradle == false && save(i);
}
return hasGradle;
}
public boolean save(int index) {
if(index >= 0 && index < mFragments.size()) {
boolean hasGradle = false;
EditorFragment frag = getFrag(index);
hasGradle = frag.isModified() && frag.getFile().getName().endsWith(EditorFragment.EXT_GRADLE);
frag.save();
return hasGradle;
}
return false;
}
public EditorFragment getFrag(int index) {
return (EditorFragment) getItem(index);
}
public List<Fragment> getFragments() {
return mFragments;
}
public List<File> getOpenedFiles() {
return mOpenedFiles;
}
@Override
public Fragment getItem(int p1)
{
return p1 < 0 || p1 >= mFragments.size() ? null : mFragments.get(p1);
}
@Override
public int getCount()
{
return mFragments.size();
}
@Override
public CharSequence getPageTitle(int position)
{
return mOpenedFiles.get(position).getName();
}
}
@@ -0,0 +1,39 @@
package com.itsaky.androidide.adapters;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.ArrayList;
public class MainPagerAdapter extends FragmentPagerAdapter
{
private ArrayList<Fragment> mFragmentList;
public MainPagerAdapter(FragmentManager manager)
{
super(manager);
mFragmentList = new ArrayList<>();
}
@Override
public Fragment getItem(int p1)
{
return mFragmentList != null && p1 < mFragmentList.size() ? mFragmentList.get(p1) : null;
}
@Override
public int getCount()
{
return mFragmentList != null ? mFragmentList.size() : 0;
}
public MainPagerAdapter addFragment(Fragment frag)
{
getFragments().add(frag);
return this;
}
public ArrayList<Fragment> getFragments()
{
return mFragmentList == null ? mFragmentList = new ArrayList<>() : mFragmentList;
}
}
@@ -0,0 +1,53 @@
package com.itsaky.androidide.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.databinding.LayoutOptionssheetItemBinding;
import com.itsaky.androidide.models.SheetOption;
import java.util.List;
public class OptionsSheetAdapter extends RecyclerView.Adapter<OptionsSheetAdapter.VH> {
private OnOptionsClickListener listener;
private List<SheetOption> options;
public OptionsSheetAdapter(List<SheetOption> options, OnOptionsClickListener listener) {
this.options = options;
this.listener = listener;
}
@Override
public OptionsSheetAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
return new VH(LayoutOptionssheetItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
}
@Override
public void onBindViewHolder(OptionsSheetAdapter.VH p1, int p2) {
final LayoutOptionssheetItemBinding binding = p1.binding;
final SheetOption option = options.get(p2);
binding.text.setText(option.titleRes);
binding.icon.setImageResource(option.iconRes);
binding.getRoot().setOnClickListener(v -> { if(listener != null) listener.onOptionClick(option); });
}
@Override
public int getItemCount() {
return options.size();
}
public class VH extends RecyclerView.ViewHolder {
private LayoutOptionssheetItemBinding binding;
public VH(LayoutOptionssheetItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
public static interface OnOptionsClickListener {
public void onOptionClick(SheetOption option);
}
}
@@ -0,0 +1,124 @@
package com.itsaky.androidide.adapters;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutProjectsItemBinding;
import com.itsaky.androidide.models.AndroidProject;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import androidx.transition.TransitionManager;
import androidx.transition.ChangeBounds;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.blankj.utilcode.util.FileUtils;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.toaster.Toaster;
public class ProjectsListAdapter extends RecyclerView.Adapter<ProjectsListAdapter.VH> {
private ArrayList<AndroidProject> mProjects;
private OnProjectClickListener listener;
private final Comparator<AndroidProject> PROJECTS_SORTER = new Comparator<AndroidProject>(){
@Override
public int compare(AndroidProject p1, AndroidProject p2) {
return p1.getAppName().compareTo(p2.getAppName());
}
};
public ProjectsListAdapter(ArrayList<AndroidProject> mProjects, OnProjectClickListener listener) {
this.mProjects = mProjects;
this.listener = listener;
Collections.sort(this.mProjects, PROJECTS_SORTER);
}
@Override
public ProjectsListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
return new VH(LayoutProjectsItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
}
@Override
public void onBindViewHolder(ProjectsListAdapter.VH holder, int p2) {
final AndroidProject project = mProjects.get(p2);
holder.binding.projecsitemName.setText(project.getAppName());
holder.binding.projecsitemPkgName.setText(project.getPackageName());
holder.binding.deleteMsg.setText(holder.binding.deleteMsg.getContext().getString(R.string.msg_confirm_delete, project.getAppName()));
if(!TextUtils.isEmpty(project.getIconPath())) Glide.with(holder.itemView.getContext().getApplicationContext()).load(new File(project.getIconPath())).into(holder.binding.projecsitemIcon);
else holder.binding.projecsitemIcon.setImageResource(R.drawable.ic_android);
holder.binding.projectsitemClickableContainer.setOnClickListener(v -> sendClickEvent(project));
holder.binding.projectsitemClickableContainer.setOnLongClickListener(v -> holder.toggleDelete());
holder.binding.delete.setOnClickListener(v -> holder.showConfirmDelete());
holder.binding.deleteCancel.setOnClickListener(v -> holder.hideAllDelete());
holder.binding.deleteOk.setOnClickListener(v -> deleteProject(project, p2));
}
private void deleteProject(AndroidProject project, int index) {
index = index >= mProjects.size() ? mProjects.size() - 1 : index;
if(index < 0) return;
mProjects.remove(index);
notifyItemRemoved(index);
new TaskExecutor().executeAsync(() -> FileUtils.delete(project.getProjectPath()), __ -> {
StudioApp.getInstance().toast(__ ? R.string.msg_project_deleted : R.string.msg_project_delete_failed, Toaster.Type.SUCCESS);
});
}
private void sendClickEvent(AndroidProject project) {
if(listener != null) listener.onProjectClick(project);
}
@Override
public int getItemCount() {
return mProjects.size();
}
public class VH extends RecyclerView.ViewHolder {
LayoutProjectsItemBinding binding;
public VH(LayoutProjectsItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void showConfirmDelete() {
binding.confirmDeleteContainer.setEnabled(true);
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
binding.confirmDeleteContainer.setVisibility(View.VISIBLE);
}
public void hideAllDelete() {
hideDelete();
hideConfirmDelete();
}
public boolean toggleDelete() {
binding.delete.setEnabled(!binding.delete.isEnabled());
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
binding.delete.setVisibility(binding.delete.isEnabled() ? View.VISIBLE : View.GONE);
return true;
}
public void hideDelete() {
binding.delete.setEnabled(false);
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
binding.delete.setVisibility(View.GONE);
}
public void hideConfirmDelete() {
binding.confirmDeleteContainer.setEnabled(false);
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
binding.confirmDeleteContainer.setVisibility(View.GONE);
}
}
public interface OnProjectClickListener {
public void onProjectClick(AndroidProject project);
}
}
@@ -0,0 +1,67 @@
package com.itsaky.androidide.adapters;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.core.widget.TextViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.databinding.LayoutSymbolItemBinding;
import com.itsaky.androidide.views.SymbolInputView.Symbol;
import io.github.rosemoe.editor.widget.CodeEditor;
import io.github.rosemoe.editor.widget.SymbolChannel;
import android.util.TypedValue;
public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.VH> {
private SymbolChannel channel;
private Symbol[] symbols;
public SymbolInputAdapter(CodeEditor editor) {
this(editor, null);
}
public SymbolInputAdapter(CodeEditor editor, Symbol[] symbols) {
this.channel = editor.createNewSymbolChannel();
this.symbols = symbols == null ? new Symbol[0] : symbols;
}
public SymbolInputAdapter setSymbols(Symbol... symbols) {
return setSymbols(false, symbols);
}
public SymbolInputAdapter setSymbols(boolean notify, Symbol... symbols) {
this.symbols = symbols;
if(notify) {
notifyDataSetChanged();
}
return this;
}
@Override
public VH onCreateViewHolder(ViewGroup parent, int itemType) {
return new VH(LayoutSymbolItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
@Override
public void onBindViewHolder(VH holder, int position) {
if(symbols == null || symbols[position] == null) return;
final Symbol symbol = symbols[position];
holder.binding.symbol.setText(symbol.label);
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(holder.binding.symbol, 16, 27, 1, TypedValue.COMPLEX_UNIT_SP);
holder.binding.symbol.setOnClickListener(__ -> channel.insertSymbol(symbol.commit, symbol.offset));
}
@Override
public int getItemCount() {
return symbols == null ? 0 : symbols.length;
}
public class VH extends RecyclerView.ViewHolder {
LayoutSymbolItemBinding binding;
public VH(LayoutSymbolItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
@@ -0,0 +1,72 @@
package com.itsaky.androidide.adapters.viewholders;
import android.content.Context;
import android.graphics.PorterDuff;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import com.github.ybq.android.spinkit.style.Circle;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.LayoutFiletreeItemBinding;
import com.itsaky.androidide.utils.Environment;
import com.unnamed.b.atv.model.TreeNode;
import java.io.File;
public class FileTreeViewHolder extends TreeNode.BaseNodeViewHolder<File> {
private Context context;
private ImageView chevron;
private LayoutFiletreeItemBinding binding;
public FileTreeViewHolder(Context context) {
super(context);
this.context = context;
}
@Override
public View createNodeView(TreeNode node, File file) {
binding = LayoutFiletreeItemBinding.inflate(LayoutInflater.from(context));
int dp15 = context instanceof StudioActivity ? ((StudioActivity) context).dpToPx(15) : (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics()));
int icon = 0;
if (file.isDirectory())
icon = R.drawable.ic_folder;
else if (file.getName().endsWith(".java"))
icon = R.drawable.ic_language_java;
else if (file.getName().endsWith(".kt"))
icon = R.drawable.ic_language_kotlin;
else if (file.getName().endsWith(".xml"))
icon = R.drawable.ic_language_xml;
else if (file.getName().endsWith(".gradle"))
icon = R.drawable.ic_language_gradle;
else if (file.getName().endsWith(".json"))
icon = R.drawable.ic_language_json;
else
icon = R.drawable.ic_file_unknown;
final boolean isGradle = file.getAbsolutePath().equals(Environment.GRADLE_PROPS_DIR.getAbsolutePath());
chevron = binding.getRoot().findViewById(R.id.filetree_chevron);
binding.filetreeName.setText(isGradle ? "GRADLE_HOME" : file.getName());
binding.filetreeIcon.setImageResource(icon);
binding.getRoot().setPadding(binding.getRoot().getPaddingLeft() + (dp15 * (node.getLevel() - 1)), binding.getRoot().getPaddingTop(), binding.getRoot().getPaddingRight(), binding.getRoot().getPaddingBottom());
chevron.setVisibility(file.isFile() ? View.INVISIBLE : View.VISIBLE);
chevron.setImageResource(node.isExpanded() ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
chevron.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
binding.filetreeIcon.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
return binding.getRoot();
}
public void setLoading() {
chevron.setImageDrawable(new Circle());
chevron.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
}
public void updateChevron(boolean isExpanded) {
chevron.setImageResource(isExpanded ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
chevron.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
}
}
@@ -0,0 +1,106 @@
package com.itsaky.androidide.app;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.AttrRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import com.itsaky.androidide.R;
import com.itsaky.androidide.services.IDEService;
public abstract class StudioActivity extends AppCompatActivity {
private boolean toRequestStorage = true;
public static final String TAG = "StudioActivity";
public static final int REQCODE_STORAGE = 1009;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(bindLayout());
onSetContentView();
if (toRequestStorage && !isStoragePermissionGranted())
requestStorage();
else if (isStoragePermissionGranted())
onStorageAlreadyGranted();
}
public boolean isStoragePermissionGranted() {
return Build.VERSION.SDK_INT < 23 ||
(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}
protected void doNotRequestStorage() {
this.toRequestStorage = false;
}
protected void requestStorage() {
if (isStoragePermissionGranted()) { onStorageGranted(); return; }
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQCODE_STORAGE);
}
public void loadFrament(Fragment frag, View view) {
loadFragment(frag, view.getId());
}
public void loadFragment(Fragment fragment, int id) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(id, fragment);
transaction.commit();
}
public StudioApp getApp() {
return (StudioApp) getApplication();
}
public IDEService getBuildService() {
return getApp().getBuildService();
}
public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
TypedValue typedValue = new TypedValue();
getTheme().resolveAttribute(attrRes, typedValue, true);
return typedValue;
}
public int getColorPrimary() {
return getTypedValueForAttr(R.attr.colorPrimary).data;
}
public int getColorPrimaryDark() {
return getTypedValueForAttr(R.attr.colorPrimaryDark).data;
}
public int getColorAccent() {
return getTypedValueForAttr(R.attr.colorAccent).data;
}
public int dpToPx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQCODE_STORAGE) {
if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
onStorageGranted();
else onStorageDenied();
}
}
protected void onStorageAlreadyGranted() {}
protected void onStorageGranted() {}
protected void onStorageDenied() {}
protected void onSetContentView() {}
protected abstract View bindLayout();
}
@@ -0,0 +1,487 @@
package com.itsaky.androidide.app;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import androidx.core.app.NotificationManagerCompat;
import androidx.multidex.MultiDexApplication;
import com.blankj.utilcode.util.EncodeUtils;
import com.blankj.utilcode.util.ResourceUtils;
import com.blankj.utilcode.util.SPStaticUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import com.elvishew.xlog.LogConfiguration;
import com.elvishew.xlog.LogLevel;
import com.elvishew.xlog.XLog;
import com.elvishew.xlog.printer.Printer;
import com.elvishew.xlog.printer.file.FilePrinter;
import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator;
import com.google.firebase.messaging.FirebaseMessaging;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.interfaces.LanguageServerListener;
import com.itsaky.androidide.language.java.manager.JavaCharacter;
import com.itsaky.androidide.language.java.provider.JavaCompletionProvider;
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.services.MessagingService;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.StudioUtils;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import com.itsaky.androidide.models.ConstantsBridge;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.io.FileOutputStream;
import java.io.IOException;
import com.itsaky.androidide.services.compiler.JavaCompilerService;
import com.itsaky.androidide.interfaces.CompileListener;
import com.blankj.utilcode.util.FileUtils;
public class StudioApp extends MultiDexApplication
{
private static StudioApp instance;
private StudioUtils mUtils;
private IDEService buildService;
private JavaCompilerService mJavaCompilerService;
private JavaCompletionProvider mCompletionProvider;
private XMLCompletionService mXmlCompletionService;
private PreferenceManager mPrefsManager;
private NotificationManager mNotificationManager;
private boolean stopGradleDaemon = true;
private String sArch = "";
private List<LanguageServerListener> serveralisteners = new ArrayList<>();
public static final boolean DEBUG = com.itsaky.androidide.BuildConfig.DEBUG;
public static final long BUSYBOX_VERSION = 1;
public static final String TAG = "StudioApp";
public static final String ASSETS_DATA_DIR = "data/";
public static final String PROJECTS_FOLDER = "AndroidIDEProjects";
public static final String NOTIFICATION_ID_UPDATE = "17571";
public static final String NOTIFICATION_ID_DEVS = "17572";
public static final String TELEGRAM_GROUP_URL = "https://t.me/androidide_discussions";
public static final String SUGGESTIONS_URL = "https://github.com/itsaky/AndroidIDE";
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
@Override
public void onCreate() {
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);
JavaCharacter.initMap();
newShell(line -> handleLog(line)).bgAppend("logcat -v threadtime");
setupLibsIfNeeded();
if(isFrameworkInstalled()) {
newShell(t -> XLog.i(t)).bgAppend("printenv > /sdcard/androidideenv.txt && java -version");
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannels();
}
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
}
private void handleLog(CharSequence seq) {
if(seq == null)
return;
String line = seq.toString();
line = line.endsWith("\n") ? line : line + "\n";
File log = new File(FileUtil.getExternalStorageDir(), "ide_xlog/idelog.txt");
try {
FileOutputStream os = new FileOutputStream(log, true);
os.write(line.getBytes());
os.close();
} catch (IOException e) {}
}
@SuppressLint("NewApi")
private void createNotificationChannels() {
NotificationChannel updateChannel = new NotificationChannel(NOTIFICATION_ID_UPDATE, getNotificationChannelNameForId(NOTIFICATION_ID_UPDATE), NotificationManager.IMPORTANCE_HIGH);
updateChannel.enableLights(true);
updateChannel.enableVibration(true);
updateChannel.setLightColor(Color.RED);
updateChannel.setVibrationPattern(new long[]{10, 50, 10});
NotificationManagerCompat.from(this).createNotificationChannel(updateChannel);
NotificationChannel devsChannels = new NotificationChannel(NOTIFICATION_ID_DEVS, getNotificationChannelNameForId(NOTIFICATION_ID_DEVS), NotificationManager.IMPORTANCE_MIN);
devsChannels.enableLights(true);
devsChannels.enableVibration(true);
devsChannels.setLightColor(Color.BLUE);
devsChannels.setVibrationPattern(new long[]{10, 50, 10});
NotificationManagerCompat.from(this).createNotificationChannel(devsChannels);
}
public String getNotificationChannelNameForId(String id) {
if(id.equals(NOTIFICATION_ID_UPDATE))
return getUpdateNotificationChannelName();
else if(id.equals(NOTIFICATION_ID_DEVS))
return getDevNotificationChannelName();
else return "AndroidIDE Notifications";
}
private String getUpdateNotificationChannelName() {
return getString(R.string.cms_channel_id_update);
}
private String getDevNotificationChannelName() {
return getString(R.string.cms_channel_id_devs);
}
private void initLogger() {
LogConfiguration config = new LogConfiguration.Builder()
.logLevel(DEBUG ? LogLevel.ALL : LogLevel.NONE)
.tag("Android IDE")
.enableThreadInfo()
.enableStackTrace(2)
.enableBorder()
.build();
Printer filePrinter = new FilePrinter
.Builder(FileUtil.getExternalStorageDir() + "/ide_xlog")
.fileNameGenerator(new DateFileNameGenerator())
.build();
XLog.init(
config,
filePrinter);
}
private boolean setupLibsIfNeeded() {
File lib = Environment.LIBDIR;
return extractLibs(lib);
// if(lib != null && lib.exists() && lib.isDirectory()) {
// boolean libhook, librt, libgcc, libgcc_real, libpthread, libz;
// libhook = librt = libgcc = libgcc_real = libpthread = libz = false;
// File[] list = lib.listFiles();
// if(list != null) {
// for(File f : list) {
// libhook = libhook == false && f.getName().equals("libhook.so");
// librt = librt == false && f.getName().equals("librt.so");
// libpthread = libpthread == false && f.getName().equals("libpthread.so");
// libgcc = libgcc == false && f.getName().equals("libgcc.so");
// libgcc_real = libgcc_real = false & f.getName().equals("libgcc_real.so");
// libz = libz == false && f.getName().equals("libz.so.1");
// }
// } else return extractLibs(lib);
// return !(libhook && librt && libgcc && libgcc_real && libpthread && libz) ? extractLibs(lib) : true;
// } else return extractLibs(lib);
}
private boolean extractLibs(File lib) {
if(lib.exists())
FileUtils.delete(lib.getParentFile());
return lib.mkdirs() && ResourceUtils.copyFileFromAssets(getAssetsDataFile("lib"), lib.getAbsolutePath());
}
private File getBusybox() {
File parentFile = getRootDir().getParentFile();
File busyboxDir = new File(parentFile, "bin/busybox");
if (busyboxDir.exists() && busyboxDir.isDirectory())
FileUtil.deleteFile(busyboxDir.getAbsolutePath());
if ((!busyboxDir.exists() || SPStaticUtils.getLong("BUSYBOX_VERSION", 0) < BUSYBOX_VERSION) && Build.SUPPORTED_64_BIT_ABIS.length > 0) {
String arch = getArch(Build.SUPPORTED_64_BIT_ABIS[0]);
if (arch == null) {
return null;
} else if (ResourceUtils.copyFileFromAssets(getAssetsDataFile(new StringBuffer().append("busybox/").append(arch).toString()), parentFile.getAbsolutePath())) {
SPStaticUtils.put("busyboxArch", arch);
busyboxDir.setExecutable(true);
SPStaticUtils.put("BUSYBOX_VERSION", BUSYBOX_VERSION);
return busyboxDir;
} else return null;
}
if (!busyboxDir.canExecute())
busyboxDir.setExecutable(true);
return busyboxDir;
}
public void onGradleUpdated() {
IDEService service = getBuildService();
if(service != null) {
service.onGradleUpdated();
}
}
public void startBuildService() {
if(!IDEService.isRunning()) {
startService(new Intent(this, IDEService.class));
}
}
public void storeBuildServiceInstance(IDEService.BuildListener listener) {
buildService = IDEService.getInstance();
buildService.setListener(listener);
}
public IDEService getBuildService() {
return buildService;
}
public void createCompletionService(AndroidProject project) {
this.mXmlCompletionService = new XMLCompletionService();
this.mCompletionProvider = new JavaCompletionProvider();
this.mCompletionProvider.loadProject(project);
}
public void createJavaCompiler(AndroidProject project, CompileListener listener) {
this.mJavaCompilerService =
new JavaCompilerService(project)
.setListener(listener);
}
public JavaCompilerService getJavaCompiler() {
return mJavaCompilerService;
}
public JavaCompletionProvider getCompletionProvider() {
return mCompletionProvider;
}
public XMLCompletionService getXmlCompletionService() {
return mXmlCompletionService;
}
public boolean areCompletorsStarted() {
return mCompletionProvider != null
&& mXmlCompletionService != null
&& mCompletionProvider.isInitiated()
&& mXmlCompletionService.isInitiated()
&& ConstantsBridge.CLASS_LOAD_SUCCESS;
}
public void setStopGradleDaemon(boolean startGradleDaemon) {
this.stopGradleDaemon = startGradleDaemon;
}
public boolean isStopGradleDaemon() {
return stopGradleDaemon;
}
public static StudioApp getInstance() {
return instance;
}
public ShellServer newShell(ShellServer.Callback callback) {
ShellServer shellServer = new ShellServer(callback, "sh", Environment.mkdirIfNotExits(getRootDir()).getAbsolutePath());
if (getBusybox() != null) shellServer.append(String.format("export BUSYBOX=%s\n", getBusybox().getAbsolutePath()), false);
shellServer.start();
return shellServer;
}
public void addServerListener(LanguageServerListener listener) {
if(serveralisteners == null) serveralisteners = new ArrayList<>();
if(!serveralisteners.contains(listener))
serveralisteners.add(listener);
}
public void removeServerListener(LanguageServerListener listener) {
if(serveralisteners == null) serveralisteners = new ArrayList<>();
if(serveralisteners.contains(listener))
serveralisteners.remove(listener);
}
public void writeException(Throwable th) {
FileUtil.writeFile(new File(FileUtil.getExternalStorageDir(), "idelog.txt").getAbsolutePath(), ThrowableUtils.getFullStackTrace(th));
}
public File getIndexedDir() {
return Environment.mkdirIfNotExits(new File(getRootDir(), "indexed"));
}
public File getD8Jar() {
File f = new File(getRootDir(), "d8.jar");
if(f.exists() && f.isFile())
return f;
if(ResourceUtils.copyFileFromAssets("data/d8.jar", f.getAbsolutePath())) {
return f;
}
return null;
}
public File getRootDir() {
return new File(getIDEDataDir(), "framework");
}
public File getRootDirOld() {
return new File(getFilesDir(), "framework");
}
public File getIDEDataDir() {
return Environment.mkdirIfNotExits(new File("/data/data/com.itsaky.androidide/files"));
}
public final File getLogSenderDir() {
return new File(getRootDir(), "logsender");
}
public final File getToolsDownloadDirectory() {
return new File(getRootDir(), "downloads");
}
public final File getTempProjectDir() {
return Environment.mkdirIfNotExits(new File(getRootDir(), "tempProject"));
}
public final File getTempClassesDir() {
return Environment.mkdirIfNotExits(new File(FileUtil.getExternalStorageDir() /*getTempProjectDir() */, "classes"));
}
public final String getDownloadRequestUrl() {
return new String(EncodeUtils.base64Decode("aHR0cHM6Ly9hbmRyb2lkaWRlLmNvbS9idWlsZC90b29scy9kb3dubG9hZC8="));
}
public boolean isFrameworkDownloaded() {
return getToolsDownloadDirectory().exists() && mPrefsManager.isFrameworkDownloaded();
}
public boolean isFrameworkInstalled() {
return getRootDir().exists() && mPrefsManager.isFrameworkInstalled();
}
public PreferenceManager getPrefManager() {
return mPrefsManager;
}
public File getProjectsDir() {
return Environment.mkdirIfNotExits(new File(FileUtil.getExternalStorageDir(), PROJECTS_FOLDER));
}
public File[] listProjects() {
return getProjectsDir().listFiles(p1 -> p1.isDirectory());
}
public boolean is64Bit() {
if(Build.VERSION.SDK_INT >= 23) {
return android.os.Process.is64Bit();
} else {
String[] abis = Build.SUPPORTED_64_BIT_ABIS;
return abis != null && abis.length > 0;
}
}
public String getArch() {
if (sArch == null) {
sArch = getArch(System.getProperty("os.arch"));
}
return sArch;
}
private void handleCrash(Thread thread, Throwable th) {
if(DEBUG)
writeException(th);
if(this.uncaughtExceptionHandler != null) {
this.uncaughtExceptionHandler.uncaughtException(thread, th);
}
try {
android.os.Process.killProcess(android.os.Process.myPid());
} catch (Throwable ignored) {}
}
private String getArch(String str) {
if (str.length() > 0) {
switch (str.toLowerCase(Locale.US).charAt(0)) {
case 'a':
return str.equals("amd64") ? "x86_64" : str.contains("64") ? "arm64" : "arm";
case 'i':
case 'x':
return str.contains("64") ? "x86_64" : "x86";
case 'm':
return str.contains("64") ? "mips64" : "mips";
}
}
return "unknown";
}
public String getAssetsDataFile() {
return ASSETS_DATA_DIR;
}
public String getAssetsDataFile(String str) {
return new StringBuffer().append(getAssetsDataFile()).append(str).toString();
}
public void checkAndUpdateGradle() {
File gradle = new File(Environment.GRADLE_DIR, "bin/gradle");
if(gradle.exists()) {
String[] txt = FileUtil.readFile(gradle.getAbsolutePath()).split("\n");
if(txt != null && txt.length > 1 && txt[0].contains("#!/usr/bin/env sh")) {
txt[0] = "";
FileUtil.writeFile(gradle.getAbsolutePath(), TextUtils.join("\n", txt));
if(!gradle.canExecute())
gradle.setExecutable(true);
}
}
}
public StudioUtils getUtils() {
return mUtils == null ? mUtils = new StudioUtils(this) : mUtils;
}
public void toast(String msg, Toaster.Type type) {
getUtils().toast(msg, type);
}
public void toast(int msg, Toaster.Type type) {
getUtils().toast(msg, type);
}
public void toastLong(String msg, Toaster.Type type) {
getUtils().toastLong(msg, type);
}
public void toastLong(int msg, Toaster.Type type) {
getUtils().toastLong(msg, type);
}
public static File getCompilerOut() {
return new File(FileUtil.getExternalStorageDir(), "ide_xlog/compiler_out.txt");
}
public static File getCompilerError() {
return new File(FileUtil.getExternalStorageDir(), "ide_xlog/compiler_err.txt");
}
public void openTelegramGroup() {
Intent open = new Intent();
open.setAction(Intent.ACTION_VIEW);
open.setData(Uri.parse(StudioApp.TELEGRAM_GROUP_URL));
open.setPackage("org.telegram.messenger");
open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(open);
}
public void openIssueTracker() {
Intent open = new Intent();
open.setAction(Intent.ACTION_VIEW);
open.setData(Uri.parse(StudioApp.SUGGESTIONS_URL));
open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(open);
}
}
@@ -0,0 +1,11 @@
package com.itsaky.androidide.fragments;
import androidx.fragment.app.Fragment;
import com.itsaky.androidide.app.StudioActivity;
public class BaseFragment extends Fragment
{
protected StudioActivity getStudioActivity() {
return (StudioActivity) getActivity();
}
}
@@ -0,0 +1,276 @@
package com.itsaky.androidide.fragments;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.itsaky.androidide.adapters.CompletionListAdapter;
import com.itsaky.androidide.databinding.FragmentEditorBinding;
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
import com.itsaky.androidide.language.groovy.GroovyLanguage;
import com.itsaky.androidide.language.java.JavaLanguage;
import com.itsaky.androidide.language.xml.XMLLanguage;
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.ConstantsBridge;
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
import com.itsaky.androidide.syntax.colorschemes.SchemeWombat;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.ReadFileTask;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.TypefaceUtils;
import io.github.rosemoe.editor.interfaces.EditorEventListener;
import io.github.rosemoe.editor.langs.EmptyLanguage;
import io.github.rosemoe.editor.widget.CodeEditor;
import io.github.rosemoe.editor.widget.schemes.SchemeVS2019;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
import com.elvishew.xlog.XLog;
import android.text.TextUtils;
import com.itsaky.androidide.services.compiler.JavaCompilerService;
public class EditorFragment extends BaseFragment implements EditorEventListener {
public FragmentEditorBinding binding;
private File mFile;
private boolean isRead = false;
private boolean isModified = false;
private boolean isFirstCreate = false;
private static AndroidProject project;
private List<CompilerDiagnostic> diags;
public static final String KEY_FILE_PATH = "file_path";
public static final String KEY_PROJECT = "project";
public static final String EXT_JAVA = ".java";
public static final String EXT_XML = ".xml";
public static final String EXT_HTML = ".html";
public static final String EXT_GRADLE = ".gradle";
public static final String EXT_GROOVY = ".groovy";
public static final String EXT_KOTLIN = ".kt";
public static final String EXT_JSON = ".json";
public static EditorFragment newInstance(File file, AndroidProject project) {
Bundle bundle = new Bundle();
bundle.putString(KEY_FILE_PATH, file.getAbsolutePath());
bundle.putParcelable(KEY_PROJECT, project);
EditorFragment frag = new EditorFragment();
frag.setArguments(bundle);
return frag;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isFirstCreate = true;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentEditorBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getActivity() == null || getArguments() == null || !getArguments().containsKey(KEY_FILE_PATH) || !getArguments().containsKey(KEY_PROJECT)) return;
mFile = new File(getArguments().getString(KEY_FILE_PATH));
project = getArguments().getParcelable(KEY_PROJECT);
binding.editorCodeEditor.setOverScrollEnabled(false);
binding.editorCodeEditor.setTypefaceText(TypefaceUtils.jetbrainsMono());
binding.editorCodeEditor.setTextActionMode(CodeEditor.TextActionMode.ACTION_MODE);
binding.editorCodeEditor.setHighlightCurrentBlock(true);
binding.editorCodeEditor.setEventListener(this);
binding.editorCodeEditor.setAutoCompletionOnComposing(true);
binding.editorCodeEditor.setAutoCompletionItemAdapter(new CompletionListAdapter());
binding.editorCodeEditor.setLineColorsEnabled(true);
configureEditorIfNeeded();
new TaskExecutor().executeAsync(new ReadFileTask(mFile), result -> {
binding.editorCodeEditor.setText(result);
postRead();
});
}
public void setDiagnostics(List<CompilerDiagnostic> diags) {
this.diags = diags;
XLog.i("[EditorFragment] Diagnostics for file: " + getFile().getAbsolutePath()
+ "\ndiagnostics: " + TextUtils.join("\n", diags));
}
private void configureEditorIfNeeded() {
boolean sizeChanged = isFirstCreate || ConstantsBridge.EDITORPREF_SIZE_CHANGED;
boolean flagsChanged = isFirstCreate || ConstantsBridge.EDITORPREF_FLAGS_CHANGED;
boolean drawHexChanged = isFirstCreate || ConstantsBridge.EDITORPREF_DRAW_HEX_CHANGED;
final PreferenceManager prefs = getStudioActivity().getApp().getPrefManager();
if(sizeChanged) {
float textSize = prefs.getFloat(EditorPreferences.KEY_EDITOR_FONT_SIZE);
if(textSize < 6 || textSize > 32)
textSize = 14;
binding.editorCodeEditor.setTextSize(textSize);
ConstantsBridge.EDITORPREF_SIZE_CHANGED = false;
}
if(flagsChanged) {
int flags = 0;
if(prefs.getBoolean(PreferenceManager.KEY_EDITORFLAG_WS_LEADING, true))
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_LEADING;
if(prefs.getBoolean(PreferenceManager.KEY_EDITORFLAG_WS_TRAILING, false))
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_TRAILING;
if(prefs.getBoolean(PreferenceManager.KEY_EDITORFLAG_WS_INNER, true))
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_INNER;
if(prefs.getBoolean(PreferenceManager.KEY_EDITORFLAG_WS_EMPTY_LINE, true))
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_FOR_EMPTY_LINE;
if(prefs.getBoolean(PreferenceManager.KEY_EDITORFLAG_LINE_BREAK, true))
flags |= CodeEditor.FLAG_DRAW_LINE_SEPARATOR;
binding.editorCodeEditor.setNonPrintablePaintingFlags(flags);
ConstantsBridge.EDITORPREF_FLAGS_CHANGED = false;
}
if(drawHexChanged) {
binding.editorCodeEditor.setLineColorsEnabled(prefs.getBoolean(PreferenceManager.KEY_EDITOR_DRAW_HEX, true));
ConstantsBridge.EDITORPREF_DRAW_HEX_CHANGED = false;
}
isFirstCreate = false;
}
public boolean isModified() {
return isModified;
}
public void undo() {
if(binding.editorCodeEditor.canUndo())
binding.editorCodeEditor.undo();
}
public void redo() {
if(binding.editorCodeEditor.canRedo())
binding.editorCodeEditor.redo();
}
public void save() {
FileUtil.writeFile(mFile.getAbsolutePath(), binding.editorCodeEditor.getText().toString());
isModified = false;
}
public File getFile() {
return mFile;
}
@Override
public void onResume() {
super.onResume();
configureEditorIfNeeded();
}
private void postRead() {
if (mFile.isFile() && mFile.getName().endsWith(EXT_JAVA)) {
binding.editorCodeEditor.setEditorLanguage(new JavaLanguage(binding.editorCodeEditor, project));
binding.editorCodeEditor.setColorScheme(new SchemeVS2019());
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_XML)) {
binding.editorCodeEditor.setEditorLanguage(new XMLLanguage(binding.editorCodeEditor));
binding.editorCodeEditor.setColorScheme(new SchemeWombat());
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_GRADLE)) {
binding.editorCodeEditor.setEditorLanguage(new GroovyLanguage());
binding.editorCodeEditor.setColorScheme(new SchemeVS2019());
} else {
binding.editorCodeEditor.setEditorLanguage(new EmptyLanguage());
}
isRead = true;
}
private void closeCurrentTag(String text, int line, int col) {
try {
XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(text)));
Token token;
boolean wasSlash = false, wasOpen = false;
ArrayList<String> currentNames = new ArrayList<>();
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
final int type = token.getType();
if(type == XMLLexer.OPEN) {
wasOpen = true;
} else if(type == XMLLexer.Name) {
if(wasOpen && wasSlash && currentNames.size() > 0) {
currentNames.remove(0);
} else if(wasOpen){
currentNames.add(0, token.getText());
wasOpen = false;
}
} else if(type == XMLLexer.OPEN_SLASH) {
int l = token.getLine() - 1;
int c = token.getCharPositionInLine();
if(l == line && c == col) {
break;
}
else if(currentNames.size() > 0)
currentNames.remove(0);
} else if(type == XMLLexer.SLASH_CLOSE
|| type == XMLLexer.SPECIAL_CLOSE) {
if(currentNames.size() > 0 && token.getText().trim().endsWith("/>"))
currentNames.remove(0);
} else if(type == XMLLexer.SLASH) {
wasSlash = true;
} else wasOpen = wasSlash = false;
}
if(currentNames.size() > 0) {
binding.editorCodeEditor.getText().insert(line, col + 2, currentNames.get(0));
}
} catch (Throwable th) {}
}
@Override
public boolean onRequestFormat(CodeEditor editor, boolean async) {
return false;
}
@Override
public boolean onFormatFail(CodeEditor editor, Throwable cause) {
return false;
}
@Override
public void onFormatSucceed(CodeEditor editor) {
}
@Override
public void onNewTextSet(CodeEditor editor) {
isModified = true;
}
@Override
public void afterDelete(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
isModified = true;
}
@Override
public void afterInsert(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence insertedContent) {
isModified = true;
boolean isOpen = false;
try {
isOpen = editor.getText().charAt(editor.getCursor().getLeft() - 2) == '<';
} catch (Throwable th) {}
if(isOpen && insertedContent.toString().equals("/")) {
closeCurrentTag(editor.getText().toString(), endLine, endColumn);
}
JavaCompilerService compiler = getStudioActivity().getApp().getJavaCompiler();
if(compiler != null) {
compiler.compileAll();
}
}
@Override
public void beforeReplace(CodeEditor editor, CharSequence content) {
}
}
@@ -0,0 +1,240 @@
package com.itsaky.androidide.fragments;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ProgressBar;
import androidx.transition.ChangeBounds;
import androidx.transition.TransitionManager;
import com.blankj.utilcode.util.FileIOUtils;
import com.bumptech.glide.Glide;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.itsaky.androidide.R;
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutEditorFileTreeBinding;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.FileTreeCallable;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
import com.unnamed.b.atv.view.AndroidTreeView;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
public class FileTreeFragment extends BottomSheetDialogFragment implements TreeNode.TreeNodeClickListener, TreeNode.TreeNodeLongClickListener {
private LayoutEditorFileTreeBinding binding;
private AndroidTreeView mFileTreeView;
private FileActionListener mFileActionListener;
private AndroidProject mProject;
private TreeNode mRoot;
private ArrayList<String> compatibleExtensions = new ArrayList<>();
public FileTreeFragment() {
compatibleExtensions = new ArrayList<>();
compatibleExtensions.add(".java"); // Java
compatibleExtensions.add(".kt"); // Kotlin
compatibleExtensions.add(".xml"); // XML
compatibleExtensions.add(".json"); // JSON
compatibleExtensions.add(".gradle"); // Gradle
compatibleExtensions.add(".txt"); // Text
compatibleExtensions.add(".properties"); // properties (User may need to edit gradle.properties)
compatibleExtensions.add(".md"); // MarkDown (to edit README.md and other files)
}
public FileTreeFragment setFileActionListener(FileActionListener listener) {
this.mFileActionListener = listener;
return this;
}
public static FileTreeFragment newInstance(AndroidProject project) {
Bundle bundle = new Bundle();
bundle.putParcelable("project", project);
FileTreeFragment frag = new FileTreeFragment();
frag.setArguments(bundle);
return frag;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = LayoutEditorFileTreeBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getActivity() == null || getArguments() == null || !getArguments().containsKey("project")) return;
mProject = getArguments().getParcelable("project");
if(mProject.getIconPath() == null || mProject.getIconPath().trim().length() <= 0) {
StudioApp.getInstance().toast(R.string.msg_failed_load_icon_path, Toaster.Type.ERROR);
} else {
Glide.with(getActivity().getApplicationContext()).load(new File(mProject.getIconPath())).into(binding.filetreenavigationAppIcon);
}
binding.filetreenavigationProjectDetails.setText(getResources().getString(R.string.project_with_package, mProject.getAppName(), mProject.getPackageName()));
listProjectFiles();
}
@Override
public void onClick(TreeNode node, Object p2) {
final File f = (File) p2;
if(f.isFile()) {
if (mFileActionListener != null && f.isFile()) {
for(String ext : compatibleExtensions) {
if(f.getName().endsWith(ext)) {
mFileActionListener.openFile(f);
break;
}
}
}
} else if(f.isDirectory() && f.exists()) {
if(node.isExpanded()) {
collapseNode(node);
} else if(f.getAbsolutePath().equals(Environment.GRADLE_PROPS_DIR.getAbsolutePath()) && !node.isExpanded()) {
expandNode(node);
} else {
setLoading(node);
listNode(node, true);
}
}
}
private void listNode(TreeNode node, boolean wasClicked) {
node.getChildren().clear();
node.setExpanded(false);
new TaskExecutor().executeAsync(() -> {
getNodeFromFiles(node.getValue().listFiles(/* new FileTreeCallable.HiddenFilesFilter() */), node);
TreeNode temp = node;
while(temp.size() == 1) {
temp = temp.childAt(0);
if(!temp.getValue().isDirectory()) break;
getNodeFromFiles(temp.getValue().listFiles(/* new FileTreeCallable.HiddenFilesFilter() */), temp);
temp.setExpanded(true);
}
return null;
}, __ -> {
updateChevron(node);
expandNode(node);
});
}
private void getNodeFromFiles(File[] files, TreeNode parent) {
Arrays.sort(files, new FileTreeCallable.SortFileName());
Arrays.sort(files, new FileTreeCallable.SortFolder());
for (File file : files) {
TreeNode node = new TreeNode(file);
node.setViewHolder(new FileTreeViewHolder(getContext()));
parent.addChild(node);
}
}
private void setLoading(TreeNode node) {
if (node.getViewHolder() instanceof FileTreeViewHolder) {
((FileTreeViewHolder) node.getViewHolder()).setLoading();
}
}
private void updateChevron(TreeNode node) {
if (node.getViewHolder() instanceof FileTreeViewHolder) {
((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded());
}
}
@Override
public boolean onLongClick(TreeNode node, Object value) {
if(mFileActionListener != null)
mFileActionListener.showFileOptions((File) value, node);
return true;
}
public void expandNode(TreeNode node) {
if(mFileTreeView == null) {
return;
}
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
mFileTreeView.expandNode(node);
}
public void collapseNode(TreeNode node) {
if(mFileTreeView == null) {
return;
}
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
mFileTreeView.collapseNode(node);
}
public void listProjectFiles() {
if (mProject == null)
return;
final File gradleProps = Environment.GRADLE_PROPS;
final File gradleHome = Environment.GRADLE_PROPS_DIR;
final File root = StudioApp.getInstance().getRootDir().getParentFile();
File projectDir = new File(mProject.getProjectPath());
projectDir = root;
mRoot = TreeNode.root(projectDir);
if(gradleHome.exists() && gradleHome.isDirectory()) {
if(!gradleProps.exists())
FileIOUtils.writeFileFromString(gradleProps, Environment.SAMPLE_GRADLE_PROP_CONTENTS);
TreeNode home = new TreeNode(gradleHome);
home.setViewHolder(new FileTreeViewHolder(getContext()));
TreeNode prop = new TreeNode(gradleProps);
prop.setViewHolder(new FileTreeViewHolder(getContext()));
home.addChild(prop);
mRoot.addChild(home);
}
mRoot.setViewHolder(new FileTreeViewHolder(getContext()));
getScrollView().setVisibility(View.GONE);
getLoadingProgress().setVisibility(View.VISIBLE);
new TaskExecutor().executeAsync(new FileTreeCallable(getContext(), mRoot, projectDir), (result) -> {
getScrollView().setVisibility(View.VISIBLE);
getLoadingProgress().setVisibility(View.GONE);
AndroidTreeView tree = createTreeView(mRoot);
if(tree != null) {
tree.setUseAutoToggle(false);
tree.setDefaultNodeClickListener(FileTreeFragment.this);
tree.setDefaultNodeLongClickListener(FileTreeFragment.this);
getScrollView().removeAllViews();
getScrollView().addView(tree.getView());
}
});
}
public AndroidTreeView createTreeView(TreeNode node) {
Context ctx = null;
if(getActivity() != null)
ctx = getActivity();
else if(getContext() != null) {
ctx = getContext();
}
if(ctx == null)
return null;
return mFileTreeView = new AndroidTreeView(ctx, node, R.drawable.bg_ripple);
}
public HorizontalScrollView getScrollView() {
return binding.filetreeHorizontalScrollView;
}
public ProgressBar getLoadingProgress() {
return binding.fileTreeLoadingProgress;
}
public interface FileActionListener {
public void openFile(File file);
public void showFileOptions(File file, TreeNode node);
}
}
@@ -0,0 +1,340 @@
package com.itsaky.androidide.fragments;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.transition.ChangeBounds;
import androidx.transition.Transition;
import androidx.transition.TransitionManager;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutCreateProjectBinding;
import com.itsaky.androidide.databinding.LayoutCreateProjectContentBinding;
import com.itsaky.androidide.databinding.LayoutMainBinding;
import com.itsaky.androidide.interfaces.ProjectWriterCallback;
import com.itsaky.androidide.models.ProjectTemplate;
import com.itsaky.androidide.views.transition.ArcMotionPlus;
import com.itsaky.toaster.Toaster;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import com.itsaky.androidide.utils.ProjectWriter;
import com.itsaky.androidide.models.NewProjectDetails;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable;
public class MainFragment extends BaseFragment implements OnClickListener, ProjectWriterCallback
{
private LayoutMainBinding binding;
private LayoutCreateProjectBinding createBinding;
private LayoutCreateProjectContentBinding createLayoutBinding;
private OnCreateProjectListener createProjectListener;
private ProgressSheet mWritingProgressSheet;
private ArrayList<ProjectTemplate> mTemplates = new ArrayList<ProjectTemplate>();
private OnCreateProjectVisibilityListener visibilityChangeListener;
public static final String TAG = "MainFragment";
private int currentTemplateIndex = 0;
public MainFragment setCreateProjectListener(OnCreateProjectListener createProjectListener) {
this.createProjectListener = createProjectListener;
return this;
}
public MainFragment setOnCreateProjectVisibilityListener(OnCreateProjectVisibilityListener visibilityChangeListener) {
this.visibilityChangeListener = visibilityChangeListener;
return this;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = LayoutMainBinding.inflate(inflater, container, false);
createBinding = binding.createLayout;
createLayoutBinding = createBinding.createProjectContent;
return binding.getRoot();
}
@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.createNew.setOnClickListener(this);
createBinding.createprojectClose.setOnClickListener(this);
createLayoutBinding.createprojectCreate.setOnClickListener(this);
createLayoutBinding.previousCard.setOnClickListener(this);
createLayoutBinding.nextCard.setOnClickListener(this);
binding.createNewCard.setVisibility(View.GONE);
currentTemplateIndex = 0;
createTemplates();
createProgressSheet();
showTemplate(currentTemplateIndex);
}
@Override
public void onClick(View p1) {
if(p1.getId() == binding.createNew.getId())
showCreateProject();
else if(p1.getId() == createBinding.createprojectClose.getId())
hideCreateProject();
else if(p1.getId() == createLayoutBinding.nextCard.getId()) {
currentTemplateIndex++;
if(currentTemplateIndex >= mTemplates.size()) currentTemplateIndex = 0;
showTemplate(currentTemplateIndex);
} else if(p1.getId() == createLayoutBinding.previousCard.getId()) {
currentTemplateIndex--;
if(currentTemplateIndex < 0) currentTemplateIndex = mTemplates.size() - 1;
showTemplate(currentTemplateIndex);
} else if(p1.getId() == createLayoutBinding.createprojectCreate.getId())
createNewProject();
}
@Override
public void beforeBegin() {
if(mWritingProgressSheet == null)
createProgressSheet();
setMessage(R.string.msg_begin_project_write);
}
@Override
public void onProcessTask(String taskName) {
setMessage(taskName);
}
@Override
public void onSuccess() {
if(mWritingProgressSheet != null)
createProgressSheet();
if(mWritingProgressSheet.isShowing())
mWritingProgressSheet.dismiss();
getStudioActivity().getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
if(createProjectListener != null)
createProjectListener.onNewProjectCreated();
}
@Override
public void onFailed(String reason) {
if(mWritingProgressSheet != null)
createProgressSheet();
if(mWritingProgressSheet.isShowing())
mWritingProgressSheet.dismiss();
getStudioActivity().getApp().toast(reason, Toaster.Type.ERROR);
}
private void showTemplate(int index) {
if(index < 0 || index >= mTemplates.size()) return;
final ProjectTemplate template = mTemplates.get(index);
createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
}
/**
* Must be called in/after onViewCreated
*/
private void createTemplates() {
mTemplates = new ArrayList<>();
ProjectTemplate
empty = new ProjectTemplate()
.setId(0)
.setName(getContext(), R.string.template_empty)
.setDescription(getContext(), R.string.template_description_empty)
.setImageId(R.drawable.template_empty),
basic = new ProjectTemplate()
.setId(1)
.setName(getContext(), R.string.template_basic)
.setDescription(getContext(), R.string.template_description_basic)
.setImageId(R.drawable.template_basic),
drawer = new ProjectTemplate()
.setId(2)
.setName(getContext(), R.string.template_navigation_drawer)
.setDescription(getContext(), R.string.template_description_navigation_drawer)
.setImageId(R.drawable.template_navigation_drawer);
// navigation = new ProjectTemplate()
// .setId(3)
// .setName(getContext(), R.string.template_bottom_navigation)
// .setDescription(getContext(), R.string.template_description_bottom_navigation)
// .setImageId(R.drawable.template_bottom_navigation),
// kotlin = new ProjectTemplate()
// .setId(4)
// .setName(getContext(), R.string.template_basic_kotlin)
// .setDescription(getContext(), R.string.template_description_kotlin)
// .setImageId(R.drawable.template_kotlin);
mTemplates.add(empty);
mTemplates.add(basic);
mTemplates.add(drawer);
// mTemplates.add(navigation);
// mTemplates.add(kotlin);
}
private void createProgressSheet() {
mWritingProgressSheet = new ProgressSheet();
}
private void setMessage(int msg) {
if(getContext() != null) {
setMessage(getContext().getString(msg));
}
}
private void setMessage(String msg) {
mWritingProgressSheet.setMessage(msg);
}
private void createNewProject() {
final String appName = createLayoutBinding.createprojectTextAppName.getEditText().getText().toString().trim();
final String packageName = createLayoutBinding.createprojectTextPackageName.getEditText().getText().toString().trim();
final int minSdk = getMinSdk();
final int targetSdk = getTargetSdk();
if(!isValid(appName, packageName, minSdk, targetSdk)) {
getStudioActivity().getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
return;
}
createProject(appName, packageName, minSdk, targetSdk);
}
private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
new TaskExecutor().executeAsync(new ProjectCreatorCallable(mTemplates.get(currentTemplateIndex), new NewProjectDetails(appName, packageName, minSdk, targetSdk), this), r -> {});
}
private boolean isValid(String appName, String packageName, int minSdk, int targetSdk)
{
return appName != null && appName.length() > 0
&& packageName != null && packageName.length() > 0
&& minSdk > 1 && minSdk < 99
&& targetSdk > 1 && minSdk < 99;
}
private int getMinSdk()
{
try
{
return Integer.parseInt(createLayoutBinding.createprojectTextMinSdk.getEditText().getText().toString());
} catch (Exception e)
{ getStudioActivity().getApp().toast(e.getMessage(), Toaster.Type.ERROR); }
return -1;
}
private int getTargetSdk()
{
try
{
return Integer.parseInt(createLayoutBinding.createprojectTextTargetSdk.getEditText().getText().toString());
} catch (Exception e)
{ getStudioActivity().getApp().toast(e.getMessage(), Toaster.Type.ERROR); }
return -1;
}
private void showCreateProject()
{
ChangeBounds bounds = new ChangeBounds();
bounds.setPathMotion(new ArcMotionPlus().setArcAngle(90));
bounds.setDuration(300);
bounds.addListener(new OnEndTransitionListener(() -> {
final int x = (int) binding.createNew.getX() + (binding.createNew.getWidth() / 2);
final int y = (int) binding.createNew.getY() + (binding.createNew.getHeight() / 2);
binding.createNewCard.setVisibility(View.VISIBLE);
int startRadius = binding.createNew.getWidth() / 2;
int endRadius = Math.max(binding.getRoot().getWidth(), binding.getRoot().getHeight());
Animator anim = ViewAnimationUtils.createCircularReveal(binding.createNewCard, x, y, startRadius, endRadius);
anim.setDuration(600);
anim.start();
if(visibilityChangeListener != null)
visibilityChangeListener.onVisibilityChanged(true);
return null;
}));
TransitionManager.beginDelayedTransition(binding.getRoot(), bounds);
ConstraintLayout.LayoutParams p = (ConstraintLayout.LayoutParams) binding.createNew.getLayoutParams();
p.topToTop = p.PARENT_ID;
p.rightToRight = p.PARENT_ID;
p.leftToLeft = p.UNSET;
binding.createNew.setLayoutParams(p);
}
private void hideCreateProject()
{
int x = (int) binding.createNew.getX() + (binding.createNew.getWidth() / 2);
int y = (int) binding.createNew.getY() + (binding.createNew.getHeight() / 2);
int startRadius = Math.max(binding.getRoot().getWidth(), binding.getRoot().getHeight());
int endRadius = binding.createNew.getWidth() / 2;
Animator anim = ViewAnimationUtils.createCircularReveal(binding.createNewCard, x, y, startRadius, endRadius);
anim.setDuration(500);
anim.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(Animator anim)
{
binding.createNewCard.setVisibility(View.GONE);
if(visibilityChangeListener != null)
visibilityChangeListener.onVisibilityChanged(false);
ChangeBounds bounds = new ChangeBounds();
bounds.setPathMotion(new ArcMotionPlus().setArcAngle(90));
bounds.setDuration(300);
TransitionManager.beginDelayedTransition(binding.getRoot(), bounds);
ConstraintLayout.LayoutParams p = (ConstraintLayout.LayoutParams) binding.createNew.getLayoutParams();
p.topToTop = p.UNSET;
p.topToBottom = binding.greetingText.getId();
p.rightToRight = p.PARENT_ID;
p.leftToLeft = p.PARENT_ID;
binding.createNew.setLayoutParams(p);
}
});
anim.start();
}
private final class OnEndTransitionListener implements Transition.TransitionListener {
Callable<Void> c;
public OnEndTransitionListener(Callable<Void> c) {
this.c = c;
}
@Override
public void onTransitionStart(Transition p1) {
}
@Override
public void onTransitionEnd(Transition p1) {
try {
c.call();
} catch (Exception e) {}
}
@Override
public void onTransitionCancel(Transition p1) {
}
@Override
public void onTransitionPause(Transition p1) {
}
@Override
public void onTransitionResume(Transition p1) {
}
}
public interface OnCreateProjectVisibilityListener {
public void onVisibilityChanged(boolean isVisible);
}
public static interface OnCreateProjectListener {
public void onNewProjectCreated();
}
}
@@ -0,0 +1,239 @@
package com.itsaky.androidide.fragments;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.EditorActivity;
import com.itsaky.androidide.adapters.ProjectsListAdapter;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutProjectsBinding;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.utils.FileUtil;
import fr.arnaudguyon.xmltojsonlib.XmlToJson;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.json.JSONException;
import org.json.JSONObject;
import com.blankj.utilcode.util.FileUtils;
import java.util.stream.Collectors;
public class ProjectsFragment extends BaseFragment {
private LayoutProjectsBinding binding;
private ArrayList<AndroidProject> mProjects;
private boolean isLoading = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = LayoutProjectsBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getActivity() == null) return;
loadProjects();
}
public void loadProjects() {
if (!isLoading && getStudioActivity().isStoragePermissionGranted()) {
getProgressBar().setVisibility(View.VISIBLE);
getRecyclerView().setVisibility(View.GONE);
setLoading(true);
final ProjectsFragment frag = this;
new TaskExecutor().executeAsync(new GetProjectsTask(), (result) -> {
frag.setLoading(false);
if (frag.getProjectsList() == null || frag.getProjectsList().size() == 0) {
frag.getProgressBar().setVisibility(View.GONE);
frag.getRecyclerView().setVisibility(View.GONE);
frag.getNothingLayout().setVisibility(View.VISIBLE);
} else {
frag.getNothingLayout().setVisibility(View.GONE);
frag.getProgressBar().setVisibility(View.GONE);
frag.getRecyclerView().setVisibility(View.VISIBLE);
frag.getRecyclerView().setLayoutManager(new LinearLayoutManager(getActivity()));
frag.getRecyclerView().setAdapter(new ProjectsListAdapter(mProjects, (project) -> openProject(project)));
}
});
}
}
private void openProject(AndroidProject project) {
startActivity(new Intent(getActivity(), EditorActivity.class)
.putExtra("project", project));
}
public void getProjects() throws Exception {
mProjects = new ArrayList<>();
File[] files = StudioApp.getInstance().listProjects();
for (File file : files) {
if (!file.isDirectory() || file.isHidden()) continue;
File settings = new File(file, "settings.gradle");
if (!settings.exists()) continue;
String content = FileUtil.readFile(settings.getAbsolutePath());
if (TextUtils.isEmpty(content)) continue;
boolean foundIcon = false;
AndroidProject project = new AndroidProject();
for (File module : file.listFiles()) {
String[] result;
if (module.isDirectory()
&& !module.isHidden()
&& (result = proceedModule(file, module, project)) != null
&& result.length == 2
&& result[0] != null
&& result[1] != null
&& !foundIcon) {
foundIcon = true;
String packageName = result[0];
String icon = result[1];
project.setPackageName(packageName)
.setIconPath(icon)
.setMainModule(module.getName())
.setMainSourcePath(new File(module, "src/main/java").getAbsolutePath());
}
}
project.setAppName(file.getName());
project.setProjectPath(file.getAbsolutePath());
mProjects.add(project);
}
}
private String[] proceedModule(File file, File module, AndroidProject project) throws JSONException {
File projectGradle = new File(file, "build.gradle");
File moduleGradle = new File(module, "build.gradle");
File manifest = new File(module, "src/main/AndroidManifest.xml");
File res = new File(module, "src/main/res");
if (!module.exists() || !projectGradle.exists() || !moduleGradle.exists() || !manifest.exists()) return null;
if (!module.isDirectory() || !moduleGradle.isFile() || !projectGradle.isFile() || !manifest.isFile()) return null;
File java = new File(module, "src/main/java");
if (java.exists() && java.isDirectory())
project.addSource(java.getAbsolutePath())
.addModule(module.getAbsolutePath());
File libsFolder = new File(module, "libs");
if (libsFolder.exists() && libsFolder.isDirectory()) {
File[] libs = libsFolder.listFiles();
if (libs != null && libs.length > 0) {
for (File lib : libs)
if (lib.getName().endsWith(".jar"))
project.addClasspath(lib.getAbsolutePath());
}
}
project.addSource(new File(module, "build/generated/source/buildConfig/debug").getAbsolutePath());
project.addSource(new File(module, "build/generated/data_binding_base_class_source_out/debug/out").getAbsolutePath());
JSONObject obj = new XmlToJson.Builder(FileUtil.readFile(manifest.getAbsolutePath())).build().toJson();
if (!obj.has("manifest")) return null;
String iconPath = null;
if (obj.has("manifest") && obj.getJSONObject("manifest").has("application") && obj.getJSONObject("manifest").getJSONObject("application").has("android:icon")) {
String iconRes = obj.getJSONObject("manifest").getJSONObject("application").getString("android:icon");
if (iconRes.startsWith("@drawable")) {
final String iconName = iconRes.replace("@drawable/", "");
File[] drawables = listDrawables(res);
for (File drawable : drawables) {
File[] icons = listFilesNotEndingWith(drawable, ".xml");
for (File icon : icons) {
String nameWithoutExtension = icon.getName().substring(0, icon.getName().lastIndexOf("."));
if (nameWithoutExtension.equals(iconName)) {
iconPath = icon.getAbsolutePath();
break;
}
}
}
} else if (iconRes.startsWith("@mipmap")) {
final String iconName = iconRes.replace("@mipmap/", "");
File[] drawables = listMipmaps(res);
for (File drawable : drawables) {
File[] icons = listFilesNotEndingWith(drawable, ".xml");
for (File icon : icons) {
String nameWithoutExtension = icon.getName().substring(0, icon.getName().lastIndexOf("."));
if (nameWithoutExtension.equals(iconName)) {
iconPath = icon.getAbsolutePath();
break;
}
}
}
}
}
final String packageName = obj.getJSONObject("manifest").getString("package");
if (iconPath == null) return null;
return new String[]{ packageName, iconPath };
}
private File[] listDrawables(File file) {
return listFolderStartingWith(file, "drawable");
}
private File[] listMipmaps(File file) {
return listFolderStartingWith(file, "mipmap");
}
private File[] listFolderStartingWith(File file, final String prefix) {
return file.listFiles(new FileFilter(){
@Override
public boolean accept(File p1) {
return p1.isDirectory() && p1.getName().startsWith(prefix);
}
});
}
private File[] listFilesNotEndingWith(File file, final String suffix) {
return file.listFiles(new FileFilter(){
@Override
public boolean accept(File p1) {
return p1.isFile() && !p1.getName().endsWith(suffix);
}
});
}
public ProgressBar getProgressBar() {
return binding.projectsProgressBar;
}
public RecyclerView getRecyclerView() {
return binding.mainProjectsRecyclerView;
}
public boolean isLoading() {
return isLoading;
}
public void setLoading(boolean isLoading) {
this.isLoading = isLoading;
}
public ArrayList<AndroidProject> getProjectsList() {
return mProjects;
}
public View getNothingLayout() {
return binding.projectsNothingLayout;
}
private class GetProjectsTask implements Callable<List<AndroidProject>> {
@Override
public List<AndroidProject> call() throws Exception {
getProjects();
return null;
}
}
}
@@ -0,0 +1,12 @@
package com.itsaky.androidide.fragments.preferences;
import android.os.Bundle;
import com.itsaky.androidide.R;
public class AppearancePreferences extends BasePreferenceFragment {
@Override
public void onCreatePreferences(Bundle p1, String p2) {
super.onCreatePreferences(p1, p2);
}
}
@@ -0,0 +1,31 @@
package com.itsaky.androidide.fragments.preferences;
import androidx.preference.PreferenceFragmentCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.os.Bundle;
import android.view.ViewGroup;
import androidx.core.content.ContextCompat;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.utils.PreferenceManager;
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
protected PreferenceManager mPrefManager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = super.onCreateView(inflater, container, savedInstanceState);
v.setBackgroundColor(ContextCompat.getColor(StudioApp.getInstance(), com.itsaky.androidide.R.color.content_background));
return v;
}
@Override
public void onCreatePreferences(Bundle p1, String p2) {
setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2);
}
protected PreferenceManager getPrefManager() {
return mPrefManager == null ? mPrefManager = new PreferenceManager(getContext()) : mPrefManager;
}
}
@@ -0,0 +1,226 @@
package com.itsaky.androidide.fragments.preferences;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.ZipUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.util.List;
import java.util.Set;
import static com.itsaky.androidide.utils.PreferenceManager.*;
public class BuildPreferences extends BasePreferenceFragment implements Preference.OnPreferenceClickListener {
public static final String KEY_GRADLE_INSTALL = "idepref_build_gradleInstall";
public static final String KEY_GRADLE_COMMAMDS = "idepref_build_gradleCommands";
public static final String KEY_GRADLE_CLEAR_CACHE = "idepref_build_gradleClearCache";
public static final String KEY_CHOOSE_GRADLE = "idepref_build_chooseGradle";
private ProgressSheet progressSheet;
@Override
public void onCreatePreferences(Bundle p1, String p2) {
super.onCreatePreferences(p1, p2);
if(getContext() == null) return;
final PreferenceScreen screen = getPreferenceScreen();
final PreferenceCategory categoryGradle = new PreferenceCategory(getContext());
final Preference customGradle = new Preference(getContext());
final Preference chooseGradle = new Preference(getContext());
final Preference customCommands = new Preference(getContext());
final Preference clearCache = new Preference(getContext());
screen.addPreference(categoryGradle);
customGradle.setKey(KEY_GRADLE_INSTALL);
customGradle.setIcon(R.drawable.ic_language_gradle);
customGradle.setTitle(R.string.idepref_build_installgradle_title);
customGradle.setSummary(R.string.idepref_build_installgradle_summary);
chooseGradle.setKey(KEY_CHOOSE_GRADLE);
chooseGradle.setIcon(R.drawable.ic_file_rename);
chooseGradle.setTitle(R.string.idepref_build_choosegradle_title);
chooseGradle.setSummary(R.string.idepref_build_choosegradle_summary);
customCommands.setKey(KEY_GRADLE_COMMAMDS);
customCommands.setIcon(R.drawable.ic_bash_commands);
customCommands.setTitle(R.string.idepref_build_customgradlecommands_title);
customCommands.setSummary(R.string.idepref_build_customgradlecommands_summary);
clearCache.setKey(KEY_GRADLE_CLEAR_CACHE);
clearCache.setIcon(R.drawable.ic_file_delete);
clearCache.setTitle(R.string.idepref_build_clearCache_title);
clearCache.setSummary(R.string.idepref_build_clearCache_summary);
categoryGradle.setTitle(R.string.gradle);
// categoryGradle.addPreference(customGradle);
// categoryGradle.addPreference(chooseGradle);
categoryGradle.addPreference(customCommands);
categoryGradle.addPreference(clearCache);
setPreferenceScreen(screen);
customGradle.setOnPreferenceClickListener(this);
chooseGradle.setOnPreferenceClickListener(this);
customCommands.setOnPreferenceClickListener(this);
clearCache.setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceClick(Preference p1) {
final String key = p1.getKey();
if(key.equals(KEY_GRADLE_INSTALL)) {
showInstallGradleDialog();
} else if(key.equals(KEY_GRADLE_COMMAMDS)) {
showGradleCommandsDialog();
} else if(key.equals(KEY_GRADLE_CLEAR_CACHE)) {
showClearCacheDialog();
} else if(key.equals(KEY_CHOOSE_GRADLE)) {
showChooseGradle();
}
return true;
}
private void showChooseGradle() {
final Set<String> installed = getPrefManager().getGradleFolderNames();
final String[] labels = installed.toArray(new String[installed.size()]);
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext());
builder.setTitle(R.string.idepref_build_choosegradle_title);
builder.setSingleChoiceItems(labels, 0, (p1, p2) -> {
DialogInterface iface = p1;
int pos = p2;
});
builder.create().show();
}
private void showInstallGradleDialog() {
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(LayoutInflater.from(getContext()));
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext());
binding.name.setHint(R.string.zip_file_path);
builder.setTitle(R.string.idepref_build_installgradle_title);
builder.setMessage(R.string.msg_custom_gradle);
builder.setView(binding.getRoot());
builder.setPositiveButton(android.R.string.ok, (p1, p2) -> {
final String path = binding.name.getEditText().getText().toString();
if(path.trim().length() <= 0) {
StudioApp.getInstance().toast(R.string.invalid_zip, Toaster.Type.ERROR);
return;
}
File file = new File(path.trim());
if(!(file.exists() && file.isFile() && file.getName().endsWith(".zip"))) {
StudioApp.getInstance().toast(R.string.msg_file_should_be_zip, Toaster.Type.ERROR);
return;
}
p1.dismiss();
getProgressSheet().setCancelable(false);
getProgressSheet().setShowShadow(false);
getProgressSheet().setSubMessageEnabled(false);
getProgressSheet().setWelcomeTextEnabled(false);
getProgressSheet().show(getChildFragmentManager(), "custom_gradle_progress");
new TaskExecutor().executeAsync(() -> {
try {
List<File> extracted = ZipUtils.unzipFile(file, Environment.HOME);
if(extracted != null && extracted.size() > 0) {
File newestFolder = null;
for(File f : extracted) {
if((newestFolder = f.getParentFile()).getAbsolutePath().equals(Environment.HOME.getAbsolutePath())) {
break;
}
}
if(newestFolder != null) {
getPrefManager().setCurentGradleFolderName(newestFolder.getName());
Environment.GRADLE_FOLDER_NAME = newestFolder.getName();
Environment.updateGradleDir();
StudioApp.getInstance().toast(R.string.msg_gradle_installed, Toaster.Type.SUCCESS);
getProgressSheet().dismiss();
}
} else {
StudioApp.getInstance().toast(R.string.failed_unzip, Toaster.Type.ERROR);
}
} catch (Throwable th) {
StudioApp.getInstance().toast(R.string.failed_unzip, Toaster.Type.ERROR);
}
return null;
}, __ -> {});
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.create().show();
}
private void showGradleCommandsDialog() {
final String[] labels = {
"--stacktrace",
"--info",
"--debug",
"--scan",
"--warning-mode all"
};
final boolean[] checked = {
getPrefManager().isStracktraceEnabled(),
getPrefManager().isGradleInfoEnabled(),
getPrefManager().isGradleDebugEnabled(),
getPrefManager().isGradleScanEnabled(),
getPrefManager().isGradleWarningEnabled()
};
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext());
builder.setTitle(R.string.idepref_build_customgradlecommands_title);
builder.setMultiChoiceItems(labels, checked, (p1, p2, p3) -> {
boolean isChecked = p3;
int pos = p2;
if(pos == 0) {
getPrefManager().setGradleStacktraceEnabled(isChecked);
} else if(pos == 1) {
getPrefManager().setGradleInfoEnabled(isChecked);
} else if(pos == 2) {
getPrefManager().setGradleDebugEnabled(isChecked);
} else if(pos == 3) {
getPrefManager().setGradleScanEnabled(isChecked);
} else if(pos == 4) {
getPrefManager().setGradleWarningEnabled(isChecked);
}
});
builder.setPositiveButton(android.R.string.ok, null);
builder.setCancelable(false);
builder.create().show();
}
private void showClearCacheDialog() {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext());
builder.setTitle(R.string.idepref_build_clearCache_title);
builder.setMessage(R.string.msg_clear_cache);
builder.setCancelable(false);
builder.setPositiveButton(android.R.string.yes, (p1, p2) -> {
p1.dismiss();
getProgressSheet().show(getChildFragmentManager(), "progress_sheet");
new TaskExecutor().executeAsync(() -> deleteCaches(), __ -> {
getProgressSheet().dismiss();
});
});
builder.setNegativeButton(android.R.string.no, null);
builder.create().show();
}
private Object deleteCaches() {
File file = new File(Environment.HOME, ".gradle/caches");
if(file.exists()) {
FileUtils.delete(file);
}
return null;
}
private ProgressSheet getProgressSheet() {
return progressSheet == null ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait)) : progressSheet;
}
}
@@ -0,0 +1,153 @@
package com.itsaky.androidide.fragments.preferences;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutTextSizeSliderBinding;
import static com.itsaky.androidide.utils.PreferenceManager.*;
import com.itsaky.androidide.models.ConstantsBridge;
import com.google.android.material.slider.LabelFormatter;
import java.text.NumberFormat;
import java.util.Locale;
public class EditorPreferences extends BasePreferenceFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
public static final String KEY_EDITOR_FONT_SIZE = "idepref_editor_fontSize";
public static final String KEY_EDITOR_PRINTABLE_CHARS = "idepref_editor_nonPrintableFlags";
public static final String KEY_EDITOR_DRAW_HEX = "idepref_editor_drawHexColors";
@Override
public void onCreatePreferences(Bundle p1, String p2) {
super.onCreatePreferences(p1, p2);
if(getContext() == null) return;
final PreferenceScreen screen = getPreferenceScreen();
final Preference fontSize = new Preference(getContext());
final Preference nonPrintable = new Preference(getContext());
final SwitchPreference drawHex = new SwitchPreference(getContext());
fontSize.setIcon(R.drawable.ic_text_size);
fontSize.setKey(KEY_EDITOR_FONT_SIZE);
fontSize.setTitle(R.string.idepref_editor_fontsize_title);
fontSize.setSummary(R.string.idepref_editor_fontsize_summary);
nonPrintable.setIcon(R.drawable.ic_drawing);
nonPrintable.setKey(KEY_EDITOR_PRINTABLE_CHARS);
nonPrintable.setTitle(R.string.idepref_editor_paintingflags_title);
nonPrintable.setSummary(R.string.idepref_editor_paintingflags_summary);
drawHex.setIcon(R.drawable.ic_hexadecimal);
drawHex.setKey(KEY_EDITOR_DRAW_HEX);
drawHex.setTitle(R.string.idepref_editor_drawhexcolors_title);
drawHex.setSummary(R.string.idepref_editor_drawhexcolors_summary);
screen.addPreference(fontSize);
screen.addPreference(nonPrintable);
screen.addPreference(drawHex);
setPreferenceScreen(screen);
fontSize.setOnPreferenceClickListener(this);
nonPrintable.setOnPreferenceClickListener(this);
drawHex.setOnPreferenceChangeListener(this);
drawHex.setChecked(getPrefManager().getBoolean(KEY_EDITOR_DRAW_HEX, true));
}
@Override
public boolean onPreferenceChange(Preference p1, Object p2) {
if(p1.getKey().equals(KEY_EDITOR_DRAW_HEX)) {
boolean drawHex = (boolean) p2;
getPrefManager().putBoolean(KEY_EDITOR_DRAW_HEX, drawHex);
ConstantsBridge.EDITORPREF_DRAW_HEX_CHANGED = true;
}
return true;
}
@Override
public boolean onPreferenceClick(Preference p1) {
final String key = p1.getKey();
if(key.equals(KEY_EDITOR_FONT_SIZE)) {
showTextSizeDialog();
} else if(key.equals(KEY_EDITOR_PRINTABLE_CHARS)) {
showPrintableCharsDialog();
}
return true;
}
private void showTextSizeDialog() {
final LayoutTextSizeSliderBinding binding = LayoutTextSizeSliderBinding.inflate(LayoutInflater.from(getContext()));
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext(), R.style.AppTheme_MaterialAlertDialog);
float size = getPrefManager().getFloat(KEY_EDITOR_FONT_SIZE, 14);
if(size < 6 || size > 32) {
size = 14;
changeTextSize(binding, size);
}
binding.slider.setLabelFormatter(p1 -> String.valueOf((int) p1));
builder.setTitle(R.string.title_change_text_size);
builder.setMessage(R.string.msg_editor_font_size);
builder.setView(binding.getRoot());
builder.setPositiveButton(android.R.string.ok, (p1, p2) -> {
p1.dismiss();
float s = binding.slider.getValue();
changeTextSize(binding, s);
});
builder.setNeutralButton(R.string.reset, (p1, p2) -> {
changeTextSize(binding, 14);
});
builder.setCancelable(false);
builder.create().show();
}
private void changeTextSize(LayoutTextSizeSliderBinding binding, float size) {
getPrefManager().putFloat(KEY_EDITOR_FONT_SIZE, size);
binding.slider.setValue(size);
ConstantsBridge.EDITORPREF_SIZE_CHANGED = true;
}
private void showPrintableCharsDialog() {
String[] labels = {
"Leading whitespaces",
"Trailing whitespaces",
"Inner whitespaces",
"Whitespaces in empty lines",
"Line breaks"
};
final boolean[] checked = {
getPrefManager().getBoolean(KEY_EDITORFLAG_WS_LEADING, true),
getPrefManager().getBoolean(KEY_EDITORFLAG_WS_TRAILING, false),
getPrefManager().getBoolean(KEY_EDITORFLAG_WS_INNER, true),
getPrefManager().getBoolean(KEY_EDITORFLAG_WS_EMPTY_LINE, true),
getPrefManager().getBoolean(KEY_EDITORFLAG_LINE_BREAK, true)
};
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext(), R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.idepref_editor_paintingflags_title);
builder.setMultiChoiceItems(labels, checked, (p1, p2, p3) -> {
DialogInterface iface = p1;
int pos = p2;
boolean isChecked = p3;
if(pos == 0) {
getPrefManager().putBoolean(KEY_EDITORFLAG_WS_LEADING, isChecked);
} else if(pos == 1) {
getPrefManager().putBoolean(KEY_EDITORFLAG_WS_TRAILING, isChecked);
} else if(pos == 2) {
getPrefManager().putBoolean(KEY_EDITORFLAG_WS_INNER, isChecked);
} else if(pos == 3) {
getPrefManager().putBoolean(KEY_EDITORFLAG_WS_EMPTY_LINE, isChecked);
} else if(pos == 4) {
getPrefManager().putBoolean(KEY_EDITORFLAG_LINE_BREAK, isChecked);
}
ConstantsBridge.EDITORPREF_FLAGS_CHANGED = true;
});
builder.setPositiveButton(android.R.string.ok, null);
builder.setCancelable(false);
builder.create().show();
}
}
@@ -0,0 +1,169 @@
package com.itsaky.androidide.fragments.preferences;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.StyleSpan;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.blankj.utilcode.util.ResourceUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import org.json.JSONArray;
import org.json.JSONObject;
import com.blankj.utilcode.util.SizeUtils;
public class IDEPreferences extends BasePreferenceFragment {
private AppearancePreferences mAppearancePrefs;
private BuildPreferences mBuildPreferences;
private EditorPreferences mEditorPreferences;
private String CHANGELOG = null;
public static final String KEY_APPEARANCE = "idepref_appearance";
public static final String KEY_EDITOR = "idepref_editor";
public static final String KEY_BUILD = "idepref_build";
public static final String KEY_TELEGRAM = "idepref_telegram";
public static final String KEY_ISSUES = "idepref_issues";
public static final String KEY_CHANGELOG = "idepref_changelog";
@Override
public void onCreatePreferences(Bundle savedState, String rootKey) {
super.onCreatePreferences(savedState, rootKey);
if(getContext() == null) return;
final PreferenceScreen screen = getPreferenceScreen();
final Preference appearance = new Preference(getContext());
final Preference editor = new Preference(getContext());
final Preference build = new Preference(getContext());
final Preference telegram = new Preference(getContext());
final Preference issueTracker = new Preference(getContext());
final Preference changelog = new Preference(getContext());
appearance.setKey(KEY_APPEARANCE);
appearance.setIconSpaceReserved(false);
appearance.setFragment(getAppearanceFrag().getClass().getName());
appearance.setTitle(R.string.idepref_appearance_title);
appearance.setSummary(R.string.idepref_appearance_summary);
editor.setKey(KEY_EDITOR);
editor.setIconSpaceReserved(false);
editor.setFragment(getEditorFrag().getClass().getName());
editor.setTitle(R.string.idepref_editor_title);
editor.setSummary(R.string.idepref_editor_summary);
build.setKey(KEY_BUILD);
build.setIconSpaceReserved(false);
build.setFragment(getBuildFrag().getClass().getName());
build.setTitle(R.string.idepref_build_title);
build.setSummary(R.string.idepref_build_summary);
issueTracker.setKey(KEY_ISSUES);
issueTracker.setIconSpaceReserved(false);
issueTracker.setTitle(R.string.user_suggestions);
issueTracker.setSummary(R.string.issue_tracker_summary);
telegram.setKey(KEY_TELEGRAM);
telegram.setIconSpaceReserved(false);
telegram.setTitle(R.string.discussions_on_telegram);
telegram.setSummary(R.string.discussions_on_telegram_summary);
changelog.setKey(KEY_CHANGELOG);
changelog.setIconSpaceReserved(false);
changelog.setTitle(R.string.pref_changelog);
changelog.setSummary(R.string.pref_changelog_summary);
screen.addPreference(editor);
screen.addPreference(build);
// screen.addPreference(issueTracker);
screen.addPreference(telegram);
screen.addPreference(changelog);
final Preference.OnPreferenceClickListener listener = getListener();
issueTracker.setOnPreferenceClickListener(listener);
telegram.setOnPreferenceClickListener(listener);
changelog.setOnPreferenceClickListener(listener);
}
private AppearancePreferences getAppearanceFrag() {
return mAppearancePrefs == null ? mAppearancePrefs = new AppearancePreferences() : mAppearancePrefs;
}
private EditorPreferences getEditorFrag() {
return mEditorPreferences == null ? mEditorPreferences = new EditorPreferences() : mEditorPreferences;
}
private BuildPreferences getBuildFrag() {
return mBuildPreferences == null ? mBuildPreferences = new BuildPreferences() : mBuildPreferences;
}
private void showChangelog() {
final int dp8 = SizeUtils.dp2px(8);
final int dp16 = SizeUtils.dp2px(16);
final HorizontalScrollView scroll = new HorizontalScrollView(getContext());
scroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
scroll.setFillViewport(true);
scroll.setPadding(dp16, dp8, dp16, dp8);
scroll.setPaddingRelative(dp16, dp8, dp16, dp8);
final TextView text = new TextView(getContext());
text.setText(CHANGELOG == null ? CHANGELOG = changelogAsString() : CHANGELOG);
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
text.setTextColor(ContextCompat.getColor(getContext(), R.color.primaryTextColor));
scroll.removeAllViews();
scroll.addView(text, new ViewGroup.LayoutParams(-1, -1));
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext(), R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.pref_changelog);
builder.setView(scroll);
builder.setPositiveButton(android.R.string.ok, null);
builder.create().show();
}
private String changelogAsString() {
try {
final SpannableStringBuilder sb = new SpannableStringBuilder();
JSONArray arr = new JSONArray(ResourceUtils.readAssets2String("changelog.json"));
for(int i =0;i<arr.length();i++) {
JSONObject version = arr.getJSONObject(i);
sb.append("v".concat(version.getString("version")), new StyleSpan(Typeface.BOLD), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
JSONArray changes = version.getJSONArray("changes");
for(int j=0;j<changes.length();j++) {
sb.append("\n ");
sb.append("\u2022 ".concat(changes.getString(j)));
}
sb.append("\n\n");
}
return sb.toString();
} catch (Throwable th) {
return getString(R.string.msg_failed_get_changelog);
}
}
private Preference.OnPreferenceClickListener getListener() {
return new Preference.OnPreferenceClickListener(){
@Override
public boolean onPreferenceClick(Preference p1) {
final String key = p1.getKey();
if(key.equals(KEY_CHANGELOG)) {
showChangelog();
} else if(key.equals(KEY_TELEGRAM)) {
StudioApp.getInstance().openTelegramGroup();
} else if(key.equals(KEY_ISSUES)) {
StudioApp.getInstance().openIssueTracker();
}
return true;
}
};
}
}
@@ -0,0 +1,94 @@
package com.itsaky.androidide.fragments.sheets;
import android.app.Dialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.itsaky.androidide.databinding.LayoutSheetBinding;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.MotionEvent;
import android.graphics.Rect;
import com.r0adkll.slidr.model.SlidrInterface;
import com.r0adkll.slidr.Slidr;
import com.r0adkll.slidr.model.SlidrConfig;
import com.r0adkll.slidr.model.SlidrPosition;
public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment {
protected Dialog mDialog;
private LayoutSheetBinding binding;
protected boolean shadowEnabled = true;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mDialog = super.onCreateDialog(savedInstanceState);
mDialog.setOnShowListener(p1 -> onShow());
return mDialog;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = LayoutSheetBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
bind(binding.container);
binding.title.setText(getTitle());
binding.title.setOnClickListener(v -> handleTitleClick());
if(shouldHideTitle()) {
binding.getRoot().removeView(binding.title);
}
if(!shadowEnabled) {
binding.shadow.setVisibility(View.GONE);
}
}
private void handleTitleClick() {
if(isCancelable()) {
dismiss();
}
}
public boolean isShowing() {
return mDialog != null && mDialog.isShowing();
}
public BaseBottomSheetFragment setTitle(int title) {
if(binding != null && binding.title != null) {
binding.title.setText(title);
}
return this;
}
public BaseBottomSheetFragment setTitle(String title) {
if(binding != null && binding.title != null) {
binding.title.setText(title);
}
return this;
}
protected boolean shouldHideTitle() {
return false;
}
protected String getTitle() {
return "";
}
public void setShowShadow(boolean enabled) {
this.shadowEnabled = enabled;
}
protected void onShow() {}
protected abstract void bind(LinearLayout container);
}
@@ -0,0 +1,65 @@
package com.itsaky.androidide.fragments.sheets;
import android.graphics.Typeface;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.blankj.utilcode.util.ThrowableUtils;
import com.elvishew.xlog.XLog;
import com.itsaky.androidide.R;
import com.itsaky.androidide.models.LogLine;
public class LogSheet extends BaseBottomSheetFragment {
private ScrollView verticalScroll;
private LinearLayout mContainer;
@Override
protected void bind(LinearLayout container) {
mContainer = new LinearLayout(getContext());
mContainer.setOrientation(LinearLayout.VERTICAL);
verticalScroll = new ScrollView(getContext());
verticalScroll.setFillViewport(true);
HorizontalScrollView scroll = new HorizontalScrollView(getContext());
scroll.setFillViewport(true);
container.removeAllViews();
verticalScroll.removeAllViews();
mContainer.removeAllViews();
scroll.removeAllViews();
container.addView(verticalScroll);
verticalScroll.addView(scroll, new ScrollView.LayoutParams(-1, -1));
scroll.addView(mContainer, new HorizontalScrollView.LayoutParams(-1, -1));
}
@Override
protected String getTitle() {
return getString(R.string.logs);
}
public void addLine(LogLine line) {
if(line != null) {
try {
mContainer.addView(createLogTextView(line), new ViewGroup.LayoutParams(-1, -2));
verticalScroll.scrollTo(0, mContainer.getChildAt(mContainer.getChildCount() - 1).getBottom());
} catch (Throwable th) {}
}
}
protected TextView createLogTextView(LogLine line) {
TextView text = new TextView(getContext());
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
text.setTextColor(line.getColor(getContext()));
text.setText(line + "");
text.setSingleLine(true);
text.setMaxLines(1);
text.setTypeface(Typeface.MONOSPACE);
return text;
}
}
@@ -0,0 +1,64 @@
package com.itsaky.androidide.fragments.sheets;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.adapters.OptionsSheetAdapter;
import com.itsaky.androidide.models.SheetOption;
import java.util.ArrayList;
import java.util.List;
public class OptionsListFragment extends BaseBottomSheetFragment {
private RecyclerView mList;
private OnOptionsClickListener listener;
private final List<SheetOption> mOptions = new ArrayList<>();
@Override
protected void bind(LinearLayout container) {
mList = new RecyclerView(getContext());
container.removeAllViews();
container.addView(mList, new LinearLayout.LayoutParams(-1, -1));
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mList.setLayoutManager(new LinearLayoutManager(getContext()));
mList.setAdapter(new OptionsSheetAdapter(mOptions, __ -> { dismiss(); if(listener != null) listener.onOptionsClick(__);}));
}
public OptionsListFragment setOnOptionsClickListener(OnOptionsClickListener listener) {
this.listener = listener;
return this;
}
public OptionsListFragment addOption(SheetOption option) {
if(!mOptions.contains(option)) {
mOptions.add(option);
}
return this;
}
public OptionsListFragment removeOption(int optionIndex) {
return removeOption(mOptions.get(optionIndex));
}
public OptionsListFragment removeOption(SheetOption option) {
mOptions.remove(option);
return this;
}
@Override
protected String getTitle() {
return getString(com.itsaky.androidide.R.string.file_options);
}
public static interface OnOptionsClickListener {
void onOptionsClick(SheetOption option);
}
}
@@ -0,0 +1,101 @@
package com.itsaky.androidide.fragments.sheets;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import com.github.ybq.android.spinkit.style.FoldingCube;
import com.itsaky.androidide.databinding.LayoutProgressSheetBinding;
import com.blankj.utilcode.util.SizeUtils;
import android.widget.RelativeLayout;
public class ProgressSheet extends BaseBottomSheetFragment {
private LayoutProgressSheetBinding binding;
private Drawable drawable = new FoldingCube();
private String message = "";
private String subMessage = "";
private boolean subMessageEnabled = false;
private boolean welcomTextEnabled = false;
@Override
protected void bind(LinearLayout container) {
binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
container.addView(binding.getRoot());
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.progress.setIndeterminateDrawable(new FoldingCube());
binding.message.setText(message);
if(subMessageEnabled) {
binding.subMessage.setText(subMessage);
binding.subMessage.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
try {
p.removeRule(RelativeLayout.CENTER_VERTICAL);
} catch (Throwable th) {}
binding.message.setLayoutParams(p);
} else {
binding.subMessage.setVisibility(View.GONE);
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
try {
p.addRule(RelativeLayout.CENTER_VERTICAL);
} catch (Throwable th) {}
binding.message.setLayoutParams(p);
}
if(!welcomTextEnabled) {
binding.welcomeText.setVisibility(View.GONE);
}
}
@Override
protected boolean shouldHideTitle() {
return true;
}
public ProgressSheet setWelcomeTextEnabled(boolean enabled) {
this.welcomTextEnabled = enabled;
return this;
}
public ProgressSheet setSubMessageEnabled(boolean enabled) {
this.subMessageEnabled = enabled;
return this;
}
public ProgressSheet setSubMessage(String msg) {
this.subMessage = msg;
if(isShowing()) {
binding.subMessage.setText(msg);
}
return this;
}
public ProgressSheet setMessage(String message) {
this.message = message;
if(isShowing()) {
binding.message.setText(message);
}
return this;
}
public ProgressSheet setProgressDrawable(Drawable drawable) {
this.drawable = drawable;
if(isShowing()) {
binding.progress.setIndeterminateDrawable(drawable);
}
return this;
}
@Override
public void dismiss() {
if(isShowing())
super.dismiss();
}
}
@@ -0,0 +1,117 @@
package com.itsaky.androidide.fragments.sheets;
import android.graphics.Color;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.itsaky.androidide.R;
import com.itsaky.androidide.utils.TypefaceUtils;
import io.github.rosemoe.editor.langs.EmptyLanguage;
import io.github.rosemoe.editor.widget.CodeEditor;
public class TextSheetFragment extends BaseBottomSheetFragment {
private SpannableStringBuilder outputBuilder;
private CodeEditor editor;
private boolean textSelectable;
public TextSheetFragment setTextSelectable(boolean textSelectable) {
this.textSelectable = textSelectable;
return this;
}
@Override
protected void bind(LinearLayout container) {
final CodeEditor e = getEditor();
container.setPadding(0, 0, 0, 0);
container.setPaddingRelative(0, 0, 0, 0);
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(-1, 0);
p.weight = 1.0f;
if(e.getParent() != null && e.getParent() instanceof ViewGroup) {
((ViewGroup) e.getParent()).removeView(e);
}
container.addView(getEditor(), p);
}
@Override
protected String getTitle() {
return getString(R.string.build_output);
}
@Override
protected void onShow() {
super.onShow();
setToEditor(SpannableString.valueOf(getOutputBuilder()));
}
private Void scrollToBottom() {
if(getActivity() != null)
getActivity().runOnUiThread(() -> {
editor.goToEnd();
});
return null;
}
public TextSheetFragment setTitleText(int res) {
super.setTitle(res);
return this;
}
public TextSheetFragment setTitleText(String res) {
super.setTitle(res);
return this;
}
public void append(String text) {
append(text, -1, true);
}
public void append(String text, int spanColor, boolean appendLine) {
if(text != null && text.trim().length() > 0) {
appendInternal(appendLine && !text.endsWith("\n") ? text.concat("\n") : text, spanColor);
}
}
private void appendInternal(String text, int spanColor) {
SpannableString str = new SpannableString(text);
str.setSpan(new ForegroundColorSpan(spanColor == -1 ? Color.WHITE : spanColor), 0, text.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
getOutputBuilder().append(str);
if(mDialog != null && mDialog.isShowing()) {
setToEditor(str);
}
}
private void setToEditor(SpannableString line) {
if(getActivity() != null)
getActivity().runOnUiThread(() -> {
editor.getText().append(line);
scrollToBottom();
});
}
private CodeEditor getEditor() {
return editor == null ? createEditor() : editor;
}
private CodeEditor createEditor() {
editor = new CodeEditor(getContext());
editor.setEditable(false);
editor.setDividerWidth(0);
editor.setEditorLanguage(new EmptyLanguage());
editor.setOverScrollEnabled(false);
editor.setTextActionMode(CodeEditor.TextActionMode.ACTION_MODE);
editor.setWordwrap(false);
editor.setUndoEnabled(false);
editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
editor.setTextSize(12);
return editor;
}
private SpannableStringBuilder getOutputBuilder() {
return outputBuilder == null ? outputBuilder = new SpannableStringBuilder() : outputBuilder;
}
}
@@ -0,0 +1,10 @@
package com.itsaky.androidide.interfaces;
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
import java.io.File;
import java.util.List;
import java.util.Map;
public interface CompileListener {
void onCompilationResult(boolean success, Map<File, List<CompilerDiagnostic>> diagnostics);
}
@@ -0,0 +1,10 @@
package com.itsaky.androidide.interfaces;
import androidx.core.util.Pair;
import java.util.List;
public interface DexResultListener {
public void onDex(String name);
public void onDexSuccess(List<String> dexes);
public void onDexFailed(String message);
}
@@ -0,0 +1,6 @@
package com.itsaky.androidide.interfaces;
public interface Filter<T> {
boolean accept(T t);
}
@@ -0,0 +1,5 @@
package com.itsaky.androidide.interfaces;
public interface LanguageServerListener
{
}
@@ -0,0 +1,8 @@
package com.itsaky.androidide.interfaces;
public interface ProjectWriterCallback {
public void beforeBegin();
public void onProcessTask(String taskName);
public void onSuccess();
public void onFailed(String reason);
}
@@ -0,0 +1,39 @@
package com.itsaky.androidide.language.groovy;
import android.graphics.Color;
import com.blankj.utilcode.util.ThrowableUtils;
import com.itsaky.androidide.syntax.lexer.impls.groovy.GroovyLexerImpl;
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
import io.github.rosemoe.editor.struct.HexColor;
import io.github.rosemoe.editor.text.CharPosition;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.text.TextAnalyzer;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.antlr.v4.runtime.Token;
public class GroovyAnalyzer implements CodeAnalyzer {
@Override
public void analyze(CharSequence content, TextAnalyzeResult colors, TextAnalyzer.AnalyzeThread.Delegate delegate) {
try {
GroovyLexerImpl lexer = new GroovyLexerImpl(content.toString(), colors);
lexer.init();
// Adding spans, hex colors, block lines and all is handled by JavaLexerImpl
while (delegate.shouldAnalyze()) {
// null = EOF
if(lexer.nextToken() == null)
break;
}
if (lexer.stack.isEmpty() && lexer.currSwitch > lexer.maxSwitch)
lexer.maxSwitch = lexer.currSwitch;
colors.determine(lexer.lastLine);
colors.setSuppressSwitch(lexer.maxSwitch + 10);
colors.setHexColors(lexer.lineColors);
} catch (Throwable e) {
}
}
}
@@ -0,0 +1,356 @@
package com.itsaky.androidide.language.groovy;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.models.GradleSuggestItem;
import com.itsaky.androidide.utils.Either;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class GroovyAutoComplete implements AutoCompleteProvider {
private static final List<String> ANDROIDX_ARTIFACTS = createArtifacts();
private static final List<String> CONFIGURATIONS = createConfigs();
private static final List<String> OTHERS = createOtherCompletions();
@Override
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
List<Either<SuggestItem, CompletionItem>> result = new ArrayList<>();
for(String artifact : ANDROIDX_ARTIFACTS) {
if(!artifact.toLowerCase(Locale.US).startsWith(prefix.toLowerCase(Locale.US))) continue;
SuggestItem item = createCompletionItem(artifact, prefix, true);
Either<SuggestItem, CompletionItem> either = Either.forLeft(item);
result.add(either);
}
for(String config : CONFIGURATIONS) {
if(!config.toLowerCase(Locale.US).startsWith(prefix.toLowerCase(Locale.US))) continue;
SuggestItem item = createCompletionItem(config, prefix, false);
Either<SuggestItem, CompletionItem> either = Either.forLeft(item);
result.add(either);
}
for(String other : OTHERS) {
if(!other.toLowerCase(Locale.US).startsWith(prefix.toLowerCase(Locale.US))) continue;
SuggestItem item = createCompletionItem(other, prefix, false);
Either<SuggestItem, CompletionItem> either = Either.forLeft(item);
result.add(either);
}
return result;
}
private SuggestItem createCompletionItem(String itemLabel, String prefix, boolean artifact) {
return new GradleSuggestItem(itemLabel, "", prefix, artifact);
}
private static List<String> createArtifacts() {
List<String> artifacts = new ArrayList<String>();
artifacts.add("androidx.arch.core:core-common");
artifacts.add("androidx.arch.core:core");
artifacts.add("androidx.arch.core:core-testing");
artifacts.add("androidx.arch.core:core-runtime");
artifacts.add("androidx.lifecycle:lifecycle-common");
artifacts.add("androidx.lifecycle:lifecycle-common-java8");
artifacts.add("androidx.lifecycle:lifecycle-compiler");
artifacts.add("androidx.lifecycle:lifecycle-extensions");
artifacts.add("androidx.lifecycle:lifecycle-livedata");
artifacts.add("androidx.lifecycle:lifecycle-livedata-core");
artifacts.add("androidx.lifecycle:lifecycle-reactivestreams");
artifacts.add("androidx.lifecycle:lifecycle-runtime");
artifacts.add("androidx.lifecycle:lifecycle-viewmodel");
artifacts.add("androidx.paging:paging-common");
artifacts.add("androidx.paging:paging-runtime");
artifacts.add("androidx.paging:paging-rxjava2");
artifacts.add("androidx.room:room-common");
artifacts.add("androidx.room:room-compiler");
artifacts.add("androidx.room:room-guava");
artifacts.add("androidx.room:room-migration");
artifacts.add("androidx.room:room-runtime");
artifacts.add("androidx.room:room-rxjava2");
artifacts.add("androidx.room:room-testing");
artifacts.add("androidx.sqlite:sqlite");
artifacts.add("androidx.sqlite:sqlite-framework");
artifacts.add("androidx.constraintlayout:constraintlayout");
artifacts.add("androidx.constraintlayout:constraintlayout-solver");
artifacts.add("androidx.test.espresso.idling:idling-concurrent");
artifacts.add("androidx.test.espresso.idling:idling-net");
artifacts.add("androidx.test.espresso:espresso-accessibility");
artifacts.add("androidx.test.espresso:espresso-contrib");
artifacts.add("androidx.test.espresso:espresso-core");
artifacts.add("androidx.test.espresso:espresso-idling-resource");
artifacts.add("androidx.test.espresso:espresso-intents");
artifacts.add("androidx.test.espresso:espresso-remote");
artifacts.add("androidx.test.espresso:espresso-web");
artifacts.add("androidx.test.jank:janktesthelper");
artifacts.add("androidx.test:test-services");
artifacts.add("androidx.test.uiautomator:uiautomator");
artifacts.add("androidx.test:monitor");
artifacts.add("androidx.test:orchestrator");
artifacts.add("androidx.test:rules");
artifacts.add("androidx.test:runner");
artifacts.add("androidx.vectordrawable:vectordrawable-animated");
artifacts.add("androidx.appcompat:appcompat");
artifacts.add("androidx.asynclayoutinflater:asynclayoutinflater");
artifacts.add("androidx.car:car");
artifacts.add("androidx.cardview:cardview");
artifacts.add("androidx.collection:collection");
artifacts.add("androidx.coordinatorlayout:coordinatorlayout");
artifacts.add("androidx.cursoradapter:cursoradapter");
artifacts.add("androidx.browser:browser");
artifacts.add("androidx.customview:customview");
artifacts.add("com.google.android.material:material");
artifacts.add("androidx.documentfile:documentfile");
artifacts.add("androidx.drawerlayout:drawerlayout");
artifacts.add("androidx.exifinterface:exifinterface");
artifacts.add("androidx.gridlayout:gridlayout");
artifacts.add("androidx.heifwriter:heifwriter");
artifacts.add("androidx.interpolator:interpolator");
artifacts.add("androidx.leanback:leanback");
artifacts.add("androidx.loader:loader");
artifacts.add("androidx.localbroadcastmanager:localbroadcastmanager");
artifacts.add("androidx.media2:media2");
artifacts.add("androidx.media2:media2-exoplayer");
artifacts.add("androidx.mediarouter:mediarouter");
artifacts.add("androidx.multidex:multidex");
artifacts.add("androidx.multidex:multidex-instrumentation");
artifacts.add("androidx.palette:palette");
artifacts.add("androidx.percentlayout:percentlayout");
artifacts.add("androidx.leanback:leanback-preference");
artifacts.add("androidx.legacy:legacy-preference-v14");
artifacts.add("androidx.preference:preference");
artifacts.add("androidx.print:print");
artifacts.add("androidx.recommendation:recommendation");
artifacts.add("androidx.recyclerview:recyclerview-selection");
artifacts.add("androidx.recyclerview:recyclerview");
artifacts.add("androidx.slice:slice-builders");
artifacts.add("androidx.slice:slice-core");
artifacts.add("androidx.slice:slice-view");
artifacts.add("androidx.slidingpanelayout:slidingpanelayout");
artifacts.add("androidx.annotation:annotation");
artifacts.add("androidx.core:core");
artifacts.add("androidx.contentpager:contentpager");
artifacts.add("androidx.legacy:legacy-support-core-ui");
artifacts.add("androidx.legacy:legacy-support-core-utils");
artifacts.add("androidx.dynamicanimation:dynamicanimation");
artifacts.add("androidx.emoji:emoji");
artifacts.add("androidx.emoji:emoji-appcompat");
artifacts.add("androidx.emoji:emoji-bundled");
artifacts.add("androidx.fragment:fragment");
artifacts.add("androidx.media:media");
artifacts.add("androidx.tvprovider:tvprovider");
artifacts.add("androidx.legacy:legacy-support-v13");
artifacts.add("androidx.legacy:legacy-support-v4");
artifacts.add("androidx.vectordrawable:vectordrawable");
artifacts.add("androidx.swiperefreshlayout:swiperefreshlayout");
artifacts.add("androidx.textclassifier:textclassifier");
artifacts.add("androidx.transition:transition");
artifacts.add("androidx.versionedparcelable:versionedparcelable");
artifacts.add("androidx.viewpager:viewpager");
artifacts.add("androidx.wear:wear");
artifacts.add("androidx.webkit:webkit");
artifacts.add("fileTree(dir: 'libs', include: ['*.jar'])");
return artifacts;
}
private static List<String> createConfigs() {
List<String> configs = new ArrayList<String>();
configs.add("androidApis");
configs.add("androidTestAnnotationProcessor");
configs.add("androidTestApi");
configs.add("androidTestApk");
configs.add("androidTestCompile");
configs.add("androidTestCompileOnly");
configs.add("androidTestDebugAnnotationProcessor");
configs.add("androidTestDebugApi");
configs.add("androidTestDebugApk");
configs.add("androidTestDebugCompile");
configs.add("androidTestDebugCompileOnly");
configs.add("androidTestDebugImplementation");
configs.add("androidTestDebugProvided");
configs.add("androidTestDebugRuntimeOnly");
configs.add("androidTestDebugWearApp");
configs.add("androidTestImplementation");
configs.add("androidTestProvided");
configs.add("androidTestRuntimeOnly");
configs.add("androidTestUtil");
configs.add("androidTestWearApp");
configs.add("annotationProcessor");
configs.add("api");
configs.add("apk");
configs.add("archives");
configs.add("compile");
configs.add("compileOnly");
configs.add("coreLibraryDesugaring");
configs.add("debugAnnotationProcessor");
configs.add("debugApi");
configs.add("debugApk");
configs.add("debugCompile");
configs.add("debugCompileOnly");
configs.add("debugImplementation");
configs.add("debugProvided");
configs.add("debugRuntimeOnly");
configs.add("debugWearApp");
configs.add("default");
configs.add("implementation");
configs.add("implementation platform()");
configs.add("lintChecks");
configs.add("lintClassPath");
configs.add("lintPublish");
configs.add("provided");
configs.add("releaseAnnotationProcessor");
configs.add("releaseApi");
configs.add("releaseApk");
configs.add("releaseCompile");
configs.add("releaseCompileOnly");
configs.add("releaseImplementation");
configs.add("releaseProvided");
configs.add("releaseRuntimeOnly");
configs.add("releaseWearApp");
configs.add("runtimeOnly");
configs.add("testAnnotationProcessor");
configs.add("testApi");
configs.add("testApk");
configs.add("testCompile");
configs.add("testCompileOnly");
configs.add("testDebugAnnotationProcessor");
configs.add("testDebugApi");
configs.add("testDebugApk");
configs.add("testDebugCompile");
configs.add("testDebugCompileOnly");
configs.add("testDebugImplementation");
configs.add("testDebugProvided");
configs.add("testDebugRuntimeOnly");
configs.add("testDebugWearApp");
configs.add("testImplementation");
configs.add("testProvided");
configs.add("testReleaseAnnotationProcessor");
configs.add("testReleaseApi");
configs.add("testReleaseApk");
configs.add("testReleaseCompile");
configs.add("testReleaseCompileOnly");
configs.add("testReleaseImplementation");
configs.add("testReleaseProvided");
configs.add("testReleaseRuntimeOnly");
configs.add("testReleaseWearApp");
configs.add("testRuntimeOnly");
configs.add("testWearApp");
configs.add("wearApp");
return configs;
}
private static List<String> createOtherCompletions() {
List<String> others = new ArrayList<String>();
// Common words that user may use
others.add("android");
others.add("applicationVariants");
others.add("compileSdkVersion");
others.add("buildToolsVersion");
others.add("defaultConfig");
others.add("applicationId");
others.add("minSdkVersion");
others.add("targetSdkVersion");
others.add("versionCode");
others.add("versionName");
others.add("multiDexEnabled");
others.add("vectorDrawables.useSupportLibrary");
others.add("compileOptions");
others.add("coreLibraryDesugaringEnabled");
others.add("sourceCompatibility");
others.add("targetCompatibility");
others.add("JavaVersion.VERSION_1_8");
others.add("JavaVersion.VERSION_1_7");
// Lint options
others.add("lintOptions");
others.add("quiet");
others.add("abortOnError");
others.add("checkReleaseBuilds");
others.add("ignoreWarnings");
others.add("checkAllWarnings");
others.add("warningsAsErrors");
others.add("disable");
others.add("enable");
others.add("check");
others.add("noLines");
others.add("showAll");
others.add("explainIssues");
others.add("lintConfig");
others.add("textReport");
others.add("textOutput");
others.add("xmlReport");
others.add("xmlOutput");
others.add("htmlReport");
others.add("htmlOutput");
others.add("fatal");
others.add("error");
others.add("warning");
others.add("ignore");
others.add("informational");
others.add("baseline");
others.add("checkTestSources");
others.add("ignoreTestSources");
others.add("checkGeneratedSources");
others.add("checkDependencies");
// buildFeatures options
others.add("buildFeatures");
others.add("aidl");
others.add("buildConfig");
others.add("compose");
others.add("prefab");
others.add("renderScript");
others.add("resValues");
others.add("shaders");
others.add("viewBinding");
// Signing configs and all
others.add("signingConfigs");
others.add("debug");
others.add("storeFile");
others.add("storePassword");
others.add("keyAlias");
others.add("keyPassword");
others.add("release");
others.add("storeFile");
others.add("storePassword");
others.add("keyAlias");
others.add("keyPassword");
others.add("buildTypes");
others.add("debug");
others.add("minifyEnabled");
others.add("proguardFiles");
others.add("signingConfig");
others.add("stringfog.enable");
others.add("release");
others.add("minifyEnabled");
others.add("proguardFiles");
others.add("signingConfig");
others.add("stringfog.enable");
others.add("packagingOptions");
others.add("exclude");
// Maybe used in root project's build.gradle
others.add("buildscript");
others.add("ext");
others.add("project.ext");
others.add("repositories");
others.add("maven");
others.add("url");
others.add("google()");
others.add("mavenLocal()");
others.add("mavenCentral()");
others.add("jcenter()");
others.add("classpath");
others.add("allprojects");
others.add("subprojects");
return others;
}
}
@@ -0,0 +1,109 @@
package com.itsaky.androidide.language.groovy;
import com.itsaky.androidide.language.groovy.lexer.GroovyLexer;
import com.itsaky.androidide.language.java.manager.JavaCharacter;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
import io.github.rosemoe.editor.interfaces.EditorLanguage;
import io.github.rosemoe.editor.interfaces.NewlineHandler;
import io.github.rosemoe.editor.text.TextUtils;
import io.github.rosemoe.editor.widget.SymbolPairMatch;
import java.io.StringReader;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
public class GroovyLanguage implements EditorLanguage {
private GroovyAnalyzer analyzer;
private GroovyAutoComplete completor;
public GroovyLanguage() {
analyzer = new GroovyAnalyzer();
completor = new GroovyAutoComplete();
}
@Override
public CodeAnalyzer getAnalyzer() {
return analyzer;
}
@Override
public AutoCompleteProvider getAutoCompleteProvider() {
return completor;
}
@Override
public boolean isAutoCompleteChar(char ch) {
return JavaCharacter.isJavaIdentifierPart(ch) || ch == '.';
}
@Override
public int getIndentAdvance(String p1)
{
try
{
GroovyLexer lexer = new GroovyLexer(CharStreams.fromReader(new StringReader(p1)));
Token token = null;
int advance = 0;
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF))
{
switch (token.getType())
{
case GroovyLexer.LBRACE:
advance++;
break;
case GroovyLexer.RBRACE:
advance --;
break;
}
}
advance = Math.max(0, advance);
return advance * 4;
} catch (Throwable e)
{}
return 0;
}
@Override
public SymbolPairMatch getSymbolPairs() {
return new SymbolPairMatch.DefaultSymbolPairs();
}
@Override
public boolean useTab() {
return true;
}
@Override
public CharSequence format(CharSequence content) {
return content;
}
private NewlineHandler[] newlineHandlers = new NewlineHandler[] { new BraceHandler() };
@Override
public NewlineHandler[] getNewlineHandlers() {
return newlineHandlers;
}
class BraceHandler implements NewlineHandler {
@Override
public boolean matchesRequirement(String beforeText, String afterText) {
beforeText = beforeText.trim();
afterText = afterText.trim();
return beforeText.endsWith("{") && afterText.startsWith("}");
}
@Override
public HandleResult handleNewline(String beforeText, String afterText, int tabSize) {
int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
int advanceBefore = getIndentAdvance(beforeText);
int advanceAfter = getIndentAdvance(afterText);
String text;
StringBuilder sb = new StringBuilder("\n").append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab())).append('\n').append(text = TextUtils.createIndent(count + advanceAfter, tabSize, useTab()));
int shiftLeft = text.length() + 1;
return new HandleResult(sb, shiftLeft);
}
}
}
@@ -0,0 +1,212 @@
/*
[The "BSD licence"]
Copyright (c) 2013 Terence Parr, Sam Harwell
Copyright (c) 2017 Ivan Kochurkin (upgrade to Java 8)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
lexer grammar GroovyLexer;
// Keywords
ABSTRACT: 'abstract';
ASSERT: 'assert';
BOOLEAN: 'boolean';
BREAK: 'break';
BYTE: 'byte';
CASE: 'case';
CATCH: 'catch';
CHAR: 'char';
CLASS: 'class';
CONST: 'const';
CONTINUE: 'continue';
DEFAULT: 'default';
DO: 'do';
DOUBLE: 'double';
ELSE: 'else';
ENUM: 'enum';
EXTENDS: 'extends';
FINAL: 'final';
FINALLY: 'finally';
FLOAT: 'float';
FOR: 'for';
IF: 'if';
GOTO: 'goto';
IMPLEMENTS: 'implements';
IMPORT: 'import';
INSTANCEOF: 'instanceof';
INT: 'int';
INTERFACE: 'interface';
LONG: 'long';
NATIVE: 'native';
NEW: 'new';
PACKAGE: 'package';
PRIVATE: 'private';
PROTECTED: 'protected';
PUBLIC: 'public';
RETURN: 'return';
SHORT: 'short';
STATIC: 'static';
STRICTFP: 'strictfp';
SUPER: 'super';
SWITCH: 'switch';
SYNCHRONIZED: 'synchronized';
THIS: 'this';
THROW: 'throw';
THROWS: 'throws';
TRANSIENT: 'transient';
TRY: 'try';
VOID: 'void';
VOLATILE: 'volatile';
WHILE: 'while';
// Literals
DECIMAL_LITERAL: ('0' | [1-9] (Digits? | '_'+ Digits)) [lL]?;
HEX_LITERAL: '0' [xX] [0-9a-fA-F] ([0-9a-fA-F_]* [0-9a-fA-F])? [lL]?;
OCT_LITERAL: '0' '_'* [0-7] ([0-7_]* [0-7])? [lL]?;
BINARY_LITERAL: '0' [bB] [01] ([01_]* [01])? [lL]?;
FLOAT_LITERAL: (Digits '.' Digits? | '.' Digits) ExponentPart? [fFdD]?
| Digits (ExponentPart [fFdD]? | [fFdD])
;
HEX_FLOAT_LITERAL: '0' [xX] (HexDigits '.'? | HexDigits? '.' HexDigits) [pP] [+-]? Digits [fFdD]?;
BOOL_LITERAL: 'true'
| 'false'
;
CHAR_LITERAL: '\'' (~['\\\r\n] | EscapeSequence) '\'';
SINGLE_QUOTE_STRING: '\'' (~['\\\r\n] | EscapeSequence)* '\'';
STRING_LITERAL: '"' (~["\\\r\n] | EscapeSequence)* '"';
NULL_LITERAL: 'null';
// Separators
LPAREN: '(';
RPAREN: ')';
LBRACE: '{';
RBRACE: '}';
LBRACK: '[';
RBRACK: ']';
SEMI: ';';
COMMA: ',';
DOT: '.';
// Operators
ASSIGN: '=';
GT: '>';
LT: '<';
BANG: '!';
TILDE: '~';
QUESTION: '?';
COLON: ':';
EQUAL: '==';
LE: '<=';
GE: '>=';
NOTEQUAL: '!=';
AND: '&&';
OR: '||';
INC: '++';
DEC: '--';
ADD: '+';
SUB: '-';
MUL: '*';
DIV: '/';
BITAND: '&';
BITOR: '|';
CARET: '^';
MOD: '%';
ADD_ASSIGN: '+=';
SUB_ASSIGN: '-=';
MUL_ASSIGN: '*=';
DIV_ASSIGN: '/=';
AND_ASSIGN: '&=';
OR_ASSIGN: '|=';
XOR_ASSIGN: '^=';
MOD_ASSIGN: '%=';
LSHIFT_ASSIGN: '<<=';
RSHIFT_ASSIGN: '>>=';
URSHIFT_ASSIGN: '>>>=';
// Java 8 tokens
ARROW: '->';
COLONCOLON: '::';
// Additional symbols not defined in the lexical specification
AT: '@';
ELLIPSIS: '...';
// Whitespace and comments
WS: [ \t\r\n\u000C]+ -> channel(HIDDEN);
COMMENT: '/*' .*? '*/' -> channel(HIDDEN);
LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN);
// Identifiers
IDENTIFIER: Letter LetterOrDigit*;
// Fragment rules
fragment ExponentPart
: [eE] [+-]? Digits
;
fragment EscapeSequence
: '\\' [btnfr"'\\]
| '\\' ([0-3]? [0-7])? [0-7]
| '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit
;
fragment HexDigits
: HexDigit ((HexDigit | '_')* HexDigit)?
;
fragment HexDigit
: [0-9a-fA-F]
;
fragment Digits
: [0-9] ([0-9_]* [0-9])?
;
fragment LetterOrDigit
: Letter
| [0-9]
;
fragment Letter
: [a-zA-Z$_] // these are the "java letters" below 0x7F
| ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate
| [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
;
@@ -0,0 +1,514 @@
// Generated from GroovyLexer.g4 by ANTLR 4.9.2
package com.itsaky.androidide.language.groovy.lexer;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class GroovyLexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.9.2", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
ABSTRACT=1, ASSERT=2, BOOLEAN=3, BREAK=4, BYTE=5, CASE=6, CATCH=7, CHAR=8,
CLASS=9, CONST=10, CONTINUE=11, DEFAULT=12, DO=13, DOUBLE=14, ELSE=15,
ENUM=16, EXTENDS=17, FINAL=18, FINALLY=19, FLOAT=20, FOR=21, IF=22, GOTO=23,
IMPLEMENTS=24, IMPORT=25, INSTANCEOF=26, INT=27, INTERFACE=28, LONG=29,
NATIVE=30, NEW=31, PACKAGE=32, PRIVATE=33, PROTECTED=34, PUBLIC=35, RETURN=36,
SHORT=37, STATIC=38, STRICTFP=39, SUPER=40, SWITCH=41, SYNCHRONIZED=42,
THIS=43, THROW=44, THROWS=45, TRANSIENT=46, TRY=47, VOID=48, VOLATILE=49,
WHILE=50, DECIMAL_LITERAL=51, HEX_LITERAL=52, OCT_LITERAL=53, BINARY_LITERAL=54,
FLOAT_LITERAL=55, HEX_FLOAT_LITERAL=56, BOOL_LITERAL=57, CHAR_LITERAL=58,
SINGLE_QUOTE_STRING=59, STRING_LITERAL=60, NULL_LITERAL=61, LPAREN=62,
RPAREN=63, LBRACE=64, RBRACE=65, LBRACK=66, RBRACK=67, SEMI=68, COMMA=69,
DOT=70, ASSIGN=71, GT=72, LT=73, BANG=74, TILDE=75, QUESTION=76, COLON=77,
EQUAL=78, LE=79, GE=80, NOTEQUAL=81, AND=82, OR=83, INC=84, DEC=85, ADD=86,
SUB=87, MUL=88, DIV=89, BITAND=90, BITOR=91, CARET=92, MOD=93, ADD_ASSIGN=94,
SUB_ASSIGN=95, MUL_ASSIGN=96, DIV_ASSIGN=97, AND_ASSIGN=98, OR_ASSIGN=99,
XOR_ASSIGN=100, MOD_ASSIGN=101, LSHIFT_ASSIGN=102, RSHIFT_ASSIGN=103,
URSHIFT_ASSIGN=104, ARROW=105, COLONCOLON=106, AT=107, ELLIPSIS=108, WS=109,
COMMENT=110, LINE_COMMENT=111, IDENTIFIER=112;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE"
};
private static String[] makeRuleNames() {
return new String[] {
"ABSTRACT", "ASSERT", "BOOLEAN", "BREAK", "BYTE", "CASE", "CATCH", "CHAR",
"CLASS", "CONST", "CONTINUE", "DEFAULT", "DO", "DOUBLE", "ELSE", "ENUM",
"EXTENDS", "FINAL", "FINALLY", "FLOAT", "FOR", "IF", "GOTO", "IMPLEMENTS",
"IMPORT", "INSTANCEOF", "INT", "INTERFACE", "LONG", "NATIVE", "NEW",
"PACKAGE", "PRIVATE", "PROTECTED", "PUBLIC", "RETURN", "SHORT", "STATIC",
"STRICTFP", "SUPER", "SWITCH", "SYNCHRONIZED", "THIS", "THROW", "THROWS",
"TRANSIENT", "TRY", "VOID", "VOLATILE", "WHILE", "DECIMAL_LITERAL", "HEX_LITERAL",
"OCT_LITERAL", "BINARY_LITERAL", "FLOAT_LITERAL", "HEX_FLOAT_LITERAL",
"BOOL_LITERAL", "CHAR_LITERAL", "SINGLE_QUOTE_STRING", "STRING_LITERAL",
"NULL_LITERAL", "LPAREN", "RPAREN", "LBRACE", "RBRACE", "LBRACK", "RBRACK",
"SEMI", "COMMA", "DOT", "ASSIGN", "GT", "LT", "BANG", "TILDE", "QUESTION",
"COLON", "EQUAL", "LE", "GE", "NOTEQUAL", "AND", "OR", "INC", "DEC",
"ADD", "SUB", "MUL", "DIV", "BITAND", "BITOR", "CARET", "MOD", "ADD_ASSIGN",
"SUB_ASSIGN", "MUL_ASSIGN", "DIV_ASSIGN", "AND_ASSIGN", "OR_ASSIGN",
"XOR_ASSIGN", "MOD_ASSIGN", "LSHIFT_ASSIGN", "RSHIFT_ASSIGN", "URSHIFT_ASSIGN",
"ARROW", "COLONCOLON", "AT", "ELLIPSIS", "WS", "COMMENT", "LINE_COMMENT",
"IDENTIFIER", "ExponentPart", "EscapeSequence", "HexDigits", "HexDigit",
"Digits", "LetterOrDigit", "Letter"
};
}
public static final String[] ruleNames = makeRuleNames();
private static String[] makeLiteralNames() {
return new String[] {
null, "'abstract'", "'assert'", "'boolean'", "'break'", "'byte'", "'case'",
"'catch'", "'char'", "'class'", "'const'", "'continue'", "'default'",
"'do'", "'double'", "'else'", "'enum'", "'extends'", "'final'", "'finally'",
"'float'", "'for'", "'if'", "'goto'", "'implements'", "'import'", "'instanceof'",
"'int'", "'interface'", "'long'", "'native'", "'new'", "'package'", "'private'",
"'protected'", "'public'", "'return'", "'short'", "'static'", "'strictfp'",
"'super'", "'switch'", "'synchronized'", "'this'", "'throw'", "'throws'",
"'transient'", "'try'", "'void'", "'volatile'", "'while'", null, null,
null, null, null, null, null, null, null, null, "'null'", "'('", "')'",
"'{'", "'}'", "'['", "']'", "';'", "','", "'.'", "'='", "'>'", "'<'",
"'!'", "'~'", "'?'", "':'", "'=='", "'<='", "'>='", "'!='", "'&&'", "'||'",
"'++'", "'--'", "'+'", "'-'", "'*'", "'/'", "'&'", "'|'", "'^'", "'%'",
"'+='", "'-='", "'*='", "'/='", "'&='", "'|='", "'^='", "'%='", "'<<='",
"'>>='", "'>>>='", "'->'", "'::'", "'@'", "'...'"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static String[] makeSymbolicNames() {
return new String[] {
null, "ABSTRACT", "ASSERT", "BOOLEAN", "BREAK", "BYTE", "CASE", "CATCH",
"CHAR", "CLASS", "CONST", "CONTINUE", "DEFAULT", "DO", "DOUBLE", "ELSE",
"ENUM", "EXTENDS", "FINAL", "FINALLY", "FLOAT", "FOR", "IF", "GOTO",
"IMPLEMENTS", "IMPORT", "INSTANCEOF", "INT", "INTERFACE", "LONG", "NATIVE",
"NEW", "PACKAGE", "PRIVATE", "PROTECTED", "PUBLIC", "RETURN", "SHORT",
"STATIC", "STRICTFP", "SUPER", "SWITCH", "SYNCHRONIZED", "THIS", "THROW",
"THROWS", "TRANSIENT", "TRY", "VOID", "VOLATILE", "WHILE", "DECIMAL_LITERAL",
"HEX_LITERAL", "OCT_LITERAL", "BINARY_LITERAL", "FLOAT_LITERAL", "HEX_FLOAT_LITERAL",
"BOOL_LITERAL", "CHAR_LITERAL", "SINGLE_QUOTE_STRING", "STRING_LITERAL",
"NULL_LITERAL", "LPAREN", "RPAREN", "LBRACE", "RBRACE", "LBRACK", "RBRACK",
"SEMI", "COMMA", "DOT", "ASSIGN", "GT", "LT", "BANG", "TILDE", "QUESTION",
"COLON", "EQUAL", "LE", "GE", "NOTEQUAL", "AND", "OR", "INC", "DEC",
"ADD", "SUB", "MUL", "DIV", "BITAND", "BITOR", "CARET", "MOD", "ADD_ASSIGN",
"SUB_ASSIGN", "MUL_ASSIGN", "DIV_ASSIGN", "AND_ASSIGN", "OR_ASSIGN",
"XOR_ASSIGN", "MOD_ASSIGN", "LSHIFT_ASSIGN", "RSHIFT_ASSIGN", "URSHIFT_ASSIGN",
"ARROW", "COLONCOLON", "AT", "ELLIPSIS", "WS", "COMMENT", "LINE_COMMENT",
"IDENTIFIER"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
/**
* @deprecated Use {@link #VOCABULARY} instead.
*/
@Deprecated
public static final String[] tokenNames;
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
public GroovyLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
@Override
public String getGrammarFileName() { return "GroovyLexer.g4"; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2r\u03bf\b\1\4\2\t"+
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
"\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+
"\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+
",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+
"\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+
"\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+
"\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+
"`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k\t"+
"k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv\4"+
"w\tw\4x\tx\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\3"+
"\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3"+
"\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t"+
"\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f"+
"\3\f\3\f\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16"+
"\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\21\3\21"+
"\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23"+
"\3\23\3\23\3\23\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25"+
"\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\30\3\30\3\30\3\30"+
"\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32"+
"\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33"+
"\3\33\3\33\3\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\35"+
"\3\35\3\35\3\36\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37\3\37\3\37"+
"\3 \3 \3 \3 \3!\3!\3!\3!\3!\3!\3!\3!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3"+
"#\3#\3#\3#\3#\3#\3#\3#\3#\3#\3$\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\3%\3"+
"%\3&\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3\'\3\'\3\'\3(\3(\3(\3(\3(\3(\3(\3"+
"(\3(\3)\3)\3)\3)\3)\3)\3*\3*\3*\3*\3*\3*\3*\3+\3+\3+\3+\3+\3+\3+\3+\3"+
"+\3+\3+\3+\3+\3,\3,\3,\3,\3,\3-\3-\3-\3-\3-\3-\3.\3.\3.\3.\3.\3.\3.\3"+
"/\3/\3/\3/\3/\3/\3/\3/\3/\3/\3\60\3\60\3\60\3\60\3\61\3\61\3\61\3\61\3"+
"\61\3\62\3\62\3\62\3\62\3\62\3\62\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3"+
"\63\3\63\3\64\3\64\3\64\5\64\u0248\n\64\3\64\6\64\u024b\n\64\r\64\16\64"+
"\u024c\3\64\5\64\u0250\n\64\5\64\u0252\n\64\3\64\5\64\u0255\n\64\3\65"+
"\3\65\3\65\3\65\7\65\u025b\n\65\f\65\16\65\u025e\13\65\3\65\5\65\u0261"+
"\n\65\3\65\5\65\u0264\n\65\3\66\3\66\7\66\u0268\n\66\f\66\16\66\u026b"+
"\13\66\3\66\3\66\7\66\u026f\n\66\f\66\16\66\u0272\13\66\3\66\5\66\u0275"+
"\n\66\3\66\5\66\u0278\n\66\3\67\3\67\3\67\3\67\7\67\u027e\n\67\f\67\16"+
"\67\u0281\13\67\3\67\5\67\u0284\n\67\3\67\5\67\u0287\n\67\38\38\38\58"+
"\u028c\n8\38\38\58\u0290\n8\38\58\u0293\n8\38\58\u0296\n8\38\38\38\58"+
"\u029b\n8\38\58\u029e\n8\58\u02a0\n8\39\39\39\39\59\u02a6\n9\39\59\u02a9"+
"\n9\39\39\59\u02ad\n9\39\39\59\u02b1\n9\39\39\59\u02b5\n9\3:\3:\3:\3:"+
"\3:\3:\3:\3:\3:\5:\u02c0\n:\3;\3;\3;\5;\u02c5\n;\3;\3;\3<\3<\3<\7<\u02cc"+
"\n<\f<\16<\u02cf\13<\3<\3<\3=\3=\3=\7=\u02d6\n=\f=\16=\u02d9\13=\3=\3"+
"=\3>\3>\3>\3>\3>\3?\3?\3@\3@\3A\3A\3B\3B\3C\3C\3D\3D\3E\3E\3F\3F\3G\3"+
"G\3H\3H\3I\3I\3J\3J\3K\3K\3L\3L\3M\3M\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3"+
"Q\3R\3R\3R\3S\3S\3S\3T\3T\3T\3U\3U\3U\3V\3V\3V\3W\3W\3X\3X\3Y\3Y\3Z\3"+
"Z\3[\3[\3\\\3\\\3]\3]\3^\3^\3_\3_\3_\3`\3`\3`\3a\3a\3a\3b\3b\3b\3c\3c"+
"\3c\3d\3d\3d\3e\3e\3e\3f\3f\3f\3g\3g\3g\3g\3h\3h\3h\3h\3i\3i\3i\3i\3i"+
"\3j\3j\3j\3k\3k\3k\3l\3l\3m\3m\3m\3m\3n\6n\u035c\nn\rn\16n\u035d\3n\3"+
"n\3o\3o\3o\3o\7o\u0366\no\fo\16o\u0369\13o\3o\3o\3o\3o\3o\3p\3p\3p\3p"+
"\7p\u0374\np\fp\16p\u0377\13p\3p\3p\3q\3q\7q\u037d\nq\fq\16q\u0380\13"+
"q\3r\3r\5r\u0384\nr\3r\3r\3s\3s\3s\3s\5s\u038c\ns\3s\5s\u038f\ns\3s\3"+
"s\3s\6s\u0394\ns\rs\16s\u0395\3s\3s\3s\3s\3s\5s\u039d\ns\3t\3t\3t\7t\u03a2"+
"\nt\ft\16t\u03a5\13t\3t\5t\u03a8\nt\3u\3u\3v\3v\7v\u03ae\nv\fv\16v\u03b1"+
"\13v\3v\5v\u03b4\nv\3w\3w\5w\u03b8\nw\3x\3x\3x\3x\5x\u03be\nx\3\u0367"+
"\2y\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35"+
"\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34\67\359\36"+
";\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67"+
"m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089F\u008bG\u008d"+
"H\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1"+
"R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5"+
"\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9"+
"f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5l\u00d7m\u00d9n\u00dbo\u00dd"+
"p\u00dfq\u00e1r\u00e3\2\u00e5\2\u00e7\2\u00e9\2\u00eb\2\u00ed\2\u00ef"+
"\2\3\2\34\3\2\63;\4\2NNnn\4\2ZZzz\5\2\62;CHch\6\2\62;CHaach\3\2\629\4"+
"\2\629aa\4\2DDdd\3\2\62\63\4\2\62\63aa\6\2FFHHffhh\4\2RRrr\4\2--//\6\2"+
"\f\f\17\17))^^\6\2\f\f\17\17$$^^\5\2\13\f\16\17\"\"\4\2\f\f\17\17\4\2"+
"GGgg\n\2$$))^^ddhhppttvv\3\2\62\65\3\2\62;\4\2\62;aa\6\2&&C\\aac|\4\2"+
"\2\u0081\ud802\udc01\3\2\ud802\udc01\3\2\udc02\ue001\2\u03ea\2\3\3\2\2"+
"\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3"+
"\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2"+
"\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2"+
"\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2"+
"\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3"+
"\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2"+
"\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2"+
"W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3"+
"\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2"+
"\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2"+
"}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2"+
"\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f"+
"\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097\3\2\2"+
"\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1"+
"\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2"+
"\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3"+
"\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2"+
"\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5"+
"\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2"+
"\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7"+
"\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2"+
"\2\2\u00e1\3\2\2\2\3\u00f1\3\2\2\2\5\u00fa\3\2\2\2\7\u0101\3\2\2\2\t\u0109"+
"\3\2\2\2\13\u010f\3\2\2\2\r\u0114\3\2\2\2\17\u0119\3\2\2\2\21\u011f\3"+
"\2\2\2\23\u0124\3\2\2\2\25\u012a\3\2\2\2\27\u0130\3\2\2\2\31\u0139\3\2"+
"\2\2\33\u0141\3\2\2\2\35\u0144\3\2\2\2\37\u014b\3\2\2\2!\u0150\3\2\2\2"+
"#\u0155\3\2\2\2%\u015d\3\2\2\2\'\u0163\3\2\2\2)\u016b\3\2\2\2+\u0171\3"+
"\2\2\2-\u0175\3\2\2\2/\u0178\3\2\2\2\61\u017d\3\2\2\2\63\u0188\3\2\2\2"+
"\65\u018f\3\2\2\2\67\u019a\3\2\2\29\u019e\3\2\2\2;\u01a8\3\2\2\2=\u01ad"+
"\3\2\2\2?\u01b4\3\2\2\2A\u01b8\3\2\2\2C\u01c0\3\2\2\2E\u01c8\3\2\2\2G"+
"\u01d2\3\2\2\2I\u01d9\3\2\2\2K\u01e0\3\2\2\2M\u01e6\3\2\2\2O\u01ed\3\2"+
"\2\2Q\u01f6\3\2\2\2S\u01fc\3\2\2\2U\u0203\3\2\2\2W\u0210\3\2\2\2Y\u0215"+
"\3\2\2\2[\u021b\3\2\2\2]\u0222\3\2\2\2_\u022c\3\2\2\2a\u0230\3\2\2\2c"+
"\u0235\3\2\2\2e\u023e\3\2\2\2g\u0251\3\2\2\2i\u0256\3\2\2\2k\u0265\3\2"+
"\2\2m\u0279\3\2\2\2o\u029f\3\2\2\2q\u02a1\3\2\2\2s\u02bf\3\2\2\2u\u02c1"+
"\3\2\2\2w\u02c8\3\2\2\2y\u02d2\3\2\2\2{\u02dc\3\2\2\2}\u02e1\3\2\2\2\177"+
"\u02e3\3\2\2\2\u0081\u02e5\3\2\2\2\u0083\u02e7\3\2\2\2\u0085\u02e9\3\2"+
"\2\2\u0087\u02eb\3\2\2\2\u0089\u02ed\3\2\2\2\u008b\u02ef\3\2\2\2\u008d"+
"\u02f1\3\2\2\2\u008f\u02f3\3\2\2\2\u0091\u02f5\3\2\2\2\u0093\u02f7\3\2"+
"\2\2\u0095\u02f9\3\2\2\2\u0097\u02fb\3\2\2\2\u0099\u02fd\3\2\2\2\u009b"+
"\u02ff\3\2\2\2\u009d\u0301\3\2\2\2\u009f\u0304\3\2\2\2\u00a1\u0307\3\2"+
"\2\2\u00a3\u030a\3\2\2\2\u00a5\u030d\3\2\2\2\u00a7\u0310\3\2\2\2\u00a9"+
"\u0313\3\2\2\2\u00ab\u0316\3\2\2\2\u00ad\u0319\3\2\2\2\u00af\u031b\3\2"+
"\2\2\u00b1\u031d\3\2\2\2\u00b3\u031f\3\2\2\2\u00b5\u0321\3\2\2\2\u00b7"+
"\u0323\3\2\2\2\u00b9\u0325\3\2\2\2\u00bb\u0327\3\2\2\2\u00bd\u0329\3\2"+
"\2\2\u00bf\u032c\3\2\2\2\u00c1\u032f\3\2\2\2\u00c3\u0332\3\2\2\2\u00c5"+
"\u0335\3\2\2\2\u00c7\u0338\3\2\2\2\u00c9\u033b\3\2\2\2\u00cb\u033e\3\2"+
"\2\2\u00cd\u0341\3\2\2\2\u00cf\u0345\3\2\2\2\u00d1\u0349\3\2\2\2\u00d3"+
"\u034e\3\2\2\2\u00d5\u0351\3\2\2\2\u00d7\u0354\3\2\2\2\u00d9\u0356\3\2"+
"\2\2\u00db\u035b\3\2\2\2\u00dd\u0361\3\2\2\2\u00df\u036f\3\2\2\2\u00e1"+
"\u037a\3\2\2\2\u00e3\u0381\3\2\2\2\u00e5\u039c\3\2\2\2\u00e7\u039e\3\2"+
"\2\2\u00e9\u03a9\3\2\2\2\u00eb\u03ab\3\2\2\2\u00ed\u03b7\3\2\2\2\u00ef"+
"\u03bd\3\2\2\2\u00f1\u00f2\7c\2\2\u00f2\u00f3\7d\2\2\u00f3\u00f4\7u\2"+
"\2\u00f4\u00f5\7v\2\2\u00f5\u00f6\7t\2\2\u00f6\u00f7\7c\2\2\u00f7\u00f8"+
"\7e\2\2\u00f8\u00f9\7v\2\2\u00f9\4\3\2\2\2\u00fa\u00fb\7c\2\2\u00fb\u00fc"+
"\7u\2\2\u00fc\u00fd\7u\2\2\u00fd\u00fe\7g\2\2\u00fe\u00ff\7t\2\2\u00ff"+
"\u0100\7v\2\2\u0100\6\3\2\2\2\u0101\u0102\7d\2\2\u0102\u0103\7q\2\2\u0103"+
"\u0104\7q\2\2\u0104\u0105\7n\2\2\u0105\u0106\7g\2\2\u0106\u0107\7c\2\2"+
"\u0107\u0108\7p\2\2\u0108\b\3\2\2\2\u0109\u010a\7d\2\2\u010a\u010b\7t"+
"\2\2\u010b\u010c\7g\2\2\u010c\u010d\7c\2\2\u010d\u010e\7m\2\2\u010e\n"+
"\3\2\2\2\u010f\u0110\7d\2\2\u0110\u0111\7{\2\2\u0111\u0112\7v\2\2\u0112"+
"\u0113\7g\2\2\u0113\f\3\2\2\2\u0114\u0115\7e\2\2\u0115\u0116\7c\2\2\u0116"+
"\u0117\7u\2\2\u0117\u0118\7g\2\2\u0118\16\3\2\2\2\u0119\u011a\7e\2\2\u011a"+
"\u011b\7c\2\2\u011b\u011c\7v\2\2\u011c\u011d\7e\2\2\u011d\u011e\7j\2\2"+
"\u011e\20\3\2\2\2\u011f\u0120\7e\2\2\u0120\u0121\7j\2\2\u0121\u0122\7"+
"c\2\2\u0122\u0123\7t\2\2\u0123\22\3\2\2\2\u0124\u0125\7e\2\2\u0125\u0126"+
"\7n\2\2\u0126\u0127\7c\2\2\u0127\u0128\7u\2\2\u0128\u0129\7u\2\2\u0129"+
"\24\3\2\2\2\u012a\u012b\7e\2\2\u012b\u012c\7q\2\2\u012c\u012d\7p\2\2\u012d"+
"\u012e\7u\2\2\u012e\u012f\7v\2\2\u012f\26\3\2\2\2\u0130\u0131\7e\2\2\u0131"+
"\u0132\7q\2\2\u0132\u0133\7p\2\2\u0133\u0134\7v\2\2\u0134\u0135\7k\2\2"+
"\u0135\u0136\7p\2\2\u0136\u0137\7w\2\2\u0137\u0138\7g\2\2\u0138\30\3\2"+
"\2\2\u0139\u013a\7f\2\2\u013a\u013b\7g\2\2\u013b\u013c\7h\2\2\u013c\u013d"+
"\7c\2\2\u013d\u013e\7w\2\2\u013e\u013f\7n\2\2\u013f\u0140\7v\2\2\u0140"+
"\32\3\2\2\2\u0141\u0142\7f\2\2\u0142\u0143\7q\2\2\u0143\34\3\2\2\2\u0144"+
"\u0145\7f\2\2\u0145\u0146\7q\2\2\u0146\u0147\7w\2\2\u0147\u0148\7d\2\2"+
"\u0148\u0149\7n\2\2\u0149\u014a\7g\2\2\u014a\36\3\2\2\2\u014b\u014c\7"+
"g\2\2\u014c\u014d\7n\2\2\u014d\u014e\7u\2\2\u014e\u014f\7g\2\2\u014f "+
"\3\2\2\2\u0150\u0151\7g\2\2\u0151\u0152\7p\2\2\u0152\u0153\7w\2\2\u0153"+
"\u0154\7o\2\2\u0154\"\3\2\2\2\u0155\u0156\7g\2\2\u0156\u0157\7z\2\2\u0157"+
"\u0158\7v\2\2\u0158\u0159\7g\2\2\u0159\u015a\7p\2\2\u015a\u015b\7f\2\2"+
"\u015b\u015c\7u\2\2\u015c$\3\2\2\2\u015d\u015e\7h\2\2\u015e\u015f\7k\2"+
"\2\u015f\u0160\7p\2\2\u0160\u0161\7c\2\2\u0161\u0162\7n\2\2\u0162&\3\2"+
"\2\2\u0163\u0164\7h\2\2\u0164\u0165\7k\2\2\u0165\u0166\7p\2\2\u0166\u0167"+
"\7c\2\2\u0167\u0168\7n\2\2\u0168\u0169\7n\2\2\u0169\u016a\7{\2\2\u016a"+
"(\3\2\2\2\u016b\u016c\7h\2\2\u016c\u016d\7n\2\2\u016d\u016e\7q\2\2\u016e"+
"\u016f\7c\2\2\u016f\u0170\7v\2\2\u0170*\3\2\2\2\u0171\u0172\7h\2\2\u0172"+
"\u0173\7q\2\2\u0173\u0174\7t\2\2\u0174,\3\2\2\2\u0175\u0176\7k\2\2\u0176"+
"\u0177\7h\2\2\u0177.\3\2\2\2\u0178\u0179\7i\2\2\u0179\u017a\7q\2\2\u017a"+
"\u017b\7v\2\2\u017b\u017c\7q\2\2\u017c\60\3\2\2\2\u017d\u017e\7k\2\2\u017e"+
"\u017f\7o\2\2\u017f\u0180\7r\2\2\u0180\u0181\7n\2\2\u0181\u0182\7g\2\2"+
"\u0182\u0183\7o\2\2\u0183\u0184\7g\2\2\u0184\u0185\7p\2\2\u0185\u0186"+
"\7v\2\2\u0186\u0187\7u\2\2\u0187\62\3\2\2\2\u0188\u0189\7k\2\2\u0189\u018a"+
"\7o\2\2\u018a\u018b\7r\2\2\u018b\u018c\7q\2\2\u018c\u018d\7t\2\2\u018d"+
"\u018e\7v\2\2\u018e\64\3\2\2\2\u018f\u0190\7k\2\2\u0190\u0191\7p\2\2\u0191"+
"\u0192\7u\2\2\u0192\u0193\7v\2\2\u0193\u0194\7c\2\2\u0194\u0195\7p\2\2"+
"\u0195\u0196\7e\2\2\u0196\u0197\7g\2\2\u0197\u0198\7q\2\2\u0198\u0199"+
"\7h\2\2\u0199\66\3\2\2\2\u019a\u019b\7k\2\2\u019b\u019c\7p\2\2\u019c\u019d"+
"\7v\2\2\u019d8\3\2\2\2\u019e\u019f\7k\2\2\u019f\u01a0\7p\2\2\u01a0\u01a1"+
"\7v\2\2\u01a1\u01a2\7g\2\2\u01a2\u01a3\7t\2\2\u01a3\u01a4\7h\2\2\u01a4"+
"\u01a5\7c\2\2\u01a5\u01a6\7e\2\2\u01a6\u01a7\7g\2\2\u01a7:\3\2\2\2\u01a8"+
"\u01a9\7n\2\2\u01a9\u01aa\7q\2\2\u01aa\u01ab\7p\2\2\u01ab\u01ac\7i\2\2"+
"\u01ac<\3\2\2\2\u01ad\u01ae\7p\2\2\u01ae\u01af\7c\2\2\u01af\u01b0\7v\2"+
"\2\u01b0\u01b1\7k\2\2\u01b1\u01b2\7x\2\2\u01b2\u01b3\7g\2\2\u01b3>\3\2"+
"\2\2\u01b4\u01b5\7p\2\2\u01b5\u01b6\7g\2\2\u01b6\u01b7\7y\2\2\u01b7@\3"+
"\2\2\2\u01b8\u01b9\7r\2\2\u01b9\u01ba\7c\2\2\u01ba\u01bb\7e\2\2\u01bb"+
"\u01bc\7m\2\2\u01bc\u01bd\7c\2\2\u01bd\u01be\7i\2\2\u01be\u01bf\7g\2\2"+
"\u01bfB\3\2\2\2\u01c0\u01c1\7r\2\2\u01c1\u01c2\7t\2\2\u01c2\u01c3\7k\2"+
"\2\u01c3\u01c4\7x\2\2\u01c4\u01c5\7c\2\2\u01c5\u01c6\7v\2\2\u01c6\u01c7"+
"\7g\2\2\u01c7D\3\2\2\2\u01c8\u01c9\7r\2\2\u01c9\u01ca\7t\2\2\u01ca\u01cb"+
"\7q\2\2\u01cb\u01cc\7v\2\2\u01cc\u01cd\7g\2\2\u01cd\u01ce\7e\2\2\u01ce"+
"\u01cf\7v\2\2\u01cf\u01d0\7g\2\2\u01d0\u01d1\7f\2\2\u01d1F\3\2\2\2\u01d2"+
"\u01d3\7r\2\2\u01d3\u01d4\7w\2\2\u01d4\u01d5\7d\2\2\u01d5\u01d6\7n\2\2"+
"\u01d6\u01d7\7k\2\2\u01d7\u01d8\7e\2\2\u01d8H\3\2\2\2\u01d9\u01da\7t\2"+
"\2\u01da\u01db\7g\2\2\u01db\u01dc\7v\2\2\u01dc\u01dd\7w\2\2\u01dd\u01de"+
"\7t\2\2\u01de\u01df\7p\2\2\u01dfJ\3\2\2\2\u01e0\u01e1\7u\2\2\u01e1\u01e2"+
"\7j\2\2\u01e2\u01e3\7q\2\2\u01e3\u01e4\7t\2\2\u01e4\u01e5\7v\2\2\u01e5"+
"L\3\2\2\2\u01e6\u01e7\7u\2\2\u01e7\u01e8\7v\2\2\u01e8\u01e9\7c\2\2\u01e9"+
"\u01ea\7v\2\2\u01ea\u01eb\7k\2\2\u01eb\u01ec\7e\2\2\u01ecN\3\2\2\2\u01ed"+
"\u01ee\7u\2\2\u01ee\u01ef\7v\2\2\u01ef\u01f0\7t\2\2\u01f0\u01f1\7k\2\2"+
"\u01f1\u01f2\7e\2\2\u01f2\u01f3\7v\2\2\u01f3\u01f4\7h\2\2\u01f4\u01f5"+
"\7r\2\2\u01f5P\3\2\2\2\u01f6\u01f7\7u\2\2\u01f7\u01f8\7w\2\2\u01f8\u01f9"+
"\7r\2\2\u01f9\u01fa\7g\2\2\u01fa\u01fb\7t\2\2\u01fbR\3\2\2\2\u01fc\u01fd"+
"\7u\2\2\u01fd\u01fe\7y\2\2\u01fe\u01ff\7k\2\2\u01ff\u0200\7v\2\2\u0200"+
"\u0201\7e\2\2\u0201\u0202\7j\2\2\u0202T\3\2\2\2\u0203\u0204\7u\2\2\u0204"+
"\u0205\7{\2\2\u0205\u0206\7p\2\2\u0206\u0207\7e\2\2\u0207\u0208\7j\2\2"+
"\u0208\u0209\7t\2\2\u0209\u020a\7q\2\2\u020a\u020b\7p\2\2\u020b\u020c"+
"\7k\2\2\u020c\u020d\7|\2\2\u020d\u020e\7g\2\2\u020e\u020f\7f\2\2\u020f"+
"V\3\2\2\2\u0210\u0211\7v\2\2\u0211\u0212\7j\2\2\u0212\u0213\7k\2\2\u0213"+
"\u0214\7u\2\2\u0214X\3\2\2\2\u0215\u0216\7v\2\2\u0216\u0217\7j\2\2\u0217"+
"\u0218\7t\2\2\u0218\u0219\7q\2\2\u0219\u021a\7y\2\2\u021aZ\3\2\2\2\u021b"+
"\u021c\7v\2\2\u021c\u021d\7j\2\2\u021d\u021e\7t\2\2\u021e\u021f\7q\2\2"+
"\u021f\u0220\7y\2\2\u0220\u0221\7u\2\2\u0221\\\3\2\2\2\u0222\u0223\7v"+
"\2\2\u0223\u0224\7t\2\2\u0224\u0225\7c\2\2\u0225\u0226\7p\2\2\u0226\u0227"+
"\7u\2\2\u0227\u0228\7k\2\2\u0228\u0229\7g\2\2\u0229\u022a\7p\2\2\u022a"+
"\u022b\7v\2\2\u022b^\3\2\2\2\u022c\u022d\7v\2\2\u022d\u022e\7t\2\2\u022e"+
"\u022f\7{\2\2\u022f`\3\2\2\2\u0230\u0231\7x\2\2\u0231\u0232\7q\2\2\u0232"+
"\u0233\7k\2\2\u0233\u0234\7f\2\2\u0234b\3\2\2\2\u0235\u0236\7x\2\2\u0236"+
"\u0237\7q\2\2\u0237\u0238\7n\2\2\u0238\u0239\7c\2\2\u0239\u023a\7v\2\2"+
"\u023a\u023b\7k\2\2\u023b\u023c\7n\2\2\u023c\u023d\7g\2\2\u023dd\3\2\2"+
"\2\u023e\u023f\7y\2\2\u023f\u0240\7j\2\2\u0240\u0241\7k\2\2\u0241\u0242"+
"\7n\2\2\u0242\u0243\7g\2\2\u0243f\3\2\2\2\u0244\u0252\7\62\2\2\u0245\u024f"+
"\t\2\2\2\u0246\u0248\5\u00ebv\2\u0247\u0246\3\2\2\2\u0247\u0248\3\2\2"+
"\2\u0248\u0250\3\2\2\2\u0249\u024b\7a\2\2\u024a\u0249\3\2\2\2\u024b\u024c"+
"\3\2\2\2\u024c\u024a\3\2\2\2\u024c\u024d\3\2\2\2\u024d\u024e\3\2\2\2\u024e"+
"\u0250\5\u00ebv\2\u024f\u0247\3\2\2\2\u024f\u024a\3\2\2\2\u0250\u0252"+
"\3\2\2\2\u0251\u0244\3\2\2\2\u0251\u0245\3\2\2\2\u0252\u0254\3\2\2\2\u0253"+
"\u0255\t\3\2\2\u0254\u0253\3\2\2\2\u0254\u0255\3\2\2\2\u0255h\3\2\2\2"+
"\u0256\u0257\7\62\2\2\u0257\u0258\t\4\2\2\u0258\u0260\t\5\2\2\u0259\u025b"+
"\t\6\2\2\u025a\u0259\3\2\2\2\u025b\u025e\3\2\2\2\u025c\u025a\3\2\2\2\u025c"+
"\u025d\3\2\2\2\u025d\u025f\3\2\2\2\u025e\u025c\3\2\2\2\u025f\u0261\t\5"+
"\2\2\u0260\u025c\3\2\2\2\u0260\u0261\3\2\2\2\u0261\u0263\3\2\2\2\u0262"+
"\u0264\t\3\2\2\u0263\u0262\3\2\2\2\u0263\u0264\3\2\2\2\u0264j\3\2\2\2"+
"\u0265\u0269\7\62\2\2\u0266\u0268\7a\2\2\u0267\u0266\3\2\2\2\u0268\u026b"+
"\3\2\2\2\u0269\u0267\3\2\2\2\u0269\u026a\3\2\2\2\u026a\u026c\3\2\2\2\u026b"+
"\u0269\3\2\2\2\u026c\u0274\t\7\2\2\u026d\u026f\t\b\2\2\u026e\u026d\3\2"+
"\2\2\u026f\u0272\3\2\2\2\u0270\u026e\3\2\2\2\u0270\u0271\3\2\2\2\u0271"+
"\u0273\3\2\2\2\u0272\u0270\3\2\2\2\u0273\u0275\t\7\2\2\u0274\u0270\3\2"+
"\2\2\u0274\u0275\3\2\2\2\u0275\u0277\3\2\2\2\u0276\u0278\t\3\2\2\u0277"+
"\u0276\3\2\2\2\u0277\u0278\3\2\2\2\u0278l\3\2\2\2\u0279\u027a\7\62\2\2"+
"\u027a\u027b\t\t\2\2\u027b\u0283\t\n\2\2\u027c\u027e\t\13\2\2\u027d\u027c"+
"\3\2\2\2\u027e\u0281\3\2\2\2\u027f\u027d\3\2\2\2\u027f\u0280\3\2\2\2\u0280"+
"\u0282\3\2\2\2\u0281\u027f\3\2\2\2\u0282\u0284\t\n\2\2\u0283\u027f\3\2"+
"\2\2\u0283\u0284\3\2\2\2\u0284\u0286\3\2\2\2\u0285\u0287\t\3\2\2\u0286"+
"\u0285\3\2\2\2\u0286\u0287\3\2\2\2\u0287n\3\2\2\2\u0288\u0289\5\u00eb"+
"v\2\u0289\u028b\7\60\2\2\u028a\u028c\5\u00ebv\2\u028b\u028a\3\2\2\2\u028b"+
"\u028c\3\2\2\2\u028c\u0290\3\2\2\2\u028d\u028e\7\60\2\2\u028e\u0290\5"+
"\u00ebv\2\u028f\u0288\3\2\2\2\u028f\u028d\3\2\2\2\u0290\u0292\3\2\2\2"+
"\u0291\u0293\5\u00e3r\2\u0292\u0291\3\2\2\2\u0292\u0293\3\2\2\2\u0293"+
"\u0295\3\2\2\2\u0294\u0296\t\f\2\2\u0295\u0294\3\2\2\2\u0295\u0296\3\2"+
"\2\2\u0296\u02a0\3\2\2\2\u0297\u029d\5\u00ebv\2\u0298\u029a\5\u00e3r\2"+
"\u0299\u029b\t\f\2\2\u029a\u0299\3\2\2\2\u029a\u029b\3\2\2\2\u029b\u029e"+
"\3\2\2\2\u029c\u029e\t\f\2\2\u029d\u0298\3\2\2\2\u029d\u029c\3\2\2\2\u029e"+
"\u02a0\3\2\2\2\u029f\u028f\3\2\2\2\u029f\u0297\3\2\2\2\u02a0p\3\2\2\2"+
"\u02a1\u02a2\7\62\2\2\u02a2\u02ac\t\4\2\2\u02a3\u02a5\5\u00e7t\2\u02a4"+
"\u02a6\7\60\2\2\u02a5\u02a4\3\2\2\2\u02a5\u02a6\3\2\2\2\u02a6\u02ad\3"+
"\2\2\2\u02a7\u02a9\5\u00e7t\2\u02a8\u02a7\3\2\2\2\u02a8\u02a9\3\2\2\2"+
"\u02a9\u02aa\3\2\2\2\u02aa\u02ab\7\60\2\2\u02ab\u02ad\5\u00e7t\2\u02ac"+
"\u02a3\3\2\2\2\u02ac\u02a8\3\2\2\2\u02ad\u02ae\3\2\2\2\u02ae\u02b0\t\r"+
"\2\2\u02af\u02b1\t\16\2\2\u02b0\u02af\3\2\2\2\u02b0\u02b1\3\2\2\2\u02b1"+
"\u02b2\3\2\2\2\u02b2\u02b4\5\u00ebv\2\u02b3\u02b5\t\f\2\2\u02b4\u02b3"+
"\3\2\2\2\u02b4\u02b5\3\2\2\2\u02b5r\3\2\2\2\u02b6\u02b7\7v\2\2\u02b7\u02b8"+
"\7t\2\2\u02b8\u02b9\7w\2\2\u02b9\u02c0\7g\2\2\u02ba\u02bb\7h\2\2\u02bb"+
"\u02bc\7c\2\2\u02bc\u02bd\7n\2\2\u02bd\u02be\7u\2\2\u02be\u02c0\7g\2\2"+
"\u02bf\u02b6\3\2\2\2\u02bf\u02ba\3\2\2\2\u02c0t\3\2\2\2\u02c1\u02c4\7"+
")\2\2\u02c2\u02c5\n\17\2\2\u02c3\u02c5\5\u00e5s\2\u02c4\u02c2\3\2\2\2"+
"\u02c4\u02c3\3\2\2\2\u02c5\u02c6\3\2\2\2\u02c6\u02c7\7)\2\2\u02c7v\3\2"+
"\2\2\u02c8\u02cd\7)\2\2\u02c9\u02cc\n\17\2\2\u02ca\u02cc\5\u00e5s\2\u02cb"+
"\u02c9\3\2\2\2\u02cb\u02ca\3\2\2\2\u02cc\u02cf\3\2\2\2\u02cd\u02cb\3\2"+
"\2\2\u02cd\u02ce\3\2\2\2\u02ce\u02d0\3\2\2\2\u02cf\u02cd\3\2\2\2\u02d0"+
"\u02d1\7)\2\2\u02d1x\3\2\2\2\u02d2\u02d7\7$\2\2\u02d3\u02d6\n\20\2\2\u02d4"+
"\u02d6\5\u00e5s\2\u02d5\u02d3\3\2\2\2\u02d5\u02d4\3\2\2\2\u02d6\u02d9"+
"\3\2\2\2\u02d7\u02d5\3\2\2\2\u02d7\u02d8\3\2\2\2\u02d8\u02da\3\2\2\2\u02d9"+
"\u02d7\3\2\2\2\u02da\u02db\7$\2\2\u02dbz\3\2\2\2\u02dc\u02dd\7p\2\2\u02dd"+
"\u02de\7w\2\2\u02de\u02df\7n\2\2\u02df\u02e0\7n\2\2\u02e0|\3\2\2\2\u02e1"+
"\u02e2\7*\2\2\u02e2~\3\2\2\2\u02e3\u02e4\7+\2\2\u02e4\u0080\3\2\2\2\u02e5"+
"\u02e6\7}\2\2\u02e6\u0082\3\2\2\2\u02e7\u02e8\7\177\2\2\u02e8\u0084\3"+
"\2\2\2\u02e9\u02ea\7]\2\2\u02ea\u0086\3\2\2\2\u02eb\u02ec\7_\2\2\u02ec"+
"\u0088\3\2\2\2\u02ed\u02ee\7=\2\2\u02ee\u008a\3\2\2\2\u02ef\u02f0\7.\2"+
"\2\u02f0\u008c\3\2\2\2\u02f1\u02f2\7\60\2\2\u02f2\u008e\3\2\2\2\u02f3"+
"\u02f4\7?\2\2\u02f4\u0090\3\2\2\2\u02f5\u02f6\7@\2\2\u02f6\u0092\3\2\2"+
"\2\u02f7\u02f8\7>\2\2\u02f8\u0094\3\2\2\2\u02f9\u02fa\7#\2\2\u02fa\u0096"+
"\3\2\2\2\u02fb\u02fc\7\u0080\2\2\u02fc\u0098\3\2\2\2\u02fd\u02fe\7A\2"+
"\2\u02fe\u009a\3\2\2\2\u02ff\u0300\7<\2\2\u0300\u009c\3\2\2\2\u0301\u0302"+
"\7?\2\2\u0302\u0303\7?\2\2\u0303\u009e\3\2\2\2\u0304\u0305\7>\2\2\u0305"+
"\u0306\7?\2\2\u0306\u00a0\3\2\2\2\u0307\u0308\7@\2\2\u0308\u0309\7?\2"+
"\2\u0309\u00a2\3\2\2\2\u030a\u030b\7#\2\2\u030b\u030c\7?\2\2\u030c\u00a4"+
"\3\2\2\2\u030d\u030e\7(\2\2\u030e\u030f\7(\2\2\u030f\u00a6\3\2\2\2\u0310"+
"\u0311\7~\2\2\u0311\u0312\7~\2\2\u0312\u00a8\3\2\2\2\u0313\u0314\7-\2"+
"\2\u0314\u0315\7-\2\2\u0315\u00aa\3\2\2\2\u0316\u0317\7/\2\2\u0317\u0318"+
"\7/\2\2\u0318\u00ac\3\2\2\2\u0319\u031a\7-\2\2\u031a\u00ae\3\2\2\2\u031b"+
"\u031c\7/\2\2\u031c\u00b0\3\2\2\2\u031d\u031e\7,\2\2\u031e\u00b2\3\2\2"+
"\2\u031f\u0320\7\61\2\2\u0320\u00b4\3\2\2\2\u0321\u0322\7(\2\2\u0322\u00b6"+
"\3\2\2\2\u0323\u0324\7~\2\2\u0324\u00b8\3\2\2\2\u0325\u0326\7`\2\2\u0326"+
"\u00ba\3\2\2\2\u0327\u0328\7\'\2\2\u0328\u00bc\3\2\2\2\u0329\u032a\7-"+
"\2\2\u032a\u032b\7?\2\2\u032b\u00be\3\2\2\2\u032c\u032d\7/\2\2\u032d\u032e"+
"\7?\2\2\u032e\u00c0\3\2\2\2\u032f\u0330\7,\2\2\u0330\u0331\7?\2\2\u0331"+
"\u00c2\3\2\2\2\u0332\u0333\7\61\2\2\u0333\u0334\7?\2\2\u0334\u00c4\3\2"+
"\2\2\u0335\u0336\7(\2\2\u0336\u0337\7?\2\2\u0337\u00c6\3\2\2\2\u0338\u0339"+
"\7~\2\2\u0339\u033a\7?\2\2\u033a\u00c8\3\2\2\2\u033b\u033c\7`\2\2\u033c"+
"\u033d\7?\2\2\u033d\u00ca\3\2\2\2\u033e\u033f\7\'\2\2\u033f\u0340\7?\2"+
"\2\u0340\u00cc\3\2\2\2\u0341\u0342\7>\2\2\u0342\u0343\7>\2\2\u0343\u0344"+
"\7?\2\2\u0344\u00ce\3\2\2\2\u0345\u0346\7@\2\2\u0346\u0347\7@\2\2\u0347"+
"\u0348\7?\2\2\u0348\u00d0\3\2\2\2\u0349\u034a\7@\2\2\u034a\u034b\7@\2"+
"\2\u034b\u034c\7@\2\2\u034c\u034d\7?\2\2\u034d\u00d2\3\2\2\2\u034e\u034f"+
"\7/\2\2\u034f\u0350\7@\2\2\u0350\u00d4\3\2\2\2\u0351\u0352\7<\2\2\u0352"+
"\u0353\7<\2\2\u0353\u00d6\3\2\2\2\u0354\u0355\7B\2\2\u0355\u00d8\3\2\2"+
"\2\u0356\u0357\7\60\2\2\u0357\u0358\7\60\2\2\u0358\u0359\7\60\2\2\u0359"+
"\u00da\3\2\2\2\u035a\u035c\t\21\2\2\u035b\u035a\3\2\2\2\u035c\u035d\3"+
"\2\2\2\u035d\u035b\3\2\2\2\u035d\u035e\3\2\2\2\u035e\u035f\3\2\2\2\u035f"+
"\u0360\bn\2\2\u0360\u00dc\3\2\2\2\u0361\u0362\7\61\2\2\u0362\u0363\7,"+
"\2\2\u0363\u0367\3\2\2\2\u0364\u0366\13\2\2\2\u0365\u0364\3\2\2\2\u0366"+
"\u0369\3\2\2\2\u0367\u0368\3\2\2\2\u0367\u0365\3\2\2\2\u0368\u036a\3\2"+
"\2\2\u0369\u0367\3\2\2\2\u036a\u036b\7,\2\2\u036b\u036c\7\61\2\2\u036c"+
"\u036d\3\2\2\2\u036d\u036e\bo\2\2\u036e\u00de\3\2\2\2\u036f\u0370\7\61"+
"\2\2\u0370\u0371\7\61\2\2\u0371\u0375\3\2\2\2\u0372\u0374\n\22\2\2\u0373"+
"\u0372\3\2\2\2\u0374\u0377\3\2\2\2\u0375\u0373\3\2\2\2\u0375\u0376\3\2"+
"\2\2\u0376\u0378\3\2\2\2\u0377\u0375\3\2\2\2\u0378\u0379\bp\2\2\u0379"+
"\u00e0\3\2\2\2\u037a\u037e\5\u00efx\2\u037b\u037d\5\u00edw\2\u037c\u037b"+
"\3\2\2\2\u037d\u0380\3\2\2\2\u037e\u037c\3\2\2\2\u037e\u037f\3\2\2\2\u037f"+
"\u00e2\3\2\2\2\u0380\u037e\3\2\2\2\u0381\u0383\t\23\2\2\u0382\u0384\t"+
"\16\2\2\u0383\u0382\3\2\2\2\u0383\u0384\3\2\2\2\u0384\u0385\3\2\2\2\u0385"+
"\u0386\5\u00ebv\2\u0386\u00e4\3\2\2\2\u0387\u0388\7^\2\2\u0388\u039d\t"+
"\24\2\2\u0389\u038e\7^\2\2\u038a\u038c\t\25\2\2\u038b\u038a\3\2\2\2\u038b"+
"\u038c\3\2\2\2\u038c\u038d\3\2\2\2\u038d\u038f\t\7\2\2\u038e\u038b\3\2"+
"\2\2\u038e\u038f\3\2\2\2\u038f\u0390\3\2\2\2\u0390\u039d\t\7\2\2\u0391"+
"\u0393\7^\2\2\u0392\u0394\7w\2\2\u0393\u0392\3\2\2\2\u0394\u0395\3\2\2"+
"\2\u0395\u0393\3\2\2\2\u0395\u0396\3\2\2\2\u0396\u0397\3\2\2\2\u0397\u0398"+
"\5\u00e9u\2\u0398\u0399\5\u00e9u\2\u0399\u039a\5\u00e9u\2\u039a\u039b"+
"\5\u00e9u\2\u039b\u039d\3\2\2\2\u039c\u0387\3\2\2\2\u039c\u0389\3\2\2"+
"\2\u039c\u0391\3\2\2\2\u039d\u00e6\3\2\2\2\u039e\u03a7\5\u00e9u\2\u039f"+
"\u03a2\5\u00e9u\2\u03a0\u03a2\7a\2\2\u03a1\u039f\3\2\2\2\u03a1\u03a0\3"+
"\2\2\2\u03a2\u03a5\3\2\2\2\u03a3\u03a1\3\2\2\2\u03a3\u03a4\3\2\2\2\u03a4"+
"\u03a6\3\2\2\2\u03a5\u03a3\3\2\2\2\u03a6\u03a8\5\u00e9u\2\u03a7\u03a3"+
"\3\2\2\2\u03a7\u03a8\3\2\2\2\u03a8\u00e8\3\2\2\2\u03a9\u03aa\t\5\2\2\u03aa"+
"\u00ea\3\2\2\2\u03ab\u03b3\t\26\2\2\u03ac\u03ae\t\27\2\2\u03ad\u03ac\3"+
"\2\2\2\u03ae\u03b1\3\2\2\2\u03af\u03ad\3\2\2\2\u03af\u03b0\3\2\2\2\u03b0"+
"\u03b2\3\2\2\2\u03b1\u03af\3\2\2\2\u03b2\u03b4\t\26\2\2\u03b3\u03af\3"+
"\2\2\2\u03b3\u03b4\3\2\2\2\u03b4\u00ec\3\2\2\2\u03b5\u03b8\5\u00efx\2"+
"\u03b6\u03b8\t\26\2\2\u03b7\u03b5\3\2\2\2\u03b7\u03b6\3\2\2\2\u03b8\u00ee"+
"\3\2\2\2\u03b9\u03be\t\30\2\2\u03ba\u03be\n\31\2\2\u03bb\u03bc\t\32\2"+
"\2\u03bc\u03be\t\33\2\2\u03bd\u03b9\3\2\2\2\u03bd\u03ba\3\2\2\2\u03bd"+
"\u03bb\3\2\2\2\u03be\u00f0\3\2\2\2\64\2\u0247\u024c\u024f\u0251\u0254"+
"\u025c\u0260\u0263\u0269\u0270\u0274\u0277\u027f\u0283\u0286\u028b\u028f"+
"\u0292\u0295\u029a\u029d\u029f\u02a5\u02a8\u02ac\u02b0\u02b4\u02bf\u02c4"+
"\u02cb\u02cd\u02d5\u02d7\u035d\u0367\u0375\u037e\u0383\u038b\u038e\u0395"+
"\u039c\u03a1\u03a3\u03a7\u03af\u03b3\u03b7\u03bd\3\2\3\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}
@@ -0,0 +1,43 @@
package com.itsaky.androidide.language.java;
import android.text.TextUtils;
import androidx.core.util.Pair;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.language.java.JavaLanguageAnalyzer;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.utils.Either;
import com.itsaky.androidide.utils.EitherOfResult;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
public class JavaAutoComplete implements AutoCompleteProvider {
private CodeEditor editor;
private JavaLanguageAnalyzer analyzer;
public JavaAutoComplete(CodeEditor editor, JavaLanguageAnalyzer analyzer) {
this.editor = editor;
this.analyzer = analyzer;
}
@Override
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
Pair<ArrayList<SuggestItem>, List<Diagnostic<? extends JavaFileObject>>> pair = StudioApp.getInstance().getCompletionProvider().getSuggestions(editor);
final ArrayList<SuggestItem> suggestions = pair.first;
final List<Diagnostic<? extends JavaFileObject>> diags = pair.second;
if(suggestions != null) {
List<Either<SuggestItem, CompletionItem>> result = new ArrayList<>();
for(SuggestItem item : suggestions) {
result.add(new EitherOfResult(item, null));
}
return result;
}
return new ArrayList<Either<SuggestItem, CompletionItem>>();
}
}
@@ -0,0 +1,135 @@
package com.itsaky.androidide.language.java;
import com.blankj.utilcode.util.ThrowableUtils;
import com.itsaky.androidide.language.java.manager.JavaCharacter;
import com.itsaky.androidide.language.java.parser.JavaLexer;
import com.itsaky.androidide.language.java.parser.JavaParser;
import com.itsaky.androidide.models.AndroidProject;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
import io.github.rosemoe.editor.interfaces.EditorLanguage;
import io.github.rosemoe.editor.interfaces.NewlineHandler;
import io.github.rosemoe.editor.text.TextUtils;
import io.github.rosemoe.editor.widget.CodeEditor;
import io.github.rosemoe.editor.widget.SymbolPairMatch;
import java.io.StringReader;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
public class JavaLanguage implements EditorLanguage
{
private JavaLanguageAnalyzer analyzer;
private JavaAutoComplete complete;
private AndroidProject project;
private CodeEditor editor;
public JavaLanguage(CodeEditor editor, AndroidProject project)
{
this.analyzer = new JavaLanguageAnalyzer();
this.complete = new JavaAutoComplete(editor, analyzer);
this.project = project;
this.editor = editor;
}
@Override
public CodeAnalyzer getAnalyzer()
{
return analyzer;
}
@Override
public AutoCompleteProvider getAutoCompleteProvider()
{
return complete;
}
@Override
public boolean isAutoCompleteChar(char p1)
{
return JavaCharacter.isJavaIdentifierPart(p1) || p1 == '.';
}
@Override
public int getIndentAdvance(String p1)
{
try
{
JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(p1)));
Token token = null;
int advance = 0;
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF))
{
switch (token.getType())
{
case JavaLexer.LBRACE:
advance++;
break;
case JavaParser.RBRACE:
advance --;
break;
}
}
advance = Math.max(0, advance);
return advance * 4;
} catch (Throwable e)
{}
return 0;
}
@Override
public SymbolPairMatch getSymbolPairs() {
return new SymbolPairMatch.DefaultSymbolPairs();
}
@Override
public boolean useTab() {
return true;
}
@Override
public CharSequence format(CharSequence content) {
return content;
}
private NewlineHandler[] newlineHandlers = new NewlineHandler[] { new BraceHandler() };
@Override
public NewlineHandler[] getNewlineHandlers() {
return newlineHandlers;
}
class BraceHandler implements NewlineHandler {
@Override
public boolean matchesRequirement(String beforeText, String afterText) {
beforeText = beforeText.trim();
afterText = afterText.trim();
return beforeText.endsWith("{") && afterText.startsWith("}");
}
@Override
public HandleResult handleNewline(String beforeText, String afterText, int tabSize) {
int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
int advanceBefore = getIndentAdvance(beforeText);
int advanceAfter = getIndentAdvance(afterText);
String text;
StringBuilder sb = new StringBuilder("\n").append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab())).append('\n').append(text = TextUtils.createIndent(count + advanceAfter, tabSize, useTab()));
int shiftLeft = text.length() + 1;
return new HandleResult(sb, shiftLeft);
}
}
public static String[] getKeywords()
{
return new String[] {
"abstract", "assert", "boolean", "break", "byte", "case",
"catch", "char", "class", "const", "continue", "default",
"do", "double", "else", "enum", "extends", "final", "finally",
"float", "for", "if", "goto", "implements", "import", "instanceof",
"int", "interface", "long", "native", "new", "package", "private",
"protected", "public", "return", "short", "static", "strictfp",
"super", "switch", "synchronized", "this", "throw", "throws",
"transient", "try", "void", "volatile", "while", "null"
};
}
}
@@ -0,0 +1,36 @@
package com.itsaky.androidide.language.java;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.syntax.lexer.Lexer;
import com.itsaky.androidide.syntax.lexer.impls.java.JavaLexerImpl;
import com.itsaky.androidide.syntax.lexer.tokens.Token;
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.text.TextAnalyzer;
import java.util.ArrayList;
import com.blankj.utilcode.util.ThrowableUtils;
public class JavaLanguageAnalyzer implements CodeAnalyzer {
@Override
public void analyze(CharSequence content, TextAnalyzeResult colors, TextAnalyzer.AnalyzeThread.Delegate delegate) {
try {
JavaLexerImpl lexer = new JavaLexerImpl(content.toString(), colors);
lexer.init();
// Adding spans, hex colors, block lines and all is handled by JavaLexerImpl
while (delegate.shouldAnalyze()) {
// null = EOF
if(lexer.nextToken() == null)
break;
}
if (lexer.stack.isEmpty() && lexer.currSwitch > lexer.maxSwitch)
lexer.maxSwitch = lexer.currSwitch;
colors.determine(lexer.lastLine);
colors.setSuppressSwitch(lexer.maxSwitch + 10);
colors.setHexColors(lexer.lineColors);
} catch (Throwable e) {
}
}
}
@@ -0,0 +1,83 @@
package com.itsaky.androidide.language.java.manager;
import java.util.Arrays;
/**
* @author Rose
* Get whether Identifier part/start quickly
*/
public class JavaCharacter {
/**
* Compressed bit set for isJavaIdentifierStart()
*/
private static int[] state_start;
/**
* Compressed bit set for isJavaIdentifierPart()
*/
private static int[] state_part;
/**
* Get bit in compressed bit set
*
* @param values Compressed bit set
* @param bitIndex Target index
* @return Boolean value at the index
*/
private static boolean get(int[] values, int bitIndex) {
return ((values[bitIndex / 32] & (1 << (bitIndex % 32))) != 0);
}
/**
* Make the given position's bit true
*
* @param values Compressed bit set
* @param bitIndex Index of bit
*/
private static void set(int[] values, int bitIndex) {
values[bitIndex / 32] |= (1 << (bitIndex % 32));
}
/**
* Init maps
*/
public static void initMap() {
if (state_start != null) {
return;
}
state_part = new int[2048];
state_start = new int[2048];
Arrays.fill(state_part, 0);
Arrays.fill(state_start, 0);
for (int i = 0; i <= 65535; i++) {
if (Character.isJavaIdentifierPart((char) i)) {
set(state_part, i);
}
if (Character.isJavaIdentifierStart((char) i)) {
set(state_start, i);
}
}
}
/**
* @param key Character
* @return Whether a identifier part
* @see Character#isJavaIdentifierPart(char)
*/
public static boolean isJavaIdentifierPart(int key) {
return get(state_part, key);
}
/**
* @param key Character
* @return Whether a identifier start
* @see Character#isJavaIdentifierStart(char)
*/
public static boolean isJavaIdentifierStart(int key) {
return get(state_start, key);
}
}
@@ -0,0 +1,27 @@
package com.itsaky.androidide.language.java.parser;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import java.util.Collections;
import java.util.ArrayList;
import javax.tools.JavaFileObject;
public class DiagnosticCollector<S> implements DiagnosticListener<S> {
private List<Diagnostic<? extends S>> diagnostics = Collections.synchronizedList(new ArrayList<Diagnostic<? extends S>>());
@Override
public void report(Diagnostic<? extends S> diagnostic) {
diagnostic.getClass();
diagnostics.add(diagnostic);
}
public void clearDiagnostics() {
diagnostics.clear();
}
public List<Diagnostic<? extends S>> getDiagnostics() {
return Collections.unmodifiableList(diagnostics);
}
}
@@ -0,0 +1,60 @@
package com.itsaky.androidide.language.java.parser;
import com.sun.tools.javac.tree.JCTree;
import java.util.LinkedList;
import java.util.List;
import static com.sun.tools.javac.tree.JCTree.JCExpression;
public class Expression {
/**
* Parent scope of statement, from higher to lower
* parentOfStatement.get(0) will be contains parentOfStatement.get(1)
*/
public final LinkedList<JCTree> parentOfStatement = new LinkedList<>();
/**
* Expression at cursor
*/
public JCExpression expression;
public Expression(JCExpression expression) {
this.expression = expression;
}
public Expression(JCTree root, JCExpression expression) {
this.expression = expression;
}
public void addRoot(JCTree tree) {
if (tree != null) {
parentOfStatement.addFirst(tree);
}
}
public JCTree getRootAt(int level) {
return parentOfStatement.get(level);
}
public JCExpression getExpression() {
return expression;
}
public int getParentLevelCount() {
return parentOfStatement.size();
}
public LinkedList<JCTree> getParentOfExpression() {
return parentOfStatement;
}
@Override
public String toString() {
if (expression instanceof JCTree.JCErroneous) {
List<? extends JCTree> errorTrees
= ((JCTree.JCErroneous) expression).getErrorTrees();
return errorTrees.toString();
}
return expression.toString();
}
}
@@ -0,0 +1,411 @@
package com.itsaky.androidide.language.java.parser;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCAssignOp;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCCase;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCForLoop;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSwitch;
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.JCTree.JCUnary;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCLambda;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.List;
import java.util.Map;
import com.sun.tools.javac.tree.EndPosTable;
public class ExpressionResolver {
private int mCursor;
private EndPosTable mEndPositions;
private JCTree.JCCompilationUnit mAst;
public ExpressionResolver(@NonNull JCTree.JCCompilationUnit unit, @NonNull CodeEditor editor) {
unit.getClass();
editor.getClass();
mAst = unit;
mCursor = editor.getCursor().getLeft();
mEndPositions = unit.endPositions;
}
@Nullable
public Expression getExpressionAtCursor() {
for (JCTree jcTree : mAst.getTypeDecls()) {
Expression expression = getExpressionContainsCursor(jcTree);
if (expression != null) {
return expression;
}
}
return null;
}
@Nullable
private Expression getExpressionContainsCursor(JCTree tree) {
if (!isCursorInsideTree(tree)) {
return null;
}
if (tree instanceof JCClassDecl) {
return getExpressionFromClass((JCClassDecl) tree);
}
return null;
}
private Expression getExpressionFromStatement(JCTree.JCStatement statement) {
if (!isCursorInsideTree(statement)) {
return null;
}
Expression result;
if (statement instanceof JCBlock) {
JCBlock jcBlock = (JCBlock) statement;
List<JCStatement> statements = jcBlock.getStatements();
return addRootIfNeeded(jcBlock/*root*/,
getExpressionFromStatements(statements));
} else if (statement instanceof JCClassDecl) {
return getExpressionFromClass((JCClassDecl) statement);
} else if (statement instanceof JCDoWhileLoop) {
JCDoWhileLoop jcDoWhileLoop = (JCDoWhileLoop) statement;
JCExpression cond = jcDoWhileLoop.cond;
if (isCursorInsideTree(cond)) {
return resolveExactlyExpression(statement, cond);
}
JCStatement body = jcDoWhileLoop.getStatement();
result = getExpressionFromStatement(body);
return addRootIfNeeded(statement, result);
} else if (statement instanceof JCEnhancedForLoop) {
JCEnhancedForLoop jcEnhancedForLoop = (JCEnhancedForLoop) statement;
JCExpression expression = jcEnhancedForLoop.getExpression();
if (isCursorInsideTree(expression)) {
return resolveExactlyExpression(jcEnhancedForLoop, expression);
}
JCVariableDecl variable = jcEnhancedForLoop.getVariable();
return addRootIfNeeded(
jcEnhancedForLoop/*root*/,
getExpressionFromStatement(variable));
} else if (statement instanceof JCExpressionStatement) {
JCExpression expression = ((JCExpressionStatement) statement).getExpression();
if (isCursorInsideTree(expression)) {
return resolveExactlyExpression(statement, expression);
}
} else if (statement instanceof JCForLoop) {
JCForLoop jcForLoop = (JCForLoop) statement;
List<JCStatement> initializer = jcForLoop.getInitializer();
result = getExpressionFromStatements(initializer);
if (result != null) {
return addRootIfNeeded(jcForLoop, result);
}
JCExpression condition = jcForLoop.getCondition();
if (isCursorInsideTree(condition)) {
return resolveExactlyExpression(jcForLoop, condition);
}
List<JCExpressionStatement> update = jcForLoop.getUpdate();
result = getExpressionFromStatements(update);
if (result != null) {
return addRootIfNeeded(jcForLoop, result);
}
result = getExpressionFromStatement(jcForLoop.getStatement());
if (result != null) {
return addRootIfNeeded(jcForLoop, result);
}
} else if (statement instanceof JCIf) {
JCIf jcIf = (JCIf) statement;
JCExpression condition = jcIf.getCondition();
if (isCursorInsideTree(condition)) {
return resolveExactlyExpression(jcIf, condition);
}
JCStatement thenStatement = jcIf.getThenStatement();
result = getExpressionFromStatement(thenStatement);
if (result != null) {
return addRootIfNeeded(jcIf, result);
}
JCStatement elseStatement = jcIf.getElseStatement();
if (elseStatement != null) {
result = getExpressionFromStatement(thenStatement);
if (result != null) {
return addRootIfNeeded(jcIf, result);
}
}
} else if (statement instanceof JCLabeledStatement) {
JCLabeledStatement jcLabeledStatement = (JCLabeledStatement) statement;
return addRootIfNeeded(jcLabeledStatement,
getExpressionFromStatement(jcLabeledStatement.getStatement()));
} else if (statement instanceof JCReturn) {
JCReturn jcReturn = (JCReturn) statement;
JCExpression expression = jcReturn.getExpression();
if (isCursorInsideTree(expression)) {
return resolveExactlyExpression(jcReturn, expression);
}
} else if (statement instanceof JCSwitch) {
JCSwitch jcSwitch = (JCSwitch) statement;
JCExpression jcExpression = jcSwitch.getExpression();
if (isCursorInsideTree(jcExpression)) {
return resolveExactlyExpression(jcSwitch, jcExpression);
}
List<JCCase> jcCases = jcSwitch.getCases();
for (JCCase jcCase : jcCases) {
JCExpression jcCaseExpression = jcCase.getExpression();
if (isCursorInsideTree(jcCaseExpression)) {
return resolveExactlyExpression(jcCase, jcCaseExpression);
}
List<JCStatement> statements = jcCase.getStatements();
result = getExpressionFromStatements(statements);
if (result != null) {
return result;
}
}
} else if (statement instanceof JCSynchronized) {
JCSynchronized jcSynchronized = (JCSynchronized) statement;
JCExpression jcExpression = jcSynchronized.getExpression();
if (isCursorInsideTree(jcExpression)) {
return resolveExactlyExpression(jcSynchronized, jcExpression);
}
JCBlock block = jcSynchronized.getBlock();
return addRootIfNeeded(
jcSynchronized/*root*/,
getExpressionFromStatement(block));
} else if (statement instanceof JCThrow) {
JCThrow jcThrow = (JCThrow) statement;
if (isCursorInsideTree(jcThrow.getExpression())) {
return resolveExactlyExpression(jcThrow, jcThrow.getExpression());
}
} else if (statement instanceof JCTry) {
JCTry jcTry = (JCTry) statement;
JCBlock block = jcTry.getBlock();
result = getExpressionFromStatement(block);
if (result != null) {
return addRootIfNeeded(jcTry, result);
}
List<JCCatch> catches = jcTry.getCatches();
for (JCCatch aCatch : catches) {
JCVariableDecl parameter = aCatch.getParameter();
result = getExpressionFromStatement(parameter);
if (result == null) {
block = aCatch.getBlock();
result = getExpressionFromStatement(block);
}
if (result != null) {
result.addRoot(aCatch);
result.addRoot(jcTry);
return result;
}
}
block = jcTry.getFinallyBlock();
result = getExpressionFromStatement(block);
return addRootIfNeeded(jcTry, result);
} else if (statement instanceof JCVariableDecl) {
JCVariableDecl jcVariableDecl = (JCVariableDecl) statement;
JCExpression initializer = jcVariableDecl.getInitializer();
if (isCursorInsideTree(initializer)) {
return resolveExactlyExpression(jcVariableDecl, initializer);
}
JCExpression vartype = jcVariableDecl.vartype;
if (isCursorInsideTree(vartype)) {
return resolveExactlyExpression(jcVariableDecl, vartype);
}
} else if (statement instanceof JCWhileLoop) {
JCWhileLoop jcWhileLoop = (JCWhileLoop) statement;
JCExpression condition = jcWhileLoop.getCondition();
if (isCursorInsideTree(condition)) {
return resolveExactlyExpression(jcWhileLoop, condition);
}
JCStatement body = jcWhileLoop.getStatement();
return addRootIfNeeded(
jcWhileLoop,
getExpressionFromStatement(body));
}
return null;
}
private Expression resolveExactlyExpression(JCTree parent, JCExpression expression) {
if (expression instanceof JCAssign) {
JCAssign jcAssign = (JCAssign) expression;
JCExpression lhs = jcAssign.getVariable();
if (isCursorInsideTree(lhs)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcAssign, lhs));
}
JCExpression rhs = jcAssign.getExpression();
if (isCursorInsideTree(rhs)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcAssign, rhs));
}
} else if (expression instanceof JCAssignOp) {
JCAssignOp jcAssign = (JCAssignOp) expression;
JCExpression lhs = jcAssign.getVariable();
if (isCursorInsideTree(lhs)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcAssign, lhs));
}
JCExpression rhs = jcAssign.getExpression();
if (isCursorInsideTree(rhs)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcAssign, rhs));
}
} else if (expression instanceof JCBinary) {
JCBinary jcBinary = (JCBinary) expression;
JCExpression leftOperand = jcBinary.getLeftOperand();
if (isCursorInsideTree(leftOperand)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcBinary, leftOperand));
}
JCExpression rightOperand = jcBinary.getRightOperand();
if (isCursorInsideTree(rightOperand)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcBinary, rightOperand));
}
} else if (expression instanceof JCConditional) {
JCConditional jcConditional = (JCConditional) expression;
JCExpression condition = jcConditional.getCondition();
if (isCursorInsideTree(condition)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcConditional, condition));
}
JCExpression trueExpression = jcConditional.getTrueExpression();
if (isCursorInsideTree(trueExpression)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcConditional, trueExpression));
}
JCExpression falseExpression = jcConditional.getFalseExpression();
if (isCursorInsideTree(falseExpression)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcConditional, falseExpression));
}
} else if (expression instanceof JCInstanceOf) {
JCInstanceOf jcInstanceOf = (JCInstanceOf) expression;
JCExpression lhs = jcInstanceOf.getExpression();
if (isCursorInsideTree(lhs)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcInstanceOf, lhs));
}
} else if (expression instanceof JCUnary) {
JCUnary jcUnary = (JCUnary) expression;
JCExpression jcExpression = jcUnary.getExpression();
if (isCursorInsideTree(jcExpression)) {
return addRootIfNeeded(
parent, resolveExactlyExpression(jcUnary, jcExpression));
}
}
return new Expression(parent, expression);
}
@Nullable
private Expression getExpressionFromClass(JCClassDecl tree) {
if (!isCursorInsideTree(tree)) {
return null;
}
Expression expression = null;
List<JCTree> members = tree.getMembers();
for (JCTree member : members) {
if (member instanceof JCMethodDecl) {
expression = getExpressionFromMethod((JCMethodDecl) member);
} else if (member instanceof JCVariableDecl) {
expression = getExpressionFromStatement((JCVariableDecl) member);
} else if (member instanceof JCClassDecl) {
expression = getExpressionFromClass((JCClassDecl) member);
}
if (expression != null) {
return addRootIfNeeded(tree, expression);
}
}
return null;
}
@Nullable
private Expression getExpressionFromMethod(JCMethodDecl method) {
Expression expression;
List<JCVariableDecl> parameters = method.getParameters();
for (JCVariableDecl parameter : parameters) {
expression = getExpressionFromStatement(parameter);
if (expression != null) {
expression.addRoot(method);
return expression;
}
}
expression = getExpressionFromStatement(method.getBody());
return addRootIfNeeded(method, expression);
}
@Nullable
private Expression getExpressionFromStatements(
@NonNull List<? extends JCStatement> statements) {
for (JCStatement statement : statements) {
Expression expression = getExpressionFromStatement(statement);
if (expression != null) {
return expression;
}
}
return null;
}
private Expression addRootIfNeeded(JCTree root, Expression expression) {
if (expression != null) {
expression.addRoot(root);
return expression;
}
return null;
}
private boolean isCursorInsideTree(JCTree tree) {
if (tree == null) {
return false;
}
int startPosition = tree.getStartPosition();
int endPosition = tree.getEndPosition(mEndPositions);
return startPosition <= mCursor && mCursor <= endPosition;
}
}
@@ -0,0 +1,506 @@
// Generated from JavaLexer.g4 by ANTLR 4.9.2
package com.itsaky.androidide.language.java.parser;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class JavaLexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.9.2", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
ABSTRACT=1, ASSERT=2, BOOLEAN=3, BREAK=4, BYTE=5, CASE=6, CATCH=7, CHAR=8,
CLASS=9, CONST=10, CONTINUE=11, DEFAULT=12, DO=13, DOUBLE=14, ELSE=15,
ENUM=16, EXTENDS=17, FINAL=18, FINALLY=19, FLOAT=20, FOR=21, IF=22, GOTO=23,
IMPLEMENTS=24, IMPORT=25, INSTANCEOF=26, INT=27, INTERFACE=28, LONG=29,
NATIVE=30, NEW=31, PACKAGE=32, PRIVATE=33, PROTECTED=34, PUBLIC=35, RETURN=36,
SHORT=37, STATIC=38, STRICTFP=39, SUPER=40, SWITCH=41, SYNCHRONIZED=42,
THIS=43, THROW=44, THROWS=45, TRANSIENT=46, TRY=47, VOID=48, VOLATILE=49,
WHILE=50, DECIMAL_LITERAL=51, HEX_LITERAL=52, OCT_LITERAL=53, BINARY_LITERAL=54,
FLOAT_LITERAL=55, HEX_FLOAT_LITERAL=56, BOOL_LITERAL=57, CHAR_LITERAL=58,
STRING_LITERAL=59, NULL_LITERAL=60, LPAREN=61, RPAREN=62, LBRACE=63, RBRACE=64,
LBRACK=65, RBRACK=66, SEMI=67, COMMA=68, DOT=69, ASSIGN=70, GT=71, LT=72,
BANG=73, TILDE=74, QUESTION=75, COLON=76, EQUAL=77, LE=78, GE=79, NOTEQUAL=80,
AND=81, OR=82, INC=83, DEC=84, ADD=85, SUB=86, MUL=87, DIV=88, BITAND=89,
BITOR=90, CARET=91, MOD=92, ADD_ASSIGN=93, SUB_ASSIGN=94, MUL_ASSIGN=95,
DIV_ASSIGN=96, AND_ASSIGN=97, OR_ASSIGN=98, XOR_ASSIGN=99, MOD_ASSIGN=100,
LSHIFT_ASSIGN=101, RSHIFT_ASSIGN=102, URSHIFT_ASSIGN=103, ARROW=104, COLONCOLON=105,
AT=106, ELLIPSIS=107, WS=108, COMMENT=109, LINE_COMMENT=110, IDENTIFIER=111;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE"
};
private static String[] makeRuleNames() {
return new String[] {
"ABSTRACT", "ASSERT", "BOOLEAN", "BREAK", "BYTE", "CASE", "CATCH", "CHAR",
"CLASS", "CONST", "CONTINUE", "DEFAULT", "DO", "DOUBLE", "ELSE", "ENUM",
"EXTENDS", "FINAL", "FINALLY", "FLOAT", "FOR", "IF", "GOTO", "IMPLEMENTS",
"IMPORT", "INSTANCEOF", "INT", "INTERFACE", "LONG", "NATIVE", "NEW",
"PACKAGE", "PRIVATE", "PROTECTED", "PUBLIC", "RETURN", "SHORT", "STATIC",
"STRICTFP", "SUPER", "SWITCH", "SYNCHRONIZED", "THIS", "THROW", "THROWS",
"TRANSIENT", "TRY", "VOID", "VOLATILE", "WHILE", "DECIMAL_LITERAL", "HEX_LITERAL",
"OCT_LITERAL", "BINARY_LITERAL", "FLOAT_LITERAL", "HEX_FLOAT_LITERAL",
"BOOL_LITERAL", "CHAR_LITERAL", "STRING_LITERAL", "NULL_LITERAL", "LPAREN",
"RPAREN", "LBRACE", "RBRACE", "LBRACK", "RBRACK", "SEMI", "COMMA", "DOT",
"ASSIGN", "GT", "LT", "BANG", "TILDE", "QUESTION", "COLON", "EQUAL",
"LE", "GE", "NOTEQUAL", "AND", "OR", "INC", "DEC", "ADD", "SUB", "MUL",
"DIV", "BITAND", "BITOR", "CARET", "MOD", "ADD_ASSIGN", "SUB_ASSIGN",
"MUL_ASSIGN", "DIV_ASSIGN", "AND_ASSIGN", "OR_ASSIGN", "XOR_ASSIGN",
"MOD_ASSIGN", "LSHIFT_ASSIGN", "RSHIFT_ASSIGN", "URSHIFT_ASSIGN", "ARROW",
"COLONCOLON", "AT", "ELLIPSIS", "WS", "COMMENT", "LINE_COMMENT", "IDENTIFIER",
"ExponentPart", "EscapeSequence", "HexDigits", "HexDigit", "Digits",
"LetterOrDigit", "Letter"
};
}
public static final String[] ruleNames = makeRuleNames();
private static String[] makeLiteralNames() {
return new String[] {
null, "'abstract'", "'assert'", "'boolean'", "'break'", "'byte'", "'case'",
"'catch'", "'char'", "'class'", "'const'", "'continue'", "'default'",
"'do'", "'double'", "'else'", "'enum'", "'extends'", "'final'", "'finally'",
"'float'", "'for'", "'if'", "'goto'", "'implements'", "'import'", "'instanceof'",
"'int'", "'interface'", "'long'", "'native'", "'new'", "'package'", "'private'",
"'protected'", "'public'", "'return'", "'short'", "'static'", "'strictfp'",
"'super'", "'switch'", "'synchronized'", "'this'", "'throw'", "'throws'",
"'transient'", "'try'", "'void'", "'volatile'", "'while'", null, null,
null, null, null, null, null, null, null, "'null'", "'('", "')'", "'{'",
"'}'", "'['", "']'", "';'", "','", "'.'", "'='", "'>'", "'<'", "'!'",
"'~'", "'?'", "':'", "'=='", "'<='", "'>='", "'!='", "'&&'", "'||'",
"'++'", "'--'", "'+'", "'-'", "'*'", "'/'", "'&'", "'|'", "'^'", "'%'",
"'+='", "'-='", "'*='", "'/='", "'&='", "'|='", "'^='", "'%='", "'<<='",
"'>>='", "'>>>='", "'->'", "'::'", "'@'", "'...'"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static String[] makeSymbolicNames() {
return new String[] {
null, "ABSTRACT", "ASSERT", "BOOLEAN", "BREAK", "BYTE", "CASE", "CATCH",
"CHAR", "CLASS", "CONST", "CONTINUE", "DEFAULT", "DO", "DOUBLE", "ELSE",
"ENUM", "EXTENDS", "FINAL", "FINALLY", "FLOAT", "FOR", "IF", "GOTO",
"IMPLEMENTS", "IMPORT", "INSTANCEOF", "INT", "INTERFACE", "LONG", "NATIVE",
"NEW", "PACKAGE", "PRIVATE", "PROTECTED", "PUBLIC", "RETURN", "SHORT",
"STATIC", "STRICTFP", "SUPER", "SWITCH", "SYNCHRONIZED", "THIS", "THROW",
"THROWS", "TRANSIENT", "TRY", "VOID", "VOLATILE", "WHILE", "DECIMAL_LITERAL",
"HEX_LITERAL", "OCT_LITERAL", "BINARY_LITERAL", "FLOAT_LITERAL", "HEX_FLOAT_LITERAL",
"BOOL_LITERAL", "CHAR_LITERAL", "STRING_LITERAL", "NULL_LITERAL", "LPAREN",
"RPAREN", "LBRACE", "RBRACE", "LBRACK", "RBRACK", "SEMI", "COMMA", "DOT",
"ASSIGN", "GT", "LT", "BANG", "TILDE", "QUESTION", "COLON", "EQUAL",
"LE", "GE", "NOTEQUAL", "AND", "OR", "INC", "DEC", "ADD", "SUB", "MUL",
"DIV", "BITAND", "BITOR", "CARET", "MOD", "ADD_ASSIGN", "SUB_ASSIGN",
"MUL_ASSIGN", "DIV_ASSIGN", "AND_ASSIGN", "OR_ASSIGN", "XOR_ASSIGN",
"MOD_ASSIGN", "LSHIFT_ASSIGN", "RSHIFT_ASSIGN", "URSHIFT_ASSIGN", "ARROW",
"COLONCOLON", "AT", "ELLIPSIS", "WS", "COMMENT", "LINE_COMMENT", "IDENTIFIER"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
/**
* @deprecated Use {@link #VOCABULARY} instead.
*/
@Deprecated
public static final String[] tokenNames;
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
public JavaLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
@Override
public String getGrammarFileName() { return "JavaLexer.g4"; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2q\u03b3\b\1\4\2\t"+
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
"\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+
"\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+
",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+
"\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+
"\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+
"\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+
"`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k\t"+
"k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv\4"+
"w\tw\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3"+
"\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6"+
"\3\6\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3"+
"\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3"+
"\f\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16\3\17"+
"\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\21\3\21\3\21"+
"\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23"+
"\3\23\3\23\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25"+
"\3\25\3\25\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30"+
"\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32"+
"\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33"+
"\3\33\3\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\35"+
"\3\35\3\36\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3 \3"+
" \3 \3 \3!\3!\3!\3!\3!\3!\3!\3!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3#\3#"+
"\3#\3#\3#\3#\3#\3#\3#\3#\3$\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\3%\3%\3&"+
"\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3\'\3\'\3\'\3(\3(\3(\3(\3(\3(\3(\3(\3"+
"(\3)\3)\3)\3)\3)\3)\3*\3*\3*\3*\3*\3*\3*\3+\3+\3+\3+\3+\3+\3+\3+\3+\3"+
"+\3+\3+\3+\3,\3,\3,\3,\3,\3-\3-\3-\3-\3-\3-\3.\3.\3.\3.\3.\3.\3.\3/\3"+
"/\3/\3/\3/\3/\3/\3/\3/\3/\3\60\3\60\3\60\3\60\3\61\3\61\3\61\3\61\3\61"+
"\3\62\3\62\3\62\3\62\3\62\3\62\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3\63"+
"\3\63\3\64\3\64\3\64\5\64\u0246\n\64\3\64\6\64\u0249\n\64\r\64\16\64\u024a"+
"\3\64\5\64\u024e\n\64\5\64\u0250\n\64\3\64\5\64\u0253\n\64\3\65\3\65\3"+
"\65\3\65\7\65\u0259\n\65\f\65\16\65\u025c\13\65\3\65\5\65\u025f\n\65\3"+
"\65\5\65\u0262\n\65\3\66\3\66\7\66\u0266\n\66\f\66\16\66\u0269\13\66\3"+
"\66\3\66\7\66\u026d\n\66\f\66\16\66\u0270\13\66\3\66\5\66\u0273\n\66\3"+
"\66\5\66\u0276\n\66\3\67\3\67\3\67\3\67\7\67\u027c\n\67\f\67\16\67\u027f"+
"\13\67\3\67\5\67\u0282\n\67\3\67\5\67\u0285\n\67\38\38\38\58\u028a\n8"+
"\38\38\58\u028e\n8\38\58\u0291\n8\38\58\u0294\n8\38\38\38\58\u0299\n8"+
"\38\58\u029c\n8\58\u029e\n8\39\39\39\39\59\u02a4\n9\39\59\u02a7\n9\39"+
"\39\59\u02ab\n9\39\39\59\u02af\n9\39\39\59\u02b3\n9\3:\3:\3:\3:\3:\3:"+
"\3:\3:\3:\5:\u02be\n:\3;\3;\3;\5;\u02c3\n;\3;\3;\3<\3<\3<\7<\u02ca\n<"+
"\f<\16<\u02cd\13<\3<\3<\3=\3=\3=\3=\3=\3>\3>\3?\3?\3@\3@\3A\3A\3B\3B\3"+
"C\3C\3D\3D\3E\3E\3F\3F\3G\3G\3H\3H\3I\3I\3J\3J\3K\3K\3L\3L\3M\3M\3N\3"+
"N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3S\3S\3S\3T\3T\3T\3U\3U\3U\3"+
"V\3V\3W\3W\3X\3X\3Y\3Y\3Z\3Z\3[\3[\3\\\3\\\3]\3]\3^\3^\3^\3_\3_\3_\3`"+
"\3`\3`\3a\3a\3a\3b\3b\3b\3c\3c\3c\3d\3d\3d\3e\3e\3e\3f\3f\3f\3f\3g\3g"+
"\3g\3g\3h\3h\3h\3h\3h\3i\3i\3i\3j\3j\3j\3k\3k\3l\3l\3l\3l\3m\6m\u0350"+
"\nm\rm\16m\u0351\3m\3m\3n\3n\3n\3n\7n\u035a\nn\fn\16n\u035d\13n\3n\3n"+
"\3n\3n\3n\3o\3o\3o\3o\7o\u0368\no\fo\16o\u036b\13o\3o\3o\3p\3p\7p\u0371"+
"\np\fp\16p\u0374\13p\3q\3q\5q\u0378\nq\3q\3q\3r\3r\3r\3r\5r\u0380\nr\3"+
"r\5r\u0383\nr\3r\3r\3r\6r\u0388\nr\rr\16r\u0389\3r\3r\3r\3r\3r\5r\u0391"+
"\nr\3s\3s\3s\7s\u0396\ns\fs\16s\u0399\13s\3s\5s\u039c\ns\3t\3t\3u\3u\7"+
"u\u03a2\nu\fu\16u\u03a5\13u\3u\5u\u03a8\nu\3v\3v\5v\u03ac\nv\3w\3w\3w"+
"\3w\5w\u03b2\nw\3\u035b\2x\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25"+
"\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32"+
"\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a"+
"\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085D\u0087"+
"E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009b"+
"O\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00af"+
"Y\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3"+
"c\u00c5d\u00c7e\u00c9f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5l\u00d7"+
"m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1\2\u00e3\2\u00e5\2\u00e7\2\u00e9\2"+
"\u00eb\2\u00ed\2\3\2\34\3\2\63;\4\2NNnn\4\2ZZzz\5\2\62;CHch\6\2\62;CH"+
"aach\3\2\629\4\2\629aa\4\2DDdd\3\2\62\63\4\2\62\63aa\6\2FFHHffhh\4\2R"+
"Rrr\4\2--//\6\2\f\f\17\17))^^\6\2\f\f\17\17$$^^\5\2\13\f\16\17\"\"\4\2"+
"\f\f\17\17\4\2GGgg\n\2$$))^^ddhhppttvv\3\2\62\65\3\2\62;\4\2\62;aa\6\2"+
"&&C\\aac|\4\2\2\u0081\ud802\udc01\3\2\ud802\udc01\3\2\udc02\ue001\2\u03dc"+
"\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2"+
"\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2"+
"\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2"+
"\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2"+
"\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3"+
"\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2"+
"\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2"+
"U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3"+
"\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2"+
"\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2"+
"{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085"+
"\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2"+
"\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097"+
"\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2"+
"\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9"+
"\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2"+
"\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb"+
"\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2"+
"\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd"+
"\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2"+
"\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df"+
"\3\2\2\2\3\u00ef\3\2\2\2\5\u00f8\3\2\2\2\7\u00ff\3\2\2\2\t\u0107\3\2\2"+
"\2\13\u010d\3\2\2\2\r\u0112\3\2\2\2\17\u0117\3\2\2\2\21\u011d\3\2\2\2"+
"\23\u0122\3\2\2\2\25\u0128\3\2\2\2\27\u012e\3\2\2\2\31\u0137\3\2\2\2\33"+
"\u013f\3\2\2\2\35\u0142\3\2\2\2\37\u0149\3\2\2\2!\u014e\3\2\2\2#\u0153"+
"\3\2\2\2%\u015b\3\2\2\2\'\u0161\3\2\2\2)\u0169\3\2\2\2+\u016f\3\2\2\2"+
"-\u0173\3\2\2\2/\u0176\3\2\2\2\61\u017b\3\2\2\2\63\u0186\3\2\2\2\65\u018d"+
"\3\2\2\2\67\u0198\3\2\2\29\u019c\3\2\2\2;\u01a6\3\2\2\2=\u01ab\3\2\2\2"+
"?\u01b2\3\2\2\2A\u01b6\3\2\2\2C\u01be\3\2\2\2E\u01c6\3\2\2\2G\u01d0\3"+
"\2\2\2I\u01d7\3\2\2\2K\u01de\3\2\2\2M\u01e4\3\2\2\2O\u01eb\3\2\2\2Q\u01f4"+
"\3\2\2\2S\u01fa\3\2\2\2U\u0201\3\2\2\2W\u020e\3\2\2\2Y\u0213\3\2\2\2["+
"\u0219\3\2\2\2]\u0220\3\2\2\2_\u022a\3\2\2\2a\u022e\3\2\2\2c\u0233\3\2"+
"\2\2e\u023c\3\2\2\2g\u024f\3\2\2\2i\u0254\3\2\2\2k\u0263\3\2\2\2m\u0277"+
"\3\2\2\2o\u029d\3\2\2\2q\u029f\3\2\2\2s\u02bd\3\2\2\2u\u02bf\3\2\2\2w"+
"\u02c6\3\2\2\2y\u02d0\3\2\2\2{\u02d5\3\2\2\2}\u02d7\3\2\2\2\177\u02d9"+
"\3\2\2\2\u0081\u02db\3\2\2\2\u0083\u02dd\3\2\2\2\u0085\u02df\3\2\2\2\u0087"+
"\u02e1\3\2\2\2\u0089\u02e3\3\2\2\2\u008b\u02e5\3\2\2\2\u008d\u02e7\3\2"+
"\2\2\u008f\u02e9\3\2\2\2\u0091\u02eb\3\2\2\2\u0093\u02ed\3\2\2\2\u0095"+
"\u02ef\3\2\2\2\u0097\u02f1\3\2\2\2\u0099\u02f3\3\2\2\2\u009b\u02f5\3\2"+
"\2\2\u009d\u02f8\3\2\2\2\u009f\u02fb\3\2\2\2\u00a1\u02fe\3\2\2\2\u00a3"+
"\u0301\3\2\2\2\u00a5\u0304\3\2\2\2\u00a7\u0307\3\2\2\2\u00a9\u030a\3\2"+
"\2\2\u00ab\u030d\3\2\2\2\u00ad\u030f\3\2\2\2\u00af\u0311\3\2\2\2\u00b1"+
"\u0313\3\2\2\2\u00b3\u0315\3\2\2\2\u00b5\u0317\3\2\2\2\u00b7\u0319\3\2"+
"\2\2\u00b9\u031b\3\2\2\2\u00bb\u031d\3\2\2\2\u00bd\u0320\3\2\2\2\u00bf"+
"\u0323\3\2\2\2\u00c1\u0326\3\2\2\2\u00c3\u0329\3\2\2\2\u00c5\u032c\3\2"+
"\2\2\u00c7\u032f\3\2\2\2\u00c9\u0332\3\2\2\2\u00cb\u0335\3\2\2\2\u00cd"+
"\u0339\3\2\2\2\u00cf\u033d\3\2\2\2\u00d1\u0342\3\2\2\2\u00d3\u0345\3\2"+
"\2\2\u00d5\u0348\3\2\2\2\u00d7\u034a\3\2\2\2\u00d9\u034f\3\2\2\2\u00db"+
"\u0355\3\2\2\2\u00dd\u0363\3\2\2\2\u00df\u036e\3\2\2\2\u00e1\u0375\3\2"+
"\2\2\u00e3\u0390\3\2\2\2\u00e5\u0392\3\2\2\2\u00e7\u039d\3\2\2\2\u00e9"+
"\u039f\3\2\2\2\u00eb\u03ab\3\2\2\2\u00ed\u03b1\3\2\2\2\u00ef\u00f0\7c"+
"\2\2\u00f0\u00f1\7d\2\2\u00f1\u00f2\7u\2\2\u00f2\u00f3\7v\2\2\u00f3\u00f4"+
"\7t\2\2\u00f4\u00f5\7c\2\2\u00f5\u00f6\7e\2\2\u00f6\u00f7\7v\2\2\u00f7"+
"\4\3\2\2\2\u00f8\u00f9\7c\2\2\u00f9\u00fa\7u\2\2\u00fa\u00fb\7u\2\2\u00fb"+
"\u00fc\7g\2\2\u00fc\u00fd\7t\2\2\u00fd\u00fe\7v\2\2\u00fe\6\3\2\2\2\u00ff"+
"\u0100\7d\2\2\u0100\u0101\7q\2\2\u0101\u0102\7q\2\2\u0102\u0103\7n\2\2"+
"\u0103\u0104\7g\2\2\u0104\u0105\7c\2\2\u0105\u0106\7p\2\2\u0106\b\3\2"+
"\2\2\u0107\u0108\7d\2\2\u0108\u0109\7t\2\2\u0109\u010a\7g\2\2\u010a\u010b"+
"\7c\2\2\u010b\u010c\7m\2\2\u010c\n\3\2\2\2\u010d\u010e\7d\2\2\u010e\u010f"+
"\7{\2\2\u010f\u0110\7v\2\2\u0110\u0111\7g\2\2\u0111\f\3\2\2\2\u0112\u0113"+
"\7e\2\2\u0113\u0114\7c\2\2\u0114\u0115\7u\2\2\u0115\u0116\7g\2\2\u0116"+
"\16\3\2\2\2\u0117\u0118\7e\2\2\u0118\u0119\7c\2\2\u0119\u011a\7v\2\2\u011a"+
"\u011b\7e\2\2\u011b\u011c\7j\2\2\u011c\20\3\2\2\2\u011d\u011e\7e\2\2\u011e"+
"\u011f\7j\2\2\u011f\u0120\7c\2\2\u0120\u0121\7t\2\2\u0121\22\3\2\2\2\u0122"+
"\u0123\7e\2\2\u0123\u0124\7n\2\2\u0124\u0125\7c\2\2\u0125\u0126\7u\2\2"+
"\u0126\u0127\7u\2\2\u0127\24\3\2\2\2\u0128\u0129\7e\2\2\u0129\u012a\7"+
"q\2\2\u012a\u012b\7p\2\2\u012b\u012c\7u\2\2\u012c\u012d\7v\2\2\u012d\26"+
"\3\2\2\2\u012e\u012f\7e\2\2\u012f\u0130\7q\2\2\u0130\u0131\7p\2\2\u0131"+
"\u0132\7v\2\2\u0132\u0133\7k\2\2\u0133\u0134\7p\2\2\u0134\u0135\7w\2\2"+
"\u0135\u0136\7g\2\2\u0136\30\3\2\2\2\u0137\u0138\7f\2\2\u0138\u0139\7"+
"g\2\2\u0139\u013a\7h\2\2\u013a\u013b\7c\2\2\u013b\u013c\7w\2\2\u013c\u013d"+
"\7n\2\2\u013d\u013e\7v\2\2\u013e\32\3\2\2\2\u013f\u0140\7f\2\2\u0140\u0141"+
"\7q\2\2\u0141\34\3\2\2\2\u0142\u0143\7f\2\2\u0143\u0144\7q\2\2\u0144\u0145"+
"\7w\2\2\u0145\u0146\7d\2\2\u0146\u0147\7n\2\2\u0147\u0148\7g\2\2\u0148"+
"\36\3\2\2\2\u0149\u014a\7g\2\2\u014a\u014b\7n\2\2\u014b\u014c\7u\2\2\u014c"+
"\u014d\7g\2\2\u014d \3\2\2\2\u014e\u014f\7g\2\2\u014f\u0150\7p\2\2\u0150"+
"\u0151\7w\2\2\u0151\u0152\7o\2\2\u0152\"\3\2\2\2\u0153\u0154\7g\2\2\u0154"+
"\u0155\7z\2\2\u0155\u0156\7v\2\2\u0156\u0157\7g\2\2\u0157\u0158\7p\2\2"+
"\u0158\u0159\7f\2\2\u0159\u015a\7u\2\2\u015a$\3\2\2\2\u015b\u015c\7h\2"+
"\2\u015c\u015d\7k\2\2\u015d\u015e\7p\2\2\u015e\u015f\7c\2\2\u015f\u0160"+
"\7n\2\2\u0160&\3\2\2\2\u0161\u0162\7h\2\2\u0162\u0163\7k\2\2\u0163\u0164"+
"\7p\2\2\u0164\u0165\7c\2\2\u0165\u0166\7n\2\2\u0166\u0167\7n\2\2\u0167"+
"\u0168\7{\2\2\u0168(\3\2\2\2\u0169\u016a\7h\2\2\u016a\u016b\7n\2\2\u016b"+
"\u016c\7q\2\2\u016c\u016d\7c\2\2\u016d\u016e\7v\2\2\u016e*\3\2\2\2\u016f"+
"\u0170\7h\2\2\u0170\u0171\7q\2\2\u0171\u0172\7t\2\2\u0172,\3\2\2\2\u0173"+
"\u0174\7k\2\2\u0174\u0175\7h\2\2\u0175.\3\2\2\2\u0176\u0177\7i\2\2\u0177"+
"\u0178\7q\2\2\u0178\u0179\7v\2\2\u0179\u017a\7q\2\2\u017a\60\3\2\2\2\u017b"+
"\u017c\7k\2\2\u017c\u017d\7o\2\2\u017d\u017e\7r\2\2\u017e\u017f\7n\2\2"+
"\u017f\u0180\7g\2\2\u0180\u0181\7o\2\2\u0181\u0182\7g\2\2\u0182\u0183"+
"\7p\2\2\u0183\u0184\7v\2\2\u0184\u0185\7u\2\2\u0185\62\3\2\2\2\u0186\u0187"+
"\7k\2\2\u0187\u0188\7o\2\2\u0188\u0189\7r\2\2\u0189\u018a\7q\2\2\u018a"+
"\u018b\7t\2\2\u018b\u018c\7v\2\2\u018c\64\3\2\2\2\u018d\u018e\7k\2\2\u018e"+
"\u018f\7p\2\2\u018f\u0190\7u\2\2\u0190\u0191\7v\2\2\u0191\u0192\7c\2\2"+
"\u0192\u0193\7p\2\2\u0193\u0194\7e\2\2\u0194\u0195\7g\2\2\u0195\u0196"+
"\7q\2\2\u0196\u0197\7h\2\2\u0197\66\3\2\2\2\u0198\u0199\7k\2\2\u0199\u019a"+
"\7p\2\2\u019a\u019b\7v\2\2\u019b8\3\2\2\2\u019c\u019d\7k\2\2\u019d\u019e"+
"\7p\2\2\u019e\u019f\7v\2\2\u019f\u01a0\7g\2\2\u01a0\u01a1\7t\2\2\u01a1"+
"\u01a2\7h\2\2\u01a2\u01a3\7c\2\2\u01a3\u01a4\7e\2\2\u01a4\u01a5\7g\2\2"+
"\u01a5:\3\2\2\2\u01a6\u01a7\7n\2\2\u01a7\u01a8\7q\2\2\u01a8\u01a9\7p\2"+
"\2\u01a9\u01aa\7i\2\2\u01aa<\3\2\2\2\u01ab\u01ac\7p\2\2\u01ac\u01ad\7"+
"c\2\2\u01ad\u01ae\7v\2\2\u01ae\u01af\7k\2\2\u01af\u01b0\7x\2\2\u01b0\u01b1"+
"\7g\2\2\u01b1>\3\2\2\2\u01b2\u01b3\7p\2\2\u01b3\u01b4\7g\2\2\u01b4\u01b5"+
"\7y\2\2\u01b5@\3\2\2\2\u01b6\u01b7\7r\2\2\u01b7\u01b8\7c\2\2\u01b8\u01b9"+
"\7e\2\2\u01b9\u01ba\7m\2\2\u01ba\u01bb\7c\2\2\u01bb\u01bc\7i\2\2\u01bc"+
"\u01bd\7g\2\2\u01bdB\3\2\2\2\u01be\u01bf\7r\2\2\u01bf\u01c0\7t\2\2\u01c0"+
"\u01c1\7k\2\2\u01c1\u01c2\7x\2\2\u01c2\u01c3\7c\2\2\u01c3\u01c4\7v\2\2"+
"\u01c4\u01c5\7g\2\2\u01c5D\3\2\2\2\u01c6\u01c7\7r\2\2\u01c7\u01c8\7t\2"+
"\2\u01c8\u01c9\7q\2\2\u01c9\u01ca\7v\2\2\u01ca\u01cb\7g\2\2\u01cb\u01cc"+
"\7e\2\2\u01cc\u01cd\7v\2\2\u01cd\u01ce\7g\2\2\u01ce\u01cf\7f\2\2\u01cf"+
"F\3\2\2\2\u01d0\u01d1\7r\2\2\u01d1\u01d2\7w\2\2\u01d2\u01d3\7d\2\2\u01d3"+
"\u01d4\7n\2\2\u01d4\u01d5\7k\2\2\u01d5\u01d6\7e\2\2\u01d6H\3\2\2\2\u01d7"+
"\u01d8\7t\2\2\u01d8\u01d9\7g\2\2\u01d9\u01da\7v\2\2\u01da\u01db\7w\2\2"+
"\u01db\u01dc\7t\2\2\u01dc\u01dd\7p\2\2\u01ddJ\3\2\2\2\u01de\u01df\7u\2"+
"\2\u01df\u01e0\7j\2\2\u01e0\u01e1\7q\2\2\u01e1\u01e2\7t\2\2\u01e2\u01e3"+
"\7v\2\2\u01e3L\3\2\2\2\u01e4\u01e5\7u\2\2\u01e5\u01e6\7v\2\2\u01e6\u01e7"+
"\7c\2\2\u01e7\u01e8\7v\2\2\u01e8\u01e9\7k\2\2\u01e9\u01ea\7e\2\2\u01ea"+
"N\3\2\2\2\u01eb\u01ec\7u\2\2\u01ec\u01ed\7v\2\2\u01ed\u01ee\7t\2\2\u01ee"+
"\u01ef\7k\2\2\u01ef\u01f0\7e\2\2\u01f0\u01f1\7v\2\2\u01f1\u01f2\7h\2\2"+
"\u01f2\u01f3\7r\2\2\u01f3P\3\2\2\2\u01f4\u01f5\7u\2\2\u01f5\u01f6\7w\2"+
"\2\u01f6\u01f7\7r\2\2\u01f7\u01f8\7g\2\2\u01f8\u01f9\7t\2\2\u01f9R\3\2"+
"\2\2\u01fa\u01fb\7u\2\2\u01fb\u01fc\7y\2\2\u01fc\u01fd\7k\2\2\u01fd\u01fe"+
"\7v\2\2\u01fe\u01ff\7e\2\2\u01ff\u0200\7j\2\2\u0200T\3\2\2\2\u0201\u0202"+
"\7u\2\2\u0202\u0203\7{\2\2\u0203\u0204\7p\2\2\u0204\u0205\7e\2\2\u0205"+
"\u0206\7j\2\2\u0206\u0207\7t\2\2\u0207\u0208\7q\2\2\u0208\u0209\7p\2\2"+
"\u0209\u020a\7k\2\2\u020a\u020b\7|\2\2\u020b\u020c\7g\2\2\u020c\u020d"+
"\7f\2\2\u020dV\3\2\2\2\u020e\u020f\7v\2\2\u020f\u0210\7j\2\2\u0210\u0211"+
"\7k\2\2\u0211\u0212\7u\2\2\u0212X\3\2\2\2\u0213\u0214\7v\2\2\u0214\u0215"+
"\7j\2\2\u0215\u0216\7t\2\2\u0216\u0217\7q\2\2\u0217\u0218\7y\2\2\u0218"+
"Z\3\2\2\2\u0219\u021a\7v\2\2\u021a\u021b\7j\2\2\u021b\u021c\7t\2\2\u021c"+
"\u021d\7q\2\2\u021d\u021e\7y\2\2\u021e\u021f\7u\2\2\u021f\\\3\2\2\2\u0220"+
"\u0221\7v\2\2\u0221\u0222\7t\2\2\u0222\u0223\7c\2\2\u0223\u0224\7p\2\2"+
"\u0224\u0225\7u\2\2\u0225\u0226\7k\2\2\u0226\u0227\7g\2\2\u0227\u0228"+
"\7p\2\2\u0228\u0229\7v\2\2\u0229^\3\2\2\2\u022a\u022b\7v\2\2\u022b\u022c"+
"\7t\2\2\u022c\u022d\7{\2\2\u022d`\3\2\2\2\u022e\u022f\7x\2\2\u022f\u0230"+
"\7q\2\2\u0230\u0231\7k\2\2\u0231\u0232\7f\2\2\u0232b\3\2\2\2\u0233\u0234"+
"\7x\2\2\u0234\u0235\7q\2\2\u0235\u0236\7n\2\2\u0236\u0237\7c\2\2\u0237"+
"\u0238\7v\2\2\u0238\u0239\7k\2\2\u0239\u023a\7n\2\2\u023a\u023b\7g\2\2"+
"\u023bd\3\2\2\2\u023c\u023d\7y\2\2\u023d\u023e\7j\2\2\u023e\u023f\7k\2"+
"\2\u023f\u0240\7n\2\2\u0240\u0241\7g\2\2\u0241f\3\2\2\2\u0242\u0250\7"+
"\62\2\2\u0243\u024d\t\2\2\2\u0244\u0246\5\u00e9u\2\u0245\u0244\3\2\2\2"+
"\u0245\u0246\3\2\2\2\u0246\u024e\3\2\2\2\u0247\u0249\7a\2\2\u0248\u0247"+
"\3\2\2\2\u0249\u024a\3\2\2\2\u024a\u0248\3\2\2\2\u024a\u024b\3\2\2\2\u024b"+
"\u024c\3\2\2\2\u024c\u024e\5\u00e9u\2\u024d\u0245\3\2\2\2\u024d\u0248"+
"\3\2\2\2\u024e\u0250\3\2\2\2\u024f\u0242\3\2\2\2\u024f\u0243\3\2\2\2\u0250"+
"\u0252\3\2\2\2\u0251\u0253\t\3\2\2\u0252\u0251\3\2\2\2\u0252\u0253\3\2"+
"\2\2\u0253h\3\2\2\2\u0254\u0255\7\62\2\2\u0255\u0256\t\4\2\2\u0256\u025e"+
"\t\5\2\2\u0257\u0259\t\6\2\2\u0258\u0257\3\2\2\2\u0259\u025c\3\2\2\2\u025a"+
"\u0258\3\2\2\2\u025a\u025b\3\2\2\2\u025b\u025d\3\2\2\2\u025c\u025a\3\2"+
"\2\2\u025d\u025f\t\5\2\2\u025e\u025a\3\2\2\2\u025e\u025f\3\2\2\2\u025f"+
"\u0261\3\2\2\2\u0260\u0262\t\3\2\2\u0261\u0260\3\2\2\2\u0261\u0262\3\2"+
"\2\2\u0262j\3\2\2\2\u0263\u0267\7\62\2\2\u0264\u0266\7a\2\2\u0265\u0264"+
"\3\2\2\2\u0266\u0269\3\2\2\2\u0267\u0265\3\2\2\2\u0267\u0268\3\2\2\2\u0268"+
"\u026a\3\2\2\2\u0269\u0267\3\2\2\2\u026a\u0272\t\7\2\2\u026b\u026d\t\b"+
"\2\2\u026c\u026b\3\2\2\2\u026d\u0270\3\2\2\2\u026e\u026c\3\2\2\2\u026e"+
"\u026f\3\2\2\2\u026f\u0271\3\2\2\2\u0270\u026e\3\2\2\2\u0271\u0273\t\7"+
"\2\2\u0272\u026e\3\2\2\2\u0272\u0273\3\2\2\2\u0273\u0275\3\2\2\2\u0274"+
"\u0276\t\3\2\2\u0275\u0274\3\2\2\2\u0275\u0276\3\2\2\2\u0276l\3\2\2\2"+
"\u0277\u0278\7\62\2\2\u0278\u0279\t\t\2\2\u0279\u0281\t\n\2\2\u027a\u027c"+
"\t\13\2\2\u027b\u027a\3\2\2\2\u027c\u027f\3\2\2\2\u027d\u027b\3\2\2\2"+
"\u027d\u027e\3\2\2\2\u027e\u0280\3\2\2\2\u027f\u027d\3\2\2\2\u0280\u0282"+
"\t\n\2\2\u0281\u027d\3\2\2\2\u0281\u0282\3\2\2\2\u0282\u0284\3\2\2\2\u0283"+
"\u0285\t\3\2\2\u0284\u0283\3\2\2\2\u0284\u0285\3\2\2\2\u0285n\3\2\2\2"+
"\u0286\u0287\5\u00e9u\2\u0287\u0289\7\60\2\2\u0288\u028a\5\u00e9u\2\u0289"+
"\u0288\3\2\2\2\u0289\u028a\3\2\2\2\u028a\u028e\3\2\2\2\u028b\u028c\7\60"+
"\2\2\u028c\u028e\5\u00e9u\2\u028d\u0286\3\2\2\2\u028d\u028b\3\2\2\2\u028e"+
"\u0290\3\2\2\2\u028f\u0291\5\u00e1q\2\u0290\u028f\3\2\2\2\u0290\u0291"+
"\3\2\2\2\u0291\u0293\3\2\2\2\u0292\u0294\t\f\2\2\u0293\u0292\3\2\2\2\u0293"+
"\u0294\3\2\2\2\u0294\u029e\3\2\2\2\u0295\u029b\5\u00e9u\2\u0296\u0298"+
"\5\u00e1q\2\u0297\u0299\t\f\2\2\u0298\u0297\3\2\2\2\u0298\u0299\3\2\2"+
"\2\u0299\u029c\3\2\2\2\u029a\u029c\t\f\2\2\u029b\u0296\3\2\2\2\u029b\u029a"+
"\3\2\2\2\u029c\u029e\3\2\2\2\u029d\u028d\3\2\2\2\u029d\u0295\3\2\2\2\u029e"+
"p\3\2\2\2\u029f\u02a0\7\62\2\2\u02a0\u02aa\t\4\2\2\u02a1\u02a3\5\u00e5"+
"s\2\u02a2\u02a4\7\60\2\2\u02a3\u02a2\3\2\2\2\u02a3\u02a4\3\2\2\2\u02a4"+
"\u02ab\3\2\2\2\u02a5\u02a7\5\u00e5s\2\u02a6\u02a5\3\2\2\2\u02a6\u02a7"+
"\3\2\2\2\u02a7\u02a8\3\2\2\2\u02a8\u02a9\7\60\2\2\u02a9\u02ab\5\u00e5"+
"s\2\u02aa\u02a1\3\2\2\2\u02aa\u02a6\3\2\2\2\u02ab\u02ac\3\2\2\2\u02ac"+
"\u02ae\t\r\2\2\u02ad\u02af\t\16\2\2\u02ae\u02ad\3\2\2\2\u02ae\u02af\3"+
"\2\2\2\u02af\u02b0\3\2\2\2\u02b0\u02b2\5\u00e9u\2\u02b1\u02b3\t\f\2\2"+
"\u02b2\u02b1\3\2\2\2\u02b2\u02b3\3\2\2\2\u02b3r\3\2\2\2\u02b4\u02b5\7"+
"v\2\2\u02b5\u02b6\7t\2\2\u02b6\u02b7\7w\2\2\u02b7\u02be\7g\2\2\u02b8\u02b9"+
"\7h\2\2\u02b9\u02ba\7c\2\2\u02ba\u02bb\7n\2\2\u02bb\u02bc\7u\2\2\u02bc"+
"\u02be\7g\2\2\u02bd\u02b4\3\2\2\2\u02bd\u02b8\3\2\2\2\u02bet\3\2\2\2\u02bf"+
"\u02c2\7)\2\2\u02c0\u02c3\n\17\2\2\u02c1\u02c3\5\u00e3r\2\u02c2\u02c0"+
"\3\2\2\2\u02c2\u02c1\3\2\2\2\u02c3\u02c4\3\2\2\2\u02c4\u02c5\7)\2\2\u02c5"+
"v\3\2\2\2\u02c6\u02cb\7$\2\2\u02c7\u02ca\n\20\2\2\u02c8\u02ca\5\u00e3"+
"r\2\u02c9\u02c7\3\2\2\2\u02c9\u02c8\3\2\2\2\u02ca\u02cd\3\2\2\2\u02cb"+
"\u02c9\3\2\2\2\u02cb\u02cc\3\2\2\2\u02cc\u02ce\3\2\2\2\u02cd\u02cb\3\2"+
"\2\2\u02ce\u02cf\7$\2\2\u02cfx\3\2\2\2\u02d0\u02d1\7p\2\2\u02d1\u02d2"+
"\7w\2\2\u02d2\u02d3\7n\2\2\u02d3\u02d4\7n\2\2\u02d4z\3\2\2\2\u02d5\u02d6"+
"\7*\2\2\u02d6|\3\2\2\2\u02d7\u02d8\7+\2\2\u02d8~\3\2\2\2\u02d9\u02da\7"+
"}\2\2\u02da\u0080\3\2\2\2\u02db\u02dc\7\177\2\2\u02dc\u0082\3\2\2\2\u02dd"+
"\u02de\7]\2\2\u02de\u0084\3\2\2\2\u02df\u02e0\7_\2\2\u02e0\u0086\3\2\2"+
"\2\u02e1\u02e2\7=\2\2\u02e2\u0088\3\2\2\2\u02e3\u02e4\7.\2\2\u02e4\u008a"+
"\3\2\2\2\u02e5\u02e6\7\60\2\2\u02e6\u008c\3\2\2\2\u02e7\u02e8\7?\2\2\u02e8"+
"\u008e\3\2\2\2\u02e9\u02ea\7@\2\2\u02ea\u0090\3\2\2\2\u02eb\u02ec\7>\2"+
"\2\u02ec\u0092\3\2\2\2\u02ed\u02ee\7#\2\2\u02ee\u0094\3\2\2\2\u02ef\u02f0"+
"\7\u0080\2\2\u02f0\u0096\3\2\2\2\u02f1\u02f2\7A\2\2\u02f2\u0098\3\2\2"+
"\2\u02f3\u02f4\7<\2\2\u02f4\u009a\3\2\2\2\u02f5\u02f6\7?\2\2\u02f6\u02f7"+
"\7?\2\2\u02f7\u009c\3\2\2\2\u02f8\u02f9\7>\2\2\u02f9\u02fa\7?\2\2\u02fa"+
"\u009e\3\2\2\2\u02fb\u02fc\7@\2\2\u02fc\u02fd\7?\2\2\u02fd\u00a0\3\2\2"+
"\2\u02fe\u02ff\7#\2\2\u02ff\u0300\7?\2\2\u0300\u00a2\3\2\2\2\u0301\u0302"+
"\7(\2\2\u0302\u0303\7(\2\2\u0303\u00a4\3\2\2\2\u0304\u0305\7~\2\2\u0305"+
"\u0306\7~\2\2\u0306\u00a6\3\2\2\2\u0307\u0308\7-\2\2\u0308\u0309\7-\2"+
"\2\u0309\u00a8\3\2\2\2\u030a\u030b\7/\2\2\u030b\u030c\7/\2\2\u030c\u00aa"+
"\3\2\2\2\u030d\u030e\7-\2\2\u030e\u00ac\3\2\2\2\u030f\u0310\7/\2\2\u0310"+
"\u00ae\3\2\2\2\u0311\u0312\7,\2\2\u0312\u00b0\3\2\2\2\u0313\u0314\7\61"+
"\2\2\u0314\u00b2\3\2\2\2\u0315\u0316\7(\2\2\u0316\u00b4\3\2\2\2\u0317"+
"\u0318\7~\2\2\u0318\u00b6\3\2\2\2\u0319\u031a\7`\2\2\u031a\u00b8\3\2\2"+
"\2\u031b\u031c\7\'\2\2\u031c\u00ba\3\2\2\2\u031d\u031e\7-\2\2\u031e\u031f"+
"\7?\2\2\u031f\u00bc\3\2\2\2\u0320\u0321\7/\2\2\u0321\u0322\7?\2\2\u0322"+
"\u00be\3\2\2\2\u0323\u0324\7,\2\2\u0324\u0325\7?\2\2\u0325\u00c0\3\2\2"+
"\2\u0326\u0327\7\61\2\2\u0327\u0328\7?\2\2\u0328\u00c2\3\2\2\2\u0329\u032a"+
"\7(\2\2\u032a\u032b\7?\2\2\u032b\u00c4\3\2\2\2\u032c\u032d\7~\2\2\u032d"+
"\u032e\7?\2\2\u032e\u00c6\3\2\2\2\u032f\u0330\7`\2\2\u0330\u0331\7?\2"+
"\2\u0331\u00c8\3\2\2\2\u0332\u0333\7\'\2\2\u0333\u0334\7?\2\2\u0334\u00ca"+
"\3\2\2\2\u0335\u0336\7>\2\2\u0336\u0337\7>\2\2\u0337\u0338\7?\2\2\u0338"+
"\u00cc\3\2\2\2\u0339\u033a\7@\2\2\u033a\u033b\7@\2\2\u033b\u033c\7?\2"+
"\2\u033c\u00ce\3\2\2\2\u033d\u033e\7@\2\2\u033e\u033f\7@\2\2\u033f\u0340"+
"\7@\2\2\u0340\u0341\7?\2\2\u0341\u00d0\3\2\2\2\u0342\u0343\7/\2\2\u0343"+
"\u0344\7@\2\2\u0344\u00d2\3\2\2\2\u0345\u0346\7<\2\2\u0346\u0347\7<\2"+
"\2\u0347\u00d4\3\2\2\2\u0348\u0349\7B\2\2\u0349\u00d6\3\2\2\2\u034a\u034b"+
"\7\60\2\2\u034b\u034c\7\60\2\2\u034c\u034d\7\60\2\2\u034d\u00d8\3\2\2"+
"\2\u034e\u0350\t\21\2\2\u034f\u034e\3\2\2\2\u0350\u0351\3\2\2\2\u0351"+
"\u034f\3\2\2\2\u0351\u0352\3\2\2\2\u0352\u0353\3\2\2\2\u0353\u0354\bm"+
"\2\2\u0354\u00da\3\2\2\2\u0355\u0356\7\61\2\2\u0356\u0357\7,\2\2\u0357"+
"\u035b\3\2\2\2\u0358\u035a\13\2\2\2\u0359\u0358\3\2\2\2\u035a\u035d\3"+
"\2\2\2\u035b\u035c\3\2\2\2\u035b\u0359\3\2\2\2\u035c\u035e\3\2\2\2\u035d"+
"\u035b\3\2\2\2\u035e\u035f\7,\2\2\u035f\u0360\7\61\2\2\u0360\u0361\3\2"+
"\2\2\u0361\u0362\bn\2\2\u0362\u00dc\3\2\2\2\u0363\u0364\7\61\2\2\u0364"+
"\u0365\7\61\2\2\u0365\u0369\3\2\2\2\u0366\u0368\n\22\2\2\u0367\u0366\3"+
"\2\2\2\u0368\u036b\3\2\2\2\u0369\u0367\3\2\2\2\u0369\u036a\3\2\2\2\u036a"+
"\u036c\3\2\2\2\u036b\u0369\3\2\2\2\u036c\u036d\bo\2\2\u036d\u00de\3\2"+
"\2\2\u036e\u0372\5\u00edw\2\u036f\u0371\5\u00ebv\2\u0370\u036f\3\2\2\2"+
"\u0371\u0374\3\2\2\2\u0372\u0370\3\2\2\2\u0372\u0373\3\2\2\2\u0373\u00e0"+
"\3\2\2\2\u0374\u0372\3\2\2\2\u0375\u0377\t\23\2\2\u0376\u0378\t\16\2\2"+
"\u0377\u0376\3\2\2\2\u0377\u0378\3\2\2\2\u0378\u0379\3\2\2\2\u0379\u037a"+
"\5\u00e9u\2\u037a\u00e2\3\2\2\2\u037b\u037c\7^\2\2\u037c\u0391\t\24\2"+
"\2\u037d\u0382\7^\2\2\u037e\u0380\t\25\2\2\u037f\u037e\3\2\2\2\u037f\u0380"+
"\3\2\2\2\u0380\u0381\3\2\2\2\u0381\u0383\t\7\2\2\u0382\u037f\3\2\2\2\u0382"+
"\u0383\3\2\2\2\u0383\u0384\3\2\2\2\u0384\u0391\t\7\2\2\u0385\u0387\7^"+
"\2\2\u0386\u0388\7w\2\2\u0387\u0386\3\2\2\2\u0388\u0389\3\2\2\2\u0389"+
"\u0387\3\2\2\2\u0389\u038a\3\2\2\2\u038a\u038b\3\2\2\2\u038b\u038c\5\u00e7"+
"t\2\u038c\u038d\5\u00e7t\2\u038d\u038e\5\u00e7t\2\u038e\u038f\5\u00e7"+
"t\2\u038f\u0391\3\2\2\2\u0390\u037b\3\2\2\2\u0390\u037d\3\2\2\2\u0390"+
"\u0385\3\2\2\2\u0391\u00e4\3\2\2\2\u0392\u039b\5\u00e7t\2\u0393\u0396"+
"\5\u00e7t\2\u0394\u0396\7a\2\2\u0395\u0393\3\2\2\2\u0395\u0394\3\2\2\2"+
"\u0396\u0399\3\2\2\2\u0397\u0395\3\2\2\2\u0397\u0398\3\2\2\2\u0398\u039a"+
"\3\2\2\2\u0399\u0397\3\2\2\2\u039a\u039c\5\u00e7t\2\u039b\u0397\3\2\2"+
"\2\u039b\u039c\3\2\2\2\u039c\u00e6\3\2\2\2\u039d\u039e\t\5\2\2\u039e\u00e8"+
"\3\2\2\2\u039f\u03a7\t\26\2\2\u03a0\u03a2\t\27\2\2\u03a1\u03a0\3\2\2\2"+
"\u03a2\u03a5\3\2\2\2\u03a3\u03a1\3\2\2\2\u03a3\u03a4\3\2\2\2\u03a4\u03a6"+
"\3\2\2\2\u03a5\u03a3\3\2\2\2\u03a6\u03a8\t\26\2\2\u03a7\u03a3\3\2\2\2"+
"\u03a7\u03a8\3\2\2\2\u03a8\u00ea\3\2\2\2\u03a9\u03ac\5\u00edw\2\u03aa"+
"\u03ac\t\26\2\2\u03ab\u03a9\3\2\2\2\u03ab\u03aa\3\2\2\2\u03ac\u00ec\3"+
"\2\2\2\u03ad\u03b2\t\30\2\2\u03ae\u03b2\n\31\2\2\u03af\u03b0\t\32\2\2"+
"\u03b0\u03b2\t\33\2\2\u03b1\u03ad\3\2\2\2\u03b1\u03ae\3\2\2\2\u03b1\u03af"+
"\3\2\2\2\u03b2\u00ee\3\2\2\2\62\2\u0245\u024a\u024d\u024f\u0252\u025a"+
"\u025e\u0261\u0267\u026e\u0272\u0275\u027d\u0281\u0284\u0289\u028d\u0290"+
"\u0293\u0298\u029b\u029d\u02a3\u02a6\u02aa\u02ae\u02b2\u02bd\u02c2\u02c9"+
"\u02cb\u0351\u035b\u0369\u0372\u0377\u037f\u0382\u0389\u0390\u0395\u0397"+
"\u039b\u03a3\u03a7\u03ab\u03b1\3\2\3\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,749 @@
// Generated from JavaParser.g4 by ANTLR 4.9.2
package com.itsaky.androidide.language.java.parser;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
/**
* This class provides an empty implementation of {@link JavaParserVisitor},
* which can be extended to create a visitor which only needs to handle a subset
* of the available methods.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public class JavaParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements JavaParserVisitor<T> {
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitCompilationUnit(JavaParser.CompilationUnitContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitImportDeclaration(JavaParser.ImportDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeDeclaration(JavaParser.TypeDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitModifier(JavaParser.ModifierContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitClassOrInterfaceModifier(JavaParser.ClassOrInterfaceModifierContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVariableModifier(JavaParser.VariableModifierContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeParameters(JavaParser.TypeParametersContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeParameter(JavaParser.TypeParameterContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeBound(JavaParser.TypeBoundContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitEnumConstants(JavaParser.EnumConstantsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitEnumConstant(JavaParser.EnumConstantContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitEnumBodyDeclarations(JavaParser.EnumBodyDeclarationsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitClassBody(JavaParser.ClassBodyContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitInterfaceBody(JavaParser.InterfaceBodyContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitMemberDeclaration(JavaParser.MemberDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitMethodBody(JavaParser.MethodBodyContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeTypeOrVoid(JavaParser.TypeTypeOrVoidContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitGenericMethodDeclaration(JavaParser.GenericMethodDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitGenericConstructorDeclaration(JavaParser.GenericConstructorDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitInterfaceBodyDeclaration(JavaParser.InterfaceBodyDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitInterfaceMemberDeclaration(JavaParser.InterfaceMemberDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitConstDeclaration(JavaParser.ConstDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitConstantDeclarator(JavaParser.ConstantDeclaratorContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitInterfaceMethodModifier(JavaParser.InterfaceMethodModifierContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitGenericInterfaceMethodDeclaration(JavaParser.GenericInterfaceMethodDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVariableDeclarators(JavaParser.VariableDeclaratorsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVariableDeclarator(JavaParser.VariableDeclaratorContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVariableDeclaratorId(JavaParser.VariableDeclaratorIdContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitVariableInitializer(JavaParser.VariableInitializerContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitArrayInitializer(JavaParser.ArrayInitializerContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitClassOrInterfaceType(JavaParser.ClassOrInterfaceTypeContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeArgument(JavaParser.TypeArgumentContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitQualifiedNameList(JavaParser.QualifiedNameListContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFormalParameters(JavaParser.FormalParametersContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFormalParameterList(JavaParser.FormalParameterListContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFormalParameter(JavaParser.FormalParameterContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLastFormalParameter(JavaParser.LastFormalParameterContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitQualifiedName(JavaParser.QualifiedNameContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLiteral(JavaParser.LiteralContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitIntegerLiteral(JavaParser.IntegerLiteralContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFloatLiteral(JavaParser.FloatLiteralContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAltAnnotationQualifiedName(JavaParser.AltAnnotationQualifiedNameContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotation(JavaParser.AnnotationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitElementValuePairs(JavaParser.ElementValuePairsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitElementValuePair(JavaParser.ElementValuePairContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitElementValue(JavaParser.ElementValueContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitElementValueArrayInitializer(JavaParser.ElementValueArrayInitializerContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotationTypeBody(JavaParser.AnnotationTypeBodyContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotationTypeElementDeclaration(JavaParser.AnnotationTypeElementDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotationTypeElementRest(JavaParser.AnnotationTypeElementRestContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotationMethodOrConstantRest(JavaParser.AnnotationMethodOrConstantRestContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotationMethodRest(JavaParser.AnnotationMethodRestContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitAnnotationConstantRest(JavaParser.AnnotationConstantRestContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitDefaultValue(JavaParser.DefaultValueContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitBlock(JavaParser.BlockContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitBlockStatement(JavaParser.BlockStatementContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLocalTypeDeclaration(JavaParser.LocalTypeDeclarationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStatement(JavaParser.StatementContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitCatchClause(JavaParser.CatchClauseContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitCatchType(JavaParser.CatchTypeContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitFinallyBlock(JavaParser.FinallyBlockContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitResourceSpecification(JavaParser.ResourceSpecificationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitResources(JavaParser.ResourcesContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitResource(JavaParser.ResourceContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitSwitchBlockStatementGroup(JavaParser.SwitchBlockStatementGroupContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitSwitchLabel(JavaParser.SwitchLabelContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitForControl(JavaParser.ForControlContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitForInit(JavaParser.ForInitContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitEnhancedForControl(JavaParser.EnhancedForControlContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitParExpression(JavaParser.ParExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpressionList(JavaParser.ExpressionListContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitMethodCall(JavaParser.MethodCallContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExpression(JavaParser.ExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLambdaExpression(JavaParser.LambdaExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLambdaParameters(JavaParser.LambdaParametersContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLambdaBody(JavaParser.LambdaBodyContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitPrimary(JavaParser.PrimaryContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitClassType(JavaParser.ClassTypeContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitCreator(JavaParser.CreatorContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitCreatedName(JavaParser.CreatedNameContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitInnerCreator(JavaParser.InnerCreatorContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitArrayCreatorRest(JavaParser.ArrayCreatorRestContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitClassCreatorRest(JavaParser.ClassCreatorRestContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExplicitGenericInvocation(JavaParser.ExplicitGenericInvocationContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeArgumentsOrDiamond(JavaParser.TypeArgumentsOrDiamondContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitNonWildcardTypeArgumentsOrDiamond(JavaParser.NonWildcardTypeArgumentsOrDiamondContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitNonWildcardTypeArguments(JavaParser.NonWildcardTypeArgumentsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeList(JavaParser.TypeListContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeType(JavaParser.TypeTypeContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitPrimitiveType(JavaParser.PrimitiveTypeContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeArguments(JavaParser.TypeArgumentsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitSuperSuffix(JavaParser.SuperSuffixContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitExplicitGenericInvocationSuffix(JavaParser.ExplicitGenericInvocationSuffixContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitArguments(JavaParser.ArgumentsContext ctx) { return visitChildren(ctx); }
}
@@ -0,0 +1,643 @@
// Generated from JavaParser.g4 by ANTLR 4.9.2
package com.itsaky.androidide.language.java.parser;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
/**
* This interface defines a complete generic visitor for a parse tree produced
* by {@link JavaParser}.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public interface JavaParserVisitor<T> extends ParseTreeVisitor<T> {
/**
* Visit a parse tree produced by {@link JavaParser#compilationUnit}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCompilationUnit(JavaParser.CompilationUnitContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#packageDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitPackageDeclaration(JavaParser.PackageDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#importDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitImportDeclaration(JavaParser.ImportDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeDeclaration(JavaParser.TypeDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#modifier}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitModifier(JavaParser.ModifierContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#classOrInterfaceModifier}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitClassOrInterfaceModifier(JavaParser.ClassOrInterfaceModifierContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#variableModifier}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitVariableModifier(JavaParser.VariableModifierContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#classDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitClassDeclaration(JavaParser.ClassDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeParameters}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeParameters(JavaParser.TypeParametersContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeParameter}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeParameter(JavaParser.TypeParameterContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeBound}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeBound(JavaParser.TypeBoundContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#enumDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitEnumDeclaration(JavaParser.EnumDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#enumConstants}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitEnumConstants(JavaParser.EnumConstantsContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#enumConstant}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitEnumConstant(JavaParser.EnumConstantContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#enumBodyDeclarations}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitEnumBodyDeclarations(JavaParser.EnumBodyDeclarationsContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#interfaceDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#classBody}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitClassBody(JavaParser.ClassBodyContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#interfaceBody}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitInterfaceBody(JavaParser.InterfaceBodyContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#classBodyDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#memberDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMemberDeclaration(JavaParser.MemberDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#methodDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMethodDeclaration(JavaParser.MethodDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#methodBody}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMethodBody(JavaParser.MethodBodyContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeTypeOrVoid}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeTypeOrVoid(JavaParser.TypeTypeOrVoidContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#genericMethodDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitGenericMethodDeclaration(JavaParser.GenericMethodDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#genericConstructorDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitGenericConstructorDeclaration(JavaParser.GenericConstructorDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#constructorDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#fieldDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFieldDeclaration(JavaParser.FieldDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#interfaceBodyDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitInterfaceBodyDeclaration(JavaParser.InterfaceBodyDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#interfaceMemberDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitInterfaceMemberDeclaration(JavaParser.InterfaceMemberDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#constDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitConstDeclaration(JavaParser.ConstDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#constantDeclarator}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitConstantDeclarator(JavaParser.ConstantDeclaratorContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#interfaceMethodDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#interfaceMethodModifier}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitInterfaceMethodModifier(JavaParser.InterfaceMethodModifierContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#genericInterfaceMethodDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitGenericInterfaceMethodDeclaration(JavaParser.GenericInterfaceMethodDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#variableDeclarators}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitVariableDeclarators(JavaParser.VariableDeclaratorsContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#variableDeclarator}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitVariableDeclarator(JavaParser.VariableDeclaratorContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#variableDeclaratorId}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitVariableDeclaratorId(JavaParser.VariableDeclaratorIdContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#variableInitializer}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitVariableInitializer(JavaParser.VariableInitializerContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#arrayInitializer}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitArrayInitializer(JavaParser.ArrayInitializerContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#classOrInterfaceType}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitClassOrInterfaceType(JavaParser.ClassOrInterfaceTypeContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeArgument}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeArgument(JavaParser.TypeArgumentContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#qualifiedNameList}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitQualifiedNameList(JavaParser.QualifiedNameListContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#formalParameters}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFormalParameters(JavaParser.FormalParametersContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#formalParameterList}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFormalParameterList(JavaParser.FormalParameterListContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#formalParameter}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFormalParameter(JavaParser.FormalParameterContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#lastFormalParameter}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLastFormalParameter(JavaParser.LastFormalParameterContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#qualifiedName}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitQualifiedName(JavaParser.QualifiedNameContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#literal}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLiteral(JavaParser.LiteralContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#integerLiteral}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitIntegerLiteral(JavaParser.IntegerLiteralContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#floatLiteral}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFloatLiteral(JavaParser.FloatLiteralContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#altAnnotationQualifiedName}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAltAnnotationQualifiedName(JavaParser.AltAnnotationQualifiedNameContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotation}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotation(JavaParser.AnnotationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#elementValuePairs}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitElementValuePairs(JavaParser.ElementValuePairsContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#elementValuePair}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitElementValuePair(JavaParser.ElementValuePairContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#elementValue}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitElementValue(JavaParser.ElementValueContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#elementValueArrayInitializer}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitElementValueArrayInitializer(JavaParser.ElementValueArrayInitializerContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotationTypeDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotationTypeBody}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotationTypeBody(JavaParser.AnnotationTypeBodyContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotationTypeElementDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotationTypeElementDeclaration(JavaParser.AnnotationTypeElementDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotationTypeElementRest}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotationTypeElementRest(JavaParser.AnnotationTypeElementRestContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotationMethodOrConstantRest}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotationMethodOrConstantRest(JavaParser.AnnotationMethodOrConstantRestContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotationMethodRest}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotationMethodRest(JavaParser.AnnotationMethodRestContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#annotationConstantRest}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAnnotationConstantRest(JavaParser.AnnotationConstantRestContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#defaultValue}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitDefaultValue(JavaParser.DefaultValueContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#block}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBlock(JavaParser.BlockContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#blockStatement}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBlockStatement(JavaParser.BlockStatementContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#localVariableDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#localTypeDeclaration}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLocalTypeDeclaration(JavaParser.LocalTypeDeclarationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#statement}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitStatement(JavaParser.StatementContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#catchClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCatchClause(JavaParser.CatchClauseContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#catchType}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCatchType(JavaParser.CatchTypeContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#finallyBlock}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFinallyBlock(JavaParser.FinallyBlockContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#resourceSpecification}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitResourceSpecification(JavaParser.ResourceSpecificationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#resources}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitResources(JavaParser.ResourcesContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#resource}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitResource(JavaParser.ResourceContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#switchBlockStatementGroup}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSwitchBlockStatementGroup(JavaParser.SwitchBlockStatementGroupContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#switchLabel}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSwitchLabel(JavaParser.SwitchLabelContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#forControl}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitForControl(JavaParser.ForControlContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#forInit}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitForInit(JavaParser.ForInitContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#enhancedForControl}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitEnhancedForControl(JavaParser.EnhancedForControlContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#parExpression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitParExpression(JavaParser.ParExpressionContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#expressionList}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitExpressionList(JavaParser.ExpressionListContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#methodCall}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMethodCall(JavaParser.MethodCallContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitExpression(JavaParser.ExpressionContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#lambdaExpression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLambdaExpression(JavaParser.LambdaExpressionContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#lambdaParameters}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLambdaParameters(JavaParser.LambdaParametersContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#lambdaBody}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLambdaBody(JavaParser.LambdaBodyContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#primary}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitPrimary(JavaParser.PrimaryContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#classType}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitClassType(JavaParser.ClassTypeContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#creator}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCreator(JavaParser.CreatorContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#createdName}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCreatedName(JavaParser.CreatedNameContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#innerCreator}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitInnerCreator(JavaParser.InnerCreatorContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#arrayCreatorRest}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitArrayCreatorRest(JavaParser.ArrayCreatorRestContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#classCreatorRest}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitClassCreatorRest(JavaParser.ClassCreatorRestContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#explicitGenericInvocation}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitExplicitGenericInvocation(JavaParser.ExplicitGenericInvocationContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeArgumentsOrDiamond}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeArgumentsOrDiamond(JavaParser.TypeArgumentsOrDiamondContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#nonWildcardTypeArgumentsOrDiamond}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitNonWildcardTypeArgumentsOrDiamond(JavaParser.NonWildcardTypeArgumentsOrDiamondContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#nonWildcardTypeArguments}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitNonWildcardTypeArguments(JavaParser.NonWildcardTypeArgumentsContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeList}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeList(JavaParser.TypeListContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeType}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeType(JavaParser.TypeTypeContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#primitiveType}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitPrimitiveType(JavaParser.PrimitiveTypeContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#typeArguments}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeArguments(JavaParser.TypeArgumentsContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#superSuffix}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSuperSuffix(JavaParser.SuperSuffixContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#explicitGenericInvocationSuffix}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitExplicitGenericInvocationSuffix(JavaParser.ExplicitGenericInvocationSuffixContext ctx);
/**
* Visit a parse tree produced by {@link JavaParser#arguments}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitArguments(JavaParser.ArgumentsContext ctx);
}
@@ -0,0 +1,283 @@
package com.itsaky.androidide.language.java.parser;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.itsaky.androidide.javadoc.JavaDoc;
import com.itsaky.androidide.javadoc.JavaDocParser;
import com.itsaky.androidide.javadoc.JavaDocParserBuilder;
import com.itsaky.androidide.javadoc.OutputType;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
import com.itsaky.androidide.language.java.parser.model.ClassDescription;
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
import com.itsaky.androidide.language.java.parser.model.FieldDescription;
import com.itsaky.androidide.language.java.parser.model.MethodDescription;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.parser.Parser;
import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.parser.Tokens;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Options;
import io.github.rosemoe.editor.text.Content;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.Modifier;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import static com.sun.tools.javac.tree.JCTree.*;
public class JavacParser {
private Context context;
private ParserFactory parserFactory;
private JavaDocParser javadocParser;
private DiagnosticCollector<JavaFileObject> diagnostics;
private boolean canParse = true;
private static final String DOT = ".";
private static final String CONSTRUCTOR_NAME = "<init>";
public JavacParser() {
context = new Context();
diagnostics = new DiagnosticCollector<JavaFileObject>();
context.put(DiagnosticListener.class, diagnostics);
Options.instance(context).put("allowStringFolding", "false");
JavacFileManager fileManager = new JavacFileManager(context, true, Charsets.UTF_8);
try {
fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.<File>of());
} catch (IOException e) {
canParse = false;
}
parserFactory = ParserFactory.instance(context);
}
@Nullable
public JCCompilationUnit parse(Content src) {
return parse(src.toString());
}
@Nullable
public JCCompilationUnit parse(final String src) {
if (!canParse) return null;
SimpleJavaFileObject source = new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return src;
}
};
Log.instance(context).useSource(source);
diagnostics.clearDiagnostics();
Parser parser =
parserFactory.newParser(src,
/*keepDocComments=*/ true, // Necessary, because we show doc comments in AndroidIDE
/*keepEndPos=*/ true,
/*keepLineMap=*/ true);
JCCompilationUnit unit;
unit = parser.parseCompilationUnit();
unit.sourcefile = source;
return unit;
}
@Nullable
public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() {
if (!canParse) return null;
return diagnostics.getDiagnostics();
}
public List<IClass> parseClasses(JCCompilationUnit unit) {
List<IClass> classes = new ArrayList<>();
List<JCTree> typeDecls = unit.getTypeDecls();
for (JCTree typeDecl : typeDecls) {
if (typeDecl instanceof JCClassDecl) {
classes.add(parseClass(unit, (JCClassDecl) typeDecl));
}
}
return classes;
}
private IClass parseClass(JCCompilationUnit unit, JCClassDecl classDecl) {
final String className = unit.getPackageName() + DOT + classDecl.getSimpleName();
final int modifiers = JavaUtil.toJavaModifiers(classDecl.getModifiers().getFlags());
ClassDescription clazz = new ClassDescription(
className,
modifiers,
false,
classDecl.getKind() == Tree.Kind.ANNOTATION_TYPE,
classDecl.getKind() == Tree.Kind.ENUM,
false);
IClass extendsClass = JavaUtil.jcTypeToClass(unit, classDecl.getExtendsClause());
if (extendsClass != null) {
clazz.setSuperclass(extendsClass);
} else {
clazz.setSuperclass(JavaClassManager.getInstance().getParsedClass(Object.class.getName()));
}
List<JCTree> members = classDecl.getMembers();
for (JCTree member : members) {
if (member instanceof JCTree.JCMethodDecl) {
addMethod(unit, clazz, (JCTree.JCMethodDecl) member);
} else if (member instanceof JCTree.JCVariableDecl) {
addVariable(unit, clazz, classDecl, (JCTree.JCVariableDecl) member);
} else if(member instanceof JCTree.JCClassDecl) {
addNestedClass(unit, classDecl, (JCTree.JCClassDecl) member);
}
}
if(clazz != null && unit.docComments != null && unit.docComments.hasComment(classDecl)) {
Tokens.Comment comment = unit.docComments.getComment(classDecl);
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
processJavaDoc(comment, clazz);
}
}
//now add constructor
//add methods
return clazz;
}
private void addNestedClass(@NonNull JCCompilationUnit ast, JCClassDecl parent, JCClassDecl classDecl) {
final String className = ast.getPackageName() + DOT + parent.getSimpleName() + "$" + classDecl.getSimpleName();
final int modifiers = JavaUtil.toJavaModifiers(classDecl.getModifiers().getFlags());
ClassDescription clazz = new ClassDescription(
className,
modifiers,
false,
classDecl.getKind() == Tree.Kind.ANNOTATION_TYPE,
classDecl.getKind() == Tree.Kind.ENUM,
false);
IClass extendsClass = JavaUtil.jcTypeToClass(ast, classDecl.getExtendsClause());
if (extendsClass != null) {
clazz.setSuperclass(extendsClass);
} else {
clazz.setSuperclass(JavaClassManager.getInstance().getParsedClass(Object.class.getName()));
}
List<JCTree> members = classDecl.getMembers();
for (JCTree member : members) {
if (member instanceof JCTree.JCMethodDecl) {
addMethod(ast, clazz, (JCTree.JCMethodDecl) member);
} else if (member instanceof JCTree.JCVariableDecl) {
addVariable(ast, clazz, classDecl, (JCTree.JCVariableDecl) member);
} else if(member instanceof JCTree.JCClassDecl) {
addNestedClass(ast, classDecl, (JCTree.JCClassDecl) member);
}
}
if(clazz != null && ast.docComments != null && ast.docComments.hasComment(classDecl)) {
Tokens.Comment comment = ast.docComments.getComment(classDecl);
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
processJavaDoc(comment, clazz);
}
}
}
private void addVariable(@NonNull JCCompilationUnit unit,
ClassDescription clazz, @NonNull JCClassDecl classDecl,
@NonNull JCTree.JCVariableDecl member) {
String name = member.getName().toString();
int modifiers = JavaUtil.toJavaModifiers(member.getModifiers().getFlags());
IClass type = JavaUtil.jcTypeToClass(unit, member.getType());
JCTree.JCExpression initializer = member.getInitializer();
FieldDescription fieldDescription = new FieldDescription(
modifiers,
type,
name,
initializer != null ? initializer.toString() : null);
clazz.addField(fieldDescription);
if(fieldDescription != null && unit.docComments != null && unit.docComments.hasComment(member)) {
Tokens.Comment comment = unit.docComments.getComment(member);
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
processJavaDoc(comment, fieldDescription);
}
}
}
private void addMethod(@NonNull JCCompilationUnit unit,
@NonNull ClassDescription clazz,
@NonNull JCTree.JCMethodDecl member) {
final String methodName = member.getName().toString();
final int modifiers = JavaUtil.toJavaModifiers(member.getModifiers().getFlags());
final List<IClass> methodParameters = new ArrayList<>();
List<JCTree.JCVariableDecl> parameters = member.getParameters();
for (JCTree.JCVariableDecl parameter : parameters) {
JCTree type = parameter.getType();
IClass paramType = JavaUtil.jcTypeToClass(unit, type);
methodParameters.add(paramType);
}
IClass returnType = JavaUtil.jcTypeToClass(unit, member.getReturnType());
IJavaDocCommentable commetable = null;
if (member.getName().toString().equals(CONSTRUCTOR_NAME)) {
ConstructorDescription constructor = new ConstructorDescription(
clazz.getFullClassName(),
methodParameters);
clazz.addConstructor(constructor);
commetable = constructor;
} else {
MethodDescription methodDescription = new MethodDescription(
methodName,
modifiers,
methodParameters,
returnType);
clazz.addMethod(methodDescription);
commetable = methodDescription;
}
if(commetable != null && unit.docComments != null && unit.docComments.hasComment(member)) {
Tokens.Comment comment = unit.docComments.getComment(member);
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
processJavaDoc(comment, commetable);
}
}
}
private void processJavaDoc(Tokens.Comment comment, IJavaDocCommentable commetable) {
if(javadocParser == null) {
javadocParser = JavaDocParserBuilder.withBasicTags()
.withOutputType(OutputType.HTML)
.build();
}
final JavaDoc doc = javadocParser.parse(comment.getText());
final String html = doc.getSummary() + "\n" + doc.getDescription();
commetable.setHtmlJavaDoc(html);
}
private void addConstructor(ClassDescription clazz, JCTree.JCMethodDecl member) {
ConstructorDescription constructorDescription;
}
private JCClassDecl resolveClassSimpleName(List<JCTree> typeDecls) {
for (JCTree typeDecl : typeDecls) {
if (typeDecl instanceof JCClassDecl) {
JCModifiers modifiers = ((JCClassDecl) typeDecl).getModifiers();
Set<Modifier> flags = modifiers.getFlags();
for (Modifier flag : flags) {
if (flag.equals(Modifier.PUBLIC)) {
return (JCClassDecl) typeDecl;
}
}
}
}
return null;
}
}
@@ -0,0 +1,151 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.IField;
import com.itsaky.androidide.language.java.parser.internal.IMethod;
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.sun.tools.javac.tree.EndPosTable;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static com.sun.tools.javac.tree.JCTree.JCErroneous;
import static com.sun.tools.javac.tree.JCTree.JCExpression;
/**
* This class very complex, implement after
*/
public class CompleteExpression extends JavaCompleteMatcherImpl {
private static final String TAG = "CompleteExpression";
private final JavaDexClassLoader mClassLoader;
private EndPosTable mEndPositions;
private JCCompilationUnit mAst;
private IClass mCurrentType;
private int mCursor;
private CodeEditor mEditor;
public CompleteExpression(@NonNull JavaDexClassLoader classLoader) {
mClassLoader = classLoader;
}
public void prepare(CodeEditor editor, JCCompilationUnit ast) {
mEditor = editor;
mCursor = editor.getCursor().getLeft();
mAst = ast;
mEndPositions = ast.endPositions;
List<JCTree> typeDecls = mAst.getTypeDecls();
for (JCTree typeDecl : typeDecls) {
if (typeDecl instanceof JCTree.JCClassDecl) {
int startPosition = typeDecl.getStartPosition();
int endPosition = typeDecl.getEndPosition(mEndPositions);
if (startPosition <= mCursor && mCursor <= endPosition) {
String simpleName = ((JCTree.JCClassDecl) typeDecl).getSimpleName().toString();
JCExpression packageName = ast.getPackageName();
mCurrentType = JavaClassManager.getInstance()
.getParsedClass(packageName + "." + simpleName);
}
}
}
}
@Override
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
}
@Override
public boolean process(JCCompilationUnit ast, CodeEditor editor, Expression expression,
String unused, ArrayList<SuggestItem> result) {
try {
prepare(editor, ast);
return expression != null && performComplete(expression.getExpression(), result);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private boolean performComplete(JCExpression jcExpression, ArrayList<SuggestItem> result) {
if (jcExpression instanceof JCErroneous) {
List<? extends JCTree> errorTrees = ((JCErroneous) jcExpression).getErrorTrees();
JCTree errExpr = errorTrees.get(0);
if (errExpr instanceof JCExpression) {
return performComplete((JCExpression) errExpr, result);
}
} else if (jcExpression instanceof JCFieldAccess) {
return performCompleteFieldAccess((JCFieldAccess) jcExpression, result);
} else if (jcExpression instanceof JCTree.JCIdent) {
return performCompleteIdent((JCTree.JCIdent) jcExpression, result);
}
return false;
}
/**
* Suggestion method, variable
*/
private boolean performCompleteIdent(JCTree.JCIdent jcIdent, ArrayList<SuggestItem> result) {
return false;
}
private boolean performCompleteFieldAccess(@NonNull JCFieldAccess jcFieldAccess,
@NonNull ArrayList<SuggestItem> result) {
int startPosition = jcFieldAccess.getStartPosition();
int cursorOffset = mCursor - startPosition;
int incompleteLength = jcFieldAccess.getIdentifier().length();
String incomplete = jcFieldAccess.getIdentifier().toString();
//simple hack, improve later
if (incomplete.equalsIgnoreCase("<error>")) {
incomplete = "";
}
JCExpression expression = jcFieldAccess.getExpression();
TypeResolver resolver = new TypeResolver(mClassLoader, mAst, mCurrentType);
IClass type = resolver.resolveType(expression, mCursor);
if (type != null) {
List<IMethod> methods = type.getMethods();
for (IMethod method : methods) {
if(Modifier.isPublic(method.getModifiers())) {
if(resolver.isStatic() && !Modifier.isStatic(method.getModifiers()))
continue;
if (method.getMethodName().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
setInfo(method, mEditor, incomplete);
result.add(method);
}
}
}
ArrayList<IField> fields = type.getFields();
for (IField field : fields) {
if(Modifier.isPublic(field.getFieldModifiers())) {
if(resolver.isStatic() && !Modifier.isStatic(field.getFieldModifiers()))
continue;
if (field.getFieldName().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
setInfo(field, mEditor, incomplete);
result.add(field);
}
}
}
return true;
}
return false;
}
}
@@ -0,0 +1,81 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
import com.itsaky.androidide.language.java.parser.model.PrimitiveArrayConstructorDescription;
import com.sun.tools.javac.tree.JCTree;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Locale;
/**
* Suggest constructor
*/
public class CompleteNewKeyword extends JavaCompleteMatcherImpl {
private static final Pattern NEW_CLASS = Pattern.compile(
"(\\s*new\\s+)(" + CONSTRUCTOR.pattern() + ")$", Pattern.MULTILINE);
private static final String[] PRIMITIVE_ARRAY_TYPE =
{"boolean", "byte", "double", "char", "float", "int", "long", "short"};
private final JavaDexClassLoader mClassLoader;
public CompleteNewKeyword(JavaDexClassLoader classLoader) {
mClassLoader = classLoader;
}
@Override
public boolean process(JCTree.JCCompilationUnit ast,
CodeEditor editor, Expression expression,
String statement, ArrayList<SuggestItem> result) {
Matcher matcher = NEW_CLASS.matcher(statement);
if (matcher.find()) {
String incompleteCts = matcher.group(2);
return getSuggestionInternal(editor, incompleteCts, result);
}
return false;
}
@Override
public void getSuggestion(@NonNull CodeEditor editor,
@NonNull String incomplete,
@NonNull List<SuggestItem> suggestItems) {
getSuggestionInternal(editor, incomplete, suggestItems);
}
private boolean getSuggestionInternal(@NonNull CodeEditor editor, @NonNull String incomplete,
@NonNull List<SuggestItem> suggestItems) {
if (incomplete.isEmpty()) {
return false;
}
boolean handled = false;
//try to find constructor
List<IClass> classes = mClassLoader.findAllWithPrefix(incomplete);
for (IClass clazz : classes) {
List<ConstructorDescription> constructors = clazz.getConstructors();
setInfo(constructors, editor, incomplete);
suggestItems.addAll(constructors);
handled = true;
}
for (String type : PRIMITIVE_ARRAY_TYPE) {
if (type.toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
PrimitiveArrayConstructorDescription c
= new PrimitiveArrayConstructorDescription(type);
setInfo(c, editor, incomplete);
suggestItems.add(c);
handled = true;
}
}
return handled;
}
}
@@ -0,0 +1,93 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.internal.PackageManager;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.language.java.parser.model.PackageDescription;
import com.sun.tools.javac.tree.JCTree;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Locale;
/**
* Complete package
* e.g.
* <p>
* java.i|
* java.util.|
* java.util.Hash|
*/
public class CompletePackage extends JavaCompleteMatcherImpl {
private static final String TAG = "CompletePackage";
private static final Pattern PACKAGE
= Pattern.compile("^\\s*(package)\\s+([_A-Za-z0-9.]+)");
private static final Pattern IMPORT_OR_IMPORT_STATIC
= Pattern.compile("^\\s*import(\\s+static)?\\s+([_A-Za-z0-9.]+)");
@NonNull
private final PackageManager mPackageManager;
public CompletePackage(@NonNull PackageManager packageManager) {
mPackageManager = packageManager;
}
@Override
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
Matcher matcher = PACKAGE.matcher(statement);
if (matcher.find()) {
String incompletePkg = matcher.group(2);
getSuggestion(editor, incompletePkg, result);
return true;
}
matcher = IMPORT_OR_IMPORT_STATIC.matcher(statement);
if (matcher.find()) {
boolean isStatic = matcher.group(1) != null;
String incompletePkg = matcher.group(2);
getSuggestion(editor, incompletePkg, result);
}
return false;
}
@Override
public void getSuggestion(CodeEditor editor, String expr, List<SuggestItem> suggestItems) {
getSuggestionInternal(editor, expr, suggestItems, false);
}
private void getSuggestionInternal(CodeEditor editor, String expr, List<SuggestItem> suggestItems,
boolean addSemicolon) {
//package must be contains dot (.)
if (!expr.contains(".")) {
return;
}
//completed package java.lang
int lastDotIndex = expr.lastIndexOf(".");
String completedPart = expr.substring(0, lastDotIndex /*not contains dot*/);
String incompletePart = expr.substring(lastDotIndex + 1);
PackageDescription packages = mPackageManager.trace(completedPart);
if (packages != null) {
//members of current package
//such as java has more member (util, io, lang)
HashMap<String, PackageDescription> members = packages.getChild();
for (Map.Entry<String, PackageDescription> entry : members.entrySet()) {
PackageDescription packageDescription = entry.getValue();
if (packageDescription.getName().toLowerCase(Locale.US).startsWith(incompletePart.toLowerCase(Locale.US))) {
setInfo(packageDescription, editor, incompletePart);
suggestItems.add(packageDescription);
}
}
}
}
}
@@ -0,0 +1,65 @@
package com.itsaky.androidide.language.java.parser.expression;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.IMethod;
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.sun.tools.javac.tree.JCTree;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Locale;
import java.lang.reflect.Modifier;
public class CompleteString extends JavaCompleteMatcherImpl {
public static final Pattern STRING_DOT =
Pattern.compile("\"\\s*\\.\\s*$", Pattern.MULTILINE);
public static final Pattern STRING_DOT_EXPR
= Pattern.compile("\"\\s*\\.\\s*(" + METHOD_NAME.pattern() + ")$");
private static final String TAG = "CompleteString";
private final JavaDexClassLoader mClassLoader;
public CompleteString(JavaDexClassLoader classLoader) {
mClassLoader = classLoader;
}
@Override
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> suggestItems) {
Matcher matcher = STRING_DOT.matcher(statement);
if (matcher.find()) {
getSuggestion(editor, "", suggestItems);
return true;
}
matcher = STRING_DOT_EXPR.matcher(statement);
if (matcher.find()) {
String incompleteMethod = matcher.group(1);
getSuggestion(editor, incompleteMethod, suggestItems);
return true;
}
return false;
}
/**
* Complete string only filter method, not filter field or constructor
*/
@Override
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
JavaClassManager reader = mClassLoader.getClassReader();
IClass stringClass = reader.getParsedClass(String.class.getName());
assert stringClass != null;
ArrayList<SuggestItem> methods = new ArrayList<>();
for (IMethod method : stringClass.getMethods()) {
if(!Modifier.isPublic(method.getModifiers()))
continue;
if (method.getMethodName().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
suggestItems.add(method);
}
}
setInfo(methods, editor, incomplete);
suggestItems.addAll(methods);
}
}
@@ -0,0 +1,124 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.language.java.parser.model.FieldDescription;
import com.itsaky.androidide.language.java.parser.model.MethodDescription;
import com.sun.tools.javac.tree.JCTree;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.sun.tools.javac.tree.JCTree.JCClassDecl;
import static com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import static com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import static com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import static com.sun.tools.javac.tree.JCTree.JCVariableDecl;
public class CompleteThisKeyword extends JavaCompleteMatcherImpl {
private static final Pattern THIS_DOT = Pattern.compile("(\\W)this\\s*\\.\\s*$");
private static final Pattern THIS_DOT_EXPR = Pattern.compile("\\Wthis\\s*\\.\\s*(" + Patterns.IDENTIFIER.pattern() + ")$");
private static final String TAG = "CompleteThisKeyword";
private static JavaDexClassLoader mClassLoader;
public CompleteThisKeyword(JavaDexClassLoader classLoader) {
this.mClassLoader = classLoader;
}
protected static void addMethod(JCCompilationUnit unit, JCMethodDecl method, CodeEditor editor, String incomplete,
List<SuggestItem> result) {
if (method.getName().toString().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
List<JCTypeParameter> typeParameters
= method.getTypeParameters();
ArrayList<String> paramsStr = new ArrayList<>();
for (JCTypeParameter typeParameter : typeParameters) {
paramsStr.add(typeParameter.toString());
}
IClass returnType = null;
String rName = JavaUtil.findImportedClassName(unit, method.getReturnType().toString());
if(rName != null && rName.trim().length() > 0) {
returnType = mClassLoader.getClassReader().getParsedClass(rName);
}
MethodDescription desc = new MethodDescription(
method.getName().toString(),
returnType,
method.getModifiers().flags,
paramsStr);
setInfo(desc, editor, incomplete);
result.add(desc);
}
}
protected static void addVariable(JCCompilationUnit unit, JCVariableDecl member, CodeEditor editor, String incomplete,
List<SuggestItem> result) {
if (member.getName().toString().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
IClass returnType = null;
String rName = JavaUtil.findImportedClassName(unit, member.getType().toString());
if(rName != null && rName.trim().length() > 0) {
returnType = mClassLoader.getClassReader().getParsedClass(rName);
}
int flags = (int) member.getModifiers().flags;
FieldDescription desc = new FieldDescription(
flags,
returnType,
member.getName().toString(),
null);
setInfo(desc, editor, incomplete);
result.add(desc);
}
}
@Override
public boolean process(JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
Matcher matcher = THIS_DOT.matcher(statement);
if (matcher.find()) {
getSuggestionInternal(editor, result, ast, "");
return true;
}
matcher = THIS_DOT_EXPR.matcher(statement);
if (matcher.find()) {
String incomplete = matcher.group(1);
getSuggestionInternal(editor, result, ast, incomplete);
return true;
}
return false;
}
@Override
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
}
private void getSuggestionInternal(@NonNull CodeEditor editor, @NonNull ArrayList<SuggestItem> result,
@NonNull JCCompilationUnit unit,
@NonNull String incomplete) {
if (unit == null) {
return;
}
//current file declare
List<JCTree> typeDecls = unit.getTypeDecls();
if (typeDecls.isEmpty()) {
return;
}
JCTree jcTree = typeDecls.get(0);
if (jcTree instanceof JCClassDecl) {
List<JCTree> members =
((JCClassDecl) jcTree).getMembers();
for (JCTree member : members) {
if (member instanceof JCVariableDecl) {
addVariable(unit, (JCVariableDecl) member, editor, incomplete, result);
} else if (member instanceof JCMethodDecl) {
JCMethodDecl method = (JCMethodDecl) member;
addMethod(unit, method, editor, incomplete, result);
}
}
}
}
}
@@ -0,0 +1,130 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.interfaces.Filter;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.sun.tools.javac.tree.JCTree;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Complete class declaration
* <p>
* public? static? final? class Name (extends otherClass)? (implements otherInterfaces)?
* public? enum Name
* public? final? interface Name (extends otherInterfaces)?
*/
public class CompleteTypeDeclared extends JavaCompleteMatcherImpl {
//case: public class A
//case: public class Name extends OtherClass
//case: class Name extends C implements D
public static final Pattern CLASS_DECLARE = Pattern.compile(
//more modifiers, public static final ....
"((public|protected|private|abstract|static|final|strictfp)\\s+)*" +
//type
"((class|inteface|enum)\\s+)" +
//name
"([a-zA-Z_][a-zA-Z0-9_]*)" +
//inherit
"(\\s+extends\\s+([a-zA-Z_][a-zA-Z0-9_]*))?" +
"(\\s+implements\\s+([a-zA-Z_][a-zA-Z0-9_]*))?");
private static final Pattern END_EXTENDS
= Pattern.compile("\\s+extends\\s+([a-zA-Z_][a-zA-Z0-9_]*)$");
private static final Pattern END_IMPLEMENTS
= Pattern.compile("\\s+implements\\s+([a-zA-Z_][a-zA-Z0-9_]*)$");
private static final String TAG = "CompleteClassDeclared";
private JavaDexClassLoader mClassLoader;
public CompleteTypeDeclared(JavaDexClassLoader classLoader) {
mClassLoader = classLoader;
}
@Override
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
Matcher matcher = CLASS_DECLARE.matcher(statement);
if (matcher.find()) {
matcher = END_IMPLEMENTS.matcher(statement);
if (matcher.find()) {
String incompleteInterface = matcher.group(1);
return getSuggestionInternal(editor, incompleteInterface, result,
"interface");
}
matcher = END_EXTENDS.matcher(statement);
if (matcher.find()) {
String incompleteInterface = matcher.group(1);
return getSuggestionInternal(editor, incompleteInterface, result,
"class");
}
}
return false;
}
/**
* @param declareType - enum, class, annotation, interface
*/
private boolean getSuggestionInternal(@NonNull CodeEditor editor,
@NonNull String incomplete,
@NonNull List<SuggestItem> result,
@Nullable String declareType) {
//filter interfaces or classes
Filter<IClass> filter = null;
switch (declareType) {
case "interface":
filter = new Filter<IClass>() {
@Override
public boolean accept(IClass clazz) {
if (Modifier.isFinal(clazz.getModifiers())) {
return false;
}
if (!Modifier.isPublic(clazz.getModifiers())) {
return false;
}
return clazz.isInterface();
}
};
break;
case "class":
filter = new Filter<IClass>() {
@Override
public boolean accept(IClass clazz) {
if (Modifier.isFinal(clazz.getModifiers())) {
return false;
}
if (!Modifier.isPublic(clazz.getModifiers())) {
return false;
}
return !clazz.isInterface();
}
};
break;
}
List<IClass> classes = mClassLoader.findAllWithPrefix(incomplete, filter);
if (classes.size() > 0) {
setInfo(classes, editor, incomplete);
result.addAll(classes);
return true;
}
return false;
}
@Override
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
getSuggestionInternal(editor, incomplete, suggestItems, null);
}
}
@@ -0,0 +1,157 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.JavacParser;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.language.java.parser.model.KeywordDescription;
import com.sun.tools.javac.tree.JCTree;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.itsaky.androidide.language.java.parser.expression.CompleteThisKeyword.*;
import java.util.Locale;
/**
* Final complete, no context, suggest class name, method name,
* variable or any start with incomplete
*/
public class CompleteWord extends JavaCompleteMatcherImpl {
private static final String TAG = "CompleteWord";
private final static Set<String> KEYWORDS;
static {
String[] kws = {
"abstract", "continue", "for", "new", "switch",
"assert", "default", "if", "package", "synchronized",
"boolean", "do", "goto", "private", "this",
"break", "double", "implements", "protected", "throw",
"byte", "else", "import", "public", "throws",
"case", "enum", "instanceof", "return", "transient",
"catch", "extends", "int", "short", "try",
"char", "final", "interface", "static", "void",
"class", "finally", "long", "strictfp", "volatile",
"const", "float", "native", "super", "while",
"null", "true", "false"
};
Set<String> s = new HashSet<>(Arrays.asList(kws));
KEYWORDS = Collections.unmodifiableSet(s);
}
private JavacParser mJavaParser;
private JavaDexClassLoader mClassLoader;
public CompleteWord(JavacParser javaParser, JavaDexClassLoader classLoader) {
this.mJavaParser = javaParser;
mClassLoader = classLoader;
}
@Override
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
return getSuggestionInternal(editor, statement, result);
}
@Override
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
getSuggestionInternal(editor, incomplete, suggestItems);
}
private boolean getSuggestionInternal(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
boolean result = getPossibleInCurrentFile(editor, incomplete, suggestItems);
result |= getKeyword(editor, incomplete, suggestItems);
return result;
}
private boolean getKeyword(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
if (incomplete.trim().isEmpty()) {
return false;
}
boolean found = false;
for (String keyword : KEYWORDS) {
if (keyword.toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US)) && !keyword.equalsIgnoreCase(incomplete)) {
KeywordDescription e = new KeywordDescription(keyword);
setInfo(e, editor, incomplete);
suggestItems.add(e);
found = true;
}
}
return found;
}
private boolean getPossibleInCurrentFile(@NonNull CodeEditor editor, @NonNull String incomplete,
@NonNull List<SuggestItem> suggestItems) {
if (incomplete.trim().isEmpty()) {
return false;
}
JCTree.JCCompilationUnit ast;
try {
ast = mJavaParser.parse(editor.getText());
} catch (Exception e) {
return false;
}
getPossibleResult(editor, suggestItems, ast, incomplete);
List<IClass> classes = mClassLoader.findAllWithPrefix(incomplete);
setInfo(classes, editor, incomplete);
suggestItems.addAll(classes);
return classes.size() > 0;
}
private void getPossibleResult(CodeEditor editor, List<SuggestItem> result,
JCTree.JCCompilationUnit ast, String incomplete) {
if (ast == null) {
return;
}
List<JCTree> typeDecls = ast.getTypeDecls();
if (typeDecls.isEmpty()) {
return;
}
JCTree jcTree = typeDecls.get(0);
if (jcTree instanceof JCTree.JCClassDecl) {
List<JCTree> members =
((JCTree.JCClassDecl) jcTree).getMembers();
for (JCTree member : members) {
if (member instanceof JCTree.JCVariableDecl) {
addVariable(ast, (JCTree.JCVariableDecl) member, editor, incomplete, result);
} else if (member instanceof JCTree.JCMethodDecl) {
JCTree.JCMethodDecl method = (JCTree.JCMethodDecl) member;
addMethod(ast, method, editor, incomplete, result);
if (method.getStartPosition() <= editor.getCursor().getLeft()
&& method.getBody().getEndPosition(ast.endPositions) >= editor.getCursor().getLeft()) {
collectFromMethod(ast, editor, incomplete, result, method);
}
}
}
}
}
private void collectFromMethod(JCTree.JCCompilationUnit unit, CodeEditor editor, String incomplete, List<SuggestItem> result,
JCTree.JCMethodDecl method) {
List<JCTree.JCStatement> statements
= method.getBody().getStatements();
for (JCTree.JCStatement statement : statements) {
if (statement instanceof JCTree.JCVariableDecl) {
JCTree.JCVariableDecl field = (JCTree.JCVariableDecl) statement;
addVariable(unit, field, editor, incomplete, result);
}
}
List<JCTree.JCVariableDecl> parameters = method.getParameters();
for (JCTree.JCVariableDecl parameter : parameters) {
addVariable(unit, parameter, editor, incomplete, result);
}
}
}
@@ -0,0 +1,18 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import com.itsaky.androidide.language.java.parser.Expression;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.sun.tools.javac.tree.JCTree;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.List;
public interface IJavaCompleteMatcher {
boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result)
throws Exception;
void getSuggestion(@NonNull CodeEditor editor,
@NonNull String incomplete,
@NonNull List<SuggestItem> suggestItems);
}
@@ -0,0 +1,58 @@
package com.itsaky.androidide.language.java.parser.expression;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.language.java.parser.model.JavaSuggestItemImpl;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.List;
import java.util.regex.Pattern;
public abstract class JavaCompleteMatcherImpl implements IJavaCompleteMatcher {
//statement end with character, number or dot
public static final Pattern END_WITH_CHARACTER_OR_DOT
= Pattern.compile("[.0-9A-Za-z_]\\s*$", Pattern.MULTILINE);
//statement end with dot "str."
public static final Pattern END_WITH_DOT = Pattern.compile("\\.\\s*$", Pattern.MULTILINE);
/**
* Check statement is valid when end with dot
*/
public static final Pattern VALID_WHEN_END_WITH_DOT
= Pattern.compile(
"[" +
"\"" + //string: "Hello".|
")" + //expression in parentheses: (new String("")).|
"0-9A-Za-z_" + //name of variable or identifier: variable.|
"\\]" + //array access: array[].|
"]\\s*\\.\\s*$", Pattern.MULTILINE);
public static final Pattern KEYWORD_DOT
= Pattern.compile("(" + Patterns.RE_KEYWORDS.pattern() + ")\\s*\\.\\s*$");
public static final Pattern METHOD_NAME = Patterns.IDENTIFIER;
public static final Pattern VARIABLE_NAME = Patterns.IDENTIFIER;
//String or java.lang.String
public static final Pattern CLASS_NAME = Pattern.compile(
Patterns.IDENTIFIER.pattern() + "(\\s*\\.\\s*" + Patterns.IDENTIFIER.pattern() + ")*");
//java.util.*
public static final Pattern PACKAGE_NAME = CLASS_NAME;
//java.io.FileInputStream
public static final Pattern CONSTRUCTOR = CLASS_NAME;
private static final String TAG = "JavaCompleteMatcherImpl";
protected static void setInfo(List<? extends SuggestItem> members, CodeEditor editor, String incomplete) {
for (SuggestItem member : members) {
if (member instanceof JavaSuggestItemImpl) {
setInfo((JavaSuggestItemImpl) member, editor, incomplete);
} else {
}
}
}
protected static void setInfo(Object member, CodeEditor editor, String incomplete) {
if (member instanceof JavaSuggestItemImpl) {
((JavaSuggestItemImpl) member).setEditor(editor);
((JavaSuggestItemImpl) member).setIncomplete(incomplete);
}
}
}
@@ -0,0 +1,102 @@
package com.itsaky.androidide.language.java.parser.expression;
import android.text.Editable;
import android.widget.EditText;
import androidx.annotation.Nullable;
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
import com.itsaky.androidide.language.java.parser.util.EditorUtil;
import io.github.rosemoe.editor.text.Content;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.github.rosemoe.editor.text.CharPosition;
import android.text.TextUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import java.util.Set;
import java.util.HashSet;
/**
* Created by Duy on 21-Jul-17.
*/
public class PackageImporter {
private static final String TAG = "ImportUtil";
/**
* Add import statement if import does not already exist.
*/
public static void importClass(Content editor, String className) {
String packageName = JavaUtil.getPackageName(className);
if (getImportedClassName(editor, className) == null
&& !packageName.isEmpty()
&& !packageName.equals("java.lang")
&& !packageName.equals(EditorUtil.getCurrentPackage(editor.toString()))) {
organizeImports(editor, "import " + className.replace("$", ".") + ";");
}
}
public static String getImportedClassName(EditText editor, @Nullable String className) {
return getImportedClassName(editor, className);
}
public static String getImportedClassName(CharSequence src, @Nullable String className) {
if (className == null) return null;
Pattern pattern = PatternFactory.makeImportClass(className);
Matcher matcher = pattern.matcher(src);
if (matcher.find()) {
return matcher.group(2);
}
return PatternFactory.match(src, pattern);
}
public static void organizeImports(Content editor, String importStr) {
try {
ArrayList<String> imprts = getImports(editor);
imprts.add(importStr);
Collections.sort(imprts);
final Set<String> imports = new HashSet<String>();
for(String imprt : imprts) {
imports.add(imprt.trim());
}
String imprt = TextUtils.join("\n", imports).trim();
int s = PatternFactory.firstMatch(editor, PatternFactory.IMPORT);
int e = PatternFactory.lastMatch(editor, PatternFactory.IMPORT);
if (s >= 0 && e >= 0 && e < editor.length()) {
CharPosition start = editor.getIndexer().getCharPosition(s);
CharPosition end = editor.getIndexer().getCharPosition(e);
if (s < e) {
editor.replace(start.line, start.column, end.line, end.column, imprt);
} else if (s == e) {
editor.insert(start.line, start.column, imprt);
}
} else {
int p = PatternFactory.lastMatch(editor, PatternFactory.PACKAGE);
int c = PatternFactory.firstMatch(editor, CompleteTypeDeclared.CLASS_DECLARE);
if(p >= 0 && c >= 0 && p < c) {
CharPosition start = editor.getIndexer().getCharPosition(p);
CharPosition end = editor.getIndexer().getCharPosition(c);
imprt = "\n\n" + imprt + "\n";
if(p < c) {
editor.replace(start.line, start.column, end.line, end.column, imprt);
} else if(p == c) {
editor.insert(start.line, start.column, imprt);
} else {
editor.insert(0, 0, imprt);
}
}
}
} catch (Throwable th) {}
}
public static ArrayList<String> getImports(Content editor) {
return PatternFactory.allMatch(editor, PatternFactory.IMPORT);
}
}
@@ -0,0 +1,119 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternFactory {
public static final Pattern PACKAGE = Pattern.compile("package\\s+[^;]*;");
public static final Pattern PACKAGE_NAME = Pattern.compile("[A-Za-z_]+(.[A-Za-z][A-Za-z0-9_]*)*");
public static final Pattern IMPORT = Pattern.compile("(import\\s+)([^;]*)(\\s?);");
public static final Pattern WORD = Pattern.compile("[^\\s-]+$");
public static final String IDENTIFIER_STR = "[A-Za-z][A-Za-z0-9]*";
public static final Pattern IDENTIFIER = Pattern.compile(IDENTIFIER_STR);
public static final Pattern ANNOTATION = Pattern.compile("@[A-Za-z][A-Za-z0-9]*");
public static final Pattern BRACKET = Pattern.compile("\\[(.*?)\\]");
public static final Pattern MODIFIERS = Pattern.compile("\\b(public|protected|private|abstract|static|final|strictfp)\\b");
public static final Pattern SPLIT_NON_WORD = Pattern.compile("\\W+");
public static final String SPLIT_NON_WORD_STR = "\\W+";
public static final String[] PRIMITIVE_TYPE = new String[]{"boolean", "byte", "char", "int",
"short", "long", "float", "double"};
public static final String[] KEYWORD_MODIFIERS = new String[]{"public", "private", "protected",
"static", "final", "synchronized", "volatile", "transient", "native", "strictfp"};
public static final String[] KEYWORD_TYPE = new String[]{"class", "interface", "enum"};
public static final String[] KEYWORD;
public static final String GENERIC_STR = "<[A-Z][a-zA-Z0-9_<>, ]*>";
public static final Pattern FILE_NAME = Pattern.compile("[A-Za-z][A-Za-z0-9_\\-.]*");
static {
KEYWORD = new String[PRIMITIVE_TYPE.length + KEYWORD_MODIFIERS.length + KEYWORD_TYPE.length];
System.arraycopy(PRIMITIVE_TYPE, 0, KEYWORD, 0, PRIMITIVE_TYPE.length);
System.arraycopy(KEYWORD_MODIFIERS, 0, KEYWORD, PRIMITIVE_TYPE.length, KEYWORD_MODIFIERS.length);
System.arraycopy(KEYWORD_TYPE, 0, KEYWORD, KEYWORD_MODIFIERS.length, KEYWORD_TYPE.length);
}
public static Pattern makeImportClass(String className) {
return Pattern.compile("(import\\s+)(.*" + className + ")(\\s?;)");
}
@Nullable
public static String lastMatchStr(CharSequence text, Pattern pattern) {
Matcher matcher = pattern.matcher(text);
ArrayList<String> list = new ArrayList<>();
while (matcher.find()) {
list.add(matcher.group());
}
if (list.size() == 0) return null;
return list.get(list.size() - 1);
}
@Nullable
public static String match(CharSequence text, Pattern pattern) {
Matcher matcher = pattern.matcher(text);
if (matcher.find()) return matcher.group();
else return null;
}
public static ArrayList<String> allMatch(CharSequence text, Pattern pattern) {
Matcher matcher = pattern.matcher(text);
ArrayList<String> list = new ArrayList<>();
while (matcher.find()) {
list.add(matcher.group());
}
return list;
}
public static int lastMatch(CharSequence editor, Pattern pattern) {
int last = -1;
Matcher matcher = pattern.matcher(editor);
while (matcher.find()) last = matcher.end();
return last;
}
public static int matchEnd(CharSequence editor, Pattern pattern, int start) {
Matcher matcher = pattern.matcher(editor);
if (matcher.find(start)) {
return matcher.end();
}
return -1;
}
public static int firstMatch(CharSequence editor, Pattern pattern) {
Matcher matcher = pattern.matcher(editor);
if (matcher.find()) {
return matcher.start();
}
return -1;
}
public static Pattern makeInstance(String prefix) {
//ArrayList<String> list = new ArrayList();
return Pattern.compile("(" + IDENTIFIER_STR + ")" + "(\\s?)" + //type
"((" + GENERIC_STR + ")?(\\s?)|(\\s+))" + //generic or space
"(" + prefix + ")(\\s?)([,;=)])"); //name
}
public static int lastMatch(String statement, String pattern) {
return lastMatch(statement, Pattern.compile(pattern));
}
public static int lastMatch(String statement, Pattern pattern) {
return lastMatch(statement, pattern, 0);
}
public static int lastMatch(String expr, Pattern pattern, int start) {
Matcher matcher = pattern.matcher(expr.substring(start));
int index = -1;
while (matcher.find()) {
index = matcher.end();
}
return index != 0 ? index + start : index;
}
}
@@ -0,0 +1,68 @@
package com.itsaky.androidide.language.java.parser.expression;
import java.util.regex.Pattern;
import static java.util.regex.Pattern.compile;
/**
* Created by Duy on 11-Feb-17.
*/
@SuppressWarnings("DefaultFileTemplate")
public class Patterns {
public static final Pattern JAVA_COMMENTS = compile("(//.*)|(/\\*(?:.|[\\n\\r])*?\\*/)");
/**
* match string java
* include
* <p>
* 'string'
* <p>
* And can not find close quote
* <p>
* 'sadhasdhasdhashdhas ds asda sd
*/
public static final Pattern STRINGS = Pattern.compile(
"((\")(.*?)(\"))" +//'string'
"|((\")(.+))", Pattern.DOTALL); // no end string 'asdasdasd
public static final Pattern PRIMITIVE_TYPES = compile("boolean|byte|char|int|short|long|float|double");
public static final Pattern KEYWORD_TYPES = compile("class|interface|enum");
public static final Pattern KEYWORDS_MODS = compile("public|private|protected|static|final|" +
"synchronized|volatile|transient|native|strictfp|abstract");
public static final Pattern KEYWORDS = compile(PRIMITIVE_TYPES.pattern()
+ KEYWORDS_MODS.pattern() + "|"
+ KEYWORD_TYPES.pattern() + "|" +
"super|this|void|assert|break|case|catch|" +
"const|continue|default|do|else|extends|finally|for|goto|if|implements|import|instanceof|" +
"interface|new|package|return|switch|throw|throws|try|while|true|false|null");
public static final Pattern RE_BRACKETS = compile("(\\s*\\[\\s*\\])");
public static final Pattern IDENTIFIER = compile("[a-zA-Z_][a-zA-Z0-9_]*");
//case: JCTree.JCClassDecl
//case: java.util.ArrayList)
public static final Pattern RE_QUALID = compile(IDENTIFIER + "(\\s*\\.\\s*" + IDENTIFIER + ")?");
public static final Pattern RE_REFERENCE_TYPE = compile(RE_QUALID + RE_BRACKETS.pattern() + "\\*");
public static final Pattern RE_TYPE = RE_REFERENCE_TYPE;
public static final Pattern TYPE_DECLARE_MODIFIERS = compile("public|protected|private|abstract|static|final|strictfp");
public static final Pattern TYPE_DECLARE_HEAD = compile("class|inteface|enum");
//case: public class A
//case: public class Lamborghini extends Car
public static final Pattern RE_TYPE_DECL = compile(
"((public|protected|private|abstract|static|final|strictfp)\\s+)*" +
"(class|inteface|enum)\\s+" +
"[a-zA-Z_][a-zA-Z0-9_]*" +
"(\\s+(extends|implements)(\\s+)([a-zA-Z_][a-zA-Z0-9_]*))?");
public static final Pattern RE_ARRAY_TYPE = compile("^\\s*(" + RE_QUALID + ")(" + RE_BRACKETS + ")+\\s*");
public static final Pattern RE_SELECT_OR_ACCESS = compile("^\\s*(" + IDENTIFIER + ")\\s*(\\[.*])?\\s*$");
public static final Pattern RE_ARRAY_ACCESS = compile("^\\s*(" + IDENTIFIER + ")" + "\\s*(\\[.*])+\\s*$");
public static final Pattern RE_CASTING = compile("\\s*\\((" + RE_QUALID + "\\s*)\\)\\s*" + IDENTIFIER);
public static final Pattern RE_KEYWORDS = KEYWORDS;
public static final Pattern PACKAGE_NAME = compile("([A-Za-z0-9_]([.][a-zA-Z0-9_])?)*");
}
@@ -0,0 +1,68 @@
package com.itsaky.androidide.language.java.parser.expression;
import com.itsaky.androidide.language.java.parser.util.EditorUtil;
import io.github.rosemoe.editor.widget.CodeEditor;
public class StatementParser {
/**
* " Search back from the cursor position till meeting '{' or ';'.
* " '{' means statement start, ';' means end of a previous statement.
*
* @return statement before cursor
* " Note: It's the base for parsing. And It's OK for most cases.
*/
public static String resolveStatementFromCursor(CodeEditor editor) {
String lineBeforeCursor = getCurrentLine(editor);
if (lineBeforeCursor.matches("^\\s*(import|package)\\s+")) {
return lineBeforeCursor;
}
int oldCursor = editor.getCursor().getLeft();
int newCursor = oldCursor - 1;
while (true) {
if (newCursor == 0) break;
char c = editor.getText().charAt(newCursor);
if (c == '{' || c == '}' || c == ';') {
newCursor++;
break;
}
newCursor--;
}
String statement = editor.getText().subSequence(newCursor, oldCursor).toString();
return mergeLine(statement);
}
public static String getCurrentLine(CodeEditor editText) {
return EditorUtil.getLineBeforeCursor(editText, editText.getCursor().getLeft());
}
public static String mergeLine(String statement) {
statement = cleanStatement(statement);
return statement;
}
/**
* set string literal empty, remove comments, trim begining or ending spaces
* case: ' sb. /* block comment"/ append( "stringliteral" ) // comment '
* return 'sb.append("")'
*/
private static String cleanStatement(String code) {
if (code.matches("\\s*")) {
return "";
}
code = removeComment(code); //clear all comment
//clear all string content
code = code.replaceAll(Patterns.STRINGS.toString(), "\"\"");
code = EditorUtil.trimLeft(code);
code = code.replaceAll("[\n\t\r]", "");
return code;
}
/**
* remove all comment
*/
private static String removeComment(String code) {
return code.replaceAll(Patterns.JAVA_COMMENTS.toString(), "");
}
}
@@ -0,0 +1,376 @@
package com.itsaky.androidide.language.java.parser.expression;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.language.java.parser.internal.IField;
import com.itsaky.androidide.language.java.parser.internal.IMethod;
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
import com.sun.tools.javac.tree.JCTree;
import java.util.LinkedList;
import java.util.List;
import static com.itsaky.androidide.language.java.parser.internal.JavaUtil.*;
import static com.sun.tools.javac.tree.JCTree.JCArrayAccess;
import static com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import static com.sun.tools.javac.tree.JCTree.JCBlock;
import static com.sun.tools.javac.tree.JCTree.JCCatch;
import static com.sun.tools.javac.tree.JCTree.JCClassDecl;
import static com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import static com.sun.tools.javac.tree.JCTree.JCExpression;
import static com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import static com.sun.tools.javac.tree.JCTree.JCIdent;
import static com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import static com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import static com.sun.tools.javac.tree.JCTree.JCStatement;
import static com.sun.tools.javac.tree.JCTree.JCTry;
import static com.sun.tools.javac.tree.JCTree.JCVariableDecl;
public class TypeResolver {
private static final String TAG = "TypeResolver";
private JavaDexClassLoader mClassLoader;
private JCCompilationUnit mUnit;
private IClass mCurrentType;
private boolean isStatic= false;
public boolean isStatic() {
return isStatic;
}
public TypeResolver(JavaDexClassLoader classLoader,
JCCompilationUnit unit,
IClass currentType) {
mClassLoader = classLoader;
mUnit = unit;
mCurrentType = currentType;
}
public IClass resolveType(@NonNull JCExpression expression, int cursor) {
return resolveTypeImpl(expression, cursor);
}
@Nullable
private IClass resolveTypeImpl(@NonNull final JCExpression expression, int cursor) {
List<JCTree> list = extractExpressionAtCursor(expression, cursor);
if (list == null) {
return null;
}
String exceptionMessage = "Can not resolve type of expression ";
IClass currentType = mCurrentType;
for (JCTree tree : list) {
//only once time on this case
if (tree instanceof JCIdent) {
JCIdent jcIdent = (JCIdent) tree;
//variable declaration, static import or inner class
//case: variableDecl
JCVariableDecl variableDecl = getVariableDeclaration(mUnit, jcIdent);
if (variableDecl != null) {
currentType = JavaUtil.jcTypeToClass(mUnit, variableDecl.getType());
} else {
//case: System.out -> find imported class, this expression can be static access
String className = findImportedClassName(mUnit, jcIdent.getName().toString());
currentType = mClassLoader.getClassReader().getParsedClass(className);
isStatic = true;
}
// TODO: 13-Jun-18 case: static import
// TODO: 13-Jun-18 case inner class
} else if (tree instanceof JCMethodInvocation) {
// TODO: 13-Jun-18 find method in current class or import static
JCMethodInvocation jcMethod = (JCMethodInvocation) tree;
JCExpression methodSelect = jcMethod.getMethodSelect();
if (methodSelect instanceof JCFieldAccess) {
String methodName = ((JCFieldAccess) methodSelect).getIdentifier().toString();
List<JCExpression> arguments = jcMethod.getArguments();
IClass[] types = new IClass[arguments.size()];
IMethod method = currentType.getMethod(methodName, types);
if (method == null) {
throw new UnsupportedOperationException(exceptionMessage + tree);
}
currentType = method.getMethodReturnType();
} else if (methodSelect instanceof JCIdent) { //method in current class
String methodName = ((JCIdent) methodSelect).getName().toString();
IMethod method = currentType.getMethod(methodName, null);
if (method == null) {
throw new UnsupportedOperationException(exceptionMessage + tree);
}
currentType = method.getMethodReturnType();
} else {
throw new UnsupportedOperationException(exceptionMessage + tree);
}
} else if (tree instanceof JCFieldAccess) {
if (currentType == null) {
throw new UnsupportedOperationException(exceptionMessage + tree);
}
String name = ((JCFieldAccess) tree).getIdentifier().toString();
IField field = currentType.getField(name);
if (field == null) {
throw new UnsupportedOperationException(exceptionMessage + tree);
}
currentType = field.getFieldType();
} else if (tree instanceof JCArrayAccess) {
JCIdent jcIdent = (JCIdent) ((JCArrayAccess) tree).getExpression();
//variable declaration, static import or inner class
//case: variableDecl
JCVariableDecl variableDecl = getVariableDeclaration(mUnit, jcIdent);
if (variableDecl != null) {
if (!(variableDecl.getType() instanceof JCArrayTypeTree)) {
throw new UnsupportedOperationException("can not resolve type of array access " + tree);
}
String className = ((JCArrayTypeTree) variableDecl.getType())
.getType().toString();
className = findImportedClassName(mUnit, className);
currentType = mClassLoader.getClassReader().getParsedClass(className);
}
}
}
return currentType;
}
@Nullable
private List<JCTree> extractExpressionAtCursor(JCExpression expression, int cursor) {
JCTree tree = expression;
LinkedList<JCTree> list = new LinkedList<>();
while (tree != null) {
if (getEndPosition(tree) > cursor) {
break;
}
if (tree instanceof JCMethodInvocation) {
list.addFirst(tree);
JCExpression methodSelect = ((JCMethodInvocation) tree).getMethodSelect();
if (methodSelect instanceof JCIdent) {
break;
} else if (methodSelect instanceof JCFieldAccess) {
tree = ((JCFieldAccess) methodSelect).getExpression();
} else { //unsupported
return null;
}
} else if (tree instanceof JCFieldAccess) { //var.field
list.addFirst(tree);
//select before
tree = ((JCFieldAccess) tree).getExpression();
} else if (tree instanceof JCIdent) { //var, it should be before any expression
list.addFirst(tree);
break;
} else if (tree instanceof JCArrayAccess) { //variable.array[i].toString
list.addFirst(tree);
//select declare name
tree = ((JCArrayAccess) tree).getExpression(); //select array name
if (tree instanceof JCIdent) {
break;
} else if (tree instanceof JCFieldAccess) {
} else {
throw new UnsupportedOperationException("can not extract expression at array " + tree);
}
} else { //unsupported
//should not happen
return null;
}
}
return list;
}
@Nullable
private JCVariableDecl getVariableDeclaration(final JCCompilationUnit unit,
final JCIdent jcIdent) {
List<JCTree> typeDecls = unit.getTypeDecls();
for (JCTree typeDecl : typeDecls) {
JCVariableDecl variableDecl = getVariableDeclaration(typeDecl, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
}
return null;
}
/**
* Visit all scope and find variable declaration when given jcIdent
*
* @param parent - parent block to find
*/
@Nullable
private JCVariableDecl getVariableDeclaration(final JCTree parent,
final JCIdent jcIdent) {
// TODO: 14-Jun-18 need add parent if jcIdent to resolve exactly scope
if (parent instanceof JCBlock) {
List<JCStatement> statements = ((JCBlock) parent).getStatements();
return getVariableDeclarationFromStatements(parent, statements, jcIdent);
} else if (parent instanceof JCTree.JCCase) {
JCTree.JCCase jcCase = (JCTree.JCCase) parent;
return getVariableDeclarationFromStatements(parent, jcCase.getStatements(), jcIdent);
} else if (parent instanceof JCClassDecl) {
List<JCTree> members = ((JCClassDecl) parent).getMembers();
//all member equals scope
for (JCTree member : members) {
if (member instanceof JCVariableDecl) { //variable
JCVariableDecl variableDecl = (JCVariableDecl) member;
if (canBeSampleVariable(parent, variableDecl, jcIdent)) {
return variableDecl;
}
} else if (member instanceof JCMethodDecl) { //method
JCMethodDecl jcMethodDecl = (JCMethodDecl) member;
JCBlock jcMethodDeclBody = jcMethodDecl.getBody();
JCVariableDecl variableDecl = getVariableDeclaration(jcMethodDeclBody, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
variableDecl = getVariableDeclarationFromStatements(member,
((JCMethodDecl) member).getParameters(), jcIdent);
if (variableDecl != null) {
return variableDecl;
}
} else {
JCVariableDecl variableDecl = getVariableDeclaration(member, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
}
}
} else if (parent instanceof JCTree.JCDoWhileLoop) {
return getVariableDeclaration(((JCTree.JCDoWhileLoop) parent).getStatement(), jcIdent);
} else if (parent instanceof JCTree.JCEnhancedForLoop) {
JCVariableDecl variable = ((JCTree.JCEnhancedForLoop) parent).getVariable();
if (canBeSampleVariable(parent, variable, jcIdent)) {
return variable;
}
return getVariableDeclaration(((JCTree.JCEnhancedForLoop) parent).getStatement(), jcIdent);
} else if (parent instanceof JCTree.JCForLoop) {
List<JCStatement> initializer = ((JCTree.JCForLoop) parent).getInitializer();
JCVariableDecl variableDecl = getVariableDeclarationFromStatements(parent, initializer, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
return getVariableDeclaration(((JCTree.JCForLoop) parent).getStatement(), jcIdent);
} else if (parent instanceof JCTree.JCIf) {
JCTree.JCIf jcIf = (JCTree.JCIf) parent;
JCStatement thenStatement = jcIf.getThenStatement();
JCVariableDecl variableDecl = getVariableDeclaration(thenStatement, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
JCStatement elseStatement = jcIf.getElseStatement();
if (elseStatement != null) {
return getVariableDeclaration(elseStatement, jcIdent);
}
} else if (parent instanceof JCTree.JCLabeledStatement) {
return getVariableDeclaration(((JCTree.JCLabeledStatement) parent).getStatement(), jcIdent);
} else if (parent instanceof JCTree.JCSynchronized) {
return getVariableDeclaration(((JCTree.JCSynchronized) parent).getBlock(), jcIdent);
} else if (parent instanceof JCTry) {
JCTry jcTry = (JCTry) parent;
JCBlock jcTryBlock = jcTry.getBlock();
JCVariableDecl variableDecl = getVariableDeclaration(jcTryBlock, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
for (JCCatch jcCatch : jcTry.getCatches()) {
JCVariableDecl parameter = jcCatch.getParameter();
if (canBeSampleVariable(jcCatch, parameter, jcIdent)) {
return parameter;
}
JCBlock jcCatchBlock = jcCatch.getBlock();
variableDecl = getVariableDeclaration(jcCatchBlock, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
}
} else if (parent instanceof JCTree.JCWhileLoop) {
return getVariableDeclaration(((JCTree.JCWhileLoop) parent).getStatement(), jcIdent);
}
return null;
}
@Nullable
private JCVariableDecl getVariableDeclarationFromStatements(JCTree parent,
List<? extends JCStatement> statements,
JCIdent jcIdent) {
for (JCStatement statement : statements) {
if (statement instanceof JCVariableDecl) {
JCVariableDecl variableDecl = (JCVariableDecl) statement;
if (canBeSampleVariable(parent, variableDecl, jcIdent)) {
return variableDecl;
}
} else {
JCVariableDecl variableDecl = getVariableDeclaration(statement, jcIdent);
if (variableDecl != null) {
return variableDecl;
}
}
}
return null;
}
/**
* @param parent - scope of variable
*/
private boolean canBeSampleVariable(@NonNull JCTree parent,
@NonNull JCVariableDecl variable,
/*should all parent if identifier*/
@NonNull JCIdent ident) {
if (!variable.getName().equals(ident.getName())) {
return false;
}
//identifier inside or equal scope if variable o
//-------------------------------------------
// private ArrayList list = new ArrayList();
// void method(){
// list.toString()
//}
//-------------------------------------------
// void method(ArrayList list){
// list.toString()
//}
//-------------------------------------------
//void method(){
// ArrayList list;
// list.toString()
//}
if (isChildOfParent(parent, ident)) {
int startPosition = variable.getStartPosition();
int startPosition1 = ident.getStartPosition();
return true;
}
return false;
}
private boolean isChildOfParent(JCTree parent, JCTree child) {
//scope of child inside parent
return parent.getStartPosition() <= child.getStartPosition()
&& getEndPosition(parent) >= getEndPosition(child);
}
private int getEndPosition(JCTree tree) {
return tree.getEndPosition(mUnit.endPositions);
}
static class c {
static void cd() {
}
}
}
@@ -0,0 +1,55 @@
package com.itsaky.androidide.language.java.parser.internal;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.elvishew.xlog.XLog;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.ConstantsBridge;
import dalvik.system.DexFile;
import dalvik.system.PathClassLoader;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
class CompiledClassLoader {
@NonNull
public ArrayList<Class> getCompiledClassesFromProject(@NonNull AndroidProject project) {
ArrayList<Class> classes = new ArrayList<>();
try {
final List<String> dexes = ConstantsBridge.PROJECT_DEXES;
final PathClassLoader loader = new PathClassLoader(TextUtils.join(File.pathSeparator, dexes), ClassLoader.getSystemClassLoader());
if (dexes == null || dexes.size() <= 0) return classes;
for (String dex : dexes) {
final DexFile d = new DexFile(new File(dex));
Enumeration<String> entries = d.entries();
String name = null;
while (entries.hasMoreElements() && (name = entries.nextElement()) != null) {
while(name.startsWith(File.separator))
name = name.substring(1);
if(name.contains(File.separator))
name = name.replace(File.separator, ".");
Class<?> load = tryLoad(loader, name);
if (load != null)
classes.add(load);
}
d.close();
}
ConstantsBridge.CLASS_LOAD_SUCCESS = true;
return classes;
} catch (Throwable th) {
ConstantsBridge.CLASS_LOAD_SUCCESS = false;
return classes;
}
}
private Class<?> tryLoad(ClassLoader loader, String name) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException e) {
return null;
}
}
}
@@ -0,0 +1,44 @@
package com.itsaky.androidide.language.java.parser.internal;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
import java.util.ArrayList;
import java.util.List;
public interface IClass extends IJavaDocCommentable {
int getModifiers();
@Nullable
String getFullClassName();
@Nullable
String getSimpleName();
boolean isInterface();
boolean isEnum();
boolean isPrimitive();
boolean isAnnotation();
boolean isMemberClass();
@Nullable
IMethod getMethod(@NonNull String methodName, @Nullable IClass[] argsType);
@Nullable
IField getField(@NonNull String name);
List<IMethod> getMethods();
@Nullable
IClass getSuperclass();
ArrayList<IField> getFields();
List<SuggestItem> getMember(String prefix);
List<ConstructorDescription> getConstructors();
}
@@ -0,0 +1,16 @@
package com.itsaky.androidide.language.java.parser.internal;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import com.itsaky.androidide.interfaces.Filter;
import java.util.List;
public interface IClassManager {
void update(IClass clazz);
void remove(String fullClassName);
@NonNull
List<IClass> find(@NonNull String simpleNamePrefix,
@Nullable Filter<IClass> filter);
}
@@ -0,0 +1,17 @@
package com.itsaky.androidide.language.java.parser.internal;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public interface IField extends IJavaDocCommentable {
@NonNull
String getFieldName();
@NonNull
IClass getFieldType();
int getFieldModifiers();
@Nullable
Object getFieldValue();
}
@@ -0,0 +1,16 @@
package com.itsaky.androidide.language.java.parser.internal;
import androidx.annotation.NonNull;
public interface IMethod extends IJavaDocCommentable {
@NonNull
String getMethodName();
@NonNull
IClass getMethodReturnType();
@NonNull
IClass[] getMethodParameterTypes();
int getModifiers();
}
@@ -0,0 +1,253 @@
package com.itsaky.androidide.language.java.parser.internal;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
import com.itsaky.androidide.interfaces.Filter;
import com.itsaky.androidide.language.java.parser.JavacParser;
import com.itsaky.androidide.language.java.parser.model.ClassDescription;
import com.itsaky.androidide.models.AndroidProject;
import com.sun.tools.javac.tree.JCTree;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
public class JavaClassManager implements IClassManager {
private static final String TAG = "JavaClassReader";
private static final String JAVA_DOT_LANG_DOT = "java.lang.";
private static JavaClassManager INSTANCE;
/**
* All classes sorted by simple class name, fastest find list of classes start with prefix
*/
private final ArrayList<Pair<String, IClass>> mSimpleNames = new ArrayList<>();
/**
* All classes sorted by full class name, use for faster find class name by using binary search
*/
private final ArrayList<IClass> mFullNames = new ArrayList<>();
/**
* Map contains parsed class, parsed class is java class in jar file or from user
*/
private final HashMap<String, IClass> mLoaded = new HashMap<>();
private File mBootClasspath;
private JavaClassManager(File bootClassPath) {
mBootClasspath = bootClassPath;
}
public static JavaClassManager getInstance(File classpath) {
if (INSTANCE == null) {
INSTANCE = new JavaClassManager(classpath);
}
return INSTANCE;
}
public static JavaClassManager getInstance() {
if (INSTANCE == null) {
throw new RuntimeException("JavaClassManager not init");
}
return INSTANCE;
}
@NonNull
public ArrayList<IClass> getAllClasses() {
return mFullNames;
}
public void loadFromProject(AndroidProject project) {
try {
CompiledClassLoader classLoader = new CompiledClassLoader();
ArrayList<Class> classes = classLoader.getCompiledClassesFromProject(project);
for (Class clazz : classes) {
getClassWrapper(clazz);
}
JavacParser parser = new JavacParser();
List<String> javaSrcDirs = project.getSourcePaths();
for (String dir : javaSrcDirs) {
final File javaSrcDir = new File(dir);
Collection<File> javaFiles = FileUtils.listFilesInDirWithFilter(javaSrcDir, (f -> f.isFile() && f.getName().endsWith(".java")), true);
for (File javaFile : javaFiles) {
String content = FileIOUtils.readFile2String(javaFile);
JCTree.JCCompilationUnit ast = parser.parse(content);
List<IClass> parseClasses = parser.parseClasses(ast);
for (IClass aClass : parseClasses) {
update(aClass);
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* @param fullName - full class name
*/
@Nullable
public IClass getParsedClass(String fullName) {
IClass cache = mLoaded.get(fullName);
if (cache != null) {
return cache;
}
return mLoaded.get(JAVA_DOT_LANG_DOT + fullName);
}
@NonNull
public IClass getClassWrapper(@NonNull Class clazz) {
IClass cache = mLoaded.get(clazz.getName());
if (cache != null) {
return cache;
}
ClassDescription wrapper = new ClassDescription(clazz);
update(wrapper);
wrapper.initMembers(clazz);
return wrapper;
}
@Override
public void update(IClass value) {
String fullClassName = value.getFullClassName();
if (mLoaded.containsKey(fullClassName)) {
mLoaded.put(fullClassName, value);
return;
}
mLoaded.put(fullClassName, value);
if (mFullNames.size() == 0) {
mFullNames.add(value);
} else {
boolean found = false;
for (int i = 0; i < mFullNames.size(); i++) {
IClass iClass = mFullNames.get(i);
if (fullClassName.compareTo(iClass.getFullClassName()) <= 0) {
mFullNames.add(i, value);
found = true;
break;
}
}
if (!found) {
mFullNames.add(value);
}
}
String simpleName = value.getSimpleName();
Pair<String, IClass> simplePair = new Pair<String, IClass>(simpleName, value);
if (mSimpleNames.size() == 0) {
mSimpleNames.add(simplePair);
} else {
boolean found = false;
for (int i = 0; i < mSimpleNames.size(); i++) {
Pair<String, IClass> pair = mSimpleNames.get(i);
if (simpleName.compareTo(pair.first) <= 0) {
mSimpleNames.add(i, simplePair);
found = true;
break;
}
}
if (!found) {
mSimpleNames.add(simplePair);
}
}
}
@Override
public void remove(String fullClassName) {
IClass remove = mLoaded.remove(fullClassName);
int index = Collections.binarySearch(mFullNames, remove,
new Comparator<IClass>() {
@Override
public int compare(IClass o1, IClass o2) {
return o1.getFullClassName().compareTo(o2.getFullClassName());
}
});
if (index >= 0) {
mFullNames.remove(index);
}
Pair<String, IClass> key = new Pair<String, IClass>(remove.getSimpleName(), remove);
index = Collections.binarySearch(mSimpleNames, key,
new Comparator<Pair<String, IClass>>() {
@Override
public int compare(Pair<String, IClass> o1,
Pair<String, IClass> o2) {
return o1.first.compareTo(o2.first);
}
});
if (index >= 0) {
mSimpleNames.remove(index);
}
}
/**
* search class with binary search
*
* @param classes - sorted classes by name
* @param prefix - prefix if class name
*/
@Nullable
private List<Pair<String, IClass>> binarySearch(
@NonNull List<Pair<String, IClass>> classes,
@NonNull String prefix) {
int start = -1, end = -1;
int left = 0;
int right = classes.size() - 1;
while (left <= right) {
int mid = (left + right) / 2;
String name = classes.get(mid).first;
String midValue = name.substring(0, Math.min(name.length(), prefix.length()));
if (midValue.trim().toLowerCase(Locale.US).compareTo(prefix.toLowerCase(Locale.US)) < 0) {
left = mid + 1;
} else if (midValue.trim().toLowerCase(Locale.US).compareTo(prefix.toLowerCase(Locale.US)) > 0) {
right = mid - 1;
} else {
start = mid;
end = mid;
while (start >= 0 &&
classes.get(start)
.first.trim().toLowerCase(Locale.US)
.startsWith(prefix.toLowerCase(Locale.US))) {
start--;
}
while (end < classes.size() &&
classes.get(end)
.first.trim().toLowerCase(Locale.US)
.startsWith(prefix.toLowerCase(Locale.US))) {
end++;
}
start++;
end--;
break;
}
}
if (end >= 0 && start >= 0 && end - start + 1 /*length*/ >= 1) {
return classes.subList(start, end + 1);
}
return null;
}
@Override
@NonNull
public List<IClass> find(@NonNull String simpleNamePrefix, @Nullable Filter<IClass> filter) {
ArrayList<IClass> result = new ArrayList<>();
List<Pair<String, IClass>> simple = binarySearch(mSimpleNames, simpleNamePrefix);
if (simple != null) {
for (Pair<String, IClass> c : simple) {
if (filter != null) {
if (!filter.accept(c.second)) {
continue;
}
}
result.add(c.second);
}
}
return result;
}
}
@@ -0,0 +1,95 @@
package com.itsaky.androidide.language.java.parser.internal;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.interfaces.Filter;
import com.itsaky.androidide.language.java.parser.internal.IClass;
import com.itsaky.androidide.models.AndroidProject;
import java.io.File;
import java.util.List;
/**
* Created by Duy on 20-Jul-17.
*/
public class JavaDexClassLoader {
private static final Filter<IClass> mClassFilter = new Filter<IClass>() {
@Override
public boolean accept(IClass aClass) {
return !(aClass.isAnnotation() || aClass.isInterface() || aClass.isEnum() || aClass.isMemberClass());
}
};
private static final Filter<IClass> mEnumFilter = new Filter<IClass>() {
@Override
public boolean accept(IClass aClass) {
return aClass.isEnum();
}
};
private static final Filter<IClass> mInterfaceFilter = new Filter<IClass>() {
@Override
public boolean accept(IClass aClass) {
return aClass.isInterface();
}
};
private static final Filter<IClass> mAnnotationFilter = new Filter<IClass>() {
@Override
public boolean accept(IClass aClass) {
return aClass.isAnnotation();
}
};
private JavaClassManager mClassReader;
public JavaDexClassLoader(File classpath) {
mClassReader = JavaClassManager.getInstance(classpath);
}
public JavaClassManager getClassReader() {
return mClassReader;
}
@NonNull
public List<IClass> findAllWithPrefix(String simpleNamePrefix) {
return mClassReader.find(simpleNamePrefix, null);
}
@NonNull
public List<IClass> findAllWithPrefix(@NonNull String simpleNamePrefix,
@Nullable Filter<IClass> filter) {
return mClassReader.find(simpleNamePrefix, filter);
}
public List<IClass> findClasses(String simpleNamePrefix) {
return mClassReader.find(simpleNamePrefix, mClassFilter);
}
public List<IClass> findInterfaces(String simpleNamePrefix) {
return mClassReader.find(simpleNamePrefix, mInterfaceFilter);
}
public List<IClass> findEnums(String simpleNamePrefix) {
return mClassReader.find(simpleNamePrefix, mEnumFilter);
}
public List<IClass> findAnnotations(String simpleNamePrefix) {
return mClassReader.find(simpleNamePrefix, mAnnotationFilter);
}
public void loadAllClasses(AndroidProject projectFile) {
mClassReader.loadFromProject(projectFile);
}
public void updateClasses(List<IClass> classes) {
for (IClass aClass : classes) {
mClassReader.update(aClass);
}
}
public void updateClass(IClass... classes) {
for (IClass aClass : classes) {
mClassReader.update(aClass);
}
}
}
@@ -0,0 +1,191 @@
package com.itsaky.androidide.language.java.parser.internal;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.blankj.utilcode.util.FileUtils;
import com.sun.tools.javac.tree.JCTree;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.Modifier;
/**
* Created by Duy on 20-Jul-17.
*/
public class JavaUtil {
public static String getSimpleName(String className) {
if (className.contains(".")) {
className = className.substring(className.lastIndexOf(".") + 1);
}
return getArrayTypeIfNeeded(className);
}
@NonNull
public static String getPackageName(String classname) {
if (classname.contains(".")) {
return classname.substring(0, classname.lastIndexOf("."));
} else {
return "";
}
}
public static String getInverseName(String className) {
String[] split = className.split(".");
String result = "";
for (String s : split) {
result = s + result;
}
return result;
}
public static boolean isValidClassName(@Nullable String name) {
return name != null && name.matches("[A-Za-z_][A-Za-z0-9_]*");
}
@Nullable
public static String getClassName(File javaSrc, String filePath) {
if (filePath.startsWith(javaSrc.getPath())) {
//hello/src/main/java
//hello ->
String filename = filePath.substring(filePath.indexOf(javaSrc.getPath()) + javaSrc.getPath().length() + 1);
filename = filename.replace(File.separator, ".");
if (filename.endsWith(".java")) {
filename = filename.substring(0, filename.lastIndexOf(".java"));
}
return filename;
} else {
return null;
}
}
public static int toJavaModifiers(Set<Modifier> modifierSet) {
int modifiers = 0;
for (Modifier modifier : modifierSet) {
switch (modifier) {
case FINAL:
modifiers |= java.lang.reflect.Modifier.FINAL;
break;
case PROTECTED:
modifiers |= java.lang.reflect.Modifier.PROTECTED;
break;
case PRIVATE:
modifiers |= java.lang.reflect.Modifier.PRIVATE;
break;
case ABSTRACT:
modifiers |= java.lang.reflect.Modifier.ABSTRACT;
break;
case NATIVE:
modifiers |= java.lang.reflect.Modifier.NATIVE;
case PUBLIC:
modifiers |= java.lang.reflect.Modifier.PUBLIC;
case STATIC:
modifiers |= java.lang.reflect.Modifier.STATIC;
case TRANSIENT:
modifiers |= java.lang.reflect.Modifier.TRANSIENT;
break;
case VOLATILE:
modifiers |= java.lang.reflect.Modifier.VOLATILE;
break;
case SYNCHRONIZED:
modifiers |= java.lang.reflect.Modifier.SYNCHRONIZED;
break;
case STRICTFP:
modifiers |= java.lang.reflect.Modifier.STRICT;
break;
}
}
return modifiers;
}
@NonNull
public static String findImportedClassName(@NonNull JCTree.JCCompilationUnit mUnit,
@NonNull String className) {
List<JCTree.JCImport> imports = mUnit.getImports();
for (JCTree.JCImport jcImport : imports) {
String fullName = jcImport.getQualifiedIdentifier().toString();
if (fullName.equals(className) || fullName.endsWith("." + className)) {
return fullName;
}
}
return className;
}
@Nullable
public static IClass jcTypeToClass(JCTree.JCCompilationUnit unit, JCTree type) {
if (type == null) {
return null;
}
String className;
if (type instanceof JCTree.JCTypeApply) {
return jcTypeToClass(unit, ((JCTree.JCTypeApply) type).getType());
} else {
className = type.toString();
}
className = findImportedClassName(unit, className);
return JavaClassManager.getInstance().getParsedClass(className);
}
public static ArrayList<String> listClassName(File src) {
if (!src.exists()) return new ArrayList<String>();
Collection<File> files = FileUtils.listFilesInDirWithFilter(src, (f -> f.isFile() && f.getName().endsWith(".java")), true);
ArrayList<String> classes = new ArrayList<>();
String srcPath = src.getPath();
for (File file : files) {
String javaPath = file.getPath();
javaPath = javaPath.substring(srcPath.length() + 1, javaPath.length() - 5); //.java
javaPath = javaPath.replace(File.separator, ".");
classes.add(javaPath);
}
return classes;
}
public static String getArrayTypeIfNeeded(String currentType) {
if(currentType == null)
return "";
String type = new String(currentType);
if(type.startsWith("[")) {
type = type.substring(1).trim();
if (type.equals("Z")) {
type = "boolean[]";
} else if (type.equals("B")) {
type = "byte[]";
} else if (type.equals("C")) {
type = "char[]";
} else if (type.equals("S")) {
type = "short[]";
} else if (type.equals("I")) {
type = "int[]";
} else if (type.equals("J")) {
type = "long[]";
} else if (type.equals("F")) {
type = "float[]";
} else if (type.equals("D")) {
type = "double[]";
} else {
if(type.endsWith(";"))
type = type.substring(0, type.length());
type = type + "[]";
}
}
return type;
}
}

Some files were not shown because too many files have changed in this diff Show More