diff --git a/app/build.gradle b/app/build.gradle
index 6f5179476..31508fb9e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -59,7 +59,7 @@ android {
stringfog.enable false
}
release {
- minifyEnabled true
+ minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
stringfog.enable true
@@ -75,8 +75,9 @@ android {
}
stringfog {
+ def isRelease = getGradle().getStartParameter().getTaskRequests().toString().contains("release")
key 'WVV0aGMwaEFZVzVFVW05SlJFbGtaUT09'
- enable true
+ enable false
implementation 'com.github.megatronking.stringfog.xor.StringFogImpl'
}
@@ -90,6 +91,7 @@ dependencies {
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.Rosemoe:BlockLinkedList:1.0.0'
implementation 'com.github.smart-fun:XmlToJson:1.5.1'
// Dependencies for java completion
@@ -119,7 +121,6 @@ dependencies {
// Local projects
implementation project(':treeview')
- implementation project(':editor')
implementation project(':spinkit')
implementation project(':slidr')
implementation project(':javadocparser')
diff --git a/app/dictionary.txt b/app/dictionary.txt
new file mode 100644
index 000000000..8c2a35a51
--- /dev/null
+++ b/app/dictionary.txt
@@ -0,0 +1,4 @@
+o0OoO0
+0oO00Oo0
+oO0o0O0O
+0O0o00
diff --git a/app/libs/ecj.jar b/app/libs/ecj-4.6.1.jar
similarity index 87%
rename from app/libs/ecj.jar
rename to app/libs/ecj-4.6.1.jar
index 1c7e9cad3..d0323c672 100644
Binary files a/app/libs/ecj.jar and b/app/libs/ecj-4.6.1.jar differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 385351e30..0945229ca 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -1,5 +1,8 @@
-keepattributes SourceFile, LineNumberTable
-renamesourcefileattribute FuckYou
+-obfuscationdictionary dictionary.txt
+-packageobfuscationdictionary dictionary.txt
+-classobfuscationdictionary dictionary.txt
-ignorewarnings
-dontwarn
@@ -14,17 +17,14 @@
-dontwarn androidx.lifecycle.**
-keep class androidx.arch.** { *; }
-keep class androidx.lifecycle.** { *; }
--keepclasseswithmembernames,includedescriptorclasses class * {
- native ;
-}
+-keep class org.eclipse.jdt.** { (...); }
+-keep class org.eclipse.jdt.** { static *; }
+
+-keepclasseswithmembernames,includedescriptorclasses class * { native ; }
-keep public class * implements com.bumptech.glide.module.GlideModule
--keep class * extends com.bumptech.glide.module.AppGlideModule {
- (...);
-}
+-keep class * extends com.bumptech.glide.module.AppGlideModule { (...); }
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
--keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
- *** rewind();
-}
+-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder { *** rewind(); }
diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
index 24b1824cb..55507826f 100644
--- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
@@ -17,6 +17,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;
import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.Slide;
import androidx.transition.TransitionManager;
import com.blankj.utilcode.util.ClipboardUtils;
@@ -29,6 +31,7 @@ import com.google.android.material.navigation.NavigationView;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.transition.MaterialArcMotion;
import com.google.android.material.transition.MaterialContainerTransform;
+import com.itsaky.androidide.adapters.DiagnosticsAdapter;
import com.itsaky.androidide.adapters.EditorPagerAdapter;
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
import com.itsaky.androidide.app.StudioActivity;
@@ -41,9 +44,11 @@ import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
import com.itsaky.androidide.fragments.sheets.TextSheetFragment;
import com.itsaky.androidide.interfaces.CompileListener;
import com.itsaky.androidide.interfaces.DexResultListener;
+import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.androidide.language.logs.LogLanguageImpl;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.ConstantsBridge;
+import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.models.SheetOption;
import com.itsaky.androidide.receivers.LogReceiver;
@@ -64,6 +69,7 @@ import com.unnamed.b.atv.model.TreeNode;
import io.github.rosemoe.editor.langs.EmptyLanguage;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.io.File;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -71,14 +77,14 @@ import java.util.Map;
import java.util.regex.Pattern;
import me.piruin.quickaction.ActionItem;
import me.piruin.quickaction.QuickAction;
-import com.elvishew.xlog.XLog;
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
IDEService.BuildListener,
TabLayout.OnTabSelectedListener,
NavigationView.OnNavigationItemSelectedListener,
DexResultListener,
- CompileListener {
+ CompileListener,
+ DiagnosticClickListener {
private ActivityEditorBinding mBinding;
private EditorPagerAdapter mPagerAdapter;
@@ -89,6 +95,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private TreeNode mLastHolded;
private CodeEditor buildView;
private CodeEditor logView;
+ private RecyclerView diagnosticList;
private LogLanguageImpl mLogLanguageImpl;
@@ -97,6 +104,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private OptionsListFragment mFileOptionsFragment;
private boolean recreateCompletionServices = false;
+
+ private final Map> diagnostics = new HashMap<>();
private final String RES_PATH_REGEX = "/.*/src/.*/res";
private final String LAYOUTRES_PATH_REGEX = "/.*/src/.*/res/layout";
@@ -111,9 +120,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private static final int ACTIONID_CLOSE = 100;
private static final int ACTIONID_OTHERS = 101;
private static final int ACTIONID_ALL = 102;
-
- private static final HashMap> mDiagnosticsMap = new HashMap<>();
-
+
private LogReceiver mLogReceiver = new LogReceiver().setLogListener(line -> appendApkLog(line));
private BroadcastReceiver mServiceStartReceiver = new BroadcastReceiver(){
@@ -186,7 +193,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged());
registerLogReceiver();
-
setupContainers();
}
@@ -198,6 +204,10 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
if(getLogView().getParent() != null && getLogView().getParent() instanceof ViewGroup) {
((ViewGroup) getLogView().getParent()).removeView(getLogView());
}
+
+ if(getDiagnosticsList().getParent() != null && getDiagnosticsList().getParent() instanceof ViewGroup) {
+ ((ViewGroup) getDiagnosticsList().getParent()).removeView(getDiagnosticsList());
+ }
mBinding.buildOutcontainer.addView(getBuildView(), new ViewGroup.LayoutParams(-1, -1));
mBinding.buildContainer.setVisibility(View.GONE);
@@ -208,12 +218,18 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mBinding.logContainer.setVisibility(View.GONE);
mBinding.logOutToolbar.setNavigationOnClickListener(v -> hideLogResult());
mBinding.logOutToolbar.setOnClickListener(v -> hideLogResult());
+
+ mBinding.diagListContainer.addView(getDiagnosticsList(), new ViewGroup.LayoutParams(-1, -1));
+ mBinding.diagContainer.setVisibility(View.GONE);
+ mBinding.diagToolbar.setNavigationOnClickListener(v -> hideDiagnostics());
+ mBinding.diagToolbar.setOnClickListener(v -> hideDiagnostics());
mBinding.viewOptionsCard.setVisibility(View.GONE);
mBinding.transformScrim.setVisibility(View.GONE);
mBinding.transformScrim.setOnClickListener(v -> hideViewOptions());
mBinding.viewBuildOut.setOnClickListener(v -> showBuildResult());
+ mBinding.viewDiags.setOnClickListener(v -> showDiagnostics());
mBinding.viewLogs.setOnClickListener(v -> showLogResult());
mBinding.viewFiles.setOnClickListener(v -> showFiles());
mBinding.viewDaemonStatus.setOnClickListener(v -> showDaemonStatus());
@@ -250,18 +266,20 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
@Override
public void onBackPressed() {
- if(mBinding.buildContainer.getVisibility() == View.VISIBLE) {
- hideBuildResult();
- } else if(mBinding.logContainer.getVisibility() == View.VISIBLE) {
- hideLogResult();
- } else if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) {
- hideViewOptions();
- } else if(mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
+ if(mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
mBinding.getRoot().closeDrawer(GravityCompat.END);
} else if(mBinding.getRoot().isDrawerOpen(GravityCompat.START)) {
mBinding.getRoot().closeDrawer(GravityCompat.START);
} else if(getDaemonStatusFragment().isShowing()) {
getDaemonStatusFragment().dismiss();
+ } else if(mBinding.buildContainer.getVisibility() == View.VISIBLE) {
+ hideBuildResult();
+ } else if(mBinding.logContainer.getVisibility() == View.VISIBLE) {
+ hideLogResult();
+ } else if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) {
+ hideViewOptions();
+ } else if(mBinding.diagContainer.getVisibility() == View.VISIBLE) {
+ hideDiagnostics();
} else if(mFileOptionsFragment != null && mFileOptionsFragment.isShowing()) {
mFileOptionsFragment.dismiss();
} else {
@@ -411,6 +429,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
if(recreateCompletionServices) {
getApp().createCompletionService(mProject);
} else if(task.getType() == GradleTask.Type.BUILD) {
+ getApp().createJavaCompiler(mProject);
+ getApp().getJavaCompiler().compileAllAsync(this);
if(task.getTaskID() == IDEService.TASK_ASSEMBLE_DEBUG) {
if(task.buildsApk()) {
install(task.getApk(new File(mProject.getMainModulePath(), "build").getAbsolutePath(), mProject.getMainModule()));
@@ -429,6 +449,11 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
if(task.canOutput()) {
setStatus(msg);
}
+
+ if(task.getType() == GradleTask.Type.BUILD) {
+ getApp().createJavaCompiler(mProject);
+ getApp().getJavaCompiler().compileAllAsync(this);
+ }
showBuildResult();
@@ -441,9 +466,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
public void onGetDependencies(List dependencies) {
setStatus(getString(R.string.msg_starting_completion));
mProject.setClassPaths(dependencies);
- getApp().createJavaCompiler(mProject, this);
- getApp().getJavaCompiler().compileAll();
- new Jar2DexService(getApp().getIndexedDir(), dependencies, this).startDexing();
+ createCompletionServices();
}
@Override
@@ -464,19 +487,31 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
public void saveFiles() {
saveAll();
}
-
+
+ /**
+ * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
+ */
+ @Deprecated
@Override
public void onDex(String name) {
setStatus(getString(R.string.msg_indexing, name));
}
-
+
+ /**
+ * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
+ */
+ @Deprecated
@Override
public void onDexSuccess(List dexes) {
ConstantsBridge.PROJECT_DEXES = dexes;
setStatus(getString(R.string.msg_indexing_success));
createCompletionServices();
}
-
+
+ /**
+ * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
+ */
+ @Deprecated
@Override
public void onDexFailed(String message) {
appendBuildOut(message);
@@ -484,14 +519,20 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
showBuildResult();
}
- private void appendBuildOut(String str) {
- str = str.endsWith("\n") ? str : str.concat("\n");
- getBuildView(true).getText().append(str);
+ // Can be called from a different different, better to run on UI thread
+ private void appendBuildOut(final String str) {
+ runOnUiThread(() -> {
+ String strFinal = str.endsWith("\n") ? str : str.concat("\n");
+ getBuildView(true).getText().append(strFinal);
+ });
}
- private void appendLogOut(String str) {
- str = str.endsWith("\n") ? str : str.concat("\n");
- getLogView(true).getText().append(str);
+ // Can be called from a different different, better to run on UI thread
+ private void appendLogOut(final String str) {
+ runOnUiThread(() -> {
+ String strFinal = str.endsWith("\n") ? str : str.concat("\n");
+ getLogView(true).getText().append(strFinal);
+ });
}
private void showFiles() {
@@ -563,6 +604,20 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mBinding.logContainer.setVisibility(View.GONE);
mBinding.viewOptionsCard.setVisibility(View.VISIBLE);
}
+
+ private void showDiagnostics() {
+ TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewDiags, mBinding.diagContainer));
+ mBinding.diagContainer.setVisibility(View.VISIBLE);
+ mBinding.viewOptionsCard.setVisibility(View.GONE);
+ hideToolbarAndTabs();
+ }
+
+ private void hideDiagnostics() {
+ showToolbarAndTabs();
+ TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.diagContainer, mBinding.viewDiags));
+ mBinding.diagContainer.setVisibility(View.GONE);
+ mBinding.viewOptionsCard.setVisibility(View.VISIBLE);
+ }
private MaterialContainerTransform createContainerTransformFor(View start, View end) {
return createContainerTransformFor(start, end, mBinding.realContainer);
@@ -632,7 +687,12 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
@Override
- public void onCompilationResult(boolean success, Map> diagnostics) {
+ public void onCompilationResult(boolean singleFile, Map> diagnostics) {
+ if(getDiagnosticsList().getParent() == null || !(getDiagnosticsList().getParent() instanceof ViewGroup))
+ setupContainers();
+ getDiagnosticsList().setAdapter(new DiagnosticsAdapter(mapAsGroup(diagnostics), this));
+ this.diagnostics.clear();
+ this.diagnostics.putAll(diagnostics);
provideDiagnosticsToEditors(diagnostics);
}
@@ -650,6 +710,21 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
}
}
+
+ @Override
+ public void onGroupClick(DiagnosticGroup group) {
+ if(group != null
+ && group.file != null
+ && group.file.exists()
+ && FileUtils.isUtf8(group.file))
+ {
+ openFile(group.file);
+ }
+ }
+
+ @Override
+ public void onDiagnosticClick(CompilerDiagnostic diagnostic) {
+ }
/*****************************
**** Tab Related ************
@@ -690,8 +765,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
final int id = p1.getItemId();
if(id == R.id.editornav_discuss) {
getApp().openTelegramGroup();
-// } else if(id == R.id.editornav_suggest) {
-// getApp().openIssueTracker();
+ } else if(id == R.id.editornav_suggest) {
+ getApp().openIssueTracker();
} else if(id == R.id.editornav_needHelp) {
showNeedHelpDialog();
} else if(id == R.id.editornav_settings) {
@@ -796,7 +871,32 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
logView.setTextSize(12);
return logView;
}
-
+
+ private RecyclerView getDiagnosticsList() {
+ return diagnosticList == null ? createDiagnosticsList() : diagnosticList;
+ }
+
+ private RecyclerView createDiagnosticsList() {
+ diagnosticList = new RecyclerView(this);
+ diagnosticList.setLayoutManager(new LinearLayoutManager(this));
+ diagnosticList.setAdapter(new DiagnosticsAdapter(mapAsGroup(this.diagnostics), this));
+ return diagnosticList;
+ }
+
+ private List mapAsGroup(Map> diags) {
+ List groups = new ArrayList<>();
+ if(diags == null || diags.size() <= 0)
+ return groups;
+ for(File file : diags.keySet()) {
+ List fileDiags = diags.get(file);
+ if(fileDiags == null || fileDiags.size() <= 0)
+ continue;
+ DiagnosticGroup group = new DiagnosticGroup(R.drawable.ic_language_java, file, fileDiags);
+ groups.add(group);
+ }
+ return groups;
+ }
+
private LogLanguageImpl getLogLanguage() {
return mLogLanguageImpl == null ? mLogLanguageImpl = new LogLanguageImpl() : mLogLanguageImpl;
}
@@ -823,6 +923,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
boolean isGradleSaved = mPagerAdapter.saveAll();
getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS);
if(isGradleSaved) notifySyncNeeded();
+ if(getApp().getJavaCompiler() != null)
+ getApp().getJavaCompiler().compileAllAsync(this);
return isGradleSaved;
}
@@ -1148,7 +1250,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mBinding.tabs.setupWithViewPager(mBinding.editorViewPager);
mBinding.tabs.addOnTabSelectedListener(this);
}
-
+
private void closeFile(int index) {
closeFile(index, true);
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdpater.java b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdpater.java
new file mode 100644
index 000000000..ec1cebd0e
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdpater.java
@@ -0,0 +1,65 @@
+package com.itsaky.androidide.adapters;
+
+import android.graphics.PorterDuff;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
+import com.itsaky.androidide.R;
+import com.itsaky.androidide.databinding.LayoutDiagnosticItemBinding;
+import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+import java.util.List;
+import javax.tools.Diagnostic;
+import com.itsaky.androidide.interfaces.DiagnosticClickListener;
+
+public class DiagnosticItemAdpater extends RecyclerView.Adapter {
+
+ private List diags;
+ private DiagnosticClickListener listener;
+
+ public DiagnosticItemAdpater(List diags, DiagnosticClickListener listener) {
+ this.diags = diags;
+ this.listener = listener;
+ }
+
+ @Override
+ public DiagnosticItemAdpater.VH onCreateViewHolder(ViewGroup p1, int p2) {
+ return new VH(LayoutDiagnosticItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
+ }
+
+ @Override
+ public void onBindViewHolder(DiagnosticItemAdpater.VH p1, int p2) {
+ final CompilerDiagnostic diagnostic = diags.get(p2);
+ final LayoutDiagnosticItemBinding binding = p1.binding;
+
+ binding.icon.setImageResource(getDiagnosticIconId(diagnostic));
+ binding.icon.setColorFilter(ContextCompat.getColor(binding.icon.getContext(), diagnostic.kind() == Diagnostic.Kind.ERROR ? R.color.diagnostic_error : R.color.diagnostic_warning), PorterDuff.Mode.SRC_ATOP);
+ binding.title.setText(diagnostic.message());
+
+ binding.getRoot().setOnClickListener(v -> {
+ if(listener != null) {
+ listener.onDiagnosticClick(diagnostic);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return diags.size();
+ }
+
+ private int getDiagnosticIconId(CompilerDiagnostic diagnostic) {
+ if(diagnostic.kind() == Diagnostic.Kind.ERROR)
+ return R.drawable.ic_compilation_error;
+ return R.drawable.ic_info;
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ private LayoutDiagnosticItemBinding binding;
+ public VH(LayoutDiagnosticItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java
new file mode 100644
index 000000000..d5d912e17
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java
@@ -0,0 +1,63 @@
+package com.itsaky.androidide.adapters;
+
+import android.graphics.PorterDuff;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import com.itsaky.androidide.R;
+import com.itsaky.androidide.databinding.LayoutDiagnosticGroupBinding;
+import com.itsaky.androidide.interfaces.DiagnosticClickListener;
+import com.itsaky.androidide.models.DiagnosticGroup;
+import java.util.List;
+
+public class DiagnosticsAdapter extends RecyclerView.Adapter {
+
+ private final List diags;
+ private final DiagnosticClickListener listener;
+
+ public DiagnosticsAdapter(List diags, DiagnosticClickListener listener) {
+ this.diags = diags;
+ this.listener = listener;
+ }
+
+ @Override
+ public DiagnosticsAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
+ return new VH(LayoutDiagnosticGroupBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
+ }
+
+ @Override
+ public void onBindViewHolder(DiagnosticsAdapter.VH p1, int p2) {
+ final DiagnosticGroup group = diags.get(p2);
+ final LayoutDiagnosticGroupBinding binding = p1.binding;
+
+ final int color = ContextCompat.getColor(binding.info.icon.getContext(), R.color.secondaryColor);
+ binding.info.icon.setImageResource(group.icon);
+ binding.info.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ binding.info.title.setText(group.text);
+ binding.info.title.setTextColor(color);
+ binding.diagnostics.setLayoutManager(new LinearLayoutManager(binding.diagnostics.getContext()));
+ binding.diagnostics.setAdapter(new DiagnosticItemAdpater(group.diagnostics, listener));
+
+ binding.info.getRoot().setOnClickListener(v -> {
+ if(listener != null) {
+ listener.onGroupClick(group);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return diags.size();
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ private LayoutDiagnosticGroupBinding binding;
+ public VH(LayoutDiagnosticGroupBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
index bdae075e6..bf6931e33 100644
--- a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
+++ b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
@@ -11,15 +11,10 @@ import android.text.TextUtils;
import androidx.core.app.NotificationManagerCompat;
import androidx.multidex.MultiDexApplication;
import com.blankj.utilcode.util.EncodeUtils;
+import com.blankj.utilcode.util.FileUtils;
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;
@@ -28,8 +23,10 @@ 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.models.ConstantsBridge;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.services.MessagingService;
+import com.itsaky.androidide.services.compiler.JavaCompilerService;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.FileUtil;
@@ -37,16 +34,11 @@ import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.StudioUtils;
import com.itsaky.toaster.Toaster;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
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
{
@@ -93,10 +85,6 @@ public class StudioApp extends MultiDexApplication
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();
}
@@ -153,41 +141,28 @@ public class StudioApp extends MultiDexApplication
}
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);
+ if(lib != null && lib.exists() && lib.isDirectory()) {
+ boolean libhook, librt, libgcc, libgcc_real, libpthread, libz, libcpp_shared;
+ libhook = librt = libgcc = libgcc_real = libpthread = libz = libcpp_shared = false;
+ File[] list = lib.listFiles();
+ if(list != null) {
+ for(File f : list) {
+ libhook = libhook == false ? f.getName().equals("libhook.so") : libhook;
+ librt = librt == false ? f.getName().equals("librt.so") : librt;
+ libpthread = libpthread == false ? f.getName().equals("libpthread.so") : libpthread;
+ libgcc = libgcc == false ? f.getName().equals("libgcc.so") : libgcc;
+ libgcc_real = libgcc_real = false ? f.getName().equals("libgcc_real.so") : libgcc_real;
+ libz = libz == false ? f.getName().equals("libz.so.1") : libz;
+ libcpp_shared = libcpp_shared == false ? f.getName().equals("libc++_shared.so") : libcpp_shared;
+ }
+ } else return extractLibs(lib);
+ return !(libhook && librt && libgcc && libgcc_real && libpthread && libz && libcpp_shared) ? extractLibs(lib) : true;
+ } else return extractLibs(lib);
}
private boolean extractLibs(File lib) {
@@ -248,10 +223,9 @@ public class StudioApp extends MultiDexApplication
this.mCompletionProvider.loadProject(project);
}
- public void createJavaCompiler(AndroidProject project, CompileListener listener) {
+ public void createJavaCompiler(AndroidProject project) {
this.mJavaCompilerService =
- new JavaCompilerService(project)
- .setListener(listener);
+ new JavaCompilerService(project);
}
public JavaCompilerService getJavaCompiler() {
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
index 300c4b616..b71c30e4e 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
@@ -5,21 +5,26 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.itsaky.androidide.adapters.CompletionListAdapter;
+import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.FragmentEditorBinding;
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
+import com.itsaky.androidide.interfaces.CompileListener;
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.JavaCompilerService;
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.tools.SourceJavaFileObject;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.TypefaceUtils;
+import com.sun.tools.javac.resources.compiler;
import io.github.rosemoe.editor.interfaces.EditorEventListener;
import io.github.rosemoe.editor.langs.EmptyLanguage;
import io.github.rosemoe.editor.widget.CodeEditor;
@@ -28,13 +33,11 @@ import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
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 class EditorFragment extends BaseFragment implements EditorEventListener, CompileListener {
public FragmentEditorBinding binding;
private File mFile;
@@ -43,9 +46,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
private boolean isFirstCreate = false;
private static AndroidProject project;
-
- private List diags;
-
+
public static final String KEY_FILE_PATH = "file_path";
public static final String KEY_PROJECT = "project";
public static final String EXT_JAVA = ".java";
@@ -55,7 +56,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
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());
@@ -100,11 +101,30 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
postRead();
});
}
+
+ @Override
+ public void onCompilationResult(boolean singleFile, Map> diagnostics) {
+ if(diagnostics != null
+ && getFile() != null
+ && diagnostics.containsKey(getFile()))
+ {
+ binding.editorCodeEditor.setDiagnostics(diagnostics.get(getFile()));
+ }
+ }
+
+ private void requestCompilation() {
+ final long s = System.currentTimeMillis();
+ final SourceJavaFileObject source = new SourceJavaFileObject(getFile(), binding.editorCodeEditor.getText().toString());
+ JavaCompilerService compiler = StudioApp.getInstance().getJavaCompiler();
+ if(compiler != null) {
+ compiler.compileSingleAsync(source, this);
+ }
+ }
public void setDiagnostics(List diags) {
- this.diags = diags;
- XLog.i("[EditorFragment] Diagnostics for file: " + getFile().getAbsolutePath()
- + "\ndiagnostics: " + TextUtils.join("\n", diags));
+ if(binding.editorCodeEditor != null) {
+ binding.editorCodeEditor.setDiagnostics(diags);
+ }
}
private void configureEditorIfNeeded() {
@@ -176,10 +196,10 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
private void postRead() {
if (mFile.isFile() && mFile.getName().endsWith(EXT_JAVA)) {
- binding.editorCodeEditor.setEditorLanguage(new JavaLanguage(binding.editorCodeEditor, project));
+ binding.editorCodeEditor.setEditorLanguage(new JavaLanguage(project));
binding.editorCodeEditor.setColorScheme(new SchemeVS2019());
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_XML)) {
- binding.editorCodeEditor.setEditorLanguage(new XMLLanguage(binding.editorCodeEditor));
+ binding.editorCodeEditor.setEditorLanguage(new XMLLanguage());
binding.editorCodeEditor.setColorScheme(new SchemeWombat());
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_GRADLE)) {
binding.editorCodeEditor.setEditorLanguage(new GroovyLanguage());
@@ -251,6 +271,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
@Override
public void afterDelete(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
isModified = true;
+
+ requestCompilation();
}
@Override
@@ -263,11 +285,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
if(isOpen && insertedContent.toString().equals("/")) {
closeCurrentTag(editor.getText().toString(), endLine, endColumn);
}
-
- JavaCompilerService compiler = getStudioActivity().getApp().getJavaCompiler();
- if(compiler != null) {
- compiler.compileAll();
- }
+
+ requestCompilation();
}
@Override
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
index 406a877f9..d91f160c1 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
@@ -26,6 +26,7 @@ import com.unnamed.b.atv.view.AndroidTreeView;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import com.blankj.utilcode.util.FileUtils;
public class FileTreeFragment extends BottomSheetDialogFragment implements TreeNode.TreeNodeClickListener, TreeNode.TreeNodeLongClickListener {
@@ -86,13 +87,8 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
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;
- }
- }
+ if (mFileActionListener != null && FileUtils.isUtf8(f)) {
+ mFileActionListener.openFile(f);
}
} else if(f.isDirectory() && f.exists()) {
if(node.isExpanded()) {
@@ -181,7 +177,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
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())
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java
index f55a045ed..85b931607 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java
@@ -84,7 +84,7 @@ public class IDEPreferences extends BasePreferenceFragment {
screen.addPreference(editor);
screen.addPreference(build);
-// screen.addPreference(issueTracker);
+ screen.addPreference(issueTracker);
screen.addPreference(telegram);
screen.addPreference(changelog);
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/LogSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/LogSheet.java
index 219c4dc3e..cc7bd9173 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/LogSheet.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/LogSheet.java
@@ -8,7 +8,6 @@ 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;
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/CompileListener.java b/app/src/main/java/com/itsaky/androidide/interfaces/CompileListener.java
index 3919e7aa7..28c164a6e 100644
--- a/app/src/main/java/com/itsaky/androidide/interfaces/CompileListener.java
+++ b/app/src/main/java/com/itsaky/androidide/interfaces/CompileListener.java
@@ -6,5 +6,5 @@ import java.util.List;
import java.util.Map;
public interface CompileListener {
- void onCompilationResult(boolean success, Map> diagnostics);
+ void onCompilationResult(boolean singleFile, Map> diagnostics);
}
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/DexResultListener.java b/app/src/main/java/com/itsaky/androidide/interfaces/DexResultListener.java
index bd0fb564d..a2ce1ef51 100644
--- a/app/src/main/java/com/itsaky/androidide/interfaces/DexResultListener.java
+++ b/app/src/main/java/com/itsaky/androidide/interfaces/DexResultListener.java
@@ -3,6 +3,13 @@ package com.itsaky.androidide.interfaces;
import androidx.core.util.Pair;
import java.util.List;
+/**
+ * A callback to listen for updates while dexing JAR files
+ *
+ * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
+ */
+
+@Deprecated
public interface DexResultListener {
public void onDex(String name);
public void onDexSuccess(List dexes);
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java b/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
new file mode 100644
index 000000000..2d8b9dc54
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
@@ -0,0 +1,9 @@
+package com.itsaky.androidide.interfaces;
+
+import com.itsaky.androidide.models.DiagnosticGroup;
+import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+
+public interface DiagnosticClickListener {
+ void onGroupClick(DiagnosticGroup group);
+ void onDiagnosticClick(CompilerDiagnostic diagnostic);
+}
diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java
index c70ba0d5d..5fce68f9b 100644
--- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java
+++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java
@@ -6,6 +6,7 @@ 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 io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -17,7 +18,7 @@ public class GroovyAutoComplete implements AutoCompleteProvider {
private static final List OTHERS = createOtherCompletions();
@Override
- public List> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
+ public List> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
List> result = new ArrayList<>();
for(String artifact : ANDROIDX_ARTIFACTS) {
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java
index ff330bb8a..1bbe9c2f9 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java
@@ -15,22 +15,23 @@ import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
+import com.itsaky.androidide.utils.Logger;
+import java.util.stream.Collectors;
+import com.itsaky.androidide.services.compiler.model.CompilerDiagnosticImpl;
public class JavaAutoComplete implements AutoCompleteProvider {
-
- private CodeEditor editor;
private JavaLanguageAnalyzer analyzer;
- public JavaAutoComplete(CodeEditor editor, JavaLanguageAnalyzer analyzer) {
- this.editor = editor;
+ public JavaAutoComplete(JavaLanguageAnalyzer analyzer) {
this.analyzer = analyzer;
}
@Override
- public List> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
+ public List> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
Pair, List>> pair = StudioApp.getInstance().getCompletionProvider().getSuggestions(editor);
final ArrayList suggestions = pair.first;
- final List> diags = pair.second;
+// final List> diags = pair.second;
+// editor.setDiagnostics(diags.stream().map(CompilerDiagnosticImpl::fromDiagnostic).collect(Collectors.toList()));
if(suggestions != null) {
List> result = new ArrayList<>();
for(SuggestItem item : suggestions) {
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
index cef9260b5..0f9ff2ad5 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
@@ -21,14 +21,12 @@ public class JavaLanguage implements EditorLanguage
private JavaLanguageAnalyzer analyzer;
private JavaAutoComplete complete;
private AndroidProject project;
- private CodeEditor editor;
- public JavaLanguage(CodeEditor editor, AndroidProject project)
+ public JavaLanguage(AndroidProject project)
{
this.analyzer = new JavaLanguageAnalyzer();
- this.complete = new JavaAutoComplete(editor, analyzer);
+ this.complete = new JavaAutoComplete(analyzer);
this.project = project;
- this.editor = editor;
}
@Override
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/parser/expression/CompleteThisKeyword.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/expression/CompleteThisKeyword.java
index 95a760d7d..4accb5aaa 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/parser/expression/CompleteThisKeyword.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/parser/expression/CompleteThisKeyword.java
@@ -49,7 +49,7 @@ public class CompleteThisKeyword extends JavaCompleteMatcherImpl {
MethodDescription desc = new MethodDescription(
method.getName().toString(),
returnType,
- method.getModifiers().flags,
+ (int) method.getModifiers().flags,
paramsStr);
setInfo(desc, editor, incomplete);
result.add(desc);
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/CompiledClassLoader.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/CompiledClassLoader.java
deleted file mode 100644
index 416fd807a..000000000
--- a/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/CompiledClassLoader.java
+++ /dev/null
@@ -1,55 +0,0 @@
-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 getCompiledClassesFromProject(@NonNull AndroidProject project) {
- ArrayList classes = new ArrayList<>();
- try {
- final List 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 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;
- }
- }
-}
diff --git a/editor/src/main/java/com/itsaky/androidide/language/java/parser/internal/IJavaDocCommentable.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/IJavaDocCommentable.java
similarity index 100%
rename from editor/src/main/java/com/itsaky/androidide/language/java/parser/internal/IJavaDocCommentable.java
rename to app/src/main/java/com/itsaky/androidide/language/java/parser/internal/IJavaDocCommentable.java
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java
index cc5120073..c9837f6d4 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java
@@ -1,26 +1,47 @@
package com.itsaky.androidide.language.java.parser.internal;
-import androidx.annotation.Nullable;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
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.language.java.parser.model.FieldDescription;
+import com.itsaky.androidide.language.java.parser.model.MethodDescription;
import com.itsaky.androidide.models.AndroidProject;
+import com.itsaky.androidide.models.ConstantsBridge;
+import com.itsaky.androidide.utils.Logger;
import com.sun.tools.javac.tree.JCTree;
+import dalvik.system.DexFile;
+import dalvik.system.PathClassLoader;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.classfmt.FieldInfo;
+import org.eclipse.jdt.internal.compiler.env.IBinaryField;
+import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
+import org.eclipse.jdt.internal.compiler.env.IBinaryType;
+import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
+import com.itsaky.androidide.utils.Either;
+import com.blankj.utilcode.util.ThrowableUtils;
+import com.itsaky.androidide.utils.Environment;
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;
@@ -65,11 +86,9 @@ public class JavaClassManager implements IClassManager {
public void loadFromProject(AndroidProject project) {
try {
- CompiledClassLoader classLoader = new CompiledClassLoader();
- ArrayList classes = classLoader.getCompiledClassesFromProject(project);
- for (Class clazz : classes) {
- getClassWrapper(clazz);
- }
+ try {
+ loadClasspaths(project);
+ } catch (Throwable e) {}
JavacParser parser = new JavacParser();
List javaSrcDirs = project.getSourcePaths();
for (String dir : javaSrcDirs) {
@@ -84,9 +103,7 @@ public class JavaClassManager implements IClassManager {
}
}
}
- } catch (Throwable e) {
- e.printStackTrace();
- }
+ } catch (Throwable e) {}
}
/**
@@ -249,5 +266,214 @@ public class JavaClassManager implements IClassManager {
}
return result;
}
+
+ private void loadClasspaths(AndroidProject project) throws IOException, ClassFormatException, ClassNotFoundException {
+ final List classpaths = project.getClassPaths();
+ if(!classpaths.contains(Environment.BOOTCLASSPATH))
+ classpaths.add(Environment.BOOTCLASSPATH.getAbsolutePath());
+ for(int i=0;i entries = zip.entries();
+
+ ZipEntry entry = null;
+ while(entries.hasMoreElements() && ((entry = entries.nextElement()) != null)) {
+ if(!entry.getName().endsWith(".class"))
+ continue;
+ try {
+ ClassFileReader reader = ClassFileReader.read(zip, entry.getName());
+ describe(zip, reader);
+ } catch (Throwable th) {}
+ }
+ }
+ }
+
+ private ClassDescription describe(ZipFile zip, IBinaryType type) throws IOException, ClassFormatException, ClassNotFoundException {
+ if(type == null)
+ return null;
+ final char[] name = type.getName();
+ final char[] superClassName = type.getSuperclassName();
+ if(name == null) return null;
+ final int mods = type.getModifiers();
+ final boolean isPremitive = false;
+ final boolean isAnnotation = false;
+ final boolean isEnum = false;
+ final boolean isMember = type.getEnclosingTypeName() != null;
+ final IBinaryField[] fields = type.getFields();
+ final IBinaryMethod[] methods = type.getMethods();
+ final char[][] interfaces = type.getInterfaceNames();
+ ClassDescription clazz = new ClassDescription(bytecodeToSourceType(new String(name)), mods, isPremitive, isAnnotation, isEnum, isMember);
+
+ int index = 0;
+ while(fields != null && index < fields.length) {
+ IBinaryField f = fields[index];
+ if(f == null) continue;
+ FieldDescription field = describe(zip, f);
+ if(field == null) continue;
+ clazz.addField(field);
+ ++index;
+ }
+
+ index = 0;
+ while(methods != null && index < methods.length) {
+ IBinaryMethod m = methods[index];
+ if(m == null) continue;
+ Either result = describe(zip, m);
+ if(result == null) continue;
+ if(result.isLeft())
+ clazz.addMethod(result.getLeft());
+ else clazz.addConstructor(result.getRight());
+ ++index;
+ }
+
+ // TODO: Add interfaces
+
+ clazz.setSuperclass(superClassName == null ? ClassDescription.ofObject() : findType(zip, new String(superClassName)));
+
+ update(clazz);
+ return clazz;
+ }
+
+ private Either describe(ZipFile zip, IBinaryMethod m) throws IOException, IllegalArgumentException, ClassFormatException, ClassNotFoundException {
+ if(zip == null || m == null) return null;
+ final char[] descriptor = m.getMethodDescriptor();
+ final char[] signature = m.getGenericSignature();
+ final char[] name = m.getSelector();
+ final int mods = m.getModifiers();
+ if(name == null || descriptor == null) return null;
+ IClass returns = findType(zip, Signature.getReturnType(new String(descriptor)));
+ String[] paramTypes = Signature.getParameterTypes(new String(descriptor));
+ ArrayList params = new ArrayList<>();
+ for(String type : paramTypes) {
+ params.add(bytecodeToSourceType(type));
+ }
+ return m.isConstructor() ? Either.forRight(new ConstructorDescription(new String(name).replace("/", "."), params)) : Either.forLeft(new MethodDescription(new String(name).replace("/", "."), returns, mods, params));
+ }
+
+ private FieldDescription describe(ZipFile zip, IBinaryField field) throws IOException, ClassFormatException, ClassNotFoundException {
+ if(field == null)
+ return null;
+ final char[] n = field.getName();
+ final char[] t = field.getTypeName();
+ if(n == null || t == null) return null;
+ final String name = new String(n);
+ final String type = new String(t);
+ final int mods = field.getModifiers();
+ final String value = field instanceof FieldInfo && ((FieldInfo) field).hasConstant() ? field.getConstant().stringValue() : "";
+ IClass typeClass = findType(zip, type);
+ if(typeClass == null)
+ typeClass = new ClassDescription(Object.class);
+ FieldDescription f = new FieldDescription(mods, typeClass, name.replace("/", "."), value);
+ return f;
+ }
+
+ private IClass findType(ZipFile zip, String name) throws IOException, ClassFormatException, ClassNotFoundException {
+ if(zip == null || name == null)
+ return null;
+ final int arrayCount = Signature.getArrayCount(name);
+ if(arrayCount == 0) {
+ if(name.length() == 1) {
+ // A primitive type
+ final Class load = primitiveType(name.charAt(0));
+ if(load != null) {
+ ClassDescription desc = new ClassDescription(load);
+ desc.initMembers(load);
+ update(desc);
+ return desc;
+ }
+ }
+ IClass found = getParsedClass(bytecodeToSourceType(name));
+ if(found != null) {
+ return found;
+ } else {
+ ClassFileReader reader = ClassFileReader.read(zip, name);
+ if(reader == null)
+ return null;
+ else {
+ // This class will be updated in describe() method
+ return describe(zip, reader);
+ }
+ }
+ } else {
+ if(Signature.getElementType(name).length() == 1) {
+ // This type may be an array of primitive
+ Class load = Class.forName(name);
+ if(load != null) {
+ ClassDescription desc = new ClassDescription(load);
+ desc.initMembers(load);
+ update(desc);
+ return desc;
+ }
+ }
+ // TODO: Load array type classes of objects
+ // Return without loading members of this class
+ return new ClassDescription(Void.TYPE);
+ }
+ }
+
+ private String bytecodeToSourceType(String type) {
+ type = Signature.getElementType(type);
+ if(type.startsWith("L") && type.endsWith(";")) {
+ type = type.substring(1, type.lastIndexOf(";"));
+ }
+ return type.replace("/", ".");
+ }
+
+ public Class primitiveType(char typeId) {
+ switch (typeId) {
+ case 'I' : return int.class;
+ case 'B' : return byte.class;
+ case 'S' : return short.class;
+ case 'C' : return char.class;
+ case 'F' : return float.class;
+ case 'D' : return double.class;
+ case 'Z' : return boolean.class;
+ case 'J' : return long.class;
+ default : return null;
+ }
+ }
+
+
+ /**
+ * Kept as a backup if loading jar files fails for some reasons
+ */
+ private ArrayList loadFromDexes(AndroidProject project) {
+ ArrayList classes = new ArrayList<>();
+ try {
+ final List 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 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;
+ }
+ }
}
diff --git a/editor/src/main/java/com/itsaky/androidide/language/java/parser/internal/SuggestItem.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/SuggestItem.java
similarity index 100%
rename from editor/src/main/java/com/itsaky/androidide/language/java/parser/internal/SuggestItem.java
rename to app/src/main/java/com/itsaky/androidide/language/java/parser/internal/SuggestItem.java
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ClassDescription.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ClassDescription.java
index c5fc18eb3..0d9c3b8ef 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ClassDescription.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ClassDescription.java
@@ -56,6 +56,12 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
this.isMemberClass = isMember;
this.mEum = isEnum;
}
+
+ public static ClassDescription ofObject() {
+ ClassDescription desc = new ClassDescription(Object.class);
+ desc.initMembers(Object.class);
+ return desc;
+ }
@Override
public boolean isMemberClass() {
@@ -251,7 +257,6 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
for (Class anInterface : interfaces) {
IClass implement = JavaClassManager.getInstance().getClassWrapper(anInterface);
mImplements.add(implement);
-// initClassMembers(implement);
}
if (c.getSuperclass() != null) {
@@ -262,8 +267,6 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
mSuperClass = JavaClassManager.getInstance().getParsedClass(Object.class.getName());
}
}
-
-// initClassMembers(mSuperClass);
for (Constructor constructor : c.getConstructors()) {
if (Modifier.isPublic(constructor.getModifiers())) {
@@ -288,26 +291,5 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
public void setSuperclass(IClass superclass) {
this.mSuperClass = superclass;
-// initClassMembers(mSuperClass);
}
-
-// private void initClassMembers(IClass clazz) {
-// if(clazz.getFields() != null) {
-// for(IField f : clazz.getFields()) {
-// addField(f);
-// }
-// }
-//
-// if(clazz.getMethods() != null) {
-// for(IMethod method : clazz.getMethods()) {
-// addMethod(method);
-// }
-// }
-//
-// if(clazz.getConstructors() != null) {
-// for(ConstructorDescription desc : clazz.getConstructors()) {
-// addConstructor(desc);
-// }
-// }
-// }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ConstructorDescription.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ConstructorDescription.java
index 1deccde11..784f6c106 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ConstructorDescription.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/parser/model/ConstructorDescription.java
@@ -13,21 +13,26 @@ import io.github.rosemoe.editor.widget.CodeEditor;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
public class ConstructorDescription extends JavaSuggestItemImpl implements IJavaDocCommentable {
- private ArrayList mParameterTypes = new ArrayList<>();
+ private ArrayList mParameterTypes = new ArrayList<>();
private String mConstructorName;
public ConstructorDescription(Constructor constructor) {
mConstructorName = constructor.getName();
Class[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType : parameterTypes) {
- IClass type = JavaClassManager.getInstance().getClassWrapper(parameterType);
- mParameterTypes.add(type);
+ mParameterTypes.add(parameterType.getName());
}
}
public ConstructorDescription(String name, List paramTypes) {
+ mConstructorName = name;
+ mParameterTypes.addAll(paramTypes.stream().filter(c -> c != null).map(c -> c.getFullClassName()).collect(Collectors.toList()));
+ }
+
+ public ConstructorDescription(String name, ArrayList paramTypes) {
mConstructorName = name;
mParameterTypes.addAll(paramTypes);
}
@@ -95,21 +100,21 @@ public class ConstructorDescription extends JavaSuggestItemImpl implements IJava
return getSimpleName() + "()";
}
- private String paramsToString(@NonNull ArrayList parameterTypes) {
+ private String paramsToString(@NonNull ArrayList parameterTypes) {
StringBuilder result = new StringBuilder();
boolean firstTime = true;
- for (IClass parameterType : parameterTypes) {
+ for (String parameterType : parameterTypes) {
if (firstTime) {
firstTime = false;
} else {
result.append(",");
}
- result.append(parameterType.getSimpleName());
+ result.append(parameterType);
}
return result.toString();
}
- public ArrayList getParameterTypes() {
+ public ArrayList getParameterTypes() {
return mParameterTypes;
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/parser/model/MethodDescription.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/model/MethodDescription.java
index 2080ca446..e7658e479 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/parser/model/MethodDescription.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/parser/model/MethodDescription.java
@@ -28,10 +28,10 @@ public class MethodDescription extends JavaSuggestItemImpl implements Member, Su
private IClass mReturnType;
public MethodDescription(String name, IClass returnType,
- long modifiers, ArrayList parameterTypes) {
+ int modifiers, ArrayList parameterTypes) {
mMethodName = name;
mReturnType = returnType;
- mModifiers = (int) modifiers;
+ mModifiers = modifiers;
mParameterTypes = parameterTypes;
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/provider/JavaCompletionProvider.java b/app/src/main/java/com/itsaky/androidide/language/java/provider/JavaCompletionProvider.java
index 330ed5bd8..e52afeb8c 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/provider/JavaCompletionProvider.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/provider/JavaCompletionProvider.java
@@ -44,7 +44,7 @@ public class JavaCompletionProvider {
this.mPackageManager = new PackageManager();
this.mJavaParser = new JavacParser();
- addAutoComplete();
+ addMatchers();
}
public boolean loadProject(AndroidProject project) {
@@ -57,7 +57,7 @@ public class JavaCompletionProvider {
return initiated;
}
- private void addAutoComplete() {
+ private void addMatchers() {
mCompletors.add(new CompleteExpression(mClassLoader));
mCompletors.add(new CompleteNewKeyword(mClassLoader));
mCompletors.add(new CompletePackage(mPackageManager));
diff --git a/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java b/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java
index cb8268950..2f5a19cd7 100644
--- a/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java
+++ b/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java
@@ -10,6 +10,7 @@ import io.github.rosemoe.editor.interfaces.NewlineHandler;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.text.TextAnalyzer;
+import io.github.rosemoe.editor.widget.CodeEditor;
import io.github.rosemoe.editor.widget.SymbolPairMatch;
import java.util.ArrayList;
import java.util.List;
@@ -90,7 +91,7 @@ public class LogLanguageImpl implements EditorLanguage {
private static class LogCompletor implements AutoCompleteProvider {
@Override
- public List> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
+ public List> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
return new ArrayList>();
}
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/xml/XMLAutoComplete.java b/app/src/main/java/com/itsaky/androidide/language/xml/XMLAutoComplete.java
index 541bda0fa..d8131c66b 100644
--- a/app/src/main/java/com/itsaky/androidide/language/xml/XMLAutoComplete.java
+++ b/app/src/main/java/com/itsaky/androidide/language/xml/XMLAutoComplete.java
@@ -18,8 +18,6 @@ import com.itsaky.androidide.utils.EitherOfResult;
public class XMLAutoComplete implements AutoCompleteProvider {
- private CodeEditor editor;
-
private Comparator RESULTS_SORTER = new Comparator(){
@Override
@@ -27,13 +25,9 @@ public class XMLAutoComplete implements AutoCompleteProvider {
return ((CompletionListItem) p1).getSortText().compareTo(((CompletionListItem) p2).getSortText());
}
};
-
- public XMLAutoComplete(CodeEditor editor) {
- this.editor = editor;
- }
@Override
- public List> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
+ public List> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
// We do not store the completion service here in this class
// This is because, if we modify the instance of the service in app,
// it'll be updated here
diff --git a/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java b/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java
index ca753a41d..e4aad94d9 100644
--- a/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java
@@ -19,8 +19,8 @@ public class XMLLanguage implements EditorLanguage {
private XMLAutoComplete completer;
private NewlineHandler[] newlineHandlers = new NewlineHandler[] {};
- public XMLLanguage(CodeEditor editor) {
- this.completer = new XMLAutoComplete(editor);
+ public XMLLanguage() {
+ this.completer = new XMLAutoComplete();
this.analyzer = new XMLAnalyzer();
this.newlineHandlers = new NewlineHandler[]{new IndentHandler()};
}
diff --git a/editor/src/main/java/com/itsaky/androidide/models/CompletionEdit.java b/app/src/main/java/com/itsaky/androidide/models/CompletionEdit.java
similarity index 100%
rename from editor/src/main/java/com/itsaky/androidide/models/CompletionEdit.java
rename to app/src/main/java/com/itsaky/androidide/models/CompletionEdit.java
diff --git a/editor/src/main/java/com/itsaky/androidide/models/CompletionListItem.java b/app/src/main/java/com/itsaky/androidide/models/CompletionListItem.java
similarity index 100%
rename from editor/src/main/java/com/itsaky/androidide/models/CompletionListItem.java
rename to app/src/main/java/com/itsaky/androidide/models/CompletionListItem.java
diff --git a/app/src/main/java/com/itsaky/androidide/models/DiagnosticGroup.java b/app/src/main/java/com/itsaky/androidide/models/DiagnosticGroup.java
new file mode 100644
index 000000000..89acdd8dc
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/models/DiagnosticGroup.java
@@ -0,0 +1,18 @@
+package com.itsaky.androidide.models;
+import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+import java.io.File;
+import java.util.List;
+
+public class DiagnosticGroup {
+ public int icon;
+ public String text;
+ public File file;
+ public List diagnostics;
+
+ public DiagnosticGroup(int icon, File file, List diagnostics) {
+ this.icon = icon;
+ this.file = file;
+ this.text = file.getName();
+ this.diagnostics = diagnostics;
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/receivers/LogReceiver.java b/app/src/main/java/com/itsaky/androidide/receivers/LogReceiver.java
index 4f8bbe6d9..134b1bdd7 100644
--- a/app/src/main/java/com/itsaky/androidide/receivers/LogReceiver.java
+++ b/app/src/main/java/com/itsaky/androidide/receivers/LogReceiver.java
@@ -4,9 +4,9 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.itsaky.androidide.models.LogLine;
+import com.itsaky.androidide.utils.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import com.elvishew.xlog.XLog;
public class LogReceiver extends BroadcastReceiver {
@@ -37,7 +37,7 @@ public class LogReceiver extends BroadcastReceiver {
Matcher m = LOG_HEAD_PATTERN.matcher(line);
LogLine log = null;
final boolean find = line != null && line.trim().length() > 2 /*if length() == 2 then only [] is available*/ && m.find();
- XLog.i("line -> " + line + "\nlogTag find() -> " + find);
+ Logger.instance().i("line -> " + line + "\nlogTag find() -> " + find);
if(find) {
String date = "";
String time = "";
@@ -84,9 +84,9 @@ public class LogReceiver extends BroadcastReceiver {
log = new LogLine("", "", "", "", "", "", line);
}
- XLog.i("log = " + log + "\nlogListener = " + listener);
+ Logger.instance().i("log = " + log + "\nlogListener = " + listener);
if(log != null && listener != null) {
- XLog.i("listener.appendLogLine(...) log ->\n" + log);
+ Logger.instance().i("listener.appendLogLine(...) log ->\n" + log);
listener.appendLogLine(log);
logTag = null;
}
diff --git a/app/src/main/java/com/itsaky/androidide/services/IDEService.java b/app/src/main/java/com/itsaky/androidide/services/IDEService.java
index 2d4fb802a..393cffd4c 100644
--- a/app/src/main/java/com/itsaky/androidide/services/IDEService.java
+++ b/app/src/main/java/com/itsaky/androidide/services/IDEService.java
@@ -207,6 +207,8 @@ public class IDEService extends Service implements ShellServer.Callback {
@Override
public void onDestroy() {
super.onDestroy();
+
+ // Delete the tmp directory. This directory is not needed anymore...
FileUtils.delete(Environment.TMP_DIR);
isRunning = false;
}
diff --git a/app/src/main/java/com/itsaky/androidide/services/Jar2DexService.java b/app/src/main/java/com/itsaky/androidide/services/Jar2DexService.java
index 1073cd147..7e6643bf1 100644
--- a/app/src/main/java/com/itsaky/androidide/services/Jar2DexService.java
+++ b/app/src/main/java/com/itsaky/androidide/services/Jar2DexService.java
@@ -12,6 +12,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
+/**
+ * A utility class to convert JAR files to DEX
+ *
+ * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
+ */
+@Deprecated
public class Jar2DexService implements ShellServer.Callback {
private final List mClasspaths;
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerProgress.java b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerProgress.java
new file mode 100644
index 000000000..99f9a8e92
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerProgress.java
@@ -0,0 +1,33 @@
+package com.itsaky.androidide.services.compiler;
+
+import org.eclipse.jdt.core.compiler.CompilationProgress;
+
+public class CompilerProgress extends CompilationProgress {
+
+ private boolean canceled = false;
+
+ public void setCanceled(boolean canceled) {
+ this.canceled = canceled;
+ }
+
+ @Override
+ public void begin(int p1) {
+ }
+
+ @Override
+ public void done() {
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return canceled;
+ }
+
+ @Override
+ public void setTaskName(String p1) {
+ }
+
+ @Override
+ public void worked(int p1, int p2) {
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerRequestor.java b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerRequestor.java
new file mode 100644
index 000000000..f644b19b0
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerRequestor.java
@@ -0,0 +1,101 @@
+package com.itsaky.androidide.services.compiler;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileObject;
+import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
+import org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl;
+import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
+import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject;
+
+public class CompilerRequestor implements ICompilerRequestor {
+
+ private DiagnosticListener super JavaFileObject> listener;
+
+ public CompilerRequestor(DiagnosticListener super JavaFileObject> listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void acceptResult(CompilationResult p1) {
+ if (p1 != null && p1.hasProblems()) {
+ for (CategorizedProblem problem : p1.getAllProblems()) {
+ report(problem);
+ }
+ }
+ }
+
+ private void report(final CategorizedProblem problem) {
+ this.listener.report(new Diagnostic() {
+ @Override
+ public String getCode() {
+ return null;
+ }
+ @Override
+ public long getColumnNumber() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).column;
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public long getEndPosition() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).getSourceEnd();
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public Kind getKind() {
+ if (problem.isError()) {
+ return Diagnostic.Kind.ERROR;
+ }
+ if (problem.isWarning()) {
+ return Diagnostic.Kind.WARNING;
+ } else if (problem instanceof DefaultProblem && ((DefaultProblem) problem).isInfo()) {
+ return Diagnostic.Kind.NOTE;
+ }
+ return Diagnostic.Kind.OTHER;
+ }
+ @Override
+ public long getLineNumber() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).getSourceLineNumber();
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public String getMessage(Locale locale) {
+ return problem.getMessage();
+ }
+ @Override
+ public long getPosition() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).getSourceStart();
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public JavaFileObject getSource() {
+ if (problem instanceof DefaultProblem) {
+ File f = new File(new String(problem.getOriginatingFileName()));
+ if (f.exists()) {
+ return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
+ }
+ return null;
+ }
+ return null;
+ }
+ @Override
+ public long getStartPosition() {
+ return getPosition();
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerWrapper.java b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerWrapper.java
new file mode 100644
index 000000000..fcdf35c50
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerWrapper.java
@@ -0,0 +1,138 @@
+package com.itsaky.androidide.services.compiler;
+
+import java.io.PrintWriter;
+import org.eclipse.jdt.core.compiler.CompilationProgress;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.Compiler;
+import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
+import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
+import org.eclipse.jdt.internal.compiler.IProblemFactory;
+import org.eclipse.jdt.internal.compiler.ReadManager;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ImportReference;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException;
+import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
+import org.eclipse.jdt.internal.compiler.util.Messages;
+
+public class CompilerWrapper extends Compiler {
+
+ public CompilerWrapper(INameEnvironment env,
+ IErrorHandlingPolicy errorHandlingPolicy,
+ CompilerOptions options,
+ ICompilerRequestor compilerRequestor,
+ IProblemFactory problemFactory,
+ PrintWriter out,
+ CompilationProgress progress) {
+ super(env, errorHandlingPolicy, options, compilerRequestor, problemFactory, out, progress);
+ this.useSingleThread = true;
+ }
+
+ public CompilerWrapper setProgress(CompilationProgress progress) {
+ this.progress = progress;
+ return this;
+ }
+
+ @Override
+ public void compile(ICompilationUnit[] sourceUnits) {
+ compileInternal(sourceUnits, false);
+ }
+
+ private void compileInternal(ICompilationUnit[] sourceUnits, boolean lastRound) {
+ this.stats.startTime = System.currentTimeMillis();
+ try {
+ reportProgress(Messages.compilation_beginningToCompile);
+ if (this.annotationProcessorManager == null) {
+ beginToCompile(sourceUnits);
+ } else {
+ ICompilationUnit[] originalUnits = sourceUnits.clone(); // remember source units in case a source type collision occurs
+ try {
+ beginToCompile(sourceUnits);
+ if (!lastRound) processAnnotations();
+ if (!this.options.generateClassFiles) return;
+ } catch (SourceTypeCollisionException e) {
+ backupAptProblems();
+ reset();
+ int originalLength = originalUnits.length;
+ int newProcessedLength = e.newAnnotationProcessorUnits.length;
+ ICompilationUnit[] combinedUnits = new ICompilationUnit[originalLength + newProcessedLength];
+ System.arraycopy(originalUnits, 0, combinedUnits, 0, originalLength);
+ System.arraycopy(e.newAnnotationProcessorUnits, 0, combinedUnits, originalLength, newProcessedLength);
+ this.annotationProcessorStartIndex = originalLength;
+ compileInternal(combinedUnits, e.isLastRound);
+ return;
+ }
+ }
+ restoreAptProblems();
+ processCompiledUnits(0, lastRound);
+ } catch (AbortCompilation e) {
+ this.handleInternalException(e, null);
+ } catch (Throwable th) {
+ AbortCompilation a = new AbortCompilation();
+ a.isSilent = true;
+ handleInternalException(a, null);
+ }
+ if (this.options.verbose) {
+ if (this.totalUnits > 1) {
+ this.out.println(Messages.bind(Messages.compilation_units, String.valueOf(this.totalUnits)));
+ } else {
+ this.out.println(Messages.bind(Messages.compilation_unit, String.valueOf(this.totalUnits)));
+ }
+ }
+ }
+
+ @Override
+ protected void internalBeginToCompile(ICompilationUnit[] sourceUnits, int maxUnits) {
+ if (!this.useSingleThread && maxUnits >= ReadManager.THRESHOLD)
+ this.parser.readManager = new ReadManager(sourceUnits, maxUnits);
+
+ for (int i = 0; i < maxUnits; i++) {
+ CompilationResult unitResult = null;
+ try {
+ if (this.options.verbose) {
+ this.out.println(
+ Messages.bind(Messages.compilation_request,
+ new String[] {
+ String.valueOf(i + 1),
+ String.valueOf(maxUnits),
+ new String(sourceUnits[i].getFileName())
+ }));
+ }
+ CompilationUnitDeclaration parsedUnit;
+ unitResult = new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit);
+ long parseStart = System.currentTimeMillis();
+ if (this.totalUnits < this.parseThreshold) {
+ parsedUnit = this.parser.parse(sourceUnits[i], unitResult);
+ } else {
+ parsedUnit = this.parser.dietParse(sourceUnits[i], unitResult);
+ }
+ long resolveStart = System.currentTimeMillis();
+ this.stats.parseTime += resolveStart - parseStart;
+ this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
+ this.stats.resolveTime += System.currentTimeMillis() - resolveStart;
+ addCompilationUnit(sourceUnits[i], parsedUnit);
+ ImportReference currentPackage = parsedUnit.currentPackage;
+ if (currentPackage != null) {
+ unitResult.recordPackageName(currentPackage.tokens);
+ }
+ if (this.parser.readManager != null) {
+ this.parser.readManager.shutdown();
+ this.parser.readManager = null;
+ }
+ this.lookupEnvironment.completeTypeBindings();
+ } catch (AbortCompilation a) {
+ if (a.compilationResult == null)
+ a.compilationResult = unitResult;
+ throw a;
+ } catch (Throwable th) {
+ AbortCompilation a = new AbortCompilation();
+ a.isSilent = true;
+ throw a;
+ } finally {
+ sourceUnits[i] = null;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/JavaCompilerService.java b/app/src/main/java/com/itsaky/androidide/services/compiler/JavaCompilerService.java
index 1a965dbb7..e4318d42a 100644
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/JavaCompilerService.java
+++ b/app/src/main/java/com/itsaky/androidide/services/compiler/JavaCompilerService.java
@@ -2,13 +2,18 @@ package com.itsaky.androidide.services.compiler;
import android.text.TextUtils;
import com.blankj.utilcode.util.FileUtils;
+import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.interfaces.CompileListener;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
import com.itsaky.androidide.services.compiler.model.CompilerDiagnosticImpl;
+import com.itsaky.androidide.services.compiler.request.CompilationRequest;
+import com.itsaky.androidide.tools.SourceJavaFileObject;
import com.itsaky.androidide.utils.Environment;
+import com.itsaky.androidide.utils.Logger;
import java.io.File;
import java.io.FileFilter;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -18,20 +23,41 @@ import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
+import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
+import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
+import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
+import org.eclipse.jdt.internal.compiler.IProblemFactory;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
-import com.elvishew.xlog.XLog;
+import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
public class JavaCompilerService implements DiagnosticListener {
- private AndroidProject project;
-
- private CompileListener listener;
- private EclipseCompiler compiler;
- private EclipseFileManager fileManager;
-
- private List options;
- private final Map> diagnostics = new HashMap<>();
+ private AndroidProject mProject;
+
+ private EclipseCompiler mCompiler;
+ private CompilerWrapper mSingleFileCompiler;
+ private EclipseFileManager mFileManager;
+
+ private FileSystem sf_nameEnvironment;
+ private CompilerOptions sf_options;
+ private IProblemFactory sf_problemFactory;
+ private ICompilerRequestor sf_compilerRequest;
+ private CompilerProgress sf_progress;
+ private IErrorHandlingPolicy sf_policy;
+ private PrintWriter sf_outWriter;
+
+ private List mOptions;
+ private List mSources;
+
+ private static final Map> mDiagnostics = new HashMap<>();
private FileFilter JAVA_FILTER = new FileFilter(){
@@ -42,90 +68,344 @@ public class JavaCompilerService implements DiagnosticListener {
};
public JavaCompilerService(AndroidProject project) {
- this.project = project;
-
- getCompiler();
+ this.mProject = project;
+ this.createOptions();
}
-
- public JavaCompilerService setListener(CompileListener listener) {
- this.listener = listener;
- return this;
+
+ private void createOptions() {
+ this.mOptions = new ArrayList<>();
+
+ mOptions.add("-1.8");
+ mOptions.add("-d");
+ mOptions.add(Environment.TMP_DIR.getAbsolutePath());
+ mOptions.add("-proc:none");
+ mOptions.add("-warn:all");
+ mOptions.add("-warn:-nls");
+ mOptions.add("-g");
+ mOptions.add("-bootclasspath");
+ mOptions.add(Environment.BOOTCLASSPATH.getAbsolutePath());
+ mOptions.add("-cp");
+ mOptions.add(TextUtils.join(File.pathSeparator, mProject.getClassPaths()));
+ }
+
+ private EclipseCompiler getCompiler() {
+ return getCompiler(false);
}
-
- private EclipseCompiler getCompiler() {
- if(compiler == null) {
- this.compiler = new EclipseCompiler();
- this.fileManager = (EclipseFileManager) compiler.getStandardFileManager(this, Locale.US, null);
- this.options = new ArrayList<>();
-
- options.add("-1.8");
- options.add("-d");
- options.add("none");
- options.add("-proc:none");
- options.add("-warn:all");
- options.add("-g");
- options.add("-bootclasspath");
- options.add(Environment.BOOTCLASSPATH.getAbsolutePath());
- options.add("-cp");
- options.add(TextUtils.join(File.pathSeparator, project.getClassPaths()));
- }
-
- return compiler;
+
+ private EclipseCompiler getCompiler(boolean forceCreate) {
+ if(mCompiler == null || forceCreate) {
+ this.mCompiler = new EclipseCompiler();
+ this.mFileManager = (EclipseFileManager) mCompiler.getStandardFileManager(this, Locale.US, null);
+ }
+ return mCompiler;
}
-
- public boolean compileSingle(File file) {
- List ops = new ArrayList(options);
- ops.add("-sourcepath");
- ops.add(TextUtils.join(File.pathSeparator, project.getSourcePaths()));
-
- JavaCompiler.CompilationTask task = getCompiler().getTask(null, fileManager, this, ops, null, fileManager.getJavaFileObjects(file));
- diagnostics.clear();
- boolean success = task.call();
- if(this.listener != null) {
- this.listener.onCompilationResult(success, diagnostics);
- }
+
+ private EclipseFileManager getFileManager() {
+ if(mFileManager == null)
+ getCompiler(true);
+
+ return mFileManager;
+ }
+
+ private CompilerWrapper getSingleFileCompiler() {
+ if(this.sf_nameEnvironment == null)
+ this.sf_nameEnvironment = new FileSystem(createClasspaths(mProject.getClassPaths()), null, null);
+
+ if(this.sf_compilerRequest == null)
+ this.sf_compilerRequest = new CompilerRequestor(this);
+
+ if(this.sf_problemFactory == null)
+ this.sf_problemFactory = getProblemFactory();
+
+ if(this.sf_options == null) {
+ this.sf_options = new CompilerOptions();
+ putOptions(sf_options);
+ }
+
+ if(sf_policy == null)
+ this.sf_policy = DefaultErrorHandlingPolicies.exitAfterAllProblems();
+
+ if(sf_outWriter == null)
+ sf_outWriter = new PrintWriter(System.out);
+
+ if(mSingleFileCompiler == null) {
+ mSingleFileCompiler = new CompilerWrapper(
+ sf_nameEnvironment,
+ sf_policy,
+ sf_options,
+ sf_compilerRequest,
+ sf_problemFactory,
+ sf_outWriter,
+ sf_progress = new CompilerProgress()
+ );
+ }
+ return mSingleFileCompiler;
+ }
+
+ private void putOptions(CompilerOptions options) {
+ final long languageLevel = ClassFileConstants.JDK1_8;
+ options.sourceLevel = options.targetJDK = options.complianceLevel = languageLevel;
+ options.processAnnotations = false;
+ Map map = options.getMap();
+ for(String warn : CompilerOptions.warningTokens) {
+ map.put(warn, CompilerOptions.ENABLED);
+ }
+ map.put(CompilerOptions.OPTION_ReportNonExternalizedStringLiteral, CompilerOptions.DISABLED);
+ map.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE);
+ map.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.DO_NOT_GENERATE);
+ map.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.DO_NOT_GENERATE);
+ options.set(map);
+ }
+
+ private String[] createClasspaths(List classpaths) {
+ if (classpaths == null) {
+ classpaths = new ArrayList<>();
+ }
+
+ if(!classpaths.contains(Environment.BOOTCLASSPATH.getAbsolutePath())) {
+ classpaths.add(Environment.BOOTCLASSPATH.getAbsolutePath());
+ }
+
+ String[] result = new String[classpaths.size()];
+ return classpaths.toArray(result);
+ }
+
+ private CompilationRequest createCompilationRequestForAll(CompileListener listener) {
+ if(mSources == null || mSources.size() <= 0) {
+ loadSources();
+ }
+ return CompilationRequest.Builder
+ .newInstance()
+ .withCallback(listener)
+ .withFiles(mSources)
+ .build();
+ }
+
+ private void notifyCompiled(CompileListener listener, Map> diags) {
+ ThreadUtils.runOnUiThread(() -> {
+ listener.onCompilationResult(true, diags);
+ });
+ }
+
+ protected void cancelCompilation() {
+ getCompiler().setCompilationCancelled(true);
+ if(sf_progress != null) {
+ sf_progress.setCanceled(true);
+ }
+ }
+
+ public List getClasspaths() {
+ return mProject.getClassPaths();
+ }
+
+ public void loadSources() {
+ mSources = new ArrayList();
+ for(String path : mProject.getSourcePaths()) {
+ if(path == null || path.trim().length() <= 0) continue;
+ File p = new File(path);
+ if(!p.exists() || !p.isDirectory()) continue;
+ mSources.addAll(FileUtils.listFilesInDirWithFilter(p, JAVA_FILTER, true));
+ }
+ }
+
+ public void compileAllAsync(CompileListener listener) {
+ new Thread(() -> {
+ compile(createCompilationRequestForAll(listener));
+ }).start();
+ }
+
+ public void compileAsync(CompilationRequest request) {
+ new Thread(() -> {
+ compile(request);
+ }).start();
+ }
+
+ public void compileSingleAsync(SourceJavaFileObject source, CompileListener listener) {
+ new Thread(() -> {
+ compileSingle(source, listener);
+ }).start();
+ }
+
+ public boolean compile(final CompilationRequest request) {
+ this.mDiagnostics.clear();
+ final List fileObjects = (ArrayList) getFileManager().getJavaFileObjectsFromFiles(request.files());
+ JavaCompiler.CompilationTask task = getCompiler()
+ .setCompilationCancelled()
+ .newProgress()
+ .getTask(null, getFileManager(), this, mOptions, null, fileObjects);
+ boolean success = task.call();
+ notifyCompiled(request.callback(), mDiagnostics);
return success;
- }
-
- public boolean compileAll() {
- List files = new ArrayList();
- for(String path : project.getSourcePaths()) {
- if(path == null || path.trim().length() <= 0) continue;
- File p = new File(path);
- if(!p.exists() || !p.isDirectory()) continue;
- files.addAll(FileUtils.listFilesInDirWithFilter(p, JAVA_FILTER, true));
- }
- return compile(files);
- }
-
- public boolean compile(List files) {
- JavaCompiler.CompilationTask task = getCompiler().getTask(null, fileManager, this, options, null, fileManager.getJavaFileObjectsFromFiles(files));
- diagnostics.clear();
- final long s = System.nanoTime();
- boolean success = task.call();
- XLog.i("[JavaCompiler]"
- + "\ncompiled: " + files.size() + " files"
- + "\ntimeElapsed: " + (System.nanoTime() - s) + "ns");
- return success;
- }
-
- public Map> getDiagnostics() {
- return diagnostics;
- }
+ }
+
+ public void compileSingle(final SourceJavaFileObject source, final CompileListener listener) {
+ mDiagnostics.clear();
+ cancelCompilation();
+ if(mDiagnostics != null) mDiagnostics.clear();
+ getSingleFileCompiler()
+ .setProgress(this.sf_progress = new CompilerProgress())
+ .compile(new CompilationUnit[]{source.asUnit()});
+ notifyCompiled(listener, mDiagnostics);
+ }
@Override
public void report(Diagnostic extends JavaFileObject> diagnostic) {
CompilerDiagnostic d = CompilerDiagnosticImpl.fromDiagnostic(diagnostic);
- if(diagnostics.containsKey(d.file())) {
- List diags = diagnostics.get(d.file());
+ if(mDiagnostics.containsKey(d.file())) {
+ List diags = mDiagnostics.get(d.file());
if(diags == null)
diags = new ArrayList<>();
diags.add(d);
- diagnostics.put(d.file(), diags);
+ mDiagnostics.put(d.file(), diags);
} else {
List diags = new ArrayList<>();
diags.add(d);
- diagnostics.put(d.file(), diags);
+ mDiagnostics.put(d.file(), diags);
}
}
+
+ private IProblemFactory getProblemFactory() {
+ return new DefaultProblemFactory() {
+ @Override
+ public CategorizedProblem createProblem(
+ final char[] originatingFileName,
+ final int problemId,
+ final String[] problemArguments,
+ final String[] messageArguments,
+ final int severity,
+ final int startPosition,
+ final int endPosition,
+ final int lineNumber,
+ final int columnNumber) {
+
+ report(new Diagnostic() {
+ @Override
+ public String getCode() {
+ return Integer.toString(problemId);
+ }
+ @Override
+ public long getColumnNumber() {
+ return columnNumber;
+ }
+ @Override
+ public long getEndPosition() {
+ return endPosition;
+ }
+ @Override
+ public Kind getKind() {
+ if ((severity & ProblemSeverities.Error) != 0) {
+ return Diagnostic.Kind.ERROR;
+ }
+ if ((severity & ProblemSeverities.Optional) != 0) {
+ return Diagnostic.Kind.WARNING;
+ }
+ if ((severity & ProblemSeverities.Warning) != 0) {
+ return Diagnostic.Kind.MANDATORY_WARNING;
+ }
+ return Diagnostic.Kind.OTHER;
+ }
+ @Override
+ public long getLineNumber() {
+ return lineNumber;
+ }
+ @Override
+ public String getMessage(Locale locale) {
+ if (locale != null) {
+ setLocale(locale);
+ }
+ return getLocalizedMessage(problemId, problemArguments);
+ }
+ @Override
+ public long getPosition() {
+ return startPosition;
+ }
+ @Override
+ public JavaFileObject getSource() {
+ File f = new File(new String(originatingFileName));
+ if (f.exists()) {
+ return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
+ }
+ return null;
+ }
+ @Override
+ public long getStartPosition() {
+ return startPosition;
+ }
+ });
+ return super.createProblem(originatingFileName, problemId, problemArguments, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
+ }
+ @Override
+ public CategorizedProblem createProblem(
+ final char[] originatingFileName,
+ final int problemId,
+ final String[] problemArguments,
+ final int elaborationID,
+ final String[] messageArguments,
+ final int severity,
+ final int startPosition,
+ final int endPosition,
+ final int lineNumber,
+ final int columnNumber) {
+
+ report(new Diagnostic() {
+ @Override
+ public String getCode() {
+ return Integer.toString(problemId);
+ }
+ @Override
+ public long getColumnNumber() {
+ return columnNumber;
+ }
+ @Override
+ public long getEndPosition() {
+ return endPosition;
+ }
+ @Override
+ public Kind getKind() {
+ if ((severity & ProblemSeverities.Error) != 0) {
+ return Diagnostic.Kind.ERROR;
+ }
+ if ((severity & ProblemSeverities.Info) != 0) {
+ return Diagnostic.Kind.NOTE;
+ }
+ if ((severity & ProblemSeverities.Optional) != 0) {
+ return Diagnostic.Kind.WARNING;
+ }
+ if ((severity & ProblemSeverities.Warning) != 0) {
+ return Diagnostic.Kind.MANDATORY_WARNING;
+ }
+ return Diagnostic.Kind.OTHER;
+ }
+ @Override
+ public long getLineNumber() {
+ return lineNumber;
+ }
+ @Override
+ public String getMessage(Locale locale) {
+ if (locale != null) {
+ setLocale(locale);
+ }
+ return getLocalizedMessage(problemId, problemArguments);
+ }
+ @Override
+ public long getPosition() {
+ return startPosition;
+ }
+ @Override
+ public JavaFileObject getSource() {
+ File f = new File(new String(originatingFileName));
+ if (f.exists()) {
+ return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
+ }
+ return null;
+ }
+ @Override
+ public long getStartPosition() {
+ return startPosition;
+ }
+ });
+ return super.createProblem(originatingFileName, problemId, problemArguments, elaborationID, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
+ }
+ };
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnostic.java b/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnostic.java
index 1c7437096..5c3ae0004 100644
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnostic.java
+++ b/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnostic.java
@@ -4,11 +4,13 @@ import java.io.File;
import javax.tools.Diagnostic;
public interface CompilerDiagnostic {
- File file();
- long line();
- long column();
- long start();
- long end();
- String message();
- Diagnostic.Kind kind();
+ File file();
+ long line();
+ long column();
+ long start();
+ long end();
+ String message();
+ Diagnostic.Kind kind();
+
+ int color();
}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnosticImpl.java b/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnosticImpl.java
index e4f18610d..5ad0c28b1 100644
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnosticImpl.java
+++ b/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnosticImpl.java
@@ -15,16 +15,20 @@ public class CompilerDiagnosticImpl implements CompilerDiagnostic {
protected long end = 0;
protected Diagnostic.Kind kind;
+
+ public static final int COLOR_ERROR = 0xfff44336;
+ public static final int COLOR_WARNING = 0xffffb300;
+ public static final int COLOR_NOTE = 0xffffffff;
private CompilerDiagnosticImpl(){}
private CompilerDiagnosticImpl(Diagnostic extends JavaFileObject> diagnostic) {
this.file = new File(diagnostic.getSource().toUri());
this.message = diagnostic.getMessage(Locale.US);
- this.line = diagnostic.getLineNumber();
+ this.line = diagnostic.getLineNumber() - 1;
this.column = diagnostic.getColumnNumber();
this.start = diagnostic.getStartPosition();
- this.end = diagnostic.getEndPosition();
+ this.end = diagnostic.getEndPosition() + 1;
this.kind = diagnostic.getKind();
}
@@ -80,9 +84,34 @@ public class CompilerDiagnosticImpl implements CompilerDiagnostic {
public String message() {
return message;
}
+
+ @Override
+ public int color() {
+ if(kind() == Diagnostic.Kind.ERROR) {
+ return COLOR_ERROR;
+ } else if(kind() == Diagnostic.Kind.WARNING || kind() == Diagnostic.Kind.MANDATORY_WARNING) {
+ return COLOR_WARNING;
+ }
+ return COLOR_NOTE;
+ }
@Override
public String toString() {
- return "CompilerDiagnosticImpl(file=" + file() + ", line=" + line() + ", column=" + column() + ", start=" + start() + ", end=" + end() + ", message=" + message() + ")";
+ return "CompilerDiagnosticImpl(file=" + file() + ", line=" + line() + ", column=" + column() + ", start=" + start() + ", end=" + end() + ", message=" + message() + ", kind=" + kind().toString() + ")";
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof CompilerDiagnosticImpl) {
+ CompilerDiagnosticImpl that = (CompilerDiagnosticImpl) obj;
+ return this.file().equals(that.file()) &&
+ this.line() == that.line() &&
+ this.column() == that.column() &&
+ this.start() == that.start() &&
+ this.end() == that.end() &&
+ this.message().equals(that.message()) &&
+ this.kind() == that.kind();
+ }
+ return false;
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/request/CompilationRequest.java b/app/src/main/java/com/itsaky/androidide/services/compiler/request/CompilationRequest.java
new file mode 100644
index 000000000..8d6ccb737
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/services/compiler/request/CompilationRequest.java
@@ -0,0 +1,71 @@
+package com.itsaky.androidide.services.compiler.request;
+
+import com.itsaky.androidide.interfaces.CompileListener;
+import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+import com.itsaky.androidide.tools.SourceJavaFileObject;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public interface CompilationRequest {
+
+ List files();
+ CompileListener callback();
+
+ public static class Builder {
+
+ private List files;
+ private CompileListener callback;
+
+ private Builder(){
+ files = new ArrayList<>();
+ }
+
+ public static Builder newInstance() {
+ return new Builder();
+ }
+
+ public Builder withFiles(File... files) {
+ if(files == null) {
+ this.files = new ArrayList<>();
+ return this;
+ }
+ this.files = new ArrayList<>();
+ for(File file : files) {
+ this.files.add(file);
+ }
+ return this;
+ }
+
+ public Builder withFiles(List files) {
+ if(files == null) {
+ this.files = new ArrayList<>();
+ return this;
+ }
+ this.files = files;
+ return this;
+ }
+
+ public Builder withCallback(CompileListener callback) {
+ this.callback = callback;
+ return this;
+ }
+
+ public CompilationRequest build() {
+ return new CompilationRequest(){
+
+ @Override
+ public List files() {
+ return files;
+ }
+
+ @Override
+ public CompileListener callback() {
+ return callback;
+ }
+ };
+ }
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
index 828f99a08..9d7ee42ab 100644
--- a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
+++ b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
@@ -2,7 +2,6 @@ package com.itsaky.androidide.shell;
import com.blankj.utilcode.util.CloseUtils;
import com.blankj.utilcode.util.ThrowableUtils;
-import com.elvishew.xlog.XLog;
import com.itsaky.androidide.utils.Environment;
import java.io.BufferedReader;
import java.io.Closeable;
@@ -38,25 +37,30 @@ public class ShellServer extends Thread {
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.ANDROID_HOME) + "/cmdline-tools/latest/bin");
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.ANDROID_HOME) + "/cmake/bin");
+ // Provide paths to .so files needed to execute JDK
final String lib = Environment.LIBDIR.getAbsolutePath();
stringBuilder.append("\nexport LD_LIBRARY_PATH=" + lib);
stringBuilder.append(System.getenv().containsKey("LD_LIBRARY_PATH") ? ":" + System.getenv().get("LD_LIBRARY_PATH") : "");
if(android.os.Build.VERSION.SDK_INT >= 30) {
+ // Hook calls to malloc() in native binaries
+ // Required only in Android 11 and up
stringBuilder.append("\nexport LD_PRELOAD=" + Environment.LIBHOOKSO.getAbsolutePath());
}
+ // Monotonic clock fix, needed for JDK
stringBuilder.append("\nls -l /system/lib64/libc.so && " +
"ln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/librt.so && " +
"ln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/libpthread.so");
-
+
+ // Make sure there are proper permissions set to the $HOME directory
stringBuilder.append("\nchmod -R 777 $HOME");
- stringBuilder.append("\njava -XshowSettings:all > /sdcard/androidide_jdk_settings.txt");
+
+ // Execute all commands...
append(stringBuilder.toString(), false);
} catch (Throwable th) {
if (callback != null) {
String out = ThrowableUtils.getFullStackTrace(th).concat("\n");
- XLog.e("[ShellServer] error setting up server ->\n" + out);
callback.output(out);
}
}
diff --git a/app/src/main/java/com/itsaky/androidide/tools/SourceJavaFileObject.java b/app/src/main/java/com/itsaky/androidide/tools/SourceJavaFileObject.java
new file mode 100644
index 000000000..c6ff217a4
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/tools/SourceJavaFileObject.java
@@ -0,0 +1,39 @@
+package com.itsaky.androidide.tools;
+
+import java.io.File;
+import java.net.URI;
+import javax.tools.SimpleJavaFileObject;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+
+public class SourceJavaFileObject extends SimpleJavaFileObject {
+
+ private File file;
+ private String source;
+
+ public SourceJavaFileObject(File file, String source) {
+ super(URI.create("source"), Kind.SOURCE);
+ this.file = file;
+ this.source = source;
+ }
+
+ public File getFile() {
+ return this.file;
+ }
+
+ @Override
+ public String getName() {
+ return file.getAbsolutePath();
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean p1) {
+ return source;
+ }
+
+ public CompilationUnit asUnit() {
+ return new CompilationUnit(
+ getCharContent(true).toString().toCharArray(),
+ getFile().getAbsolutePath(),
+ "UTF-8");
+ }
+}
diff --git a/editor/src/main/java/com/itsaky/androidide/utils/Either.java b/app/src/main/java/com/itsaky/androidide/utils/Either.java
similarity index 100%
rename from editor/src/main/java/com/itsaky/androidide/utils/Either.java
rename to app/src/main/java/com/itsaky/androidide/utils/Either.java
diff --git a/app/src/main/java/com/itsaky/androidide/utils/Logger.java b/app/src/main/java/com/itsaky/androidide/utils/Logger.java
new file mode 100644
index 000000000..0520f9286
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/utils/Logger.java
@@ -0,0 +1,119 @@
+package com.itsaky.androidide.utils;
+
+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.LevelFileNameGenerator;
+
+public class Logger {
+
+ private static Logger instance;
+ private static com.elvishew.xlog.Logger xLogger;
+ private static String TAG = "AndroidIDE";
+
+ public static Logger instance() {
+ return instance == null ? createInstance(TAG) : instance;
+ }
+
+ public static Logger instance(String tag) {
+ return instance == null ? createInstance(tag) : instance;
+ }
+
+ private Logger(String tag) {
+ this.TAG = tag;
+ }
+
+ private static Logger createInstance(String tag) {
+ LogConfiguration config = new LogConfiguration.Builder()
+ .logLevel(com.itsaky.androidide.BuildConfig.DEBUG ? LogLevel.ALL : LogLevel.NONE)
+ .tag("AndroidIDE")
+ .enableThreadInfo()
+ .enableStackTrace(10)
+ .disableBorder()
+ .build();
+ Printer filePrinter = new FilePrinter
+ .Builder(FileUtil.getExternalStorageDir() + "/ide_xlog")
+ .fileNameGenerator(new LevelFileNameGenerator())
+ .cleanStrategy(f -> false)
+ .build();
+ XLog.init(config, filePrinter);
+ xLogger = XLog.tag("AndroidIDE").build();
+
+ instance = new Logger(tag);
+ return instance;
+ }
+
+ public Logger d(Object... messages) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ sb.append(TAG);
+ sb.append("]");
+ sb.append("\n");
+ if(messages == null) {
+ xLogger.d("null");
+ return this;
+ }
+ for(Object msg : messages) {
+ sb.append(msg);
+ sb.append("\n");
+ }
+ xLogger.d(sb.toString());
+ return this;
+ }
+
+ public Logger e(Object... messages) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ sb.append(TAG);
+ sb.append("]");
+ sb.append("\n");
+ if(messages == null) {
+ xLogger.e("null");
+ return this;
+ }
+ for(Object msg : messages) {
+ sb.append(msg);
+ sb.append("\n");
+ }
+ xLogger.e(sb.toString());
+ return this;
+ }
+
+ public Logger v(Object... messages) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ sb.append(TAG);
+ sb.append("]");
+ sb.append("\n");
+ if(messages == null) {
+ xLogger.v("null");
+ return this;
+ }
+ for(Object msg : messages) {
+ sb.append(msg);
+ sb.append("\n");
+ }
+ xLogger.v(sb.toString());
+ return this;
+ }
+
+ public Logger i(Object... messages) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ sb.append(TAG);
+ sb.append("]");
+ sb.append("\n");
+ if(messages == null) {
+ xLogger.i("null");
+ return this;
+ }
+ for(Object msg : messages) {
+ sb.append(msg);
+ sb.append("\n");
+ }
+ xLogger.i(sb.toString());
+ return this;
+ }
+}
diff --git a/editor/src/main/java/io/github/rosemoe/editor/annotations/Experimental.java b/app/src/main/java/io/github/rosemoe/editor/annotations/Experimental.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/annotations/Experimental.java
rename to app/src/main/java/io/github/rosemoe/editor/annotations/Experimental.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/interfaces/AutoCompleteProvider.java b/app/src/main/java/io/github/rosemoe/editor/interfaces/AutoCompleteProvider.java
similarity index 90%
rename from editor/src/main/java/io/github/rosemoe/editor/interfaces/AutoCompleteProvider.java
rename to app/src/main/java/io/github/rosemoe/editor/interfaces/AutoCompleteProvider.java
index 784ba9db0..88ba413d2 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/interfaces/AutoCompleteProvider.java
+++ b/app/src/main/java/io/github/rosemoe/editor/interfaces/AutoCompleteProvider.java
@@ -19,6 +19,7 @@ import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.utils.Either;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
+import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.List;
/**
@@ -37,7 +38,7 @@ public interface AutoCompleteProvider {
* @param line The line of cursor
* @return Analyzed items
*/
- List> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line);
+ List> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line);
}
diff --git a/editor/src/main/java/io/github/rosemoe/editor/interfaces/CodeAnalyzer.java b/app/src/main/java/io/github/rosemoe/editor/interfaces/CodeAnalyzer.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/interfaces/CodeAnalyzer.java
rename to app/src/main/java/io/github/rosemoe/editor/interfaces/CodeAnalyzer.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/interfaces/EditorEventListener.java b/app/src/main/java/io/github/rosemoe/editor/interfaces/EditorEventListener.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/interfaces/EditorEventListener.java
rename to app/src/main/java/io/github/rosemoe/editor/interfaces/EditorEventListener.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/interfaces/EditorLanguage.java b/app/src/main/java/io/github/rosemoe/editor/interfaces/EditorLanguage.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/interfaces/EditorLanguage.java
rename to app/src/main/java/io/github/rosemoe/editor/interfaces/EditorLanguage.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java b/app/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java
rename to app/src/main/java/io/github/rosemoe/editor/interfaces/NewlineHandler.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/langs/EmptyLanguage.java b/app/src/main/java/io/github/rosemoe/editor/langs/EmptyLanguage.java
similarity index 94%
rename from editor/src/main/java/io/github/rosemoe/editor/langs/EmptyLanguage.java
rename to app/src/main/java/io/github/rosemoe/editor/langs/EmptyLanguage.java
index 032d4cb98..0dfcee871 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/langs/EmptyLanguage.java
+++ b/app/src/main/java/io/github/rosemoe/editor/langs/EmptyLanguage.java
@@ -24,6 +24,7 @@ import io.github.rosemoe.editor.interfaces.NewlineHandler;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.text.TextAnalyzer.AnalyzeThread.Delegate;
+import io.github.rosemoe.editor.widget.CodeEditor;
import io.github.rosemoe.editor.widget.SymbolPairMatch;
import java.util.ArrayList;
import java.util.List;
@@ -79,7 +80,7 @@ public class EmptyLanguage implements EditorLanguage {
public static class EmptyAutoCompleteProvider implements AutoCompleteProvider {
@Override
- public List> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
+ public List> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
return new ArrayList>();
}
diff --git a/editor/src/main/java/io/github/rosemoe/editor/struct/BlockLine.java b/app/src/main/java/io/github/rosemoe/editor/struct/BlockLine.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/struct/BlockLine.java
rename to app/src/main/java/io/github/rosemoe/editor/struct/BlockLine.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/struct/CompletionItem.java b/app/src/main/java/io/github/rosemoe/editor/struct/CompletionItem.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/struct/CompletionItem.java
rename to app/src/main/java/io/github/rosemoe/editor/struct/CompletionItem.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/struct/ErrorOrWarning.java b/app/src/main/java/io/github/rosemoe/editor/struct/ErrorOrWarning.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/struct/ErrorOrWarning.java
rename to app/src/main/java/io/github/rosemoe/editor/struct/ErrorOrWarning.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/struct/HexColor.java b/app/src/main/java/io/github/rosemoe/editor/struct/HexColor.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/struct/HexColor.java
rename to app/src/main/java/io/github/rosemoe/editor/struct/HexColor.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/struct/NavigationItem.java b/app/src/main/java/io/github/rosemoe/editor/struct/NavigationItem.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/struct/NavigationItem.java
rename to app/src/main/java/io/github/rosemoe/editor/struct/NavigationItem.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/struct/Span.java b/app/src/main/java/io/github/rosemoe/editor/struct/Span.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/struct/Span.java
rename to app/src/main/java/io/github/rosemoe/editor/struct/Span.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/CachedIndexer.java b/app/src/main/java/io/github/rosemoe/editor/text/CachedIndexer.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/CachedIndexer.java
rename to app/src/main/java/io/github/rosemoe/editor/text/CachedIndexer.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/CharPosition.java b/app/src/main/java/io/github/rosemoe/editor/text/CharPosition.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/CharPosition.java
rename to app/src/main/java/io/github/rosemoe/editor/text/CharPosition.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/Content.java b/app/src/main/java/io/github/rosemoe/editor/text/Content.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/Content.java
rename to app/src/main/java/io/github/rosemoe/editor/text/Content.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/ContentLine.java b/app/src/main/java/io/github/rosemoe/editor/text/ContentLine.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/ContentLine.java
rename to app/src/main/java/io/github/rosemoe/editor/text/ContentLine.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/ContentListener.java b/app/src/main/java/io/github/rosemoe/editor/text/ContentListener.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/ContentListener.java
rename to app/src/main/java/io/github/rosemoe/editor/text/ContentListener.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/Cursor.java b/app/src/main/java/io/github/rosemoe/editor/text/Cursor.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/Cursor.java
rename to app/src/main/java/io/github/rosemoe/editor/text/Cursor.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/FontCache.java b/app/src/main/java/io/github/rosemoe/editor/text/FontCache.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/FontCache.java
rename to app/src/main/java/io/github/rosemoe/editor/text/FontCache.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/FormatThread.java b/app/src/main/java/io/github/rosemoe/editor/text/FormatThread.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/FormatThread.java
rename to app/src/main/java/io/github/rosemoe/editor/text/FormatThread.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/Indexer.java b/app/src/main/java/io/github/rosemoe/editor/text/Indexer.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/Indexer.java
rename to app/src/main/java/io/github/rosemoe/editor/text/Indexer.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/LineNumberCalculator.java b/app/src/main/java/io/github/rosemoe/editor/text/LineNumberCalculator.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/LineNumberCalculator.java
rename to app/src/main/java/io/github/rosemoe/editor/text/LineNumberCalculator.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/LineRemoveListener.java b/app/src/main/java/io/github/rosemoe/editor/text/LineRemoveListener.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/LineRemoveListener.java
rename to app/src/main/java/io/github/rosemoe/editor/text/LineRemoveListener.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/NoCacheIndexer.java b/app/src/main/java/io/github/rosemoe/editor/text/NoCacheIndexer.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/NoCacheIndexer.java
rename to app/src/main/java/io/github/rosemoe/editor/text/NoCacheIndexer.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/ObjectAllocator.java b/app/src/main/java/io/github/rosemoe/editor/text/ObjectAllocator.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/ObjectAllocator.java
rename to app/src/main/java/io/github/rosemoe/editor/text/ObjectAllocator.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java b/app/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java
rename to app/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/SpanRecycler.java b/app/src/main/java/io/github/rosemoe/editor/text/SpanRecycler.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/SpanRecycler.java
rename to app/src/main/java/io/github/rosemoe/editor/text/SpanRecycler.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/TextAnalyzeResult.java b/app/src/main/java/io/github/rosemoe/editor/text/TextAnalyzeResult.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/TextAnalyzeResult.java
rename to app/src/main/java/io/github/rosemoe/editor/text/TextAnalyzeResult.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/TextAnalyzer.java b/app/src/main/java/io/github/rosemoe/editor/text/TextAnalyzer.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/TextAnalyzer.java
rename to app/src/main/java/io/github/rosemoe/editor/text/TextAnalyzer.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/TextUtils.java b/app/src/main/java/io/github/rosemoe/editor/text/TextUtils.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/TextUtils.java
rename to app/src/main/java/io/github/rosemoe/editor/text/TextUtils.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/text/UndoManager.java b/app/src/main/java/io/github/rosemoe/editor/text/UndoManager.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/text/UndoManager.java
rename to app/src/main/java/io/github/rosemoe/editor/text/UndoManager.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java b/app/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java
rename to app/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/util/IntPair.java b/app/src/main/java/io/github/rosemoe/editor/util/IntPair.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/util/IntPair.java
rename to app/src/main/java/io/github/rosemoe/editor/util/IntPair.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/util/LongArrayList.java b/app/src/main/java/io/github/rosemoe/editor/util/LongArrayList.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/util/LongArrayList.java
rename to app/src/main/java/io/github/rosemoe/editor/util/LongArrayList.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/util/Objects.java b/app/src/main/java/io/github/rosemoe/editor/util/Objects.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/util/Objects.java
rename to app/src/main/java/io/github/rosemoe/editor/util/Objects.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/AbstractLayout.java b/app/src/main/java/io/github/rosemoe/editor/widget/AbstractLayout.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/AbstractLayout.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/AbstractLayout.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
similarity index 95%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
index 97f5fe4d7..ffd1315ca 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
@@ -56,7 +56,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
-import io.github.rosemoe.editor.R;
+import com.itsaky.androidide.R;
import io.github.rosemoe.editor.interfaces.EditorEventListener;
import io.github.rosemoe.editor.interfaces.EditorLanguage;
import io.github.rosemoe.editor.interfaces.NewlineHandler;
@@ -82,6 +82,13 @@ import java.util.LinkedList;
import java.util.List;
import com.blankj.utilcode.util.DeviceUtils;
import io.github.rosemoe.editor.struct.HexColor;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.PointF;
+import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+import javax.tools.Diagnostic;
+import java.util.stream.Collectors;
+import com.itsaky.androidide.utils.Logger;
/**
* CodeEditor is a editor that can highlight text regions by doing basic syntax analyzing
@@ -219,6 +226,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
private EditorLanguage mLanguage;
private long mLastMakeVisible = 0;
private EditorAutoCompleteWindow mCompletionWindow;
+ private DiagnosticWindow mDiagnosticWindow;
private EditorTouchEventHandler mEventHandler;
private Paint.Align mLineNumberAlign;
private GestureDetector mBasicDetector;
@@ -243,7 +251,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
KeyMetaStates mKeyMetaStates = new KeyMetaStates(this);
private HashMap mLineColors;
-
+ private HashMap> mDiagnostics;
+
public CodeEditor(Context context) {
this(context, null);
}
@@ -431,6 +440,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
setFocusableInTouchMode(true);
mConnection = new EditorInputConnection(this);
mCompletionWindow = new EditorAutoCompleteWindow(this);
+ mDiagnosticWindow = new DiagnosticWindow(this);
mVerticalEdgeGlow = new MaterialEdgeEffect();
mHorizontalGlow = new MaterialEdgeEffect();
mOverrideSymbolPairs = new SymbolPairMatch();
@@ -462,6 +472,57 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
mErrorColor = Color.parseColor("#FF5252");
mWarningColor = Color.parseColor("#FFD740");
mRect2.set(-1, -1, -1, -1);
+
+ mDiagnostics = new HashMap<>();
+ }
+
+ public void showOrHideDiagnosticWindow() {
+ int line = getCursor().getLeftLine();
+ int index = getCursor().getLeft();
+ boolean hide = false;
+ boolean has = mDiagnostics.containsKey(Long.valueOf(line));
+ if(has) {
+ List diags = mDiagnostics.get(Long.valueOf(line));
+ if(diags != null) {
+ diags = diags.stream().filter(d -> d.start() <= index && d.end() >= index).collect(Collectors.toList());
+ if(diags != null && diags.size() > 0){
+ CompilerDiagnostic d = diags.get(0);
+ mDiagnosticWindow.setDiagnostic(d);
+ if(!mDiagnosticWindow.isShowing())
+ mDiagnosticWindow.show();
+ else updateDiagnosticWindowPosition();
+ } else hide = true;
+ } else hide = true;
+ } else hide = true;
+ if(hide && mDiagnosticWindow.isShowing()) {
+ mDiagnosticWindow.hide();
+ }
+ }
+
+ public void setDiagnostics(List diags) {
+ this.mDiagnostics = new HashMap<>();
+ mapDiagnostics(diags);
+ }
+
+ private void mapDiagnostics(List diags) {
+ if(diags == null)
+ return;
+ for(int i=0;i ds = mDiagnostics.get(d.line());
+ if(ds == null)
+ ds = new ArrayList<>();
+ if(!ds.contains(d))
+ ds.add(d);
+ } else {
+ List ds = new ArrayList<>();
+ ds.add(d);
+ mDiagnostics.put(Long.valueOf(d.line()), ds);
+ }
+ }
}
public CodeEditor setLineColors(HashMap map) {
@@ -1141,7 +1202,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
}
- // Draw hex color values
+ // Draw hex colors
if(isLineColorsEnabled() && mLineColors != null && mLineColors.size() > 0) {
for(HexColor c : mLineColors.keySet()) {
if(c != null && c.isSameLine(line) && mLineColors.get(c) != null) {
@@ -1159,7 +1220,35 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
}
}
-
+
+ // Draw errors and warnings
+ if(mDiagnostics != null && mDiagnostics.containsKey(Long.valueOf(line))) {
+ List diags = mDiagnostics.get(Long.valueOf(line));
+ if(diags != null && diags.size() > 0) {
+ for(int i=0;i=1;i--) {
+ float startSeed = i * seed + sideDiff + left;
+ float endSeed = startSeed - seed;
+ if (i == 1) {
+ endSeed -= sideDiff;
+ }
+ path.lineTo(startSeed - halfSeed, innerHeight);
+ path.lineTo(endSeed, y);
+ }
+
+ canvas.drawPath(path, paint);
+ }
/**
* Draw code block lines on screen
@@ -2141,6 +2258,36 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
mTextActionPresenter.onExit();
}
}
+
+ private void updateDiagnosticWindowPosition() {
+ float panelX = updateCursorAnchor() + mDpUnit * 20;
+ float[] rightLayoutOffset = mLayout.getCharLayoutOffset(mCursor.getRightLine(), mCursor.getRightColumn());
+ float panelY = rightLayoutOffset[0] - getOffsetY() + getRowHeight() / 2f;
+ float restY = getHeight() - panelY;
+ if (restY > mDpUnit * 120) {
+ restY = mDpUnit * 120;
+ } else if (restY < mDpUnit * 100) {
+ float offset = 0;
+ while (restY < mDpUnit * 100) {
+ restY += getRowHeight();
+ panelY -= getRowHeight();
+ offset += getRowHeight();
+ }
+ getScroller().startScroll(getOffsetX(), getOffsetY(), 0, (int) offset, 0);
+ }
+ mDiagnosticWindow.setExtendedX(panelX);
+ mDiagnosticWindow.setExtendedY(panelY);
+ if (getWidth() < 500 * mDpUnit) {
+ mDiagnosticWindow.setWidth(getWidth() * 7 / 8);
+ mDiagnosticWindow.setExtendedX(getWidth() / 8f / 2f);
+ } else {
+ int w = getWidth() / 3;
+ mDiagnosticWindow.setWidth(w * 2);
+ }
+
+ mDiagnosticWindow.setHeight((int) restY);
+ mDiagnosticWindow.updatePosition();
+ }
/**
* Apply new position of auto completion window
@@ -3227,6 +3374,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
if (mTextActionPresenter != null) {
mTextActionPresenter.onExit();
}
+
+ showOrHideDiagnosticWindow();
}
/**
@@ -3302,6 +3451,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
mTextActionPresenter.onBeginTextSelect();
}
mEventHandler.notifyTouchedSelectionHandlerLater();
+ showOrHideDiagnosticWindow();
}
/**
@@ -3767,7 +3917,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
| EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
| EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS
| EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
-
+
+ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI|EditorInfo.IME_FLAG_NO_FULLSCREEN;
+
outAttrs.initialSelStart = getCursor() != null ? getCursor().getLeft() : 0;
outAttrs.initialSelEnd = getCursor() != null ? getCursor().getRight() : 0;
outAttrs.initialCapsMode = mConnection.getCursorCapsMode(0);
@@ -4075,6 +4227,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
@Override
public void afterInsert(Content content, int startLine, int startColumn, int endLine, int endColumn, CharSequence insertedContent) {
+ if(mDiagnosticWindow != null)
+ mDiagnosticWindow.hide();
+
// Update spans
if (isSpanMapPrepared(true, endLine - startLine)) {
if (startLine == endLine) {
@@ -4135,6 +4290,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
@Override
public void afterDelete(Content content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
+ if(mDiagnosticWindow != null)
+ mDiagnosticWindow.hide();
+
if (isSpanMapPrepared(false, endLine - startLine)) {
if (startLine == endLine) {
SpanMapUpdater.shiftSpansOnSingleLineDelete(mSpanner.getResult().getSpanMap(), startLine, startColumn, endColumn);
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/CursorBlink.java b/app/src/main/java/io/github/rosemoe/editor/widget/CursorBlink.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/CursorBlink.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/CursorBlink.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/DefaultCompletionItemAdapter.java b/app/src/main/java/io/github/rosemoe/editor/widget/DefaultCompletionItemAdapter.java
similarity index 98%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/DefaultCompletionItemAdapter.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/DefaultCompletionItemAdapter.java
index 6f995e5f5..ec5279058 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/widget/DefaultCompletionItemAdapter.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/DefaultCompletionItemAdapter.java
@@ -24,7 +24,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.utils.Either;
-import io.github.rosemoe.editor.R;
+import com.itsaky.androidide.R;
import io.github.rosemoe.editor.struct.CompletionItem;
import android.graphics.drawable.Drawable;
import androidx.core.content.ContextCompat;
diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/DiagnosticWindow.java b/app/src/main/java/io/github/rosemoe/editor/widget/DiagnosticWindow.java
new file mode 100644
index 000000000..c1d47315b
--- /dev/null
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/DiagnosticWindow.java
@@ -0,0 +1,79 @@
+package io.github.rosemoe.editor.widget;
+
+import android.graphics.PorterDuff;
+import android.view.LayoutInflater;
+import androidx.core.content.ContextCompat;
+import com.itsaky.androidide.R;
+import com.itsaky.androidide.databinding.LayoutDiagnosticInfoBinding;
+import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+import javax.tools.Diagnostic;
+
+public class DiagnosticWindow extends EditorBasePopupWindow {
+
+ private CodeEditor editor;
+ private CompilerDiagnostic diagnostic;
+ private LayoutDiagnosticInfoBinding binding;
+
+ public DiagnosticWindow(CodeEditor editor) {
+ super(editor);
+ this.editor = editor;
+ this.binding = LayoutDiagnosticInfoBinding.inflate(LayoutInflater.from(editor.getContext()));
+
+ setContentView(this.binding.getRoot());
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ setupData();
+ }
+
+ public void setMaxHeight(int height) {
+
+ }
+
+ @Override
+ public void updatePosition() {
+ super.updatePosition();
+ setupData();
+ }
+
+ private void setupData() {
+ if(diagnostic != null) {
+ binding.icon.setImageResource(getDiagnosticIconId());
+ binding.icon.setColorFilter(ContextCompat.getColor(binding.icon.getContext(), R.color.primaryIconColor), PorterDuff.Mode.SRC_ATOP);
+ binding.errorType.setText(getDiagnosticTypeString());
+ binding.errorPosition.setText(
+ editor.getContext().getString(
+ com.itsaky.androidide.R.string.diagnostic_position,
+ ""+ diagnostic.line(),
+ ""+ diagnostic.column()
+ )
+ );
+
+ binding.msg.setText(diagnostic.message());
+ binding.msg.setTextIsSelectable(true);
+ }
+ }
+
+ public DiagnosticWindow setDiagnostic(CompilerDiagnostic diag) {
+ this.diagnostic = diag;
+ return this;
+ }
+
+ private int getDiagnosticIconId() {
+ if(diagnostic.kind() == Diagnostic.Kind.ERROR)
+ return R.drawable.ic_compilation_error;
+ return R.drawable.ic_info;
+ }
+
+ private String getDiagnosticTypeString() {
+ if(diagnostic.kind() == Diagnostic.Kind.ERROR) {
+ return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_error);
+ } else if(diagnostic.kind() == Diagnostic.Kind.WARNING || diagnostic.kind() == Diagnostic.Kind.MANDATORY_WARNING) {
+ return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_warning);
+ }
+ return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_note);
+ }
+
+}
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorAutoCompleteWindow.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorAutoCompleteWindow.java
similarity index 97%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorAutoCompleteWindow.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorAutoCompleteWindow.java
index 991008d4a..d7c2b7884 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorAutoCompleteWindow.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/EditorAutoCompleteWindow.java
@@ -19,14 +19,12 @@ import android.content.Context;
import android.graphics.drawable.GradientDrawable;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.Toast;
-import com.blankj.utilcode.util.DeviceUtils;
+import com.itsaky.androidide.databinding.LayoutCompletionWindowBinding;
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
import com.itsaky.androidide.models.CompletionEdit;
import com.itsaky.androidide.models.CompletionListItem;
import com.itsaky.androidide.utils.Either;
-import io.github.rosemoe.editor.databinding.LayoutCompletionWindowBinding;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.struct.CompletionItem;
import io.github.rosemoe.editor.text.CharPosition;
@@ -276,7 +274,7 @@ public class EditorAutoCompleteWindow extends EditorBasePopupWindow {
mAdapter.attachAttributes(this, results);
mBinding.list.setAdapter(mAdapter);
mCurrent = 0;
- float newHeight = mEditor.getDpUnit() * 40 * results.size();
+ float newHeight = mEditor.getDpUnit() * mAdapter.getItemHeight() * results.size();
if (isShowing()) {
update(getWidth(), (int) Math.min(newHeight, mMaxHeight));
}
@@ -307,7 +305,7 @@ public class EditorAutoCompleteWindow extends EditorBasePopupWindow {
@Override
public void run() {
try {
- displayResults(mLocalProvider.getAutoCompleteItems(mPrefix, mInner, mColors, mLine), mTime);
+ displayResults(mLocalProvider.getAutoCompleteItems(mEditor, mPrefix, mInner, mColors, mLine), mTime);
} catch (Exception e) {
e.printStackTrace();
displayResults(new ArrayList<>(), mTime);
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorBasePopupWindow.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorBasePopupWindow.java
similarity index 98%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorBasePopupWindow.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorBasePopupWindow.java
index d0084ae16..2c6542660 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorBasePopupWindow.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/EditorBasePopupWindow.java
@@ -17,7 +17,7 @@ package io.github.rosemoe.editor.widget;
import android.view.Gravity;
import android.widget.PopupWindow;
-import io.github.rosemoe.editor.R;
+import com.itsaky.androidide.R;
/**
* Editor base panel class
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorInputConnection.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorInputConnection.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorInputConnection.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorInputConnection.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorSearcher.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorSearcher.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorSearcher.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorSearcher.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionWindow.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionWindow.java
similarity index 99%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionWindow.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionWindow.java
index cf150bc5f..e62dfa213 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionWindow.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionWindow.java
@@ -24,7 +24,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
-import io.github.rosemoe.editor.R;
+import com.itsaky.androidide.R;
/**
* This will show when selecting text
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/EditorTouchEventHandler.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorTouchEventHandler.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/EditorTouchEventHandler.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/EditorTouchEventHandler.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/KeyMetaStates.java b/app/src/main/java/io/github/rosemoe/editor/widget/KeyMetaStates.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/KeyMetaStates.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/KeyMetaStates.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/Layout.java b/app/src/main/java/io/github/rosemoe/editor/widget/Layout.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/Layout.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/Layout.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/LineBreakLayout.java b/app/src/main/java/io/github/rosemoe/editor/widget/LineBreakLayout.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/LineBreakLayout.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/LineBreakLayout.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/MaterialEdgeEffect.java b/app/src/main/java/io/github/rosemoe/editor/widget/MaterialEdgeEffect.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/MaterialEdgeEffect.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/MaterialEdgeEffect.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/Row.java b/app/src/main/java/io/github/rosemoe/editor/widget/Row.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/Row.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/Row.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/RowIterator.java b/app/src/main/java/io/github/rosemoe/editor/widget/RowIterator.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/RowIterator.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/RowIterator.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java b/app/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java b/app/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/TextActionPopupWindow.java b/app/src/main/java/io/github/rosemoe/editor/widget/TextActionPopupWindow.java
similarity index 99%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/TextActionPopupWindow.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/TextActionPopupWindow.java
index 68925021c..63579251c 100644
--- a/editor/src/main/java/io/github/rosemoe/editor/widget/TextActionPopupWindow.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/TextActionPopupWindow.java
@@ -25,10 +25,8 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
-
import com.google.android.material.button.MaterialButton;
-
-import io.github.rosemoe.editor.R;
+import com.itsaky.androidide.R;
public class TextActionPopupWindow extends TextComposeBasePopup implements View.OnClickListener, CodeEditor.EditorTextActionPresenter {
private final CodeEditor mEditor;
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/TextComposeBasePopup.java b/app/src/main/java/io/github/rosemoe/editor/widget/TextComposeBasePopup.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/TextComposeBasePopup.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/TextComposeBasePopup.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/WordwrapLayout.java b/app/src/main/java/io/github/rosemoe/editor/widget/WordwrapLayout.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/WordwrapLayout.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/WordwrapLayout.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeDarcula.java b/app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeDarcula.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeDarcula.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeDarcula.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeEclipse.java b/app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeEclipse.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeEclipse.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeEclipse.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeGitHub.java b/app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeGitHub.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeGitHub.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeGitHub.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeNotepadXX.java b/app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeNotepadXX.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeNotepadXX.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeNotepadXX.java
diff --git a/editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeVS2019.java b/app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeVS2019.java
similarity index 100%
rename from editor/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeVS2019.java
rename to app/src/main/java/io/github/rosemoe/editor/widget/schemes/SchemeVS2019.java
diff --git a/app/src/main/java/org/eclipse/jdt/core/Signature.java b/app/src/main/java/org/eclipse/jdt/core/Signature.java
new file mode 100644
index 000000000..1846d3ff8
--- /dev/null
+++ b/app/src/main/java/org/eclipse/jdt/core/Signature.java
@@ -0,0 +1,2927 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * IBM Corporation - added J2SE 1.5 support
+ * Stephan Herrmann - Contribution for
+ * Bug 463533 - Signature.getSignatureSimpleName() returns different results for resolved and unresolved extends
+ *******************************************************************************/
+package org.eclipse.jdt.core;
+
+import java.util.ArrayList;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+/**
+ * Provides methods for encoding and decoding type and method signature strings.
+ *
+ * Signatures obtained from parsing source files (i.e. files with one of the
+ * {@link JavaCore#getJavaLikeExtensions() Java-like extensions}) differ subtly
+ * from ones obtained from pre-compiled binary (".class") files in class names are
+ * usually left unresolved in the former. For example, the normal resolved form
+ * of the type "String" embeds the class's package name ("Ljava.lang.String;"
+ * or "Ljava/lang/String;"), whereas the unresolved form contains only what is
+ * written "QString;".
+ *
+ *
+ * Generic types introduce to the Java language in J2SE 1.5 add three new
+ * facets to signatures: type variables, parameterized types with type arguments,
+ * and formal type parameters. Rich signatures containing these facets
+ * only occur when dealing with code that makes overt use of the new language
+ * features. All other code, and certainly all Java code written or compiled
+ * with J2SE 1.4 or earlier, involved only simple signatures.
+ *
+ *
+ * Note that the "Q", "!" and "|" formats are specific to Eclipse; the remainder
+ * are specified in the JVM spec.
+ *
+ *
+ * The syntax for a type signature is:
+ *
+ * TypeSignature ::=
+ * "B" // byte
+ * | "C" // char
+ * | "D" // double
+ * | "F" // float
+ * | "I" // int
+ * | "J" // long
+ * | "S" // short
+ * | "V" // void
+ * | "Z" // boolean
+ * | "T" + Identifier + ";" // type variable
+ * | "[" + TypeSignature // array X[]
+ * | "!" + TypeSignature // capture-of ?
+ * | "|" + TypeSignature + (":" + TypeSignature)+ // intersection type
+ * | ResolvedClassTypeSignature
+ * | UnresolvedClassTypeSignature
+ *
+ * ResolvedClassTypeSignature ::= // resolved named type (in compiled code)
+ * "L" + Identifier + OptionalTypeArguments
+ * ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
+ * | OptionalTypeParameters + "L" + Identifier +
+ * ( ( "." | "/" ) + Identifier )* + ";"
+ *
+ * UnresolvedClassTypeSignature ::= // unresolved named type (in source code)
+ * "Q" + Identifier + OptionalTypeArguments
+ * ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
+ * | OptionalTypeParameters "Q" + Identifier +
+ * ( ( "." | "/" ) + Identifier )* + ";"
+ *
+ * OptionalTypeArguments ::=
+ * "<" + TypeArgument+ + ">"
+ * |
+ *
+ * TypeArgument ::=
+ * | TypeSignature
+ * | "*" // wildcard ?
+ * | "+" TypeSignature // wildcard ? extends X
+ * | "-" TypeSignature // wildcard ? super X
+ *
+ * OptionalTypeParameters ::=
+ * "<" + FormalTypeParameterSignature+ + ">"
+ * |
+ *
+ *
+ *
+ * Examples:
+ *
+ * "[[I" denotes int[][]
+ * "Ljava.lang.String;" denotes java.lang.String in compiled code
+ * "QString;" denotes String in source code
+ * "Qjava.lang.String;" denotes java.lang.String in source code
+ * "[QString;" denotes String[] in source code
+ * "QMap<QString;*>;" denotes Map<String,?> in source code
+ * "Qjava.util.List<TV;>;" denotes java.util.List<V> in source code
+ * "<E;>Ljava.util.List;" denotes <E>java.util.List in source code
+ *
+ *
+ *
+ * The syntax for a method signature is:
+ *
+ * MethodSignature ::= OptionalTypeParameters + "(" + ParamTypeSignature* + ")" + ReturnTypeSignature
+ * ParamTypeSignature ::= TypeSignature
+ * ReturnTypeSignature ::= TypeSignature
+ *
+ *
+ * Examples:
+ *
+ * "()I" denotes int foo()
+ * "([Ljava.lang.String;)V" denotes void foo(java.lang.String[]) in compiled code
+ * "(QString;)QObject;" denotes Object foo(String) in source code
+ *
+ *
+ *
+ * The syntax for a formal type parameter signature is:
+ *
+ * FormalTypeParameterSignature ::=
+ * TypeVariableName + OptionalClassBound + InterfaceBound*
+ * TypeVariableName ::= Identifier
+ * OptionalClassBound ::=
+ * ":"
+ * | ":" + TypeSignature
+ * InterfaceBound ::=
+ * ":" + TypeSignature
+ *
+ *
+ * Examples:
+ *
+ * "X:" denotes X
+ * "X:QReader;" denotes X extends Reader in source code
+ * "X:QReader;:QSerializable;" denotes X extends Reader & Serializable in source code
+ *
+ *
+ *
+ * This class provides static methods and constants only.
+ *
+ * Note: An empty signature is considered to be syntactically incorrect. So most methods will throw
+ * an IllegalArgumentException if an empty signature is provided.
+ *
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public final class Signature {
+
+ /**
+ * Kind constant for an array type signature.
+ * @see #getTypeSignatureKind(String)
+ * @since 3.0
+ */
+ public static final int ARRAY_TYPE_SIGNATURE = 4;
+
+ /**
+ * Kind constant for a base (primitive or void) type signature.
+ * @see #getTypeSignatureKind(String)
+ * @since 3.0
+ */
+ public static final int BASE_TYPE_SIGNATURE = 2;
+
+ private static final char[] BOOLEAN = "boolean".toCharArray(); //$NON-NLS-1$
+
+ private static final char[] BYTE = "byte".toCharArray(); //$NON-NLS-1$
+
+ /**
+ * Character constant indicating an array type in a signature.
+ * Value is '['.
+ */
+ public static final char C_ARRAY = org.eclipse.jdt.internal.compiler.util.Util.C_ARRAY;
+
+ /**
+ * Character constant indicating the primitive type boolean in a signature.
+ * Value is 'Z'.
+ */
+ public static final char C_BOOLEAN = org.eclipse.jdt.internal.compiler.util.Util.C_BOOLEAN;
+
+ /**
+ * Character constant indicating the primitive type byte in a signature.
+ * Value is 'B'.
+ */
+ public static final char C_BYTE = org.eclipse.jdt.internal.compiler.util.Util.C_BYTE;
+
+ /**
+ * Character constant indicating a capture of a wildcard type in a
+ * signature. Value is '!'.
+ * @since 3.1
+ */
+ public static final char C_CAPTURE = org.eclipse.jdt.internal.compiler.util.Util.C_CAPTURE;
+
+ /**
+ * Character constant indicating the primitive type char in a signature.
+ * Value is 'C'.
+ */
+ public static final char C_CHAR = org.eclipse.jdt.internal.compiler.util.Util.C_CHAR;
+
+ /**
+ * Character constant indicating the colon in a signature.
+ * Value is ':'.
+ * @since 3.0
+ */
+ public static final char C_COLON = org.eclipse.jdt.internal.compiler.util.Util.C_COLON;
+
+ /**
+ * Character constant indicating the dollar in a signature.
+ * Value is '$'.
+ */
+ public static final char C_DOLLAR = org.eclipse.jdt.internal.compiler.util.Util.C_DOLLAR;
+
+ /**
+ * Character constant indicating the dot in a signature.
+ * Value is '.'.
+ */
+ public static final char C_DOT = org.eclipse.jdt.internal.compiler.util.Util.C_DOT;
+
+ /**
+ * Character constant indicating the primitive type double in a signature.
+ * Value is 'D'.
+ */
+ public static final char C_DOUBLE = org.eclipse.jdt.internal.compiler.util.Util.C_DOUBLE;
+
+ /**
+ * Character constant indicating an exception in a signature.
+ * Value is '^'.
+ * @since 3.1
+ */
+ public static final char C_EXCEPTION_START = org.eclipse.jdt.internal.compiler.util.Util.C_EXCEPTION_START;
+
+ /**
+ * Character constant indicating a bound wildcard type argument
+ * in a signature with extends clause.
+ * Value is '+'.
+ * @since 3.1
+ */
+ public static final char C_EXTENDS = org.eclipse.jdt.internal.compiler.util.Util.C_EXTENDS;
+
+ /**
+ * Character constant indicating the primitive type float in a signature.
+ * Value is 'F'.
+ */
+ public static final char C_FLOAT = org.eclipse.jdt.internal.compiler.util.Util.C_FLOAT;
+
+ /**
+ * Character constant indicating the end of a generic type list in a
+ * signature. Value is '>'.
+ * @since 3.0
+ */
+ public static final char C_GENERIC_END = org.eclipse.jdt.internal.compiler.util.Util.C_GENERIC_END;
+
+ /**
+ * Character constant indicating the start of a formal type parameter
+ * (or type argument) list in a signature. Value is '<'.
+ * @since 3.0
+ */
+ public static final char C_GENERIC_START = org.eclipse.jdt.internal.compiler.util.Util.C_GENERIC_START;
+
+ /**
+ * Character constant indicating the primitive type int in a signature.
+ * Value is 'I'.
+ */
+ public static final char C_INT = org.eclipse.jdt.internal.compiler.util.Util.C_INT;
+
+ /**
+ * Character constant indicating an intersection type in a
+ * signature. Value is '|'.
+ * @since 3.7.1
+ */
+ public static final char C_INTERSECTION = '|';
+
+ /**
+ * Character constant indicating the primitive type long in a signature.
+ * Value is 'J'.
+ */
+ public static final char C_LONG = org.eclipse.jdt.internal.compiler.util.Util.C_LONG;
+
+ /**
+ * Character constant indicating the end of a named type in a signature.
+ * Value is ';'.
+ */
+ public static final char C_NAME_END = org.eclipse.jdt.internal.compiler.util.Util.C_NAME_END;
+
+ /**
+ * Character constant indicating the end of a parameter type list in a
+ * signature. Value is ')'.
+ */
+ public static final char C_PARAM_END = org.eclipse.jdt.internal.compiler.util.Util.C_PARAM_END;
+
+ /**
+ * Character constant indicating the start of a parameter type list in a
+ * signature. Value is '('.
+ */
+ public static final char C_PARAM_START = org.eclipse.jdt.internal.compiler.util.Util.C_PARAM_START;
+
+ /**
+ * Character constant indicating the start of a resolved, named type in a
+ * signature. Value is 'L'.
+ */
+ public static final char C_RESOLVED = org.eclipse.jdt.internal.compiler.util.Util.C_RESOLVED;
+
+ /**
+ * Character constant indicating the semicolon in a signature.
+ * Value is ';'.
+ */
+ public static final char C_SEMICOLON = org.eclipse.jdt.internal.compiler.util.Util.C_SEMICOLON;
+
+ /**
+ * Character constant indicating the primitive type short in a signature.
+ * Value is 'S'.
+ */
+ public static final char C_SHORT = org.eclipse.jdt.internal.compiler.util.Util.C_SHORT;
+
+ /**
+ * Character constant indicating an unbound wildcard type argument
+ * in a signature.
+ * Value is '*'.
+ * @since 3.0
+ */
+ public static final char C_STAR = org.eclipse.jdt.internal.compiler.util.Util.C_STAR;
+
+ /**
+ * Character constant indicating a bound wildcard type argument
+ * in a signature with super clause.
+ * Value is '-'.
+ * @since 3.1
+ */
+ public static final char C_SUPER = org.eclipse.jdt.internal.compiler.util.Util.C_SUPER;
+
+ /**
+ * Character constant indicating the start of a resolved type variable in a
+ * signature. Value is 'T'.
+ * @since 3.0
+ */
+ public static final char C_TYPE_VARIABLE = org.eclipse.jdt.internal.compiler.util.Util.C_TYPE_VARIABLE;
+
+ /**
+ * Character constant indicating the start of an unresolved, named type in a
+ * signature. Value is 'Q'.
+ */
+ public static final char C_UNRESOLVED = org.eclipse.jdt.internal.compiler.util.Util.C_UNRESOLVED;
+
+ /**
+ * Character constant indicating result type void in a signature.
+ * Value is 'V'.
+ */
+ public static final char C_VOID = org.eclipse.jdt.internal.compiler.util.Util.C_VOID;
+
+ private static final char[] CAPTURE = "capture-of".toCharArray(); //$NON-NLS-1$
+
+ /**
+ * Kind constant for the capture of a wildcard type signature.
+ * @see #getTypeSignatureKind(String)
+ * @since 3.1
+ */
+ public static final int CAPTURE_TYPE_SIGNATURE = 6;
+
+ private static final char[] CHAR = "char".toCharArray(); //$NON-NLS-1$
+
+ /**
+ * Kind constant for a class type signature.
+ * @see #getTypeSignatureKind(String)
+ * @since 3.0
+ */
+ public static final int CLASS_TYPE_SIGNATURE = 1;
+
+ private static final char[] DOUBLE = "double".toCharArray(); //$NON-NLS-1$
+
+
+ private static final char[] EXTENDS = "extends".toCharArray(); //$NON-NLS-1$
+
+ private static final char[] FLOAT = "float".toCharArray(); //$NON-NLS-1$
+
+ private static final char[] INT = "int".toCharArray(); //$NON-NLS-1$
+
+ /**
+ * Kind constant for the intersection type signature.
+ * @see #getTypeSignatureKind(String)
+ * @since 3.7.1
+ */
+ public static final int INTERSECTION_TYPE_SIGNATURE = 7;
+
+ private static final char[] LONG = "long".toCharArray(); //$NON-NLS-1$
+
+ private static final char[] SHORT = "short".toCharArray(); //$NON-NLS-1$
+
+ /**
+ * String constant for the signature of the primitive type boolean.
+ * Value is "Z".
+ */
+ public static final String SIG_BOOLEAN = "Z"; //$NON-NLS-1$
+
+ /**
+ * String constant for the signature of the primitive type byte.
+ * Value is "B".
+ */
+ public static final String SIG_BYTE = "B"; //$NON-NLS-1$
+ /**
+ * String constant for the signature of the primitive type char.
+ * Value is "C".
+ */
+ public static final String SIG_CHAR = "C"; //$NON-NLS-1$
+ /**
+ * String constant for the signature of the primitive type double.
+ * Value is "D".
+ */
+ public static final String SIG_DOUBLE = "D"; //$NON-NLS-1$
+ /**
+ * String constant for the signature of the primitive type float.
+ * Value is "F".
+ */
+ public static final String SIG_FLOAT = "F"; //$NON-NLS-1$
+ /**
+ * String constant for the signature of the primitive type int.
+ * Value is "I".
+ */
+ public static final String SIG_INT = "I"; //$NON-NLS-1$
+ /**
+ * String constant for the signature of the primitive type long.
+ * Value is "J".
+ */
+ public static final String SIG_LONG = "J"; //$NON-NLS-1$
+ /**
+ * String constant for the signature of the primitive type short.
+ * Value is "S".
+ */
+ public static final String SIG_SHORT = "S"; //$NON-NLS-1$
+ /** String constant for the signature of result type void.
+ * Value is "V".
+ */
+ public static final String SIG_VOID = "V"; //$NON-NLS-1$
+ private static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$
+ /**
+ * Kind constant for a type variable signature.
+ * @see #getTypeSignatureKind(String)
+ * @since 3.0
+ */
+ public static final int TYPE_VARIABLE_SIGNATURE = 3;
+ private static final char[] VOID = "void".toCharArray(); //$NON-NLS-1$
+ /**
+ * Kind constant for a wildcard type signature.
+ * @see #getTypeSignatureKind(String)
+ * @since 3.1
+ */
+ public static final int WILDCARD_TYPE_SIGNATURE = 5;
+
+// .d> --> >
+private static void appendArgumentSimpleNames(char[] name, int start, int end, StringBuffer buffer) {
+ buffer.append('<');
+ int depth = 0;
+ int argumentStart = -1;
+ int argumentCount = 0;
+ for (int i = start; i <= end; i++) {
+ switch(name[i]) {
+ case '<' :
+ depth++;
+ if (depth == 1) {
+ argumentStart = i+1;
+ }
+ break;
+ case '>' :
+ if (depth == 1) {
+ if (argumentCount > 0) buffer.append(',');
+ appendSimpleName(name, argumentStart, i-1, buffer);
+ argumentCount++;
+ }
+ depth--;
+ break;
+ case ',' :
+ if (depth == 1) {
+ if (argumentCount > 0) buffer.append(',');
+ appendSimpleName(name, argumentStart, i-1, buffer);
+ argumentCount++;
+ argumentStart = i+1;
+ }
+ break;
+ }
+ }
+ buffer.append('>');
+}
+
+/**
+ * Scans the given string for an array type signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not an array type signature
+ * @see Util#scanArrayTypeSignature(char[], int)
+ */
+private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
+ return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, false);
+}
+
+/**
+ * Scans the given string for an array type signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param isVarArgs true if the array type must be displayed as a
+ * variable argument, false otherwise
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not an array type signature
+ * @see Util#scanArrayTypeSignature(char[], int)
+ */
+private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) {
+ int length = string.length;
+ // need a minimum 2 char
+ if (start >= length - 1) {
+ throw new IllegalArgumentException();
+ }
+ char c = string[start];
+ if (c != C_ARRAY) {
+ throw new IllegalArgumentException();
+ }
+
+ int index = start;
+ c = string[++index];
+ while(c == C_ARRAY) {
+ // need a minimum 2 char
+ if (index >= length - 1) {
+ throw new IllegalArgumentException();
+ }
+ c = string[++index];
+ }
+
+ int e = appendTypeSignature(string, index, fullyQualifyTypeNames, buffer);
+
+ for(int i = 1, dims = index - start; i < dims; i++) {
+ buffer.append('[').append(']');
+ }
+
+ if (isVarArgs) {
+ buffer.append('.').append('.').append('.');
+ } else {
+ buffer.append('[').append(']');
+ }
+ return e;
+}
+/**
+ * Scans the given string for an capture type signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not an array type signature
+ * @see Util#scanArrayTypeSignature(char[], int)
+ */
+private static int appendCaptureTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
+ // need a minimum 2 char
+ if (start >= string.length - 1) {
+ throw new IllegalArgumentException();
+ }
+ char c = string[start];
+ if (c != C_CAPTURE) {
+ throw new IllegalArgumentException();
+ }
+ buffer.append(CAPTURE).append(' ');
+ return appendTypeArgumentSignature(string, start + 1, fullyQualifyTypeNames, buffer);
+}
+/**
+ * Scans the given string for a class type signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param buffer the string buffer to append to
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not a class type signature
+ * @see Util#scanClassTypeSignature(char[], int)
+ */
+private static int appendClassTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
+ // need a minimum 3 chars "Lx;"
+ if (start >= string.length - 2) {
+ throw new IllegalArgumentException();
+ }
+ // must start in "L" or "Q"
+ char c = string[start];
+ if (c != C_RESOLVED && c != C_UNRESOLVED) {
+ throw new IllegalArgumentException();
+ }
+ boolean resolved = (c == C_RESOLVED);
+ boolean removePackageQualifiers = !fullyQualifyTypeNames;
+ if (!resolved) {
+ // keep everything in an unresolved name
+ removePackageQualifiers = false;
+ }
+ int p = start + 1;
+ int checkpoint = buffer.length();
+ int innerTypeStart = -1;
+ boolean inAnonymousType = false;
+ while (true) {
+ if (p >= string.length) {
+ throw new IllegalArgumentException();
+ }
+ c = string[p];
+ switch(c) {
+ case C_SEMICOLON :
+ // all done
+ return p;
+ case C_GENERIC_START :
+ int e = appendTypeArgumentSignatures(string, p, fullyQualifyTypeNames, buffer);
+ // once we hit type arguments there are no more package prefixes
+ removePackageQualifiers = false;
+ p = e;
+ break;
+ case C_DOT :
+ if (removePackageQualifiers) {
+ // erase package prefix
+ buffer.setLength(checkpoint);
+ } else {
+ buffer.append('.');
+ }
+ break;
+ case '/' :
+ if (removePackageQualifiers) {
+ // erase package prefix
+ buffer.setLength(checkpoint);
+ } else {
+ buffer.append('/');
+ }
+ break;
+ case C_DOLLAR :
+ innerTypeStart = buffer.length();
+ inAnonymousType = false;
+ if (resolved) {
+ // once we hit "$" there are no more package prefixes
+ removePackageQualifiers = false;
+ /**
+ * Convert '$' in resolved type signatures into '.'.
+ * NOTE: This assumes that the type signature is an inner type
+ * signature. This is true in most cases, but someone can define a
+ * non-inner type name containing a '$'.
+ */
+ buffer.append('.');
+ }
+ break;
+ default :
+ if (innerTypeStart != -1 && !inAnonymousType && Character.isDigit(c)) {
+ inAnonymousType = true;
+ buffer.setLength(innerTypeStart); // remove '.'
+ buffer.insert(checkpoint, "new "); //$NON-NLS-1$
+ buffer.append("(){}"); //$NON-NLS-1$
+ }
+ if (!inAnonymousType)
+ buffer.append(c);
+ innerTypeStart = -1;
+ }
+ p++;
+ }
+}
+/**
+ * Scans the given string for an intersection type signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not an array type signature
+ * @see Util#scanArrayTypeSignature(char[], int)
+ */
+private static int appendIntersectionTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
+ // need a minimum 2 char
+ if (start >= string.length - 1) {
+ throw new IllegalArgumentException();
+ }
+ char c = string[start];
+ if (c != C_INTERSECTION) {
+ throw new IllegalArgumentException();
+ }
+ start = appendClassTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
+ if (start < string.length - 1) {
+ start++;
+ if (string[start] != C_COLON) {
+ throw new IllegalArgumentException("should be a colon at this location"); //$NON-NLS-1$
+ }
+ while (string[start] == C_COLON) {
+ buffer.append(" | "); //$NON-NLS-1$
+ start = appendClassTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
+ if (start == string.length - 1) {
+ return start;
+ } else if (start > string.length - 1) {
+ throw new IllegalArgumentException("Should be at the end"); //$NON-NLS-1$
+ }
+ start++;
+ }
+ }
+ return start;
+}
+private static void appendSimpleName(char[] name, int start, int end, StringBuffer buffer) {
+ int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
+ int depth = 0;
+ if (name[start] == '?') { // wildcard
+ buffer.append("?"); //$NON-NLS-1$
+ int index = consumeWhitespace(name, start+1, end+1);
+ switch (name[index]) {
+ case 'e' :
+ int checkPos = checkName(EXTENDS, name, index, end);
+ if (checkPos > 0) {
+ buffer.append(' ').append(EXTENDS).append(' ');
+ index = consumeWhitespace(name, checkPos, end+1);
+ }
+ break;
+ case 's' :
+ checkPos = checkName(SUPER, name, index, end+1);
+ if (checkPos > 0) {
+ buffer.append(' ').append(SUPER).append(' ');
+ index = consumeWhitespace(name, checkPos, end+1);
+ }
+ break;
+ }
+ start = index; // leading segment got processed
+ }
+ lastDotLookup: for (int i = end; i >= start; i--) {
+ switch (name[i]) {
+ case '.':
+ if (depth == 0) {
+ lastDot = i;
+ char c = name[start];
+ if (c == C_EXTENDS || c == C_SUPER) {
+ buffer.append(c);
+ }
+ break lastDotLookup;
+ }
+ break;
+ case '<':
+ depth--;
+ if (depth == 0) lastGenericStart = i;
+ break;
+ case '>':
+ if (depth == 0) lastGenericEnd = i;
+ depth++;
+ break;
+ }
+ }
+ int nameStart = lastDot < 0 ? start : lastDot+1;
+ int nameEnd = lastGenericStart < 0 ? end+1 : lastGenericStart;
+ buffer.append(name, nameStart, nameEnd - nameStart);
+ if (lastGenericStart >= 0) {
+ appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer);
+ buffer.append(name, lastGenericEnd+1, end - lastGenericEnd); // copy trailing portion, may contain dimensions
+ }
+}
+
+/**
+ * Scans the given string for a type argument signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param buffer the string buffer to append to
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not a type argument signature
+ * @see Util#scanTypeArgumentSignature(char[], int)
+ */
+private static int appendTypeArgumentSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
+ // need a minimum 1 char
+ if (start >= string.length) {
+ throw new IllegalArgumentException();
+ }
+ char c = string[start];
+ switch(c) {
+ case C_STAR :
+ buffer.append('?');
+ return start;
+ case C_EXTENDS :
+ buffer.append("? extends "); //$NON-NLS-1$
+ return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
+ case C_SUPER :
+ buffer.append("? super "); //$NON-NLS-1$
+ return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
+ default :
+ return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer);
+ }
+}
+
+/**
+ * Scans the given string for a list of type arguments signature starting at the
+ * given index and appends it to the given buffer, and returns the index of the
+ * last character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param buffer the string buffer to append to
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not a list of type argument
+ * signatures
+ * @see Util#scanTypeArgumentSignatures(char[], int)
+ */
+private static int appendTypeArgumentSignatures(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
+ // need a minimum 2 char "<>"
+ if (start >= string.length - 1) {
+ throw new IllegalArgumentException();
+ }
+ char c = string[start];
+ if (c != C_GENERIC_START) {
+ throw new IllegalArgumentException();
+ }
+ buffer.append('<');
+ int p = start + 1;
+ int count = 0;
+ while (true) {
+ if (p >= string.length) {
+ throw new IllegalArgumentException();
+ }
+ c = string[p];
+ if (c == C_GENERIC_END) {
+ buffer.append('>');
+ return p;
+ }
+ if (count != 0) {
+ buffer.append(',');
+ }
+ int e = appendTypeArgumentSignature(string, p, fullyQualifyTypeNames, buffer);
+ count++;
+ p = e + 1;
+ }
+}
+/**
+ * Scans the given string for a type signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param buffer the string buffer to append to
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not a type signature
+ * @see Util#scanTypeSignature(char[], int)
+ */
+private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
+ return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer, false);
+}
+
+/**
+ * Scans the given string for a type signature starting at the given
+ * index and appends it to the given buffer, and returns the index of the last
+ * character.
+ *
+ * @param string the signature string
+ * @param start the 0-based character index of the first character
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param buffer the string buffer to append to
+ * @param isVarArgs true if the type must be displayed as a
+ * variable argument, false otherwise. In this case, the type must be an array type
+ * @return the 0-based character index of the last character
+ * @exception IllegalArgumentException if this is not a type signature, or if isVarArgs is true,
+ * and the type is not an array type signature.
+ * @see Util#scanTypeSignature(char[], int)
+ */
+private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) {
+ // need a minimum 1 char
+ if (start >= string.length) {
+ throw new IllegalArgumentException();
+ }
+ char c = string[start];
+ if (isVarArgs) {
+ switch (c) {
+ case C_ARRAY :
+ return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, true);
+ case C_RESOLVED :
+ case C_UNRESOLVED :
+ case C_TYPE_VARIABLE :
+ case C_BOOLEAN :
+ case C_BYTE :
+ case C_CHAR :
+ case C_DOUBLE :
+ case C_FLOAT :
+ case C_INT :
+ case C_LONG :
+ case C_SHORT :
+ case C_VOID :
+ case C_STAR:
+ case C_EXTENDS:
+ case C_SUPER:
+ case C_CAPTURE:
+ case C_INTERSECTION :
+ default:
+ throw new IllegalArgumentException(); // a var args is an array type
+ }
+ } else {
+ switch (c) {
+ case C_ARRAY :
+ return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer);
+ case C_RESOLVED :
+ case C_UNRESOLVED :
+ return appendClassTypeSignature(string, start, fullyQualifyTypeNames, buffer);
+ case C_TYPE_VARIABLE :
+ int e = Util.scanTypeVariableSignature(string, start);
+ buffer.append(string, start + 1, e - start - 1);
+ return e;
+ case C_BOOLEAN :
+ buffer.append(BOOLEAN);
+ return start;
+ case C_BYTE :
+ buffer.append(BYTE);
+ return start;
+ case C_CHAR :
+ buffer.append(CHAR);
+ return start;
+ case C_DOUBLE :
+ buffer.append(DOUBLE);
+ return start;
+ case C_FLOAT :
+ buffer.append(FLOAT);
+ return start;
+ case C_INT :
+ buffer.append(INT);
+ return start;
+ case C_LONG :
+ buffer.append(LONG);
+ return start;
+ case C_SHORT :
+ buffer.append(SHORT);
+ return start;
+ case C_VOID :
+ buffer.append(VOID);
+ return start;
+ case C_CAPTURE :
+ return appendCaptureTypeSignature(string, start, fullyQualifyTypeNames, buffer);
+ case C_INTERSECTION :
+ return appendIntersectionTypeSignature(string, start, fullyQualifyTypeNames, buffer);
+ case C_STAR:
+ case C_EXTENDS:
+ case C_SUPER:
+ return appendTypeArgumentSignature(string, start, fullyQualifyTypeNames, buffer);
+ default :
+ throw new IllegalArgumentException();
+ }
+ }
+}
+
+private static int checkArrayDimension(char[] typeName, int pos, int length) {
+ int genericBalance = 0;
+ while (pos < length) {
+ switch(typeName[pos]) {
+ case '<' :
+ genericBalance++;
+ break;
+ case ',' :
+ if (genericBalance == 0) return -1;
+ break;
+ case '>':
+ if (genericBalance == 0) return -1;
+ genericBalance--;
+ break;
+ case '[':
+ if (genericBalance == 0) {
+ return pos;
+ }
+ }
+ pos++;
+ }
+ return -1;
+}
+private static int checkName(char[] name, char[] typeName, int pos, int length) {
+ if (CharOperation.fragmentEquals(name, typeName, pos, true)) {
+ pos += name.length;
+ if (pos == length) return pos;
+ char currentChar = typeName[pos];
+ switch (currentChar) {
+ case ' ' :
+ case '.' :
+ case '<' :
+ case '>' :
+ case '[' :
+ case ',' :
+ return pos;
+ default:
+ if (ScannerHelper.isWhitespace(currentChar))
+ return pos;
+
+ }
+ }
+ return -1;
+}
+private static int checkNextChar(char[] typeName, char expectedChar, int pos, int length, boolean isOptional) {
+ pos = consumeWhitespace(typeName, pos, length);
+ if (pos < length && typeName[pos] == expectedChar)
+ return pos + 1;
+ if (!isOptional) throw new IllegalArgumentException(new String(typeName));
+ return -1;
+}
+
+private static int consumeWhitespace(char[] typeName, int pos, int length) {
+ while (pos < length) {
+ char currentChar = typeName[pos];
+ if (currentChar != ' ' && !ScannerHelper.isWhitespace(currentChar)) {
+ break;
+ }
+ pos++;
+ }
+ return pos;
+}
+/**
+ * Creates a new type signature with the given amount of array nesting added
+ * to the given type signature.
+ *
+ * @param typeSignature the type signature
+ * @param arrayCount the desired number of levels of array nesting
+ * @return the encoded array type signature
+ *
+ * @since 2.0
+ */
+public static char[] createArraySignature(char[] typeSignature, int arrayCount) {
+ if (arrayCount == 0) return typeSignature;
+ int sigLength = typeSignature.length;
+ char[] result = new char[arrayCount + sigLength];
+ for (int i = 0; i < arrayCount; i++) {
+ result[i] = C_ARRAY;
+ }
+ System.arraycopy(typeSignature, 0, result, arrayCount, sigLength);
+ return result;
+}
+/**
+ * Creates a new type signature with the given amount of array nesting added
+ * to the given type signature.
+ *
+ * @param typeSignature the type signature
+ * @param arrayCount the desired number of levels of array nesting
+ * @return the encoded array type signature
+ */
+public static String createArraySignature(String typeSignature, int arrayCount) {
+ return new String(createArraySignature(typeSignature.toCharArray(), arrayCount));
+}
+
+/**
+ * Creates a new type signature from the given type name encoded as a character
+ * array. The type name may contain primitive types or array types or parameterized types.
+ * This method is equivalent to
+ * createTypeSignature(new String(typeName),isResolved).toCharArray(),
+ * although more efficient for callers with character arrays rather than strings.
+ * If the type name is qualified, then it is expected to be dot-based.
+ *
+ * @param typeName the possibly qualified type name
+ * @param isResolved true if the type name is to be considered
+ * resolved (for example, a type name from a binary class file), and
+ * false if the type name is to be considered unresolved
+ * (for example, a type name found in source code)
+ * @return the encoded type signature
+ * @see #createTypeSignature(java.lang.String,boolean)
+ *
+ * @since 2.0
+ */
+public static char[] createCharArrayTypeSignature(char[] typeName, boolean isResolved) {
+ if (typeName == null) throw new IllegalArgumentException("null"); //$NON-NLS-1$
+ int length = typeName.length;
+ if (length == 0) throw new IllegalArgumentException(new String(typeName));
+ StringBuffer buffer = new StringBuffer(5);
+ int pos = encodeTypeSignature(typeName, 0, isResolved, length, buffer);
+ pos = consumeWhitespace(typeName, pos, length);
+ if (pos < length) throw new IllegalArgumentException(new String(typeName));
+ char[] result = new char[length = buffer.length()];
+ buffer.getChars(0, length, result, 0);
+ return result;
+}
+
+/**
+ * Creates a new intersection type signature from the given type signatures.
+ *
+ * The encoded type signature is dot-based.
+ *
+ * @param typeSignatures the given type signatures
+ * @return the encoded type signature
+ * @since 3.7.1
+ */
+public static String createIntersectionTypeSignature(char[][] typeSignatures) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(Signature.C_INTERSECTION);
+ for (int i = 0, max = typeSignatures.length; i < max; i++) {
+ if (i > 0) {
+ buffer.append(Signature.C_COLON);
+ }
+ buffer.append(typeSignatures[i]);
+ }
+ return String.valueOf(buffer);
+}
+
+/**
+ * Creates a new intersection type signature from the given type signatures.
+ *
+ * The encoded type signature is dot-based.
+ *
+ * @param typeSignatures the given type signatures
+ * @return the encoded type signature
+ * @since 3.7.1
+ */
+public static String createIntersectionTypeSignature(String[] typeSignatures) {
+ int typeSignaturesLenth = typeSignatures.length;
+ char[][] signatures = new char[typeSignaturesLenth][];
+ for (int i = 0; i < typeSignaturesLenth; i++) {
+ signatures[i] = typeSignatures[i].toCharArray();
+ }
+ return createIntersectionTypeSignature(signatures);
+}
+/**
+ * Creates a method signature from the given parameter and return type
+ * signatures. The encoded method signature is dot-based.
+ *
+ * @param parameterTypes the list of parameter type signatures
+ * @param returnType the return type signature
+ * @return the encoded method signature
+ *
+ * @since 2.0
+ */
+public static char[] createMethodSignature(char[][] parameterTypes, char[] returnType) {
+ int parameterTypesLength = parameterTypes.length;
+ int parameterLength = 0;
+ for (int i = 0; i < parameterTypesLength; i++) {
+ parameterLength += parameterTypes[i].length;
+
+ }
+ int returnTypeLength = returnType.length;
+ char[] result = new char[1 + parameterLength + 1 + returnTypeLength];
+ result[0] = C_PARAM_START;
+ int index = 1;
+ for (int i = 0; i < parameterTypesLength; i++) {
+ char[] parameterType = parameterTypes[i];
+ int length = parameterType.length;
+ System.arraycopy(parameterType, 0, result, index, length);
+ index += length;
+ }
+ result[index] = C_PARAM_END;
+ System.arraycopy(returnType, 0, result, index+1, returnTypeLength);
+ return result;
+}
+/**
+ * Creates a method signature from the given parameter and return type
+ * signatures. The encoded method signature is dot-based. This method
+ * is equivalent to
+ * createMethodSignature(parameterTypes, returnType).
+ *
+ * @param parameterTypes the list of parameter type signatures
+ * @param returnType the return type signature
+ * @return the encoded method signature
+ * @see Signature#createMethodSignature(char[][], char[])
+ */
+public static String createMethodSignature(String[] parameterTypes, String returnType) {
+ int parameterTypesLenth = parameterTypes.length;
+ char[][] parameters = new char[parameterTypesLenth][];
+ for (int i = 0; i < parameterTypesLenth; i++) {
+ parameters[i] = parameterTypes[i].toCharArray();
+ }
+ return new String(createMethodSignature(parameters, returnType.toCharArray()));
+}
+/**
+ * Creates a new type parameter signature with the given name and bounds.
+ *
+ * @param typeParameterName the type parameter name
+ * @param boundSignatures the signatures of associated bounds or empty array if none
+ * @return the encoded type parameter signature
+ *
+ * @since 3.1
+ */
+public static char[] createTypeParameterSignature(char[] typeParameterName, char[][] boundSignatures) {
+ int length = boundSignatures.length;
+ if (length == 0) {
+ return CharOperation.append(typeParameterName, C_COLON); // param signature with no bounds still gets trailing colon
+ }
+ int boundsSize = 0;
+ for (int i = 0; i < length; i++) {
+ boundsSize += boundSignatures[i].length + 1;
+ }
+ int nameLength = typeParameterName.length;
+ char[] result = new char[nameLength + boundsSize];
+ System.arraycopy(typeParameterName, 0, result, 0, nameLength);
+ int index = nameLength;
+ for (int i = 0; i < length; i++) {
+ result[index++] = C_COLON;
+ int boundLength = boundSignatures[i].length;
+ System.arraycopy(boundSignatures[i], 0, result, index, boundLength);
+ index += boundLength;
+ }
+ return result;
+}
+/**
+ * Creates a new type parameter signature with the given name and bounds.
+ *
+ * @param typeParameterName the type parameter name
+ * @param boundSignatures the signatures of associated bounds or empty array if none
+ * @return the encoded type parameter signature
+ *
+ * @since 3.1
+ */
+public static String createTypeParameterSignature(String typeParameterName, String[] boundSignatures) {
+ int length = boundSignatures.length;
+ char[][] boundSignatureChars = new char[length][];
+ for (int i = 0; i < length; i++) {
+ boundSignatureChars[i] = boundSignatures[i].toCharArray();
+ }
+ return new String(createTypeParameterSignature(typeParameterName.toCharArray(), boundSignatureChars));
+}
+
+/**
+ * Creates a new type signature from the given type name encoded as a character
+ * array. The type name may contain primitive types, array types or parameterized types.
+ * This method is equivalent to
+ * createTypeSignature(new String(typeName),isResolved), although
+ * more efficient for callers with character arrays rather than strings. If the
+ * type name is qualified, then it is expected to be dot-based.
+ *
+ * @param typeName the possibly qualified type name
+ * @param isResolved true if the type name is to be considered
+ * resolved (for example, a type name from a binary class file), and
+ * false if the type name is to be considered unresolved
+ * (for example, a type name found in source code)
+ * @return the encoded type signature
+ * @see #createTypeSignature(java.lang.String,boolean)
+ */
+public static String createTypeSignature(char[] typeName, boolean isResolved) {
+ return new String(createCharArrayTypeSignature(typeName, isResolved));
+}
+
+/**
+ * Creates a new type signature from the given type name. If the type name is qualified,
+ * then it is expected to be dot-based. The type name may contain primitive
+ * types or array types. However, parameterized types are not supported.
+ *
+ * For example:
+ *
+ *
+ * createTypeSignature("int", hucairz) -> "I"
+ * createTypeSignature("java.lang.String", true) -> "Ljava.lang.String;"
+ * createTypeSignature("String", false) -> "QString;"
+ * createTypeSignature("java.lang.String", false) -> "Qjava.lang.String;"
+ * createTypeSignature("int []", false) -> "[I"
+ *
+ *
+ *
+ *
+ * @param typeName the possibly qualified type name
+ * @param isResolved true if the type name is to be considered
+ * resolved (for example, a type name from a binary class file), and
+ * false if the type name is to be considered unresolved
+ * (for example, a type name found in source code)
+ * @return the encoded type signature
+ */
+public static String createTypeSignature(String typeName, boolean isResolved) {
+ return createTypeSignature(typeName == null ? null : typeName.toCharArray(), isResolved);
+}
+
+private static int encodeArrayDimension(char[] typeName, int pos, int length, StringBuffer buffer) {
+ int checkPos;
+ while (pos < length && (checkPos = checkNextChar(typeName, '[', pos, length, true)) > 0) {
+ pos = checkNextChar(typeName, ']', checkPos, length, false);
+ buffer.append(C_ARRAY);
+ }
+ return pos;
+}
+
+private static int encodeQualifiedName(char[] typeName, int pos, int length, StringBuffer buffer) {
+ int count = 0;
+ char lastAppendedChar = 0;
+ nameLoop: while (pos < length) {
+ char currentChar = typeName[pos];
+ switch (currentChar) {
+ case '<' :
+ case '>' :
+ case '[' :
+ case ',' :
+ break nameLoop;
+ case '.' :
+ buffer.append(C_DOT);
+ lastAppendedChar = C_DOT;
+ count++;
+ break;
+ default:
+ if (currentChar == ' ' || ScannerHelper.isWhitespace(currentChar)) {
+ if (lastAppendedChar == C_DOT) { // allow spaces after a dot
+ pos = consumeWhitespace(typeName, pos, length) - 1; // will be incremented
+ break;
+ }
+ // allow spaces before a dot
+ int checkPos = checkNextChar(typeName, '.', pos, length, true);
+ if (checkPos > 0) {
+ buffer.append(C_DOT); // process dot immediately to avoid one iteration
+ lastAppendedChar = C_DOT;
+ count++;
+ pos = checkPos;
+ break;
+ }
+ break nameLoop;
+ }
+ buffer.append(currentChar);
+ lastAppendedChar = currentChar;
+ count++;
+ break;
+ }
+ pos++;
+ }
+ if (count == 0) throw new IllegalArgumentException(new String(typeName));
+ return pos;
+}
+
+private static int encodeTypeSignature(char[] typeName, int start, boolean isResolved, int length, StringBuffer buffer) {
+ int pos = start;
+ pos = consumeWhitespace(typeName, pos, length);
+ if (pos >= length) throw new IllegalArgumentException(new String(typeName));
+ int checkPos;
+ char currentChar = typeName[pos];
+ switch (currentChar) {
+ // primitive type?
+ case 'b' :
+ checkPos = checkName(BOOLEAN, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_BOOLEAN);
+ return pos;
+ }
+ checkPos = checkName(BYTE, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_BYTE);
+ return pos;
+ }
+ break;
+ case 'd':
+ checkPos = checkName(DOUBLE, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_DOUBLE);
+ return pos;
+ }
+ break;
+ case 'f':
+ checkPos = checkName(FLOAT, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_FLOAT);
+ return pos;
+ }
+ break;
+ case 'i':
+ checkPos = checkName(INT, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_INT);
+ return pos;
+ }
+ break;
+ case 'l':
+ checkPos = checkName(LONG, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_LONG);
+ return pos;
+ }
+ break;
+ case 's':
+ checkPos = checkName(SHORT, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_SHORT);
+ return pos;
+ }
+ break;
+ case 'v':
+ checkPos = checkName(VOID, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_VOID);
+ return pos;
+ }
+ break;
+ case 'c':
+ checkPos = checkName(CHAR, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = encodeArrayDimension(typeName, checkPos, length, buffer);
+ buffer.append(C_CHAR);
+ return pos;
+ } else {
+ checkPos = checkName(CAPTURE, typeName, pos, length);
+ if (checkPos > 0) {
+ pos = consumeWhitespace(typeName, checkPos, length);
+ if (typeName[pos] != '?') {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ buffer.append(C_CAPTURE);
+ //$FALL-THROUGH$ for wildcard part of capture typecheckPos
+ case '?':
+ // wildcard
+ pos = consumeWhitespace(typeName, pos+1, length);
+ checkPos = checkName(EXTENDS, typeName, pos, length);
+ if (checkPos > 0) {
+ buffer.append(C_EXTENDS);
+ pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
+ return pos;
+ }
+ checkPos = checkName(SUPER, typeName, pos, length);
+ if (checkPos > 0) {
+ buffer.append(C_SUPER);
+ pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
+ return pos;
+ }
+ buffer.append(C_STAR);
+ return pos;
+ }
+ // non primitive type
+ checkPos = checkArrayDimension(typeName, pos, length);
+ int end;
+ if (checkPos > 0) {
+ end = encodeArrayDimension(typeName, checkPos, length, buffer);
+ } else {
+ end = -1;
+ }
+ buffer.append(isResolved ? C_RESOLVED : C_UNRESOLVED);
+ while (true) { // loop on qualifiedName[][.qualifiedName[]*
+ pos = encodeQualifiedName(typeName, pos, length, buffer);
+ checkPos = checkNextChar(typeName, '<', pos, length, true);
+ if (checkPos > 0) {
+ buffer.append(C_GENERIC_START);
+ // Stop gap fix for <>.
+ if ((pos = checkNextChar(typeName, '>', checkPos, length, true)) > 0) {
+ buffer.append(C_GENERIC_END);
+ } else {
+ pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
+ while ((checkPos = checkNextChar(typeName, ',', pos, length, true)) > 0) {
+ pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
+ }
+ pos = checkNextChar(typeName, '>', pos, length, false);
+ buffer.append(C_GENERIC_END);
+ }
+ }
+ checkPos = checkNextChar(typeName, '.', pos, length, true);
+ if (checkPos > 0) {
+ buffer.append(C_DOT);
+ pos = checkPos;
+ } else {
+ break;
+ }
+ }
+ buffer.append(C_NAME_END);
+ if (end > 0) pos = end; // skip array dimension which were preprocessed
+ return pos;
+}
+
+/**
+ * Returns the array count (array nesting depth) of the given type signature.
+ *
+ * @param typeSignature the type signature
+ * @return the array nesting depth, or 0 if not an array
+ * @exception IllegalArgumentException if the signature is not syntactically
+ * correct
+ *
+ * @since 2.0
+ */
+public static int getArrayCount(char[] typeSignature) throws IllegalArgumentException {
+ try {
+ int count = 0;
+ while (typeSignature[count] == C_ARRAY) {
+ ++count;
+ }
+ return count;
+ } catch (ArrayIndexOutOfBoundsException e) { // signature is syntactically incorrect if last character is C_ARRAY
+ throw new IllegalArgumentException();
+ }
+}
+
+/**
+ * Returns the array count (array nesting depth) of the given type signature.
+ *
+ * @param typeSignature the type signature
+ * @return the array nesting depth, or 0 if not an array
+ * @exception IllegalArgumentException if the signature is not syntactically
+ * correct
+ */
+public static int getArrayCount(String typeSignature) throws IllegalArgumentException {
+ return getArrayCount(typeSignature.toCharArray());
+}
+
+/**
+ * Returns the type signature without any array nesting.
+ *
+ * For example:
+ *
+ *
+ * getElementType({'[', '[', 'I'}) --> {'I'}.
+ *
+ *
+ *
+ *
+ * @param typeSignature the type signature
+ * @return the type signature without arrays
+ * @exception IllegalArgumentException if the signature is not syntactically
+ * correct
+ *
+ * @since 2.0
+ */
+public static char[] getElementType(char[] typeSignature) throws IllegalArgumentException {
+ int count = getArrayCount(typeSignature);
+ if (count == 0) return typeSignature;
+ int length = typeSignature.length;
+ char[] result = new char[length-count];
+ System.arraycopy(typeSignature, count, result, 0, length-count);
+ return result;
+}
+
+/**
+ * Returns the type signature without any array nesting.
+ *
+ * For example:
+ *
+ *
+ * getElementType("[[I") --> "I".
+ *
+ *
+ *
+ *
+ * @param typeSignature the type signature
+ * @return the type signature without arrays
+ * @exception IllegalArgumentException if the signature is not syntactically
+ * correct
+ */
+public static String getElementType(String typeSignature) throws IllegalArgumentException {
+ char[] signature = typeSignature.toCharArray();
+ char[] elementType = getElementType(signature);
+ return signature == elementType ? typeSignature : new String(elementType);
+}
+/**
+ * Extracts the type bounds' signatures from the given intersection type signature.
+ * Returns an empty array if the type signature is not an intersection type signature.
+ *
+ * @param intersectionTypeSignature the intersection type signature
+ * @return the signatures of the type bounds
+ * @exception IllegalArgumentException if the signature is syntactically incorrect
+ *
+ * @since 3.7.1
+ */
+public static char[][] getIntersectionTypeBounds(char[] intersectionTypeSignature) throws IllegalArgumentException {
+ if (getTypeSignatureKind(intersectionTypeSignature) != INTERSECTION_TYPE_SIGNATURE) {
+ return CharOperation.NO_CHAR_CHAR;
+ }
+ ArrayList args = new ArrayList();
+ int i = 1; // skip the '|'
+ int length = intersectionTypeSignature.length;
+ for (;;) {
+ int e = Util.scanClassTypeSignature(intersectionTypeSignature, i);
+ if (e < 0) {
+ throw new IllegalArgumentException("Invalid format"); //$NON-NLS-1$
+ }
+ args.add(CharOperation.subarray(intersectionTypeSignature, i, e + 1));
+ if (e == length - 1) {
+ int size = args.size();
+ char[][] result = new char[size][];
+ args.toArray(result);
+ return result;
+ } else if (intersectionTypeSignature[e + 1] != C_COLON) {
+ throw new IllegalArgumentException("Invalid format"); //$NON-NLS-1$
+ }
+ i = e + 2; // add one to skip C_COLON
+ }
+}
+/**
+ * Extracts the type bounds' signatures from the given intersection type signature.
+ * Returns an empty array if the type signature is not an intersection type signature.
+ *
+ * @param intersectionTypeSignature the intersection type signature
+ * @return the signatures of the type bounds
+ * @exception IllegalArgumentException if the signature is syntactically incorrect
+ *
+ * @since 3.7.1
+ */
+public static String[] getIntersectionTypeBounds(String intersectionTypeSignature) throws IllegalArgumentException {
+ char[][] args = getIntersectionTypeBounds(intersectionTypeSignature.toCharArray());
+ return CharOperation.toStrings(args);
+}
+/**
+ * Returns the number of parameter types in the given method signature.
+ *
+ * @param methodSignature the method signature
+ * @return the number of parameters
+ * @exception IllegalArgumentException if the signature is not syntactically
+ * correct
+ * @since 2.0
+ */
+public static int getParameterCount(char[] methodSignature) throws IllegalArgumentException {
+ try {
+ int count = 0;
+ int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
+ if (i < 0) {
+ throw new IllegalArgumentException();
+ } else {
+ i++;
+ }
+ for (;;) {
+ if (methodSignature[i] == C_PARAM_END) {
+ return count;
+ }
+ int e= Util.scanTypeSignature(methodSignature, i);
+ if (e < 0) {
+ throw new IllegalArgumentException();
+ } else {
+ i = e + 1;
+ }
+ count++;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException();
+ }
+}
+
+/**
+ * Returns the number of parameter types in the given method signature.
+ *
+ * @param methodSignature the method signature
+ * @return the number of parameters
+ * @exception IllegalArgumentException if the signature is not syntactically
+ * correct
+ */
+public static int getParameterCount(String methodSignature) throws IllegalArgumentException {
+ return getParameterCount(methodSignature.toCharArray());
+}
+
+/**
+ * Extracts the parameter type signatures from the given method signature.
+ * The method signature is expected to be dot-based.
+ *
+ * @param methodSignature the method signature
+ * @return the list of parameter type signatures
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 2.0
+ */
+public static char[][] getParameterTypes(char[] methodSignature) throws IllegalArgumentException {
+ try {
+ int count = getParameterCount(methodSignature);
+ char[][] result = new char[count][];
+ if (count == 0) {
+ return result;
+ }
+ int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
+ if (i < 0) {
+ throw new IllegalArgumentException();
+ } else {
+ i++;
+ }
+ int t = 0;
+ for (;;) {
+ if (methodSignature[i] == C_PARAM_END) {
+ return result;
+ }
+ int e = Util.scanTypeSignature(methodSignature, i);
+ if (e < 0) {
+ throw new IllegalArgumentException();
+ }
+ result[t] = CharOperation.subarray(methodSignature, i, e + 1);
+ t++;
+ i = e + 1;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException();
+ }
+}
+/**
+ * Extracts the parameter type signatures from the given method signature.
+ * The method signature is expected to be dot-based.
+ *
+ * @param methodSignature the method signature
+ * @return the list of parameter type signatures
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ */
+public static String[] getParameterTypes(String methodSignature) throws IllegalArgumentException {
+ char[][] parameterTypes = getParameterTypes(methodSignature.toCharArray());
+ return CharOperation.toStrings(parameterTypes);
+}
+
+/**
+ * Returns a char array containing all but the last segment of the given
+ * dot-separated qualified name. Returns the empty char array if it is not qualified.
+ *
+ * For example:
+ *
+ *
+ * getQualifier({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g'}
+ * getQualifier({'O', 'u', 't', 'e', 'r', '.', 'I', 'n', 'n', 'e', 'r'}) -> {'O', 'u', 't', 'e', 'r'}
+ * getQualifier({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
+ *
+ *
+ *
+ *
+ * @param name the name
+ * @return the qualifier prefix, or the empty char array if the name contains no
+ * dots
+ * @exception NullPointerException if name is null
+ * @since 2.0
+ */
+public static char[] getQualifier(char[] name) {
+ int firstGenericStart = CharOperation.indexOf(C_GENERIC_START, name);
+ int lastDot = CharOperation.lastIndexOf(C_DOT, name, 0, firstGenericStart == -1 ? name.length-1 : firstGenericStart);
+ if (lastDot == -1) {
+ return CharOperation.NO_CHAR;
+ }
+ return CharOperation.subarray(name, 0, lastDot);
+}
+
+/**
+ * Returns a string containing all but the last segment of the given
+ * dot-separated qualified name. Returns the empty string if it is not qualified.
+ *
+ * For example:
+ *
+ *
+ * getQualifier("java.lang.Object") -> "java.lang"
+ * getQualifier("Outer.Inner") -> "Outer"
+ * getQualifier("java.util.List<java.lang.String>") -> "java.util"
+ *
+ *
+ *
+ *
+ * @param name the name
+ * @return the qualifier prefix, or the empty string if the name contains no
+ * dots
+ * @exception NullPointerException if name is null
+ */
+public static String getQualifier(String name) {
+ char[] qualifier = getQualifier(name.toCharArray());
+ if (qualifier.length == 0) return org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING;
+ return new String(qualifier);
+}
+
+/**
+ * Extracts the return type from the given method signature. The method signature is
+ * expected to be dot-based.
+ *
+ * @param methodSignature the method signature
+ * @return the type signature of the return type
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 2.0
+ */
+public static char[] getReturnType(char[] methodSignature) throws IllegalArgumentException {
+ // skip type parameters
+ int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature);
+ if (paren == -1) {
+ throw new IllegalArgumentException();
+ }
+ // there could be thrown exceptions behind, thus scan one type exactly
+ int last = Util.scanTypeSignature(methodSignature, paren+1);
+ return CharOperation.subarray(methodSignature, paren + 1, last+1);
+}
+
+/**
+ * Extracts the return type from the given method signature. The method signature is
+ * expected to be dot-based.
+ *
+ * @param methodSignature the method signature
+ * @return the type signature of the return type
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ */
+public static String getReturnType(String methodSignature) throws IllegalArgumentException {
+ return new String(getReturnType(methodSignature.toCharArray()));
+}
+
+/**
+ * Returns package fragment of a type signature. The package fragment separator must be '.'
+ * and the type fragment separator must be '$'.
+ *
+ * For example:
+ *
+ *
+ * getSignatureQualifier({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
+ *
+ *
+ *
+ *
+ * @param typeSignature the type signature
+ * @return the package fragment (separators are '.')
+ * @since 3.1
+ */
+public static char[] getSignatureQualifier(char[] typeSignature) {
+ if(typeSignature == null) return CharOperation.NO_CHAR;
+
+ char[] qualifiedType = Signature.toCharArray(typeSignature);
+
+ int dotCount = 0;
+ indexFound: for(int i = 0; i < typeSignature.length; i++) {
+ switch(typeSignature[i]) {
+ case C_DOT:
+ dotCount++;
+ break;
+ case C_GENERIC_START:
+ break indexFound;
+ case C_DOLLAR:
+ break indexFound;
+ }
+ }
+
+ if(dotCount > 0) {
+ for(int i = 0; i < qualifiedType.length; i++) {
+ if(qualifiedType[i] == '.') {
+ dotCount--;
+ }
+ if(dotCount <= 0) {
+ return CharOperation.subarray(qualifiedType, 0, i);
+ }
+ }
+ }
+ return CharOperation.NO_CHAR;
+}
+/**
+ * Returns package fragment of a type signature. The package fragment separator must be '.'
+ * and the type fragment separator must be '$'.
+ *
+ * For example:
+ *
+ *
+ * getSignatureQualifier("Ljava.util.Map$Entry") -> "java.util"
+ *
+ *
+ *
+ *
+ * @param typeSignature the type signature
+ * @return the package fragment (separators are '.')
+ * @since 3.1
+ */
+public static String getSignatureQualifier(String typeSignature) {
+ return new String(getSignatureQualifier(typeSignature == null ? null : typeSignature.toCharArray()));
+}
+/**
+ * Returns type fragment of a type signature. The package fragment separator must be '.'
+ * and the type fragment separator must be '$'.
+ *
+ * For example:
+ *
+ *
+ * getSignatureSimpleName({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'M', 'a', 'p', '.', 'E', 'n', 't', 'r', 'y'}
+ *
+ *
+ *
+ *
+ * @param typeSignature the type signature
+ * @return the type fragment (separators are '.')
+ * @since 3.1
+ */
+public static char[] getSignatureSimpleName(char[] typeSignature) {
+ if(typeSignature == null) return CharOperation.NO_CHAR;
+
+ char[] qualifiedType = Signature.toCharArray(typeSignature);
+
+ int dotCount = 0;
+ indexFound: for(int i = 0; i < typeSignature.length; i++) {
+ switch(typeSignature[i]) {
+ case C_DOT:
+ dotCount++;
+ break;
+ case C_GENERIC_START:
+ break indexFound;
+ case C_DOLLAR:
+ break indexFound;
+ }
+ }
+
+ if(dotCount > 0) {
+ int typeStart = 0;
+ for(int i = 0; i < qualifiedType.length; i++) {
+ switch (qualifiedType[i]) {
+ case '.':
+ dotCount--;
+ break;
+ case ' ':
+ typeStart = i+1;
+ break;
+ }
+ if(dotCount <= 0) {
+ char[] simpleName = CharOperation.subarray(qualifiedType, i + 1, qualifiedType.length);
+ if (typeStart > 0 && typeStart < qualifiedType.length)
+ return CharOperation.concat(CharOperation.subarray(qualifiedType, 0, typeStart), simpleName);
+ return simpleName;
+ }
+ }
+ }
+ return qualifiedType;
+}
+/**
+ * Returns type fragment of a type signature. The package fragment separator must be '.'
+ * and the type fragment separator must be '$'.
+ *
+ * For example:
+ *
+ *
+ * getSignatureSimpleName("Ljava.util.Map$Entry") -> "Map.Entry"
+ *
+ *
+ *
+ *
+ * @param typeSignature the type signature
+ * @return the type fragment (separators are '.')
+ * @since 3.1
+ */
+public static String getSignatureSimpleName(String typeSignature) {
+ return new String(getSignatureSimpleName(typeSignature == null ? null : typeSignature.toCharArray()));
+}
+/**
+ * Returns the last segment of the given dot-separated qualified name.
+ * Returns the given name if it is not qualified.
+ *
+ * For example:
+ *
+ *
+ * getSimpleName({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'O', 'b', 'j', 'e', 'c', 't'}
+ *
+ *
+ *
+ *
+ * @param name the name
+ * @return the last segment of the qualified name
+ * @exception NullPointerException if name is null
+ * @since 2.0
+ */
+public static char[] getSimpleName(char[] name) {
+
+ int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
+ int depth = 0;
+ int length = name.length;
+ lastDotLookup: for (int i = length -1; i >= 0; i--) {
+ switch (name[i]) {
+ case '.':
+ if (depth == 0) {
+ lastDot = i;
+ break lastDotLookup;
+ }
+ break;
+ case '<':
+ depth--;
+ if (depth == 0) lastGenericStart = i;
+ break;
+ case '>':
+ if (depth == 0) lastGenericEnd = i;
+ depth++;
+ break;
+ }
+ }
+ if (lastGenericStart < 0) {
+ if (lastDot < 0) {
+ return name;
+ }
+ return CharOperation.subarray(name, lastDot + 1, length);
+ }
+ StringBuffer buffer = new StringBuffer(10);
+ int nameStart = lastDot < 0 ? 0 : lastDot+1;
+ buffer.append(name, nameStart, lastGenericStart - nameStart);
+ appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer);
+ buffer.append(name, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions
+ char[] result = new char[length = buffer.length()];
+ buffer.getChars(0, length, result, 0);
+ return result;
+}
+/**
+ * Returns the last segment of the given dot-separated qualified name.
+ * Returns the given name if it is not qualified.
+ *
+ * For example:
+ *
+ *
+ * getSimpleName("java.lang.Object") -> "Object"
+ *
+ *
+ * getSimpleName("java.util.Map<java.lang.String, java.lang.Object>") -> "Map<String,Object>"
+ *
+ *
+ *
+ *
+ * @param name the name
+ * @return the last segment of the qualified name
+ * @exception NullPointerException if name is null
+ */
+public static String getSimpleName(String name) {
+ int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
+ int depth = 0;
+ int length = name.length();
+ lastDotLookup: for (int i = length -1; i >= 0; i--) {
+ switch (name.charAt(i)) {
+ case '.':
+ if (depth == 0) {
+ lastDot = i;
+ break lastDotLookup;
+ }
+ break;
+ case '<':
+ depth--;
+ if (depth == 0) lastGenericStart = i;
+ break;
+ case '>':
+ if (depth == 0) lastGenericEnd = i;
+ depth++;
+ break;
+ }
+ }
+ if (lastGenericStart < 0) {
+ if (lastDot < 0) {
+ return name;
+ }
+ return name.substring(lastDot + 1, length);
+ }
+ StringBuffer buffer = new StringBuffer(10);
+ char[] nameChars = name.toCharArray();
+ int nameStart = lastDot < 0 ? 0 : lastDot+1;
+ buffer.append(nameChars, nameStart, lastGenericStart - nameStart);
+ appendArgumentSimpleNames(nameChars, lastGenericStart, lastGenericEnd, buffer);
+ buffer.append(nameChars, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions
+ return buffer.toString();
+}
+/**
+ * Returns all segments of the given dot-separated qualified name.
+ * Returns an array with only the given name if it is not qualified.
+ * Returns an empty array if the name is empty.
+ *
+ * For example:
+ *
+ *
+ * getSimpleNames({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}
+ * getSimpleNames({'O', 'b', 'j', 'e', 'c', 't'}) -> {{'O', 'b', 'j', 'e', 'c', 't'}}
+ * getSimpleNames({}) -> {}
+ * getSimpleNames({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g'}}
+ *
+ *
+ *
+ * @param name the name
+ * @return the list of simple names, possibly empty
+ * @exception NullPointerException if name is null
+ * @since 2.0
+ */
+public static char[][] getSimpleNames(char[] name) {
+ int length = name == null ? 0 : name.length;
+ if (length == 0)
+ return CharOperation.NO_CHAR_CHAR;
+
+ int wordCount = 1;
+ countingWords: for (int i = 0; i < length; i++)
+ switch(name[i]) {
+ case C_DOT:
+ wordCount++;
+ break;
+ case C_GENERIC_START:
+ break countingWords;
+ }
+ char[][] split = new char[wordCount][];
+ int last = 0, currentWord = 0;
+ for (int i = 0; i < length; i++) {
+ if (name[i] == C_GENERIC_START) break;
+ if (name[i] == C_DOT) {
+ split[currentWord] = new char[i - last];
+ System.arraycopy(
+ name,
+ last,
+ split[currentWord++],
+ 0,
+ i - last);
+ last = i + 1;
+ }
+ }
+ split[currentWord] = new char[length - last];
+ System.arraycopy(name, last, split[currentWord], 0, length - last);
+ return split;
+}
+/**
+ * Returns all segments of the given dot-separated qualified name.
+ * Returns an array with only the given name if it is not qualified.
+ * Returns an empty array if the name is empty.
+ *
+ * For example:
+ *
+ *
+ * getSimpleNames("java.lang.Object") -> {"java", "lang", "Object"}
+ * getSimpleNames("Object") -> {"Object"}
+ * getSimpleNames("") -> {}
+ * getSimpleNames("java.util.List<java.lang.String>") ->
+ * {"java", "util", "List<java.lang.String>"}
+ *
+ *
+ *
+ * @param name the name
+ * @return the list of simple names, possibly empty
+ * @exception NullPointerException if name is null
+ */
+public static String[] getSimpleNames(String name) {
+ return CharOperation.toStrings(getSimpleNames(name.toCharArray()));
+}
+
+/**
+ * Extracts the thrown exception type signatures from the given method signature if any
+ * The method signature is expected to be dot-based.
+ *
+ * @param methodSignature the method signature
+ * @return the list of thrown exception type signatures
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 3.1
+ */
+public static char[][] getThrownExceptionTypes(char[] methodSignature) throws IllegalArgumentException {
+ // skip type parameters
+ int exceptionStart = CharOperation.indexOf(C_EXCEPTION_START, methodSignature);
+ if (exceptionStart == -1) {
+ int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature);
+ if (paren == -1) {
+ throw new IllegalArgumentException();
+ }
+ // ignore return type
+ exceptionStart = Util.scanTypeSignature(methodSignature, paren+1) + 1;
+ int length = methodSignature.length;
+ if (exceptionStart == length) return CharOperation.NO_CHAR_CHAR;
+ throw new IllegalArgumentException();
+ }
+ int length = methodSignature.length;
+ int i = exceptionStart;
+ ArrayList exceptionList = new ArrayList(1);
+ while (i < length) {
+ if (methodSignature[i] == C_EXCEPTION_START) {
+ exceptionStart++;
+ i++;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ i = Util.scanTypeSignature(methodSignature, i) + 1;
+ exceptionList.add(CharOperation.subarray(methodSignature, exceptionStart,i));
+ exceptionStart = i;
+ }
+ char[][] result;
+ exceptionList.toArray(result = new char[exceptionList.size()][]);
+ return result;
+}
+/**
+ * Extracts the thrown exception type signatures from the given method signature if any
+ * The method signature is expected to be dot-based.
+ *
+ * @param methodSignature the method signature
+ * @return the list of thrown exception type signatures
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 3.1
+ */
+public static String[] getThrownExceptionTypes(String methodSignature) throws IllegalArgumentException {
+ char[][] parameterTypes = getThrownExceptionTypes(methodSignature.toCharArray());
+ return CharOperation.toStrings(parameterTypes);
+}
+/**
+ * Extracts the type argument signatures from the given type signature.
+ * Returns an empty array if the type signature is not a parameterized type signature.
+ *
+ * @param parameterizedTypeSignature the parameterized type signature
+ * @return the signatures of the type arguments
+ * @exception IllegalArgumentException if the signature is syntactically incorrect
+ *
+ * @since 3.1
+ */
+public static char[][] getTypeArguments(char[] parameterizedTypeSignature) throws IllegalArgumentException {
+ int length = parameterizedTypeSignature.length;
+ if (length < 2 || parameterizedTypeSignature[length-2] != C_GENERIC_END)
+ // cannot have type arguments otherwise signature would end by ">;"
+ return CharOperation.NO_CHAR_CHAR;
+ int count = 1; // start to count generic end/start peers
+ int start = length - 2;
+ while (start >= 0 && count > 0) {
+ switch (parameterizedTypeSignature[--start]) {
+ case C_GENERIC_START:
+ count--;
+ break;
+ case C_GENERIC_END:
+ count++;
+ break;
+ }
+ }
+ if (start < 0) // invalid number of generic start/end
+ throw new IllegalArgumentException();
+ ArrayList args = new ArrayList();
+ int p = start + 1;
+ while (true) {
+ if (p >= parameterizedTypeSignature.length) {
+ throw new IllegalArgumentException();
+ }
+ char c = parameterizedTypeSignature[p];
+ if (c == C_GENERIC_END) {
+ int size = args.size();
+ char[][] result = new char[size][];
+ args.toArray(result);
+ return result;
+ }
+ int e = Util.scanTypeArgumentSignature(parameterizedTypeSignature, p);
+ args.add(CharOperation.subarray(parameterizedTypeSignature, p, e+1));
+ p = e + 1;
+ }
+}
+/**
+ * Extracts the type argument signatures from the given type signature.
+ * Returns an empty array if the type signature is not a parameterized type signature.
+ *
+ * @param parameterizedTypeSignature the parameterized type signature
+ * @return the signatures of the type arguments
+ * @exception IllegalArgumentException if the signature is syntactically incorrect
+ *
+ * @since 3.1
+ */
+public static String[] getTypeArguments(String parameterizedTypeSignature) throws IllegalArgumentException {
+ char[][] args = getTypeArguments(parameterizedTypeSignature.toCharArray());
+ return CharOperation.toStrings(args);
+}
+/**
+ * Extracts the type erasure signature from the given parameterized type signature.
+ * Returns the given type signature if it is not parameterized.
+ *
+ * @param parameterizedTypeSignature the parameterized type signature
+ * @return the signature of the type erasure
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 3.1
+ */
+public static char[] getTypeErasure(char[] parameterizedTypeSignature) throws IllegalArgumentException {
+ int end = CharOperation.indexOf(C_GENERIC_START, parameterizedTypeSignature);
+ if (end == -1) return parameterizedTypeSignature;
+ int length = parameterizedTypeSignature.length;
+ char[] result = new char[length];
+ int pos = 0;
+ int start = 0;
+ int deep= 0;
+ for (int idx=end; idx 0) throw new IllegalArgumentException();
+ int size = pos+length-start;
+ char[] resized = new char[size];
+ System.arraycopy(result, 0, resized, 0, pos);
+ System.arraycopy(parameterizedTypeSignature, start, resized, pos, length-start);
+ return resized;
+}
+/**
+ * Extracts the type erasure signature from the given parameterized type signature.
+ * Returns the given type signature if it is not parameterized.
+ *
+ * @param parameterizedTypeSignature the parameterized type signature
+ * @return the signature of the type erasure
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 3.1
+ */
+public static String getTypeErasure(String parameterizedTypeSignature) throws IllegalArgumentException {
+ char[] signature = parameterizedTypeSignature.toCharArray();
+ char[] erasure = getTypeErasure(signature);
+ return signature == erasure ? parameterizedTypeSignature : new String(erasure);
+}
+
+/**
+ * Extracts the class and interface bounds from the given formal type
+ * parameter signature. The class bound, if present, is listed before
+ * the interface bounds. The signature is expected to be dot-based.
+ *
+ * @param formalTypeParameterSignature the formal type parameter signature
+ * @return the (possibly empty) list of type signatures for the bounds
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ * @since 3.0
+ */
+public static char[][] getTypeParameterBounds(char[] formalTypeParameterSignature) throws IllegalArgumentException {
+ int p1 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature);
+ if (p1 < 0) {
+ // no ":" means can't be a formal type parameter signature
+ throw new IllegalArgumentException();
+ }
+ if (p1 == formalTypeParameterSignature.length - 1) {
+ // no class or interface bounds
+ return CharOperation.NO_CHAR_CHAR;
+ }
+ int p2 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature, p1 + 1);
+ char[] classBound;
+ if (p2 < 0) {
+ // no interface bounds
+ classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, formalTypeParameterSignature.length);
+ return new char[][] {classBound};
+ }
+ if (p2 == p1 + 1) {
+ // no class bound, but 1 or more interface bounds
+ classBound = null;
+ } else {
+ classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, p2);
+ }
+ char[][] interfaceBounds = CharOperation.splitOn(C_COLON, formalTypeParameterSignature, p2 + 1, formalTypeParameterSignature.length);
+ if (classBound == null) {
+ return interfaceBounds;
+ }
+ int resultLength = interfaceBounds.length + 1;
+ char[][] result = new char[resultLength][];
+ result[0] = classBound;
+ System.arraycopy(interfaceBounds, 0, result, 1, interfaceBounds.length);
+ return result;
+}
+
+/**
+ * Extracts the class and interface bounds from the given formal type
+ * parameter signature. The class bound, if present, is listed before
+ * the interface bounds. The signature is expected to be dot-based.
+ *
+ * @param formalTypeParameterSignature the formal type parameter signature
+ * @return the (possibly empty) list of type signatures for the bounds
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ * @since 3.0
+ */
+public static String[] getTypeParameterBounds(String formalTypeParameterSignature) throws IllegalArgumentException {
+ char[][] bounds = getTypeParameterBounds(formalTypeParameterSignature.toCharArray());
+ return CharOperation.toStrings(bounds);
+}
+
+/**
+ * Extracts the type parameter signatures from the given method or type signature.
+ * The method or type signature is expected to be dot-based.
+ *
+ * @param methodOrTypeSignature the method or type signature
+ * @return the list of type parameter signatures
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 3.1
+ */
+public static char[][] getTypeParameters(char[] methodOrTypeSignature) throws IllegalArgumentException {
+ try {
+ int length = methodOrTypeSignature.length;
+ if (length == 0) return CharOperation.NO_CHAR_CHAR;
+ if (methodOrTypeSignature[0] != C_GENERIC_START) return CharOperation.NO_CHAR_CHAR;
+
+ ArrayList paramList = new ArrayList(1);
+ int paramStart = 1, i = 1; // start after leading '<'
+ while (i < length) {
+ if (methodOrTypeSignature[i] == C_GENERIC_END) {
+ int size = paramList.size();
+ if (size == 0) throw new IllegalArgumentException();
+ char[][] result;
+ paramList.toArray(result = new char[size][]);
+ return result;
+ }
+ i = CharOperation.indexOf(C_COLON, methodOrTypeSignature, i);
+ if (i < 0 || i >= length)
+ throw new IllegalArgumentException();
+ // iterate over bounds
+ while (methodOrTypeSignature[i] == ':') {
+ i++; // skip colon
+ switch (methodOrTypeSignature[i]) {
+ case ':':
+ // no class bound
+ break;
+ case C_GENERIC_END:
+ break;
+ case C_RESOLVED:
+ try {
+ i = Util.scanClassTypeSignature(methodOrTypeSignature, i);
+ i++; // position at start of next param if any
+ } catch (IllegalArgumentException e) {
+ // not a class type signature -> it is a new type parameter
+ }
+ break;
+ case C_ARRAY:
+ try {
+ i = Util.scanArrayTypeSignature(methodOrTypeSignature, i);
+ i++; // position at start of next param if any
+ } catch (IllegalArgumentException e) {
+ // not an array type signature -> it is a new type parameter
+ }
+ break;
+ case C_TYPE_VARIABLE:
+ try {
+ i = Util.scanTypeVariableSignature(methodOrTypeSignature, i);
+ i++; // position at start of next param if any
+ } catch (IllegalArgumentException e) {
+ // not a type variable signature -> it is a new type parameter
+ }
+ break;
+ case C_CAPTURE:
+ try {
+ i = Util.scanCaptureTypeSignature(methodOrTypeSignature, i);
+ i++; // position at start of next param if any
+ } catch (IllegalArgumentException e) {
+ // not a capture variable signature -> it is a new type parameter
+ }
+ break;
+ // default: another type parameter is starting
+ }
+ }
+ paramList.add(CharOperation.subarray(methodOrTypeSignature, paramStart, i));
+ paramStart = i; // next param start from here
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // invalid signature, fall through
+ }
+ throw new IllegalArgumentException();
+}
+/**
+ * Extracts the type parameter signatures from the given method or type signature.
+ * The method or type signature is expected to be dot-based.
+ *
+ * @param methodOrTypeSignature the method or type signature
+ * @return the list of type parameter signatures
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ *
+ * @since 3.1
+ */
+public static String[] getTypeParameters(String methodOrTypeSignature) throws IllegalArgumentException {
+ char[][] params = getTypeParameters(methodOrTypeSignature.toCharArray());
+ return CharOperation.toStrings(params);
+}
+/**
+ * Returns the kind of type signature encoded by the given string.
+ *
+ * @param typeSignature the type signature string
+ * @return the kind of type signature; one of the kind constants:
+ * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE},
+ * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE},
+ * or (since 3.1) {@link #WILDCARD_TYPE_SIGNATURE} or {@link #CAPTURE_TYPE_SIGNATURE},
+ * or (since 3.7) {@link #INTERSECTION_TYPE_SIGNATURE}
+ * @exception IllegalArgumentException if this is not a type signature
+ * @since 3.0
+ */
+public static int getTypeSignatureKind(char[] typeSignature) {
+ // need a minimum 1 char
+ if (typeSignature.length < 1) {
+ throw new IllegalArgumentException();
+ }
+ char c = typeSignature[0];
+ if (c == C_GENERIC_START) {
+ int count = 1;
+ for (int i = 1, length = typeSignature.length; i < length; i++) {
+ switch (typeSignature[i]) {
+ case C_GENERIC_START:
+ count++;
+ break;
+ case C_GENERIC_END:
+ count--;
+ break;
+ }
+ if (count == 0) {
+ if (i+1 < length)
+ c = typeSignature[i+1];
+ break;
+ }
+ }
+ }
+ switch (c) {
+ case C_ARRAY :
+ return ARRAY_TYPE_SIGNATURE;
+ case C_RESOLVED :
+ case C_UNRESOLVED :
+ return CLASS_TYPE_SIGNATURE;
+ case C_TYPE_VARIABLE :
+ return TYPE_VARIABLE_SIGNATURE;
+ case C_BOOLEAN :
+ case C_BYTE :
+ case C_CHAR :
+ case C_DOUBLE :
+ case C_FLOAT :
+ case C_INT :
+ case C_LONG :
+ case C_SHORT :
+ case C_VOID :
+ return BASE_TYPE_SIGNATURE;
+ case C_STAR :
+ case C_SUPER :
+ case C_EXTENDS :
+ return WILDCARD_TYPE_SIGNATURE;
+ case C_CAPTURE :
+ return CAPTURE_TYPE_SIGNATURE;
+ case C_INTERSECTION :
+ return INTERSECTION_TYPE_SIGNATURE;
+ default :
+ throw new IllegalArgumentException();
+ }
+}
+
+/**
+ * Returns the kind of type signature encoded by the given string.
+ *
+ * @param typeSignature the type signature string
+ * @return the kind of type signature; one of the kind constants:
+ * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE},
+ * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE},
+ * or (since 3.1) {@link #WILDCARD_TYPE_SIGNATURE} or {@link #CAPTURE_TYPE_SIGNATURE}
+ * or (since 3.7) {@link #INTERSECTION_TYPE_SIGNATURE}
+ * @exception IllegalArgumentException if this is not a type signature
+ * @since 3.0
+ */
+public static int getTypeSignatureKind(String typeSignature) {
+ return getTypeSignatureKind(typeSignature.toCharArray());
+}
+/**
+ * Extracts the type variable name from the given formal type parameter
+ * signature. The signature is expected to be dot-based.
+ *
+ * @param formalTypeParameterSignature the formal type parameter signature
+ * @return the name of the type variable
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ * @since 3.0
+ */
+public static char[] getTypeVariable(char[] formalTypeParameterSignature) throws IllegalArgumentException {
+ int p = CharOperation.indexOf(C_COLON, formalTypeParameterSignature);
+ if (p < 0) {
+ // no ":" means can't be a formal type parameter signature
+ throw new IllegalArgumentException();
+ }
+ return CharOperation.subarray(formalTypeParameterSignature, 0, p);
+}
+/**
+ * Extracts the type variable name from the given formal type parameter
+ * signature. The signature is expected to be dot-based.
+ *
+ * @param formalTypeParameterSignature the formal type parameter signature
+ * @return the name of the type variable
+ * @exception IllegalArgumentException if the signature is syntactically
+ * incorrect
+ * @since 3.0
+ */
+public static String getTypeVariable(String formalTypeParameterSignature) throws IllegalArgumentException {
+ return new String(getTypeVariable(formalTypeParameterSignature.toCharArray()));
+}
+/**
+ * Removes any capture information from the given type or method signature
+ * and returns the resulting signature.
+ * Returns the type or method signature itself if no capture information is
+ * present.
+ *
+ * For example (using equivalent string-based method):
+ *
+ *
+ * removeCapture("LTest<!+Ljava.lang.Throwable;>;")
+ * will return: "LTest<+Ljava.lang.Throwable;>;"
+ *
+ *
+ *
+ *
+ * @param methodOrTypeSignature the signature which may have been captured
+ * @return a new signature without capture information or the signature itself
+ * if no specific capture information is present
+ * @exception NullPointerException if methodOrTypeSignature is null
+ *
+ * @since 3.1
+ */
+public static char[] removeCapture(char[] methodOrTypeSignature) {
+ return CharOperation.remove(methodOrTypeSignature, C_CAPTURE);
+}
+/**
+ * Removes any capture information from the given type or method signature
+ * and returns the resulting signature.
+ * Returns the type or method signature itself if no capture information is
+ * present.
+ *
+ * For example:
+ *
+ *
+ * removeCapture("LTest<!+Ljava.lang.Throwable;>;")
+ * will return: "LTest<+Ljava.lang.Throwable;>;"
+ *
+ *
+ *
+ *
+ * @param methodOrTypeSignature the signature which may have been captured
+ * @return a new signature without capture information or the signature itself
+ * if no specific capture information is present
+ * @exception NullPointerException if methodOrTypeSignature is null
+ *
+ * @since 3.1
+ */
+public static String removeCapture(String methodOrTypeSignature) {
+ char[] array = methodOrTypeSignature.toCharArray();
+ char[] result = removeCapture(array);
+ if (array == result) return methodOrTypeSignature;
+ return new String(result);
+}
+/**
+ * Converts the given type signature to a readable string. The signature is expected to
+ * be dot-based.
+ *
+ *
+ * For example:
+ *
+ *
+ * toString({'[', 'L', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', ';'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '[', ']'}
+ * toString({'I'}) -> {'i', 'n', 't'}
+ * toString({'+', 'L', 'O', 'b', 'j', 'e', 'c', 't', ';'}) -> {'?', ' ', 'e', 'x', 't', 'e', 'n', 'd', 's', ' ', 'O', 'b', 'j', 'e', 'c', 't'}
+ *
+ *
+ *
+ *
+ * Note: This method assumes that a type signature containing a '$'
+ * is an inner type signature. While this is correct in most cases, someone could
+ * define a non-inner type name containing a '$'. Handling this
+ * correctly in all cases would have required resolving the signature, which
+ * generally not feasible.
+ *
+ *
+ * @param signature the type signature
+ * @return the string representation of the type
+ * @exception IllegalArgumentException if the signature is syntactically incorrect
+ *
+ * @since 2.0
+ */
+public static char[] toCharArray(char[] signature) throws IllegalArgumentException {
+ int sigLength = signature.length;
+ if (sigLength == 0) {
+ throw new IllegalArgumentException();
+ }
+ if (signature[0] == C_PARAM_START || signature[0] == C_GENERIC_START) {
+ return toCharArray(signature, CharOperation.NO_CHAR, null, true, true);
+ }
+
+ StringBuffer buffer = new StringBuffer(signature.length + 10);
+ appendTypeSignature(signature, 0, true, buffer);
+ char[] result = new char[buffer.length()];
+ buffer.getChars(0, buffer.length(), result, 0);
+ return result;
+}
+/**
+ * Converts the given method signature to a readable form. The method signature is expected to
+ * be dot-based.
+ *
+ * For example:
+ *
+ *
+ * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
+ *
+ *
+ *
+ *
+ * @param methodSignature the method signature to convert
+ * @param methodName the name of the method to insert in the result, or
+ * null if no method name is to be included
+ * @param parameterNames the parameter names to insert in the result, or
+ * null if no parameter names are to be included; if supplied,
+ * the number of parameter names must match that of the method signature
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param includeReturnType true if the return type is to be
+ * included
+ * @return the char array representation of the method signature
+ * @throws IllegalArgumentException if the method signature is syntactically incorrect
+ * @since 2.0
+ */
+public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) {
+ return toCharArray(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false);
+}
+
+/**
+ * Converts the given method signature to a readable form. The method signature is expected to
+ * be dot-based.
+ *
+ * For example:
+ *
+ *
+ * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
+ *
+ *
+ *
+ *
+ * @param methodSignature the method signature to convert
+ * @param methodName the name of the method to insert in the result, or
+ * null if no method name is to be included
+ * @param parameterNames the parameter names to insert in the result, or
+ * null if no parameter names are to be included; if supplied,
+ * the number of parameter names must match that of the method signature
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param includeReturnType true if the return type is to be
+ * included
+ * @param isVargArgs true if the last argument should be displayed as a
+ * variable argument, false otherwise.
+ * @return the char array representation of the method signature
+ * @throws IllegalArgumentException if the method signature is syntactically incorrect
+ *
+ * @since 3.1
+ */
+public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVargArgs) {
+ int firstParen = CharOperation.indexOf(C_PARAM_START, methodSignature);
+ if (firstParen == -1) {
+ throw new IllegalArgumentException();
+ }
+
+ StringBuffer buffer = new StringBuffer(methodSignature.length + 10);
+
+ // return type
+ if (includeReturnType) {
+ char[] rts = getReturnType(methodSignature);
+ appendTypeSignature(rts, 0 , fullyQualifyTypeNames, buffer);
+ buffer.append(' ');
+ }
+
+ // selector
+ if (methodName != null) {
+ buffer.append(methodName);
+ }
+
+ // parameters
+ buffer.append('(');
+ char[][] pts = getParameterTypes(methodSignature);
+ // search for the last array in the signature
+ int max = pts.length;
+ int index = max - 1;
+ loop: for (int i = index; i >= 0; i--) {
+ if (pts[i][0] == Signature.C_ARRAY) {
+ break loop;
+ }
+ index--;
+ }
+ for (int i = 0; i < max; i++) {
+ if (i == index) {
+ appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer, isVargArgs);
+ } else {
+ appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer);
+ }
+ if (parameterNames != null) {
+ buffer.append(' ');
+ buffer.append(parameterNames[i]);
+ }
+ if (i != pts.length - 1) {
+ buffer.append(',');
+ buffer.append(' ');
+ }
+ }
+ buffer.append(')');
+ char[] result = new char[buffer.length()];
+ buffer.getChars(0, buffer.length(), result, 0);
+ return result;
+}
+
+/**
+ * Converts the given array of qualified name segments to a qualified name.
+ *
+ * For example:
+ *
+ *
+ * toQualifiedName({{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}
+ * toQualifiedName({{'O', 'b', 'j', 'e', 'c', 't'}}) -> {'O', 'b', 'j', 'e', 'c', 't'}
+ * toQualifiedName({{}}) -> {}
+ *
+ *
+ *
+ *
+ * @param segments the list of name segments, possibly empty
+ * @return the dot-separated qualified name, or the empty string
+ *
+ * @since 2.0
+ */
+public static char[] toQualifiedName(char[][] segments) {
+ int length = segments.length;
+ if (length == 0) return CharOperation.NO_CHAR;
+ if (length == 1) return segments[0];
+
+ int resultLength = 0;
+ for (int i = 0; i < length; i++) {
+ resultLength += segments[i].length+1;
+ }
+ resultLength--;
+ char[] result = new char[resultLength];
+ int index = 0;
+ for (int i = 0; i < length; i++) {
+ char[] segment = segments[i];
+ int segmentLength = segment.length;
+ System.arraycopy(segment, 0, result, index, segmentLength);
+ index += segmentLength;
+ if (i != length-1) {
+ result[index++] = C_DOT;
+ }
+ }
+ return result;
+}
+
+/**
+ * Converts the given array of qualified name segments to a qualified name.
+ *
+ * For example:
+ *
+ *
+ * toQualifiedName(new String[] {"java", "lang", "Object"}) -> "java.lang.Object"
+ * toQualifiedName(new String[] {"Object"}) -> "Object"
+ * toQualifiedName(new String[0]) -> ""
+ *
+ *
+ *
+ *
+ * @param segments the list of name segments, possibly empty
+ * @return the dot-separated qualified name, or the empty string
+ */
+public static String toQualifiedName(String[] segments) {
+ int length = segments.length;
+ char[][] charArrays = new char[length][];
+ for (int i = 0; i < length; i++) {
+ charArrays[i] = segments[i].toCharArray();
+ }
+ return new String(toQualifiedName(charArrays));
+}
+/**
+ * Converts the given type signature to a readable string. The signature is expected to
+ * be dot-based.
+ *
+ *
+ * For example:
+ *
+ *
+ * toString("[Ljava.lang.String;") -> "java.lang.String[]"
+ * toString("I") -> "int"
+ * toString("+QObject;") -> "? extends Object"
+ *
+ *
+ *
+ *
+ * Note: This method assumes that a type signature containing a '$'
+ * is an inner type signature. While this is correct in most cases, someone could
+ * define a non-inner type name containing a '$'. Handling this
+ * correctly in all cases would have required resolving the signature, which
+ * generally not feasible.
+ *
+ *
+ * @param signature the type signature
+ * @return the string representation of the type
+ * @exception IllegalArgumentException if the signature is not syntactically
+ * correct
+ */
+public static String toString(String signature) throws IllegalArgumentException {
+ return new String(toCharArray(signature.toCharArray()));
+}
+/**
+ * Converts the given method signature to a readable string. The method signature is expected to
+ * be dot-based.
+ *
+ * @param methodSignature the method signature to convert
+ * @param methodName the name of the method to insert in the result, or
+ * null if no method name is to be included
+ * @param parameterNames the parameter names to insert in the result, or
+ * null if no parameter names are to be included; if supplied,
+ * the number of parameter names must match that of the method signature
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param includeReturnType true if the return type is to be
+ * included
+ * @see #toCharArray(char[], char[], char[][], boolean, boolean)
+ * @return the string representation of the method signature
+ */
+public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) {
+ return toString(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false);
+}
+/**
+ * Converts the given method signature to a readable string. The method signature is expected to
+ * be dot-based.
+ *
+ * @param methodSignature the method signature to convert
+ * @param methodName the name of the method to insert in the result, or
+ * null if no method name is to be included
+ * @param parameterNames the parameter names to insert in the result, or
+ * null if no parameter names are to be included; if supplied,
+ * the number of parameter names must match that of the method signature
+ * @param fullyQualifyTypeNames true if type names should be fully
+ * qualified, and false to use only simple names
+ * @param includeReturnType true if the return type is to be
+ * included
+ * @param isVarArgs true if the last argument should be displayed as a
+ * variable argument, false otherwise
+ * @see #toCharArray(char[], char[], char[][], boolean, boolean)
+ * @return the string representation of the method signature
+ *
+ * @since 3.1
+ */
+public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVarArgs) {
+ char[][] params;
+ if (parameterNames == null) {
+ params = null;
+ } else {
+ int paramLength = parameterNames.length;
+ params = new char[paramLength][];
+ for (int i = 0; i < paramLength; i++) {
+ params[i] = parameterNames[i].toCharArray();
+ }
+ }
+ return new String(toCharArray(methodSignature.toCharArray(), methodName == null ? null : methodName.toCharArray(), params, fullyQualifyTypeNames, includeReturnType, isVarArgs));
+}
+private Signature() {
+ // Not instantiable
+}
+}
diff --git a/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java b/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java
new file mode 100644
index 000000000..732695f62
--- /dev/null
+++ b/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.tool;
+
+import com.itsaky.androidide.services.compiler.CompilerProgress;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.WeakHashMap;
+import javax.annotation.processing.Processor;
+import javax.lang.model.SourceVersion;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+import org.eclipse.jdt.internal.compiler.batch.Main;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+
+/**
+ * Implementation of a batch compiler that supports the jsr199
+ */
+public class EclipseCompiler implements JavaCompiler {
+
+ private static Set SupportedSourceVersions;
+
+ static {
+ // Eclipse compiler supports all possible versions from version 0 to
+ // version 6
+ // we don't care about the order
+ EnumSet enumSet = EnumSet.range(SourceVersion.RELEASE_0, SourceVersion.RELEASE_6);
+ // we don't want anybody to modify this list
+ EclipseCompiler.SupportedSourceVersions = Collections.unmodifiableSet(enumSet);
+ }
+
+ WeakHashMap threadCache;
+ public DiagnosticListener super JavaFileObject> diagnosticListener;
+
+ private CompilerProgress progress;
+
+ public EclipseCompiler() {
+ this.threadCache = new WeakHashMap<>();
+ }
+
+ public EclipseCompiler setCompilationCancelled() {
+ return setCompilationCancelled(true);
+ }
+
+ public EclipseCompiler setCompilationCancelled(boolean p0) {
+ if(this.progress != null)
+ this.progress.setCanceled(p0);
+ return this;
+ }
+
+ public EclipseCompiler newProgress() {
+ this.progress = new CompilerProgress();
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.tools.Tool#getSourceVersions()
+ */
+ @Override
+ public Set getSourceVersions() {
+ return EclipseCompiler.SupportedSourceVersions;
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.tools.JavaCompiler#getStandardFileManager(javax.tools.DiagnosticListener,
+ * java.util.Locale, java.nio.charset.Charset)
+ */
+ @Override
+ public StandardJavaFileManager getStandardFileManager(DiagnosticListener super JavaFileObject> someDiagnosticListener, Locale locale, Charset charset) {
+ this.diagnosticListener = someDiagnosticListener;
+ return new EclipseFileManager(locale, charset);
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.tools.JavaCompiler#getTask(java.io.Writer,
+ * javax.tools.JavaFileManager, javax.tools.DiagnosticListener,
+ * java.lang.Iterable, java.lang.Iterable, java.lang.Iterable)
+ */
+ @Override
+ public CompilationTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener super JavaFileObject> someDiagnosticListener, Iterable options, Iterable classes, Iterable extends JavaFileObject> compilationUnits) {
+ PrintWriter writerOut = null;
+ PrintWriter writerErr = null;
+ if (out == null) {
+ writerOut = new PrintWriter(System.err);
+ writerErr = new PrintWriter(System.err);
+ } else {
+ writerOut = new PrintWriter(out);
+ writerErr = new PrintWriter(out);
+ }
+ final Thread currentThread = Thread.currentThread();
+ EclipseCompilerImpl eclipseCompiler = this.threadCache.get(currentThread);
+ if (eclipseCompiler == null) {
+ eclipseCompiler = new EclipseCompilerImpl(writerOut, writerErr, false);
+ this.threadCache.put(currentThread, eclipseCompiler);
+ } else {
+ eclipseCompiler.initialize(writerOut, writerErr, false, null/*options*/, null/*progress*/);
+ }
+ final EclipseCompilerImpl eclipseCompiler2 = new EclipseCompilerImpl(writerOut, writerErr, false);
+ eclipseCompiler2.compilationUnits = compilationUnits;
+ eclipseCompiler2.diagnosticListener = someDiagnosticListener;
+ eclipseCompiler2.progress = eclipseCompiler.progress = this.progress;
+ if (fileManager != null) {
+ eclipseCompiler2.fileManager = fileManager;
+ } else {
+ eclipseCompiler2.fileManager = this.getStandardFileManager(someDiagnosticListener, null, null);
+ }
+
+ eclipseCompiler2.options.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_6);
+ eclipseCompiler2.options.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_6);
+ eclipseCompiler2.options.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_6);
+
+ ArrayList allOptions = new ArrayList<>();
+ if (options != null) {
+ for (Iterator iterator = options.iterator(); iterator.hasNext(); ) {
+ eclipseCompiler2.fileManager.handleOption(iterator.next(), iterator);
+ }
+ for (String option : options) {
+ allOptions.add(option);
+ }
+ }
+
+ if (compilationUnits != null) {
+ for (JavaFileObject javaFileObject : compilationUnits) {
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6419926
+ // compells us to check that the returned URIs are absolute,
+ // which they happen not to be for the default compiler on some
+ // unices
+ URI uri = javaFileObject.toUri();
+ if (!uri.isAbsolute()) {
+ uri = URI.create("file://" + uri.toString()); //$NON-NLS-1$
+ }
+ if (uri.getScheme().equals("file")) { //$NON-NLS-1$
+ allOptions.add(new File(uri).getAbsolutePath());
+ } else {
+ allOptions.add(uri.toString());
+ }
+ }
+ }
+
+ if (classes != null) {
+ allOptions.add("-classNames"); //$NON-NLS-1$
+ StringBuilder builder = new StringBuilder();
+ int i = 0;
+ for (String className : classes) {
+ if (i != 0) {
+ builder.append(',');
+ }
+ builder.append(className);
+ i++;
+ }
+ allOptions.add(String.valueOf(builder));
+ }
+
+ final String[] optionsToProcess = new String[allOptions.size()];
+ allOptions.toArray(optionsToProcess);
+ try {
+ eclipseCompiler2.configure(optionsToProcess);
+ } catch (IllegalArgumentException e) {
+ throw e;
+ }
+
+ if (eclipseCompiler2.fileManager instanceof StandardJavaFileManager) {
+ StandardJavaFileManager javaFileManager = (StandardJavaFileManager) eclipseCompiler2.fileManager;
+ Iterable extends File> location = javaFileManager.getLocation(StandardLocation.CLASS_OUTPUT);
+ if (location != null) {
+ eclipseCompiler2.setDestinationPath(location.iterator().next().getAbsolutePath());
+ }
+ }
+
+ return new CompilationTask() {
+ private boolean hasRun = false;
+ @Override
+ public Boolean call() {
+ // set up compiler with passed options
+ if (this.hasRun) {
+ throw new IllegalStateException("This task has already been run"); //$NON-NLS-1$
+ }
+ Boolean value = eclipseCompiler2.call() ? Boolean.TRUE : Boolean.FALSE;
+ this.hasRun = true;
+ return value;
+ }
+ @Override
+ public void setLocale(Locale locale) {
+ eclipseCompiler2.setLocale(locale);
+ }
+ @Override
+ public void setProcessors(Iterable extends Processor> processors) {
+ ArrayList temp = new ArrayList<>();
+ for (Processor processor : processors) {
+ temp.add(processor);
+ }
+ Processor[] processors2 = new Processor[temp.size()];
+ temp.toArray(processors2);
+ eclipseCompiler2.processors = processors2;
+ }
+ };
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.tools.OptionChecker#isSupportedOption(java.lang.String)
+ */
+ @Override
+ public int isSupportedOption(String option) {
+ return Options.processOptions(option);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.tools.Tool#run(java.io.InputStream, java.io.OutputStream,
+ * java.io.OutputStream, java.lang.String[])
+ */
+ @Override
+ public int run(InputStream in, OutputStream out, OutputStream err, String... arguments) {
+ boolean succeed = new Main(
+ new PrintWriter(new OutputStreamWriter(out != null ? out : System.out)),
+ new PrintWriter(new OutputStreamWriter(err != null ? err : System.err)),
+ true/* systemExit */,
+ null/* options */,
+ null/* progress */).compile(arguments);
+ return succeed ? 0 : -1;
+ }
+}
diff --git a/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java b/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java
new file mode 100644
index 000000000..d0f7e40a7
--- /dev/null
+++ b/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java
@@ -0,0 +1,594 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * IBM Corporation - fix for 342936
+ * Kenneth Olson - Contribution for bug 188796 - [jsr199] Using JSR199 to extend ECJ
+ * Dennis Hendriks - Contribution for bug 188796 - [jsr199] Using JSR199 to extend ECJ
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.tool;
+
+import com.blankj.utilcode.util.ThrowableUtils;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import javax.annotation.processing.Processor;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.core.compiler.CompilationProgress;
+import org.eclipse.jdt.internal.compiler.ClassFile;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
+import org.eclipse.jdt.internal.compiler.IProblemFactory;
+import org.eclipse.jdt.internal.compiler.batch.ClasspathJsr199;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
+import org.eclipse.jdt.internal.compiler.batch.Main;
+import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
+import org.eclipse.jdt.internal.compiler.util.Messages;
+import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
+
+public class EclipseCompilerImpl extends Main {
+ private static final CompilationUnit[] NO_UNITS = new CompilationUnit[0];
+ private HashMap javaFileObjectMap;
+ Iterable extends JavaFileObject> compilationUnits;
+ public JavaFileManager fileManager;
+ protected Processor[] processors;
+ public DiagnosticListener super JavaFileObject> diagnosticListener;
+
+ public EclipseCompilerImpl(PrintWriter out, PrintWriter err, boolean systemExitWhenFinished) {
+ super(out, err, systemExitWhenFinished, null/*options*/, null/*progress*/);
+ }
+
+ public boolean call() {
+ try {
+ if(proceed) {
+ this.globalProblemsCount = 0;
+ this.globalErrorsCount = 0;
+ this.globalWarningsCount = 0;
+ this.globalTasksCount = 0;
+ this.exportedClassFilesCounter = 0;
+ // request compilation
+ performCompilation();
+ }
+ } catch(IllegalArgumentException e) {
+ this.logger.logException(e);
+ com.itsaky.androidide.utils.Logger.instance().e("[EclipseCompilerImpl]", ThrowableUtils.getFullStackTrace(e));
+ if (this.systemExitWhenFinished) {
+ cleanup();
+ System.exit(-1);
+ }
+ return false;
+ } catch (RuntimeException e) { // internal compiler failure
+ com.itsaky.androidide.utils.Logger.instance().e("[EclipseCompilerImpl]", ThrowableUtils.getFullStackTrace(e));
+ this.logger.logException(e);
+ return false;
+ } finally {
+ cleanup();
+ }
+ if (this.globalErrorsCount == 0)
+ return true;
+ return false;
+ }
+
+ private void cleanup() {
+ this.logger.flush();
+ this.logger.close();
+ this.processors = null;
+ try {
+ if (this.fileManager != null) {
+ this.fileManager.flush();
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ @Override
+ public CompilationUnit[] getCompilationUnits() {
+ if (this.compilationUnits == null) return EclipseCompilerImpl.NO_UNITS;
+ ArrayList units = new ArrayList<>();
+ for (final JavaFileObject javaFileObject : this.compilationUnits) {
+ if (javaFileObject.getKind() != JavaFileObject.Kind.SOURCE) {
+ throw new IllegalArgumentException();
+ }
+ String name = javaFileObject.getName();
+ name = name.replace('\\', '/');
+ CompilationUnit compilationUnit = new CompilationUnit(null,
+ name,
+ null) {
+
+ @Override
+ public char[] getContents() {
+ try {
+ return javaFileObject.getCharContent(true).toString().toCharArray();
+ } catch(IOException e) {
+ e.printStackTrace();
+ throw new AbortCompilationUnit(null, e, null);
+ }
+ }
+ };
+ units.add(compilationUnit);
+ this.javaFileObjectMap.put(compilationUnit, javaFileObject);
+ }
+ CompilationUnit[] result = new CompilationUnit[units.size()];
+ units.toArray(result);
+ return result;
+ }
+ /*
+ * Low-level API performing the actual compilation
+ */
+ @Override
+ public IErrorHandlingPolicy getHandlingPolicy() {
+ // passes the initial set of files to the batch oracle (to avoid finding more than once the same units when case insensitive match)
+ return new IErrorHandlingPolicy() {
+ @Override
+ public boolean proceedOnErrors() {
+ return false; // stop if there are some errors
+ }
+ @Override
+ public boolean stopOnFirstError() {
+ return false;
+ }
+ @Override
+ public boolean ignoreAllErrors() {
+ return false;
+ }
+ };
+ }
+
+ @Override
+ public IProblemFactory getProblemFactory() {
+ return new DefaultProblemFactory() {
+ @Override
+ public CategorizedProblem createProblem(
+ final char[] originatingFileName,
+ final int problemId,
+ final String[] problemArguments,
+ final String[] messageArguments,
+ final int severity,
+ final int startPosition,
+ final int endPosition,
+ final int lineNumber,
+ final int columnNumber) {
+
+ DiagnosticListener super JavaFileObject> diagListener = EclipseCompilerImpl.this.diagnosticListener;
+ if (diagListener != null) {
+ diagListener.report(new Diagnostic() {
+ @Override
+ public String getCode() {
+ return Integer.toString(problemId);
+ }
+ @Override
+ public long getColumnNumber() {
+ return columnNumber;
+ }
+ @Override
+ public long getEndPosition() {
+ return endPosition;
+ }
+ @Override
+ public Kind getKind() {
+ if ((severity & ProblemSeverities.Error) != 0) {
+ return Diagnostic.Kind.ERROR;
+ }
+ if ((severity & ProblemSeverities.Optional) != 0) {
+ return Diagnostic.Kind.WARNING;
+ }
+ if ((severity & ProblemSeverities.Warning) != 0) {
+ return Diagnostic.Kind.MANDATORY_WARNING;
+ }
+ return Diagnostic.Kind.OTHER;
+ }
+ @Override
+ public long getLineNumber() {
+ return lineNumber;
+ }
+ @Override
+ public String getMessage(Locale locale) {
+ if (locale != null) {
+ setLocale(locale);
+ }
+ return getLocalizedMessage(problemId, problemArguments);
+ }
+ @Override
+ public long getPosition() {
+ return startPosition;
+ }
+ @Override
+ public JavaFileObject getSource() {
+ File f = new File(new String(originatingFileName));
+ if (f.exists()) {
+ return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
+ }
+ return null;
+ }
+ @Override
+ public long getStartPosition() {
+ return startPosition;
+ }
+ });
+ }
+ return super.createProblem(originatingFileName, problemId, problemArguments, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
+ }
+ @Override
+ public CategorizedProblem createProblem(
+ final char[] originatingFileName,
+ final int problemId,
+ final String[] problemArguments,
+ final int elaborationID,
+ final String[] messageArguments,
+ final int severity,
+ final int startPosition,
+ final int endPosition,
+ final int lineNumber,
+ final int columnNumber) {
+
+ DiagnosticListener super JavaFileObject> diagListener = EclipseCompilerImpl.this.diagnosticListener;
+ if (diagListener != null) {
+ diagListener.report(new Diagnostic() {
+ @Override
+ public String getCode() {
+ return Integer.toString(problemId);
+ }
+ @Override
+ public long getColumnNumber() {
+ return columnNumber;
+ }
+ @Override
+ public long getEndPosition() {
+ return endPosition;
+ }
+ @Override
+ public Kind getKind() {
+ if ((severity & ProblemSeverities.Error) != 0) {
+ return Diagnostic.Kind.ERROR;
+ }
+ if ((severity & ProblemSeverities.Info) != 0) {
+ return Diagnostic.Kind.NOTE;
+ }
+ if ((severity & ProblemSeverities.Optional) != 0) {
+ return Diagnostic.Kind.WARNING;
+ }
+ if ((severity & ProblemSeverities.Warning) != 0) {
+ return Diagnostic.Kind.MANDATORY_WARNING;
+ }
+ return Diagnostic.Kind.OTHER;
+ }
+ @Override
+ public long getLineNumber() {
+ return lineNumber;
+ }
+ @Override
+ public String getMessage(Locale locale) {
+ if (locale != null) {
+ setLocale(locale);
+ }
+ return getLocalizedMessage(problemId, problemArguments);
+ }
+ @Override
+ public long getPosition() {
+ return startPosition;
+ }
+ @Override
+ public JavaFileObject getSource() {
+ File f = new File(new String(originatingFileName));
+ if (f.exists()) {
+ return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
+ }
+ return null;
+ }
+ @Override
+ public long getStartPosition() {
+ return startPosition;
+ }
+ });
+ }
+ return super.createProblem(originatingFileName, problemId, problemArguments, elaborationID, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
+ }
+ };
+ }
+
+ @Override
+ protected void initialize(PrintWriter outWriter, PrintWriter errWriter, boolean systemExit, Map customDefaultOptions, CompilationProgress compilationProgress) {
+ super.initialize(outWriter, errWriter, systemExit, customDefaultOptions, compilationProgress);
+ this.javaFileObjectMap = new HashMap<>();
+ }
+
+ @Override
+ protected void initializeAnnotationProcessorManager() {
+ super.initializeAnnotationProcessorManager();
+ if (this.batchCompiler.annotationProcessorManager != null && this.processors != null) {
+ this.batchCompiler.annotationProcessorManager.setProcessors(this.processors);
+ } else if (this.processors != null) {
+ throw new UnsupportedOperationException("Cannot handle annotation processing"); //$NON-NLS-1$
+ }
+ }
+
+ // Dump classfiles onto disk for all compilation units that where successful
+ // and do not carry a -d none spec, either directly or inherited from Main.
+ @Override
+ public void outputClassFiles(CompilationResult unitResult) {
+ if (!((unitResult == null) || (unitResult.hasErrors() && !this.proceedOnError))) {
+ ClassFile[] classFiles = unitResult.getClassFiles();
+ boolean generateClasspathStructure = this.fileManager.hasLocation(StandardLocation.CLASS_OUTPUT);
+ String currentDestinationPath = this.destinationPath;
+ File outputLocation = null;
+ if (currentDestinationPath != null) {
+ outputLocation = new File(currentDestinationPath);
+ outputLocation.mkdirs();
+ }
+ for (int i = 0, fileCount = classFiles.length; i < fileCount; i++) {
+ // retrieve the key and the corresponding classfile
+ ClassFile classFile = classFiles[i];
+ char[] filename = classFile.fileName();
+ int length = filename.length;
+ char[] relativeName = new char[length + 6];
+ System.arraycopy(filename, 0, relativeName, 0, length);
+ System.arraycopy(SuffixConstants.SUFFIX_class, 0, relativeName, length, 6);
+ CharOperation.replace(relativeName, '/', File.separatorChar);
+ String relativeStringName = new String(relativeName);
+ if (this.compilerOptions.verbose) {
+ EclipseCompilerImpl.this.out.println(
+ Messages.bind(
+ Messages.compilation_write,
+ new String[] {
+ String.valueOf(this.exportedClassFilesCounter+1),
+ relativeStringName
+ }));
+ }
+ try {
+ JavaFileObject javaFileForOutput =
+ this.fileManager.getJavaFileForOutput(
+ StandardLocation.CLASS_OUTPUT,
+ new String(filename),
+ JavaFileObject.Kind.CLASS,
+ this.javaFileObjectMap.get(unitResult.compilationUnit));
+
+ if (generateClasspathStructure) {
+ if (currentDestinationPath != null) {
+ int index = CharOperation.lastIndexOf(File.separatorChar, relativeName);
+ if (index != -1) {
+ File currentFolder = new File(currentDestinationPath, relativeStringName.substring(0, index));
+ currentFolder.mkdirs();
+ }
+ } else {
+ // create the subfolfers is necessary
+ // need a way to retrieve the folders to create
+ String path = javaFileForOutput.toUri().getPath();
+ int index = path.lastIndexOf('/');
+ if (index != -1) {
+ File file = new File(path.substring(0, index));
+ file.mkdirs();
+ }
+ }
+ }
+
+ try (OutputStream openOutputStream = javaFileForOutput.openOutputStream(); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(openOutputStream)) {
+ bufferedOutputStream.write(classFile.header, 0, classFile.headerOffset);
+ bufferedOutputStream.write(classFile.contents, 0, classFile.contentsOffset);
+ bufferedOutputStream.flush();
+ }
+ } catch (IOException e) {
+ this.logger.logNoClassFileCreated(currentDestinationPath, relativeStringName, e);
+ }
+ this.logger.logClassFile(
+ generateClasspathStructure,
+ currentDestinationPath,
+ relativeStringName);
+ this.exportedClassFilesCounter++;
+ }
+ this.batchCompiler.lookupEnvironment.releaseClassFiles(classFiles);
+ }
+ }
+
+ @Override
+ protected void setPaths(ArrayList bootclasspaths,
+ String sourcepathClasspathArg,
+ ArrayList sourcepathClasspaths,
+ ArrayList classpaths,
+ ArrayList extdirsClasspaths,
+ ArrayList endorsedDirClasspaths,
+ String customEncoding) {
+
+ ArrayList fileSystemClasspaths = new ArrayList<>();
+ EclipseFileManager eclipseJavaFileManager = null;
+ StandardJavaFileManager standardJavaFileManager = null;
+ JavaFileManager javaFileManager = null;
+ boolean havePlatformPaths = false;
+ boolean haveClassPaths = false;
+ if (this.fileManager instanceof EclipseFileManager) {
+ eclipseJavaFileManager = (EclipseFileManager) this.fileManager;
+ }
+ if (this.fileManager instanceof StandardJavaFileManager) {
+ standardJavaFileManager = (StandardJavaFileManager) this.fileManager;
+ }
+ javaFileManager = this.fileManager;
+
+ if (eclipseJavaFileManager != null) {
+ if ((eclipseJavaFileManager.flags & EclipseFileManager.HAS_ENDORSED_DIRS) == 0
+ && (eclipseJavaFileManager.flags & EclipseFileManager.HAS_BOOTCLASSPATH) != 0) {
+ fileSystemClasspaths.addAll(this.handleEndorseddirs(null));
+ }
+ }
+ Iterable extends File> location = null;
+ if (standardJavaFileManager != null) {
+ location = standardJavaFileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH);
+ if (location != null) {
+ for (File file : location) {
+ Classpath classpath = FileSystem.getClasspath(
+ file.getAbsolutePath(),
+ null,
+ null, this.options);
+ if (classpath != null) {
+ fileSystemClasspaths.add(classpath);
+ havePlatformPaths = true;
+ }
+ }
+ }
+ } else if (javaFileManager != null) {
+ Classpath classpath = new ClasspathJsr199(this.fileManager, StandardLocation.PLATFORM_CLASS_PATH);
+ fileSystemClasspaths.add(classpath);
+ havePlatformPaths = true;
+ }
+ if (eclipseJavaFileManager != null) {
+ if ((eclipseJavaFileManager.flags & EclipseFileManager.HAS_EXT_DIRS) == 0
+ && (eclipseJavaFileManager.flags & EclipseFileManager.HAS_BOOTCLASSPATH) != 0) {
+ fileSystemClasspaths.addAll(this.handleExtdirs(null));
+ }
+ }
+ if (standardJavaFileManager != null) {
+ location = standardJavaFileManager.getLocation(StandardLocation.SOURCE_PATH);
+ if (location != null) {
+ for (File file : location) {
+ Classpath classpath = FileSystem.getClasspath(
+ file.getAbsolutePath(),
+ null,
+ null, this.options);
+ if (classpath != null) {
+ fileSystemClasspaths.add(classpath);
+ }
+ }
+ }
+ location = standardJavaFileManager.getLocation(StandardLocation.CLASS_PATH);
+ if (location != null) {
+ for (File file : location) {
+ Classpath classpath = FileSystem.getClasspath(
+ file.getAbsolutePath(),
+ null,
+ null, this.options);
+ if (classpath != null) {
+ fileSystemClasspaths.add(classpath);
+ haveClassPaths = true;
+ }
+ }
+ }
+ } else if (javaFileManager != null) {
+ Classpath classpath = null;
+ if (this.fileManager.hasLocation(StandardLocation.SOURCE_PATH)) {
+ classpath = new ClasspathJsr199(this.fileManager, StandardLocation.SOURCE_PATH);
+ fileSystemClasspaths.add(classpath);
+ }
+ classpath = new ClasspathJsr199(this.fileManager, StandardLocation.CLASS_PATH);
+ fileSystemClasspaths.add(classpath);
+ haveClassPaths = true;
+ }
+ if (this.checkedClasspaths == null) {
+ // It appears to be necessary to handleBootclasspath() for IBM JVMs
+ // in order to have visibility to java.lang.String (not present in rt.jar).
+ // The jars returned by StandardFileManager.getLocation(PLATFORM_CLASS_PATH) are
+ // not sufficient to resolve all standard classes.
+ if (!havePlatformPaths) fileSystemClasspaths.addAll(this.handleBootclasspath(null, null));
+ if (!haveClassPaths) fileSystemClasspaths.addAll(this.handleClasspath(null, null));
+ }
+ fileSystemClasspaths = FileSystem.ClasspathNormalizer.normalize(fileSystemClasspaths);
+ final int size = fileSystemClasspaths.size();
+ if (size != 0) {
+ this.checkedClasspaths = new FileSystem.Classpath[size];
+ int i = 0;
+ for (FileSystem.Classpath classpath : fileSystemClasspaths) {
+ this.checkedClasspaths[i++] = classpath;
+ }
+ }
+ }
+ @Override
+ protected void loggingExtraProblems() {
+ super.loggingExtraProblems();
+ for (@SuppressWarnings("rawtypes")
+ Iterator iterator = this.extraProblems.iterator(); iterator.hasNext(); ) {
+ final CategorizedProblem problem = (CategorizedProblem) iterator.next();
+ if (this.diagnosticListener != null) {
+ this.diagnosticListener.report(new Diagnostic() {
+ @Override
+ public String getCode() {
+ return null;
+ }
+ @Override
+ public long getColumnNumber() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).column;
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public long getEndPosition() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).getSourceEnd();
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public Kind getKind() {
+ if (problem.isError()) {
+ return Diagnostic.Kind.ERROR;
+ }
+ if (problem.isWarning()) {
+ return Diagnostic.Kind.WARNING;
+ } else if (problem instanceof DefaultProblem && ((DefaultProblem) problem).isInfo()) {
+ return Diagnostic.Kind.NOTE;
+ }
+ return Diagnostic.Kind.OTHER;
+ }
+ @Override
+ public long getLineNumber() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).getSourceLineNumber();
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public String getMessage(Locale locale) {
+ return problem.getMessage();
+ }
+ @Override
+ public long getPosition() {
+ if (problem instanceof DefaultProblem) {
+ return ((DefaultProblem) problem).getSourceStart();
+ }
+ return Diagnostic.NOPOS;
+ }
+ @Override
+ public JavaFileObject getSource() {
+ if (problem instanceof DefaultProblem) {
+ File f = new File(new String(((DefaultProblem) problem).getOriginatingFileName()));
+ if (f.exists()) {
+ Charset charset = (EclipseCompilerImpl.this.fileManager instanceof EclipseFileManager) ?
+ ((EclipseFileManager) EclipseCompilerImpl.this.fileManager).charset : Charset.defaultCharset();
+ return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, charset);
+ }
+ return null;
+ }
+ return null;
+ }
+ @Override
+ public long getStartPosition() {
+ return getPosition();
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/editor/src/main/res/anim/popup_in.xml b/app/src/main/res/anim/popup_in.xml
similarity index 100%
rename from editor/src/main/res/anim/popup_in.xml
rename to app/src/main/res/anim/popup_in.xml
diff --git a/editor/src/main/res/anim/popup_out.xml b/app/src/main/res/anim/popup_out.xml
similarity index 100%
rename from editor/src/main/res/anim/popup_out.xml
rename to app/src/main/res/anim/popup_out.xml
diff --git a/app/src/main/res/drawable/ic_compilation_error.xml b/app/src/main/res/drawable/ic_compilation_error.xml
new file mode 100644
index 000000000..e0768321f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_compilation_error.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_diagnostics.xml b/app/src/main/res/drawable/ic_diagnostics.xml
new file mode 100644
index 000000000..c9b814565
--- /dev/null
+++ b/app/src/main/res/drawable/ic_diagnostics.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml
index 66773f0fb..7156b37b7 100644
--- a/app/src/main/res/drawable/ic_info.xml
+++ b/app/src/main/res/drawable/ic_info.xml
@@ -5,7 +5,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
-
+
\ No newline at end of file
diff --git a/editor/src/main/res/drawable/ic_more_vert_black_20dp.xml b/app/src/main/res/drawable/ic_more_vert_black_20dp.xml
similarity index 100%
rename from editor/src/main/res/drawable/ic_more_vert_black_20dp.xml
rename to app/src/main/res/drawable/ic_more_vert_black_20dp.xml
diff --git a/editor/src/main/res/drawable/round_content_copy_20.xml b/app/src/main/res/drawable/round_content_copy_20.xml
similarity index 100%
rename from editor/src/main/res/drawable/round_content_copy_20.xml
rename to app/src/main/res/drawable/round_content_copy_20.xml
diff --git a/editor/src/main/res/drawable/round_content_cut_20.xml b/app/src/main/res/drawable/round_content_cut_20.xml
similarity index 100%
rename from editor/src/main/res/drawable/round_content_cut_20.xml
rename to app/src/main/res/drawable/round_content_cut_20.xml
diff --git a/editor/src/main/res/drawable/round_content_paste_20.xml b/app/src/main/res/drawable/round_content_paste_20.xml
similarity index 100%
rename from editor/src/main/res/drawable/round_content_paste_20.xml
rename to app/src/main/res/drawable/round_content_paste_20.xml
diff --git a/editor/src/main/res/drawable/round_save_20.xml b/app/src/main/res/drawable/round_save_20.xml
similarity index 100%
rename from editor/src/main/res/drawable/round_save_20.xml
rename to app/src/main/res/drawable/round_save_20.xml
diff --git a/editor/src/main/res/drawable/round_select_all_20.xml b/app/src/main/res/drawable/round_select_all_20.xml
similarity index 100%
rename from editor/src/main/res/drawable/round_select_all_20.xml
rename to app/src/main/res/drawable/round_select_all_20.xml
diff --git a/app/src/main/res/layout/activity_editor.xml b/app/src/main/res/layout/activity_editor.xml
index b6198bfaf..857e904f3 100644
--- a/app/src/main/res/layout/activity_editor.xml
+++ b/app/src/main/res/layout/activity_editor.xml
@@ -133,6 +133,23 @@
app:icon="@drawable/ic_build_output"
app:backgroundTint="@color/primaryColor"
app:rippleColor="@color/rippleHighlightColor_light"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_diagnostic_info.xml b/app/src/main/res/layout/layout_diagnostic_info.xml
new file mode 100644
index 000000000..af339099c
--- /dev/null
+++ b/app/src/main/res/layout/layout_diagnostic_info.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_diagnostic_item.xml b/app/src/main/res/layout/layout_diagnostic_item.xml
new file mode 100644
index 000000000..b0c02d250
--- /dev/null
+++ b/app/src/main/res/layout/layout_diagnostic_item.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/editor/src/main/res/layout/problem_view.xml b/app/src/main/res/layout/problem_view.xml
similarity index 100%
rename from editor/src/main/res/layout/problem_view.xml
rename to app/src/main/res/layout/problem_view.xml
diff --git a/editor/src/main/res/layout/text_compose_panel.xml b/app/src/main/res/layout/text_compose_panel.xml
similarity index 100%
rename from editor/src/main/res/layout/text_compose_panel.xml
rename to app/src/main/res/layout/text_compose_panel.xml
diff --git a/editor/src/main/res/layout/text_compose_popup_window.xml b/app/src/main/res/layout/text_compose_popup_window.xml
similarity index 100%
rename from editor/src/main/res/layout/text_compose_popup_window.xml
rename to app/src/main/res/layout/text_compose_popup_window.xml
diff --git a/app/src/main/res/menu/menu_editor_nav.xml b/app/src/main/res/menu/menu_editor_nav.xml
index 029787291..4fd89364c 100644
--- a/app/src/main/res/menu/menu_editor_nav.xml
+++ b/app/src/main/res/menu/menu_editor_nav.xml
@@ -9,10 +9,10 @@
android:id="@+id/editornav_discuss"
android:icon="@drawable/ic_telegram"
android:title="@string/discussions_on_telegram"/>
-
+ android:title="@string/user_suggestions"/>
- #bdbdbd
#70ffffff
#50ffffff
+ #f0ffffff
#90424242
#ffffff
#9f9f9f
@@ -32,7 +33,13 @@
#FF7043
#4CAF50
@color/primaryTextColor
+ @color/secondaryDarkColor
+ @color/log_warning
#212121
#4CAF50
#70000000
+
+ @android:color/tertiary_text_light
+ #eeeeee
+ @android:color/black
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6a853a468..4e5188394 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,4 +1,9 @@
+ Diagnostics
+ Line: %s, Column: %s
+ ERROR
+ WARNING
+ NOTE
Project deleted
Failed to delete project!
Failed to load icon path!
@@ -224,4 +229,18 @@
Notifications from Developers
App Update Notifications
Nothing here …
+
+
- Paste
+ - Copy
+ - Select All
+ - Project
+ - Cut
+ - Last
+ - Next
+ - Replace
+ - Replace All
+ - Replacement
+ - Text to search
+ - More
+ - Long click on an item to show JavaDoc if available.
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index bf47b13af..af392f016 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -103,4 +103,71 @@
- @color/secondaryLightColor
- @color/secondaryColor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
index c2aa5bdf6..8a82caabf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,8 +8,8 @@ buildscript {
buildTools = "30.0.2"
minSdk = 21
targetSdk = 28
- versionCode = 101210625
- versionName = "1.0.1-alpha"
+ versionCode = 102210625
+ versionName = "1.0.2-alpha"
packageName = "com.itsaky.androidide"
}
diff --git a/editor/.gitignore b/editor/.gitignore
deleted file mode 100644
index 42afabfd2..000000000
--- a/editor/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/editor/build.gradle b/editor/build.gradle
deleted file mode 100644
index 85b258431..000000000
--- a/editor/build.gradle
+++ /dev/null
@@ -1,58 +0,0 @@
-/*******************************************************************************
- * Copyright 2020-2021 Rosemoe
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- ******************************************************************************/
-
-apply plugin: 'com.android.library'
-
-android {
- compileSdkVersion rootProject.ext.compileSdk
- buildToolsVersion rootProject.ext.buildTools
-
- defaultConfig {
- minSdkVersion rootProject.ext.minSdk
- targetSdkVersion rootProject.ext.targetSdk
- versionCode rootProject.ext.versionCode
- versionName rootProject.ext.versionName
- vectorDrawables.useSupportLibrary = true
- }
-
- buildFeatures {
- viewBinding true
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-}
-
-dependencies {
- implementation 'com.blankj:utilcodex:1.30.6'
- implementation 'com.elvishew:xlog:1.8.0'
- implementation 'com.github.Rosemoe:BlockLinkedList:1.0.0'
-
- implementation 'androidx.annotation:annotation:1.2.0'
- implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
- implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
- implementation 'com.google.android.material:material:1.3.0'
- implementation fileTree(dir: "libs", include: ["*.jar"])
-}
diff --git a/editor/consumer-rules.pro b/editor/consumer-rules.pro
deleted file mode 100644
index e69de29bb..000000000
diff --git a/editor/proguard-rules.pro b/editor/proguard-rules.pro
deleted file mode 100644
index 481bb4348..000000000
--- a/editor/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/editor/src/main/AndroidManifest.xml b/editor/src/main/AndroidManifest.xml
deleted file mode 100644
index ef8d25f33..000000000
--- a/editor/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/editor/src/main/res/drawable/bg_ripple.xml b/editor/src/main/res/drawable/bg_ripple.xml
deleted file mode 100644
index 9b1a5df11..000000000
--- a/editor/src/main/res/drawable/bg_ripple.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
-
diff --git a/editor/src/main/res/drawable/bg_ripple_disabled.xml b/editor/src/main/res/drawable/bg_ripple_disabled.xml
deleted file mode 100644
index 34782662b..000000000
--- a/editor/src/main/res/drawable/bg_ripple_disabled.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
diff --git a/editor/src/main/res/drawable/bg_ripple_pressed.xml b/editor/src/main/res/drawable/bg_ripple_pressed.xml
deleted file mode 100644
index b8c1e9872..000000000
--- a/editor/src/main/res/drawable/bg_ripple_pressed.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
diff --git a/editor/src/main/res/drawable/ic_info.xml b/editor/src/main/res/drawable/ic_info.xml
deleted file mode 100644
index 7156b37b7..000000000
--- a/editor/src/main/res/drawable/ic_info.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/editor/src/main/res/values-zh/strings.xml b/editor/src/main/res/values-zh/strings.xml
deleted file mode 100644
index 31991a6f0..000000000
--- a/editor/src/main/res/values-zh/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- 粘贴
- 复制
- 全选
- 剪切
- 上个
- 下个
- 替换
- 替换全部
- 替换文本
- 取消
- 欲搜索文本
-
\ No newline at end of file
diff --git a/editor/src/main/res/values/attrs.xml b/editor/src/main/res/values/attrs.xml
deleted file mode 100644
index c8c1cb818..000000000
--- a/editor/src/main/res/values/attrs.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/editor/src/main/res/values/colors.xml b/editor/src/main/res/values/colors.xml
deleted file mode 100644
index 00486d6b4..000000000
--- a/editor/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- @android:color/tertiary_text_light
- #90010101
- #eeeeee
- @android:color/black
-
diff --git a/editor/src/main/res/values/dimens.xml b/editor/src/main/res/values/dimens.xml
deleted file mode 100644
index 55344e519..000000000
--- a/editor/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/editor/src/main/res/values/strings.xml b/editor/src/main/res/values/strings.xml
deleted file mode 100644
index 68c370951..000000000
--- a/editor/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
- - Paste
- - Copy
- - Select All
- - Project
- - Cut
- - Last
- - Next
- - Replace
- - Replace All
- - Replacement
- - Cancel
- - Text to search
- - More
- - Long click on an item to show JavaDoc if available.
-
diff --git a/editor/src/main/res/values/styles.xml b/editor/src/main/res/values/styles.xml
deleted file mode 100644
index 6c67ea2cf..000000000
--- a/editor/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
index c6e33a765..cee3ae1c0 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,6 +1,5 @@
include ':app'
include ':treeview'
-include ':editor'
include ':spinkit'
include ':slidr'
include ':javadocparser'