From 6ec5c6d3cfef9568bdbeac4ca30ab86497028fb1 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Sun, 14 Aug 2022 08:42:34 +0530 Subject: [PATCH] [Gradle] Format java source code --- .../itsaky/androidide/DesignerActivity.java | 2 +- .../adapters/SimpleIconTextAdapter.java | 6 +- .../adapters/WizardTemplateAdapter.java | 1 + .../viewholders/FileTreeViewHolder.java | 2 +- .../attr/BaseValueEditorFragment.java | 2 +- .../androidide/fragments/attr/FlagEditor.java | 2 +- .../preferences/EditorPreferences.java | 2 +- .../androidide/language/IDELanguage.java | 7 +- .../language/kotlin/KotlinLanguage.java | 3 +- .../com/itsaky/androidide/utils/Symbols.java | 5 +- .../androidide/viewmodel/WizardViewModel.java | 172 ++- .../views/editor/CodeEditorView.java | 4 +- .../java/com/google/googlejavaformat/Doc.java | 1 + .../google/googlejavaformat/InputOutput.java | 1 + .../googlejavaformat/java/ImportOrderer.java | 1 + .../java/JavaFormatterOptions.java | 1 + .../googlejavaformat/java/JavaInput.java | 1 + .../google/googlejavaformat/java/Main.java | 1 + .../googlejavaformat/java/Replacement.java | 1 + .../java/javadoc/JavadocLexer.java | 1 + .../googlejavaformat/java/javadoc/Token.java | 1 + .../javac/services/NBMemberEnter.java | 1 + .../androidide/javac/services/NBNames.java | 1 + .../javac/services/NBTreeMaker.java | 1 + .../json/adapters/CollectionTypeAdapter.java | 1 + .../json/adapters/EitherTypeAdapter.java | 2 + .../json/adapters/EnumTypeAdapter.java | 1 + .../json/adapters/JsonElementTypeAdapter.java | 1 + .../json/adapters/MessageTypeAdapter.java | 1 + .../main/java/com/android/SdkConstants.java | 8 + .../ide/common/xml/XmlAttributeSortOrder.java | 1 + .../main/java/com/android/utils/SdkUtils.java | 1 + .../colorpicker/ColorPickerView.java | 12 +- .../utils/CharSequenceInputStream.java | 18 +- .../androidide/utils/CharSequenceReader.java | 13 +- .../views/virtualkeys/VirtualKeysInfo.java | 18 +- .../views/virtualkeys/VirtualKeysView.java | 2 + .../com/itsaky/terminal/TerminalBuffer.java | 1 + .../com/itsaky/terminal/TerminalEmulator.java | 8 + .../com/itsaky/terminal/TerminalSession.java | 3 + .../java/com/itsaky/terminal/TextStyle.java | 1 + .../view/GestureAndScaleRecognizer.java | 1 + .../terminal/view/TerminalRenderer.java | 1 + .../itsaky/terminal/view/TerminalView.java | 4 + .../greenrobot/eventbus/HandlerPoster.java | 98 +- .../android/AndroidComponentsImpl.java | 10 +- .../eventbus/android/AndroidLogger.java | 56 +- .../DefaultAndroidMainThreadSupport.java | 16 +- .../org/greenrobot/eventbus/AsyncPoster.java | 44 +- .../greenrobot/eventbus/BackgroundPoster.java | 81 +- .../org/greenrobot/eventbus/EventBus.java | 987 +++++++++--------- .../greenrobot/eventbus/EventBusBuilder.java | 304 +++--- .../eventbus/EventBusException.java | 24 +- .../java/org/greenrobot/eventbus/Logger.java | 77 +- .../eventbus/MainThreadSupport.java | 7 +- .../eventbus/NoSubscriberEvent.java | 19 +- .../org/greenrobot/eventbus/PendingPost.java | 63 +- .../greenrobot/eventbus/PendingPostQueue.java | 59 +- .../java/org/greenrobot/eventbus/Poster.java | 14 +- .../org/greenrobot/eventbus/Subscribe.java | 26 +- .../eventbus/SubscriberExceptionEvent.java | 36 +- .../greenrobot/eventbus/SubscriberMethod.java | 84 +- .../eventbus/SubscriberMethodFinder.java | 481 +++++---- .../org/greenrobot/eventbus/Subscription.java | 53 +- .../org/greenrobot/eventbus/ThreadMode.java | 89 +- .../eventbus/android/AndroidComponents.java | 33 +- .../android/AndroidDependenciesDetector.java | 59 +- .../eventbus/meta/AbstractSubscriberInfo.java | 103 +- .../eventbus/meta/SimpleSubscriberInfo.java | 37 +- .../eventbus/meta/SubscriberInfo.java | 8 +- .../eventbus/meta/SubscriberMethodInfo.java | 41 +- .../eventbus/util/AsyncExecutor.java | 169 +-- .../util/ExceptionToResourceMapping.java | 94 +- .../eventbus/util/HasExecutionScope.java | 5 +- .../eventbus/util/ThrowableFailureEvent.java | 59 +- .../androidide/lexers/cpp/CPP14Lexer.java | 1 + .../androidide/lexers/cpp/CPP14Parser.java | 1 + .../androidide/lexers/groovy/GroovyLexer.java | 1 + .../androidide/lexers/java/JavaLexer.java | 1 + .../androidide/lexers/java/JavaParser.java | 1 + .../lexers/javadoc/JavadocLexer.java | 1 + .../lexers/javadoc/JavadocParser.java | 1 + .../androidide/lexers/kotlin/KotlinLexer.java | 1 + .../lexers/kotlin/KotlinParser.java | 1 + .../lexers/kotlin/UnicodeClasses.java | 1 + .../androidide/lexers/xml/XMLLexer.java | 1 + .../androidide/lexers/xml/XMLParser.java | 1 + .../com/itsaky/androidide/utils/ILogger.java | 2 +- .../com/itsaky/androidide/utils/LogUtils.java | 4 +- .../lsp/java/JavaLanguageServer.java | 4 +- .../lsp/java/compiler/CompileTask.java | 3 +- .../lsp/java/compiler/SynchronizedTask.java | 2 +- .../androidide/lsp/java/parser/Parser.java | 2 +- .../java/providers/CodeFormatProvider.java | 2 +- .../java/providers/CompletionProvider.java | 2 +- .../rewrite/GenerateRecordConstructor.java | 4 +- .../rewrite/ImplementAbstractMethods.java | 5 +- .../lsp/java/rewrite/RemoveException.java | 6 +- .../lsp/java/rewrite/RemoveMethod.java | 2 +- .../androidide/lsp/java/utils/ASTFixer.java | 6 +- .../com/squareup/javapoet/LineWrapper.java | 1 + .../androidide/lsp/xml/XMLLanguageServer.java | 2 + .../compiler/classfmt/ClassFileConstants.java | 2 + .../androidide/utils/ReaderInputStream.java | 1 + .../com/itsaky/androidide/utils/VMUtils.java | 2 +- .../java/com/itsaky/inflater/IAttribute.java | 1 + .../com/itsaky/inflater/impl/BaseView.java | 18 +- 107 files changed, 1872 insertions(+), 1765 deletions(-) diff --git a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java index 8d653d314..feeac7357 100755 --- a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java +++ b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java @@ -479,7 +479,7 @@ public class DesignerActivity extends StudioActivity TransitionManager.beginDelayedTransition( mBinding.navigation, new TransitionSet().addTransition(new Slide(Gravity.END)).addTransition(new Fade())); - + this.mBinding.groupItems.getAdapter().notifyDataSetChanged(); } diff --git a/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java index 0b1a67d6f..77339898d 100755 --- a/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java @@ -39,7 +39,9 @@ public class SimpleIconTextAdapter extends IconTextAdapter { @Override public int getItemCount() { return items.size(); - } @NonNull + } + + @NonNull @Override public IconTextListItem getItemAt(int index) { return items.get(index); @@ -55,6 +57,4 @@ public class SimpleIconTextAdapter extends IconTextAdapter { public String getItemText(int index) { return getItemAt(index).getText(); } - - } diff --git a/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java index 0268a8afd..730aaff30 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java @@ -22,6 +22,7 @@ public class WizardTemplateAdapter extends RecyclerView.Adapter mItems = new ArrayList<>(); private OnItemClickListener mListener; + public WizardTemplateAdapter() {} public void setOnItemClickListener(OnItemClickListener listener) { diff --git a/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java b/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java index cc5803635..600ed248b 100755 --- a/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java @@ -76,7 +76,7 @@ public class FileTreeViewHolder extends TreeNode.BaseNodeViewHolder { } else { chevronIcon = R.drawable.ic_chevron_right; } - + TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeImageTransform()); binding.filetreeChevron.setImageResource(chevronIcon); binding diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java index 23536762e..1e81fc63a 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java @@ -74,7 +74,7 @@ public abstract class BaseValueEditorFragment extends Fragment { if (child != null) { scrollView.addView(child); } - + return scrollView; } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java index ce2234b41..344fabc61 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java @@ -40,7 +40,7 @@ import java.util.Arrays; public class FlagEditor extends FixedValueEditor { private static final ILogger LOG = ILogger.newInstance("FlagEditor"); - + @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java index df389fa60..7edd42681 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java @@ -153,7 +153,7 @@ public class EditorPreferences extends BasePreferenceFragment visiblePasswordFlag.setKey(FLAG_PASSWORD); visiblePasswordFlag.setTitle(getString(R.string.idepref_visiblePassword_title)); visiblePasswordFlag.setSummary(getString(R.string.idepref_visiblePassword_summary)); - + useIcu.setIcon(R.drawable.ic_expand_selection); useIcu.setKey(USE_ICU); useIcu.setTitle(getString(R.string.idepref_useIcu_title)); diff --git a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java index b6afb7268..41881283d 100644 --- a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java @@ -18,7 +18,6 @@ package com.itsaky.androidide.language; import androidx.annotation.NonNull; -import com.itsaky.androidide.app.StudioApp; import com.itsaky.androidide.lsp.api.ILanguageServer; import com.itsaky.androidide.models.prefs.EditorPreferencesKt; @@ -33,19 +32,19 @@ import io.github.rosemoe.sora.lang.format.Formatter; public abstract class IDELanguage implements Language { private Formatter formatter; - + @Override public boolean useTab() { return false; } - + @NonNull @Override public Formatter getFormatter() { if (formatter != null) { return formatter; } - + return formatter = new LSPFormatter(getLanguageServer()); } diff --git a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java index de53c4573..be0e8af77 100644 --- a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java @@ -68,8 +68,7 @@ public class KotlinLanguage extends IDELanguage { @NonNull CharPosition position, @NonNull CompletionPublisher publisher, @NonNull Bundle extraArguments) - throws CompletionCancelledException { - } + throws CompletionCancelledException {} @Override public int getIndentAdvance(@NonNull ContentReference content, int line, int column) { diff --git a/app/src/main/java/com/itsaky/androidide/utils/Symbols.java b/app/src/main/java/com/itsaky/androidide/utils/Symbols.java index 1b3845c2e..4b11770dd 100755 --- a/app/src/main/java/com/itsaky/androidide/utils/Symbols.java +++ b/app/src/main/java/com/itsaky/androidide/utils/Symbols.java @@ -33,8 +33,9 @@ public class Symbols { } if (file.isFile()) { - if (file.getName().endsWith(".java") || file.getName().endsWith(".gradle") || file.getName().endsWith(".kt")) - return javaSymbols(); + if (file.getName().endsWith(".java") + || file.getName().endsWith(".gradle") + || file.getName().endsWith(".kt")) return javaSymbols(); if (file.getName().endsWith(".xml")) return xmlSymbols(); } diff --git a/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java b/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java index 3b4c563e1..42b447a4a 100644 --- a/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java +++ b/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java @@ -36,99 +36,89 @@ public class WizardViewModel extends AndroidViewModel { public void createTemplatesList() { TaskExecutor.executeAsync(this::createTemplates); } - + private Void createTemplates() { List mTemplates = new ArrayList<>(); - + ProjectTemplate - empty = - new ProjectTemplate() - .setId(0) - .setSupportJava(true) - .setSupportKotlin(true) - .setName( - getApplication().getApplicationContext(), R.string.template_empty) - .setDescription( - getApplication().getApplicationContext(), - R.string.template_description_empty) - .setImageId(R.drawable.template_empty_activity), - basic = - new ProjectTemplate() - .setId(1) - .setSupportJava(true) - .setSupportKotlin(true) - .setName( - getApplication().getApplicationContext(), R.string.template_basic) - .setDescription( - getApplication().getApplicationContext(), - R.string.template_description_basic) - .setImageId(R.drawable.template_basic_activity), - drawer = - new ProjectTemplate() - .setId(2) - .setSupportJava(true) - .setSupportKotlin(true) - .setName( - getApplication().getApplicationContext(), - R.string.template_navigation_drawer) - .setDescription( - getApplication().getApplicationContext(), - R.string.template_description_navigation_drawer) - .setImageId(R.drawable.template_blank_activity_drawer), - bottomTabs = - new ProjectTemplate() - .setId(3) - .setSupportJava(true) - .setSupportKotlin(true) - .setName( - getApplication().getApplicationContext(), - R.string.template_navigation_tabs) - .setImageId(R.drawable.template_bottom_navigation_activity), - tabs = - new ProjectTemplate() - .setId(4) - .setSupportJava(true) - .setSupportKotlin(true) - .setName(getApplication().getApplicationContext(), R.string.template_tabs) - .setImageId(R.drawable.template_blank_activity_tabs), - fragmentViewModel = - new ProjectTemplate() - .setId(5) - .setSupportJava(true) - .setSupportKotlin(true) - .setName( - getApplication().getApplicationContext(), - R.string.template_fragment_and_viewmodel) - .setImageId(R.drawable.template_empty_activity), - cppWizard = - new ProjectTemplate() - .setId(6) - .setSupportJava(true) - .setSupportKotlin(true) - .setIsCpp(true) - .setName(getApplication().getApplicationContext(), R.string.template_cpp) - .setImageId(R.drawable.template_cpp_configure), - compose = - new ProjectTemplate() - .setId(7) - .setSupportKotlin(true) - .setName( - getApplication().getApplicationContext(), R.string.template_compose) - .setDescription( - getApplication().getApplicationContext(), - R.string.template_description_compose) - .setImageId(R.drawable.template_compose_empty_activity), - libgdx = - new ProjectTemplate() - .setId(8) - .setSupportJava(true) - .setName( - getApplication().getApplicationContext(), R.string.template_libgdx) - .setDescription( - getApplication().getApplicationContext(), - R.string.template_description_libgdx) - .setImageId(R.drawable.template_game_activity); - + empty = + new ProjectTemplate() + .setId(0) + .setSupportJava(true) + .setSupportKotlin(true) + .setName(getApplication().getApplicationContext(), R.string.template_empty) + .setDescription( + getApplication().getApplicationContext(), R.string.template_description_empty) + .setImageId(R.drawable.template_empty_activity), + basic = + new ProjectTemplate() + .setId(1) + .setSupportJava(true) + .setSupportKotlin(true) + .setName(getApplication().getApplicationContext(), R.string.template_basic) + .setDescription( + getApplication().getApplicationContext(), R.string.template_description_basic) + .setImageId(R.drawable.template_basic_activity), + drawer = + new ProjectTemplate() + .setId(2) + .setSupportJava(true) + .setSupportKotlin(true) + .setName( + getApplication().getApplicationContext(), R.string.template_navigation_drawer) + .setDescription( + getApplication().getApplicationContext(), + R.string.template_description_navigation_drawer) + .setImageId(R.drawable.template_blank_activity_drawer), + bottomTabs = + new ProjectTemplate() + .setId(3) + .setSupportJava(true) + .setSupportKotlin(true) + .setName( + getApplication().getApplicationContext(), R.string.template_navigation_tabs) + .setImageId(R.drawable.template_bottom_navigation_activity), + tabs = + new ProjectTemplate() + .setId(4) + .setSupportJava(true) + .setSupportKotlin(true) + .setName(getApplication().getApplicationContext(), R.string.template_tabs) + .setImageId(R.drawable.template_blank_activity_tabs), + fragmentViewModel = + new ProjectTemplate() + .setId(5) + .setSupportJava(true) + .setSupportKotlin(true) + .setName( + getApplication().getApplicationContext(), + R.string.template_fragment_and_viewmodel) + .setImageId(R.drawable.template_empty_activity), + cppWizard = + new ProjectTemplate() + .setId(6) + .setSupportJava(true) + .setSupportKotlin(true) + .setIsCpp(true) + .setName(getApplication().getApplicationContext(), R.string.template_cpp) + .setImageId(R.drawable.template_cpp_configure), + compose = + new ProjectTemplate() + .setId(7) + .setSupportKotlin(true) + .setName(getApplication().getApplicationContext(), R.string.template_compose) + .setDescription( + getApplication().getApplicationContext(), R.string.template_description_compose) + .setImageId(R.drawable.template_compose_empty_activity), + libgdx = + new ProjectTemplate() + .setId(8) + .setSupportJava(true) + .setName(getApplication().getApplicationContext(), R.string.template_libgdx) + .setDescription( + getApplication().getApplicationContext(), R.string.template_description_libgdx) + .setImageId(R.drawable.template_game_activity); + mTemplates.add(empty); mTemplates.add(basic); mTemplates.add(drawer); @@ -138,7 +128,7 @@ public class WizardViewModel extends AndroidViewModel { mTemplates.add(cppWizard); mTemplates.add(compose); mTemplates.add(libgdx); - + mProjectTemplatesList.postValue(mTemplates); mLoadingState.postValue(false); diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java index 88713d901..bd8d11bf3 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java @@ -343,7 +343,7 @@ public class CodeEditorView extends FrameLayout { binding.editor.setTextSize(textSize); } - + private void onUseIcuPrefChanged() { binding.editor.getProps().useICULibToSelectWords = getUseIcu(); } @@ -384,7 +384,7 @@ public class CodeEditorView extends FrameLayout { break; } } - + public boolean save() { final var file = getFile(); if (file == null) { diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/Doc.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/Doc.java index 0dc595562..2e7ee3aa4 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/Doc.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/Doc.java @@ -189,6 +189,7 @@ public abstract class Doc { private final RealOrImaginary realOrImaginary; private final Indent plusIndentCommentsBefore; private final Optional breakAndIndentTrailingComment; + private Token( Input.Token token, RealOrImaginary realOrImaginary, diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/InputOutput.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/InputOutput.java index db69d8a27..0682c4bbb 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/InputOutput.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/InputOutput.java @@ -29,6 +29,7 @@ public abstract class InputOutput { private static final DiscreteDomain INTEGERS = DiscreteDomain.integers(); /** The {@link Range}s of the tokens or comments lying on each line, in any part. */ protected final List> ranges = new ArrayList<>(); + private ImmutableList lines = ImmutableList.of(); /** diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java index eb859e28f..5e3d31d30 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java @@ -69,6 +69,7 @@ public class ImportOrderer { .thenComparing(Import::isThirdParty, trueFirst()) .thenComparing(Import::isJava, trueFirst()) .thenComparing(Import::imported); + private final String text; private final ImmutableList toks; private final String lineSeparator; diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java index 9ecda1af7..51488bc2c 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java @@ -31,6 +31,7 @@ public class JavaFormatterOptions { private final Style style; private final boolean formatJavadoc; + private JavaFormatterOptions(Style style, boolean formatJavadoc) { this.style = style; this.formatJavadoc = formatJavadoc; diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaInput.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaInput.java index 7192b0c8b..115541595 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaInput.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/JavaInput.java @@ -71,6 +71,7 @@ public final class JavaInput extends Input { */ /** Map from Tok index to the associated Token. */ private final Token[] kToToken; + private int kN; // The number of numbered toks (tokens or comments), excluding the EOF. private JCCompilationUnit unit; diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Main.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Main.java index 7dee58060..8954f37c7 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Main.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Main.java @@ -44,6 +44,7 @@ public final class Main { private final PrintWriter outWriter; private final PrintWriter errWriter; private final InputStream inStream; + public Main(PrintWriter outWriter, PrintWriter errWriter, InputStream inStream) { this.outWriter = outWriter; this.errWriter = errWriter; diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Replacement.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Replacement.java index 9b66523d7..08d8e1923 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Replacement.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/Replacement.java @@ -31,6 +31,7 @@ public final class Replacement { private final Range replaceRange; private final String replacementString; + private Replacement(Range replaceRange, String replacementString) { this.replaceRange = checkNotNull(replaceRange, "Null replaceRange"); this.replacementString = checkNotNull(replacementString, "Null replacementString"); diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocLexer.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocLexer.java index 937c5da7e..b72213a77 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocLexer.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocLexer.java @@ -118,6 +118,7 @@ final class JavadocLexer { private final NestingCounter codeDepth = new NestingCounter(); private final NestingCounter tableDepth = new NestingCounter(); private boolean somethingSinceNewline; + private JavadocLexer(CharStream input) { this.input = checkNotNull(input); } diff --git a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java index 89bfd8900..cb2d817ef 100644 --- a/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java +++ b/build-tools/google-java-format/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java @@ -22,6 +22,7 @@ package com.google.googlejavaformat.java.javadoc; final class Token { private final Type type; private final String value; + Token(Type type, String value) { this.type = type; this.value = value; diff --git a/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBMemberEnter.java b/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBMemberEnter.java index c1be0b02b..c30eff3e7 100644 --- a/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBMemberEnter.java +++ b/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBMemberEnter.java @@ -52,6 +52,7 @@ public class NBMemberEnter extends MemberEnter { private final CancelService cancelService; private final JavacTrees trees; private final boolean backgroundScan; + public NBMemberEnter(Context context, boolean backgroundScan) { super(context); cancelService = CancelService.instance(context); diff --git a/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBNames.java b/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBNames.java index f1ec81685..73fb80b60 100644 --- a/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBNames.java +++ b/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBNames.java @@ -55,6 +55,7 @@ public class NBNames { public static void preRegister(Context context) { context.put(nbNamesKey, (Context.Factory) NBNames::new); } + protected NBNames(Context context) { Names n = Names.instance(context); diff --git a/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBTreeMaker.java b/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBTreeMaker.java index ea06ae016..2b5336980 100644 --- a/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBTreeMaker.java +++ b/build-tools/javac-services/src/main/java/com/itsaky/androidide/javac/services/NBTreeMaker.java @@ -50,6 +50,7 @@ public class NBTreeMaker extends TreeMaker { private final Names names; private final Types types; private final Symtab syms; + protected NBTreeMaker(JCCompilationUnit toplevel, Names names, Types types, Symtab syms) { super(toplevel, names, types, syms); this.names = names; diff --git a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/CollectionTypeAdapter.java b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/CollectionTypeAdapter.java index 0676bfd98..d57c88d4c 100644 --- a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/CollectionTypeAdapter.java +++ b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/CollectionTypeAdapter.java @@ -41,6 +41,7 @@ public class CollectionTypeAdapter extends TypeAdapter> { private final Type elementType; private final TypeAdapter elementTypeAdapter; private final Supplier> constructor; + public CollectionTypeAdapter( Gson gson, Type elementType, diff --git a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EitherTypeAdapter.java b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EitherTypeAdapter.java index 4e6440626..1b4abe9b5 100644 --- a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EitherTypeAdapter.java +++ b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EitherTypeAdapter.java @@ -42,9 +42,11 @@ public class EitherTypeAdapter extends TypeAdapter> { protected final EitherTypeArgument right; protected final Predicate leftChecker; protected final Predicate rightChecker; + public EitherTypeAdapter(Gson gson, TypeToken> typeToken) { this(gson, typeToken, null, null); } + public EitherTypeAdapter( Gson gson, TypeToken> typeToken, diff --git a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EnumTypeAdapter.java b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EnumTypeAdapter.java index 260cb405f..c368c680a 100644 --- a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EnumTypeAdapter.java +++ b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EnumTypeAdapter.java @@ -31,6 +31,7 @@ public class EnumTypeAdapter> extends TypeAdapter { private final Map nameToConstant = new HashMap<>(); private final Map valueToConstant = new HashMap<>(); private final Map constantToValue = new HashMap<>(); + EnumTypeAdapter(Class classOfT) throws IllegalAccessException { try { Field valueField = classOfT.getDeclaredField(VALUE_FIELD_NAME); diff --git a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/JsonElementTypeAdapter.java b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/JsonElementTypeAdapter.java index 6bdf8bcd8..a809611dd 100644 --- a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/JsonElementTypeAdapter.java +++ b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/JsonElementTypeAdapter.java @@ -26,6 +26,7 @@ public class JsonElementTypeAdapter extends TypeAdapter { private final Gson gson; private final TypeAdapter adapter; + public JsonElementTypeAdapter(Gson gson) { this.gson = gson; this.adapter = gson.getAdapter(JsonElement.class); diff --git a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java index 1fa07e84b..53a73f59f 100644 --- a/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java +++ b/build-tools/jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/MessageTypeAdapter.java @@ -57,6 +57,7 @@ public class MessageTypeAdapter extends TypeAdapter { private static Type[] EMPTY_TYPE_ARRAY = {}; private final MessageJsonHandler handler; private final Gson gson; + public MessageTypeAdapter(MessageJsonHandler handler, Gson gson) { this.handler = handler; this.gson = gson; diff --git a/build-tools/xml-formatter/src/main/java/com/android/SdkConstants.java b/build-tools/xml-formatter/src/main/java/com/android/SdkConstants.java index 32817a585..31c347e76 100644 --- a/build-tools/xml-formatter/src/main/java/com/android/SdkConstants.java +++ b/build-tools/xml-formatter/src/main/java/com/android/SdkConstants.java @@ -105,6 +105,7 @@ public final class SdkConstants { public static final String FN_ANDROID_MANIFEST_XML = "AndroidManifest.xml"; // $NON-NLS-1$ /** pre-dex jar filename. i.e. "classes.jar" */ public static final String TOOLS_NS_NAME = "tools"; + public static final String FN_CLASSES_JAR = "classes.jar"; /** Dex filename inside the APK. i.e. "classes.dex" */ public static final String FN_APK_CLASSES_DEX = "classes.dex"; // $NON-NLS-1$ @@ -537,6 +538,7 @@ public final class SdkConstants { */ public static final String CLASS_MOCK_VIEW = "com.android.layoutlib.bridge.MockView"; //$NON-NLS-1$ + public static final String CLASS_LAYOUT_INFLATER = "android.view.LayoutInflater"; // $NON-NLS-1$ /* Android Design Support Class Constants */ public static final String CLASS_COORDINATOR_LAYOUT = @@ -606,6 +608,7 @@ public final class SdkConstants { public static final String ANDROID_NS_NAME = "android"; // $NON-NLS-1$ /** The default prefix used for the {@link #ANDROID_URI} name space including the colon */ public static final String ANDROID_NS_NAME_PREFIX = "android:"; // $NON-NLS-1$ + public static final int ANDROID_NS_NAME_PREFIX_LEN = ANDROID_NS_NAME_PREFIX.length(); /** The default prefix used for the app */ public static final String APP_PREFIX = "app"; // $NON-NLS-1$ @@ -619,6 +622,7 @@ public final class SdkConstants { public static final String LT_ENTITY = "<"; // $NON-NLS-1$ /** The entity for the greater than character */ public static final String GT_ENTITY = ">"; // $NON-NLS-1$ + public static final String NEWLINE_ENTITY = " "; /** Namespace prefix used for all resources */ public static final String URI_PREFIX = "http://schemas.android.com/apk/res/"; // $NON-NLS-1$ @@ -1017,6 +1021,7 @@ public final class SdkConstants { public static final String FN_MANIFEST_CLASS = FN_MANIFEST_BASE + DOT_JAVA; /** BuildConfig java class filename, i.e. "BuildConfig.java" */ public static final String FN_BUILD_CONFIG = FN_BUILD_CONFIG_BASE + DOT_JAVA; + public static final String DRAWABLE_FOLDER = "drawable"; // $NON-NLS-1$ public static final String DRAWABLE_XHDPI = "drawable-xhdpi"; // $NON-NLS-1$ public static final String DRAWABLE_XXHDPI = "drawable-xxhdpi"; // $NON-NLS-1$ @@ -1170,6 +1175,7 @@ public final class SdkConstants { public static final String FQCN_RELATIVE_LAYOUT = "android.widget.RelativeLayout"; // $NON-NLS-1$ /** The fully qualified class name of a RelativeLayout view */ public static final String FQCN_GRID_LAYOUT = "android.widget.GridLayout"; // $NON-NLS-1$ + public static final String FQCN_GRID_LAYOUT_V7 = "android.support.v7.widget.GridLayout"; //$NON-NLS-1$ /** The fully qualified class name of a FrameLayout view */ @@ -1208,11 +1214,13 @@ public final class SdkConstants { public static final String FQCN_RADIO_GROUP = "android.widgets.RadioGroup"; // $NON-NLS-1$ /** The fully qualified class name of a Space */ public static final String FQCN_SPACE = "android.widget.Space"; // $NON-NLS-1$ + public static final String FQCN_SPACE_V7 = "android.support.v7.widget.Space"; // $NON-NLS-1$ /** The fully qualified class name of a TextView view */ public static final String FQCN_TEXT_VIEW = "android.widget.TextView"; // $NON-NLS-1$ /** The fully qualified class name of an ImageView view */ public static final String FQCN_IMAGE_VIEW = "android.widget.ImageView"; // $NON-NLS-1$ + public static final String ATTR_SRC = "src"; // $NON-NLS-1$ public static final String ATTR_GRAVITY = "gravity"; // $NON-NLS-1$ public static final String ATTR_WEIGHT_SUM = "weightSum"; // $NON-NLS-1$ diff --git a/build-tools/xml-formatter/src/main/java/com/android/ide/common/xml/XmlAttributeSortOrder.java b/build-tools/xml-formatter/src/main/java/com/android/ide/common/xml/XmlAttributeSortOrder.java index cce58ee98..7d718d1b3 100644 --- a/build-tools/xml-formatter/src/main/java/com/android/ide/common/xml/XmlAttributeSortOrder.java +++ b/build-tools/xml-formatter/src/main/java/com/android/ide/common/xml/XmlAttributeSortOrder.java @@ -84,6 +84,7 @@ public enum XmlAttributeSortOrder { return attr1.getName().compareTo(attr2.getName()); } }; + public final String key; XmlAttributeSortOrder(String key) { diff --git a/build-tools/xml-formatter/src/main/java/com/android/utils/SdkUtils.java b/build-tools/xml-formatter/src/main/java/com/android/utils/SdkUtils.java index 9aafadb68..b7be5aa89 100644 --- a/build-tools/xml-formatter/src/main/java/com/android/utils/SdkUtils.java +++ b/build-tools/xml-formatter/src/main/java/com/android/utils/SdkUtils.java @@ -50,6 +50,7 @@ import java.util.List; public class SdkUtils { /** Prefix in comments which mark the source locations for merge results */ public static final String FILENAME_PREFIX = "From: "; + public static final List IMAGE_EXTENSIONS = ImmutableList.of(DOT_PNG, DOT_9PNG, DOT_GIF, DOT_JPEG, DOT_JPG, DOT_BMP, DOT_WEBP); /** For use by {@link #getLineSeparator()} */ diff --git a/common/src/main/java/com/itsaky/androidide/colorpicker/ColorPickerView.java b/common/src/main/java/com/itsaky/androidide/colorpicker/ColorPickerView.java index 91557abf3..ecf735cd1 100644 --- a/common/src/main/java/com/itsaky/androidide/colorpicker/ColorPickerView.java +++ b/common/src/main/java/com/itsaky/androidide/colorpicker/ColorPickerView.java @@ -161,12 +161,14 @@ public class ColorPickerView extends LinearLayout { @Override public void afterTextChanged(Editable s) {} - } /** + } + /** * Set the color value of this picker to the given hex color code. * * @param hexCode The hex color code. * @see #setColor(int) */ + public void setColor(final String hexCode) { setColor(parseColor(hexCode)); } @@ -185,12 +187,14 @@ public class ColorPickerView extends LinearLayout { * @param hexCode The hex color code of the color. */ void onPick(int color, String hexCode); - } /** + } + /** * Set the color value for this color picker. this will in turn update value of the sliders and * the color preview. * * @param color The new color value. */ + public void setColor(int color) { this.alpha = alpha(color); this.red = red(color); @@ -219,8 +223,4 @@ public class ColorPickerView extends LinearLayout { private void updatePreview() { this.binding.colorPreview.setBackgroundColor(getColor()); } - - - - } diff --git a/common/src/main/java/com/itsaky/androidide/utils/CharSequenceInputStream.java b/common/src/main/java/com/itsaky/androidide/utils/CharSequenceInputStream.java index b3cc2c9dc..dc689db94 100644 --- a/common/src/main/java/com/itsaky/androidide/utils/CharSequenceInputStream.java +++ b/common/src/main/java/com/itsaky/androidide/utils/CharSequenceInputStream.java @@ -115,12 +115,14 @@ public class CharSequenceInputStream extends InputStream { public CharSequenceInputStream( final CharSequence cs, final String charset, final int bufferSize) { this(cs, toCharset(charset), bufferSize); - } /** + } + /** * Return an estimate of the number of bytes remaining in the byte stream. * * @return the count of bytes that can be read without blocking (or returning EOF). * @throws IOException if an error occurs (probably not possible). */ + @Override public int available() throws IOException { // The cached entries are in bbuf; since encoding always creates at least one byte @@ -132,7 +134,9 @@ public class CharSequenceInputStream extends InputStream { static Charset toCharset(final String charsetName) throws UnsupportedCharsetException { return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName); - } @Override + } + + @Override public void close() throws IOException { // noop } @@ -144,11 +148,13 @@ public class CharSequenceInputStream extends InputStream { */ CharsetEncoder getCharsetEncoder() { return charsetEncoder; - } /** + } + /** * Fills the byte output buffer from the input char buffer. * * @throws CharacterCodingException an error encoding data. */ + private void fillBuffer() throws CharacterCodingException { this.bBuf.compact(); final CoderResult result = this.charsetEncoder.encode(this.cBuf, this.bBuf, true); @@ -158,8 +164,6 @@ public class CharSequenceInputStream extends InputStream { this.bBuf.flip(); } - - /** * {@inheritDoc} * @@ -288,8 +292,4 @@ public class CharSequenceInputStream extends InputStream { } return skipped; } - - - - } diff --git a/common/src/main/java/com/itsaky/androidide/utils/CharSequenceReader.java b/common/src/main/java/com/itsaky/androidide/utils/CharSequenceReader.java index 3ff7d5e70..1d0d586cf 100644 --- a/common/src/main/java/com/itsaky/androidide/utils/CharSequenceReader.java +++ b/common/src/main/java/com/itsaky/androidide/utils/CharSequenceReader.java @@ -58,6 +58,7 @@ public class CharSequenceReader extends Reader implements Serializable { * @since 2.7 */ private final Integer end; + private int idx; private int mark; @@ -132,7 +133,9 @@ public class CharSequenceReader extends Reader implements Serializable { public String toString() { final CharSequence subSequence = charSequence.subSequence(start(), end()); return subSequence.toString(); - } /** Close resets the file back to the start and removes any marked position. */ + } + /** Close resets the file back to the start and removes any marked position. */ + @Override public void close() { idx = start; @@ -147,11 +150,13 @@ public class CharSequenceReader extends Reader implements Serializable { */ private int start() { return Math.min(charSequence.length(), start); - } /** + } + /** * Returns the index in the character sequence to end reading at, taking into account its length. * * @return The end index in the character sequence (exclusive). */ + private int end() { /* * end == null for de-serialized instances that were serialized before start and end were added. @@ -281,8 +286,4 @@ public class CharSequenceReader extends Reader implements Serializable { idx = dest; return count; } - - - - } diff --git a/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java b/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java index 98c61d71b..bdb02168b 100755 --- a/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java +++ b/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java @@ -36,19 +36,19 @@ import org.json.JSONObject; * *

Examples: {@code # Empty: [] * - *

# Single row: [[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]] + *

# Single row: [[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]] * - *

# 2 row: [['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'], + *

# 2 row: [['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'], * ['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN']] * - *

# Advance: [[ {key: ESC, popup: {macro: "CTRL f d", display: "tmux exit"}}, - * {key: CTRL, popup: {macro: "CTRL f BKSP", display: "tmux ←"}}, {key: ALT, popup: {macro: "CTRL f - * TAB", display: "tmux →"}}, {key: TAB, popup: {macro: "ALT a", display: A-a}}, {key: LEFT, popup: - * HOME}, {key: DOWN, popup: PGDN}, {key: UP, popup: PGUP}, {key: RIGHT, popup: END}, {macro: "ALT - * j", display: A-j, popup: {macro: "ALT g", display: A-g}}, {key: KEYBOARD, popup: {macro: "CTRL - * d", display: exit}} ]] + *

# Advance: [[ {key: ESC, popup: {macro: "CTRL f d", display: "tmux + * exit"}}, {key: CTRL, popup: {macro: "CTRL f BKSP", display: "tmux ←"}}, {key: ALT, popup: {macro: + * "CTRL f TAB", display: "tmux →"}}, {key: TAB, popup: {macro: "ALT a", display: A-a}}, {key: LEFT, + * popup: HOME}, {key: DOWN, popup: PGDN}, {key: UP, popup: PGUP}, {key: RIGHT, popup: END}, {macro: + * "ALT j", display: A-j, popup: {macro: "ALT g", display: A-g}}, {key: KEYBOARD, popup: {macro: + * "CTRL d", display: exit}} ]] * - *

} + *

} * *

Aliases are also allowed for the keys that you can pass as {@code extraKeyAliasMap}. Check * {@link VirtualKeysConstants#CONTROL_CHARS_ALIASES}. diff --git a/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysView.java b/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysView.java index ff22625b6..1e7c3ae66 100755 --- a/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysView.java +++ b/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysView.java @@ -151,10 +151,12 @@ public final class VirtualKeysView extends GridLayout { * and a swipe up action is done on an extra key. */ private PopupWindow mPopupWindow; + private ScheduledExecutorService mScheduledExecutor; private Handler mHandler; private SpecialButtonsLongHoldRunnable mSpecialButtonsLongHoldRunnable; private int mLongPressCount; + public VirtualKeysView(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/emulatorview/src/main/java/com/itsaky/terminal/TerminalBuffer.java b/emulatorview/src/main/java/com/itsaky/terminal/TerminalBuffer.java index a4311d5d2..90071a9b1 100755 --- a/emulatorview/src/main/java/com/itsaky/terminal/TerminalBuffer.java +++ b/emulatorview/src/main/java/com/itsaky/terminal/TerminalBuffer.java @@ -15,6 +15,7 @@ public final class TerminalBuffer { private int mActiveTranscriptRows = 0; /** The index in the circular buffer where the visible screen starts. */ private int mScreenFirstRow = 0; + TerminalRow[] mLines; /** The length of {@link #mLines}. */ int mTotalRows; diff --git a/emulatorview/src/main/java/com/itsaky/terminal/TerminalEmulator.java b/emulatorview/src/main/java/com/itsaky/terminal/TerminalEmulator.java index adab11b94..66a353f48 100755 --- a/emulatorview/src/main/java/com/itsaky/terminal/TerminalEmulator.java +++ b/emulatorview/src/main/java/com/itsaky/terminal/TerminalEmulator.java @@ -33,6 +33,7 @@ public final class TerminalEmulator { public static final int MOUSE_LEFT_BUTTON = 0; /** Mouse moving while having left mouse button pressed. */ public static final int MOUSE_LEFT_BUTTON_MOVED = 32; + public static final int MOUSE_WHEELUP_BUTTON = 64; public static final int MOUSE_WHEELDOWN_BUTTON = 65; /** @@ -42,6 +43,7 @@ public final class TerminalEmulator { public static final int UNICODE_REPLACEMENT_CHAR = 0xFFFD; /** The number of terminal transcript rows that can be scrolled back to. */ public static final int TERMINAL_TRANSCRIPT_ROWS_MIN = 100; + public static final int TERMINAL_TRANSCRIPT_ROWS_MAX = 50000; public static final int DEFAULT_TERMINAL_TRANSCRIPT_ROWS = 2000; public static final int TERMINAL_CURSOR_STYLE_BLOCK = 0; @@ -101,6 +103,7 @@ public final class TerminalEmulator { private static final int MAX_OSC_STRING_LENGTH = 8192; /** DECSET 1 - application cursor keys. */ private static final int DECSET_BIT_APPLICATION_CURSOR_KEYS = 1; + private static final int DECSET_BIT_REVERSE_VIDEO = 1 << 1; /** * http://www.vt100.net/docs/vt510-rm/DECOM: "When DECOM is set, the home cursor position is at @@ -121,6 +124,7 @@ public final class TerminalEmulator { private static final int DECSET_BIT_AUTOWRAP = 1 << 3; /** DECSET 25 - if the cursor should be enabled, {@link #isCursorEnabled()}. */ private static final int DECSET_BIT_CURSOR_ENABLED = 1 << 4; + private static final int DECSET_BIT_APPLICATION_KEYPAD = 1 << 5; /** DECSET 1000 - if to report mouse press&release events. */ private static final int DECSET_BIT_MOUSE_TRACKING_PRESS_RELEASE = 1 << 6; @@ -138,6 +142,7 @@ public final class TerminalEmulator { /* The supported terminal cursor styles. */ /** Not really DECSET bit... - http://www.vt100.net/docs/vt510-rm/DECSACE */ private static final int DECSET_BIT_RECTANGULAR_CHANGEATTRIBUTE = 1 << 12; + private static final String LOG_TAG = "TerminalEmulator"; public final TerminalColors mColors = new TerminalColors(); private final Stack mTitleStack = new Stack<>(); @@ -152,6 +157,7 @@ public final class TerminalEmulator { private final int[] mArgs = new int[MAX_ESCAPE_PARAMETERS]; /** Holds OSC and device control arguments, which can be strings. */ private final StringBuilder mOSCOrDeviceControlArgs = new StringBuilder(); + private final SavedScreenState mSavedStateMain = new SavedScreenState(); private final SavedScreenState mSavedStateAlt = new SavedScreenState(); private final byte[] mUtf8InputBuffer = new byte[4]; @@ -165,6 +171,7 @@ public final class TerminalEmulator { final TerminalBuffer mAltBuffer; /** The number of character rows and columns in the terminal screen. */ public int mRows, mColumns; + private String mTitle; /** The cursor position. Between (0,0) and (mRows-1, mColumns-1). */ private int mCursorRow, mCursorCol; @@ -227,6 +234,7 @@ public final class TerminalEmulator { * selection up along with the scrolling text. */ private int mScrollCounter = 0; + private byte mUtf8ToFollow, mUtf8Index; private int mLastEmittedCodePoint = -1; TerminalSessionClient mClient; diff --git a/emulatorview/src/main/java/com/itsaky/terminal/TerminalSession.java b/emulatorview/src/main/java/com/itsaky/terminal/TerminalSession.java index 693d59cec..12174e880 100755 --- a/emulatorview/src/main/java/com/itsaky/terminal/TerminalSession.java +++ b/emulatorview/src/main/java/com/itsaky/terminal/TerminalSession.java @@ -38,6 +38,7 @@ public final class TerminalSession extends TerminalOutput { public final String mHandle = UUID.randomUUID().toString(); /** Buffer to write translate code points into utf8 before writing to mTerminalToProcessIOQueue */ private final byte[] mUtf8InputBuffer = new byte[5]; + private final String mShellPath; private final String mCwd; private final String[] mArgs; @@ -53,6 +54,7 @@ public final class TerminalSession extends TerminalOutput { * which forwards by writing to the {@link #mTerminalFileDescriptor}. */ final ByteQueue mTerminalToProcessIOQueue = new ByteQueue(4096); + final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), new MainThreadHandler()); /** Set by the application for user identification of session, not by terminal. */ public String mSessionName; @@ -61,6 +63,7 @@ public final class TerminalSession extends TerminalOutput { * calling {@link JNI#createSubprocess(String, String, String[], String[], int[], int, int)}. */ private int mTerminalFileDescriptor; + TerminalEmulator mEmulator; /** Callback which gets notified when a session finishes or changes title. */ TerminalSessionClient mClient; diff --git a/emulatorview/src/main/java/com/itsaky/terminal/TextStyle.java b/emulatorview/src/main/java/com/itsaky/terminal/TextStyle.java index d0898b095..ac250f572 100755 --- a/emulatorview/src/main/java/com/itsaky/terminal/TextStyle.java +++ b/emulatorview/src/main/java/com/itsaky/terminal/TextStyle.java @@ -26,6 +26,7 @@ public final class TextStyle { public static final int CHARACTER_ATTRIBUTE_PROTECTED = 1 << 7; /** Dim colors. Also known as faint or half intensity. */ public static final int CHARACTER_ATTRIBUTE_DIM = 1 << 8; + public static final int COLOR_INDEX_FOREGROUND = 256; public static final int COLOR_INDEX_BACKGROUND = 257; public static final int COLOR_INDEX_CURSOR = 258; diff --git a/emulatorview/src/main/java/com/itsaky/terminal/view/GestureAndScaleRecognizer.java b/emulatorview/src/main/java/com/itsaky/terminal/view/GestureAndScaleRecognizer.java index 142a337ca..beb407f1d 100755 --- a/emulatorview/src/main/java/com/itsaky/terminal/view/GestureAndScaleRecognizer.java +++ b/emulatorview/src/main/java/com/itsaky/terminal/view/GestureAndScaleRecognizer.java @@ -12,6 +12,7 @@ final class GestureAndScaleRecognizer { private final ScaleGestureDetector mScaleDetector; final Listener mListener; boolean isAfterLongPress; + public GestureAndScaleRecognizer(Context context, Listener listener) { mListener = listener; diff --git a/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalRenderer.java b/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalRenderer.java index 5197408b1..b38ac61e4 100755 --- a/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalRenderer.java +++ b/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalRenderer.java @@ -23,6 +23,7 @@ public final class TerminalRenderer { * The {@link Paint#ascent()}. See http://www.fampennings.nl/maarten/android/08numgrid/font.png */ private final int mFontAscent; + private final float[] asciiMeasures = new float[127]; final int mTextSize; final Typeface mTypeface; diff --git a/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalView.java b/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalView.java index ff5299e79..07ac7ad77 100755 --- a/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalView.java +++ b/emulatorview/src/main/java/com/itsaky/terminal/view/TerminalView.java @@ -41,6 +41,7 @@ public final class TerminalView extends View { private static final String LOG_TAG = "TerminalView"; /** Log terminal view key and IME events. */ private static boolean TERMINAL_VIEW_KEY_LOGGING_ENABLED = false; + private final boolean mAccessibilityEnabled; final GestureAndScaleRecognizer mGestureRecognizer; final Scroller mScroller; @@ -48,6 +49,7 @@ public final class TerminalView extends View { public TerminalSession mTermSession; /** Our terminal emulator whose session is {@link #mTermSession}. */ public TerminalEmulator mEmulator; + public TerminalRenderer mRenderer; public TerminalViewClient mClient; private TextSelectionCursorController mTextSelectionCursorController; @@ -61,6 +63,7 @@ public final class TerminalView extends View { } } }; + private Handler mTerminalCursorBlinkerHandler; private TerminalCursorBlinkerRunnable mTerminalCursorBlinkerRunnable; private int mTerminalCursorBlinkerRate; @@ -71,6 +74,7 @@ public final class TerminalView extends View { private long mMouseStartDownTime = -1; /** The top row of text to display. Ranges from -activeTranscriptRows to 0. */ int mTopRow; + int[] mDefaultSelectors = new int[] {-1, -1, -1, -1}; float mScaleFactor = 1.f; /** What was left in from scrolling movement. */ diff --git a/eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java index 71158e2d0..e26cd857f 100644 --- a/eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java @@ -22,60 +22,60 @@ import android.os.SystemClock; public class HandlerPoster extends Handler implements Poster { - private final PendingPostQueue queue; - private final int maxMillisInsideHandleMessage; - private final EventBus eventBus; - private boolean handlerActive; + private final PendingPostQueue queue; + private final int maxMillisInsideHandleMessage; + private final EventBus eventBus; + private boolean handlerActive; - public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { - super(looper); - this.eventBus = eventBus; - this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; - queue = new PendingPostQueue(); - } + public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { + super(looper); + this.eventBus = eventBus; + this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; + queue = new PendingPostQueue(); + } - public void enqueue(Subscription subscription, Object event) { - PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); - synchronized (this) { - queue.enqueue(pendingPost); - if (!handlerActive) { - handlerActive = true; - if (!sendMessage(obtainMessage())) { - throw new EventBusException("Could not send handler message"); - } - } + public void enqueue(Subscription subscription, Object event) { + PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); + synchronized (this) { + queue.enqueue(pendingPost); + if (!handlerActive) { + handlerActive = true; + if (!sendMessage(obtainMessage())) { + throw new EventBusException("Could not send handler message"); } + } } + } - @Override - public void handleMessage(Message msg) { - boolean rescheduled = false; - try { - long started = SystemClock.uptimeMillis(); - while (true) { - PendingPost pendingPost = queue.poll(); - if (pendingPost == null) { - synchronized (this) { - // Check again, this time in synchronized - pendingPost = queue.poll(); - if (pendingPost == null) { - handlerActive = false; - return; - } - } - } - eventBus.invokeSubscriber(pendingPost); - long timeInMethod = SystemClock.uptimeMillis() - started; - if (timeInMethod >= maxMillisInsideHandleMessage) { - if (!sendMessage(obtainMessage())) { - throw new EventBusException("Could not send handler message"); - } - rescheduled = true; - return; - } + @Override + public void handleMessage(Message msg) { + boolean rescheduled = false; + try { + long started = SystemClock.uptimeMillis(); + while (true) { + PendingPost pendingPost = queue.poll(); + if (pendingPost == null) { + synchronized (this) { + // Check again, this time in synchronized + pendingPost = queue.poll(); + if (pendingPost == null) { + handlerActive = false; + return; } - } finally { - handlerActive = rescheduled; + } } + eventBus.invokeSubscriber(pendingPost); + long timeInMethod = SystemClock.uptimeMillis() - started; + if (timeInMethod >= maxMillisInsideHandleMessage) { + if (!sendMessage(obtainMessage())) { + throw new EventBusException("Could not send handler message"); + } + rescheduled = true; + return; + } + } + } finally { + handlerActive = rescheduled; } -} \ No newline at end of file + } +} diff --git a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidComponentsImpl.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidComponentsImpl.java index 20f35b18d..d37767d7a 100644 --- a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidComponentsImpl.java +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidComponentsImpl.java @@ -1,11 +1,9 @@ package org.greenrobot.eventbus.android; -/** - * Used via reflection in the Java library by {@link AndroidDependenciesDetector}. - */ +/** Used via reflection in the Java library by {@link AndroidDependenciesDetector}. */ public class AndroidComponentsImpl extends AndroidComponents { - public AndroidComponentsImpl() { - super(new AndroidLogger("EventBus"), new DefaultAndroidMainThreadSupport()); - } + public AndroidComponentsImpl() { + super(new AndroidLogger("EventBus"), new DefaultAndroidMainThreadSupport()); + } } diff --git a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidLogger.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidLogger.java index 04cc1cf37..20586fa52 100644 --- a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidLogger.java +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidLogger.java @@ -22,39 +22,39 @@ import java.util.logging.Level; public class AndroidLogger implements Logger { - private final String tag; + private final String tag; - public AndroidLogger(String tag) { - this.tag = tag; - } + public AndroidLogger(String tag) { + this.tag = tag; + } - public void log(Level level, String msg) { - if (level != Level.OFF) { - Log.println(mapLevel(level), tag, msg); - } + public void log(Level level, String msg) { + if (level != Level.OFF) { + Log.println(mapLevel(level), tag, msg); } + } - public void log(Level level, String msg, Throwable th) { - if (level != Level.OFF) { - // That's how Log does it internally - Log.println(mapLevel(level), tag, msg + "\n" + Log.getStackTraceString(th)); - } + public void log(Level level, String msg, Throwable th) { + if (level != Level.OFF) { + // That's how Log does it internally + Log.println(mapLevel(level), tag, msg + "\n" + Log.getStackTraceString(th)); } + } - private int mapLevel(Level level) { - int value = level.intValue(); - if (value < 800) { // below INFO - if (value < 500) { // below FINE - return Log.VERBOSE; - } else { - return Log.DEBUG; - } - } else if (value < 900) { // below WARNING - return Log.INFO; - } else if (value < 1000) { // below ERROR - return Log.WARN; - } else { - return Log.ERROR; - } + private int mapLevel(Level level) { + int value = level.intValue(); + if (value < 800) { // below INFO + if (value < 500) { // below FINE + return Log.VERBOSE; + } else { + return Log.DEBUG; + } + } else if (value < 900) { // below WARNING + return Log.INFO; + } else if (value < 1000) { // below ERROR + return Log.WARN; + } else { + return Log.ERROR; } + } } diff --git a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/DefaultAndroidMainThreadSupport.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/DefaultAndroidMainThreadSupport.java index 02b3f3afa..fe47e6fc0 100644 --- a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/DefaultAndroidMainThreadSupport.java +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/DefaultAndroidMainThreadSupport.java @@ -8,13 +8,13 @@ import org.greenrobot.eventbus.Poster; public class DefaultAndroidMainThreadSupport implements MainThreadSupport { - @Override - public boolean isMainThread() { - return Looper.getMainLooper() == Looper.myLooper(); - } + @Override + public boolean isMainThread() { + return Looper.getMainLooper() == Looper.myLooper(); + } - @Override - public Poster createPoster(EventBus eventBus) { - return new HandlerPoster(eventBus, Looper.getMainLooper(), 10); - } + @Override + public Poster createPoster(EventBus eventBus) { + return new HandlerPoster(eventBus, Looper.getMainLooper(), 10); + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/AsyncPoster.java b/eventbus/src/main/java/org/greenrobot/eventbus/AsyncPoster.java index 90a30d1e0..098b0cc22 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/AsyncPoster.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/AsyncPoster.java @@ -15,35 +15,33 @@ */ package org.greenrobot.eventbus; - /** * Posts events in background. - * + * * @author Markus */ class AsyncPoster implements Runnable, Poster { - private final PendingPostQueue queue; - private final EventBus eventBus; + private final PendingPostQueue queue; + private final EventBus eventBus; - AsyncPoster(EventBus eventBus) { - this.eventBus = eventBus; - queue = new PendingPostQueue(); + AsyncPoster(EventBus eventBus) { + this.eventBus = eventBus; + queue = new PendingPostQueue(); + } + + public void enqueue(Subscription subscription, Object event) { + PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); + queue.enqueue(pendingPost); + eventBus.getExecutorService().execute(this); + } + + @Override + public void run() { + PendingPost pendingPost = queue.poll(); + if (pendingPost == null) { + throw new IllegalStateException("No pending post available"); } - - public void enqueue(Subscription subscription, Object event) { - PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); - queue.enqueue(pendingPost); - eventBus.getExecutorService().execute(this); - } - - @Override - public void run() { - PendingPost pendingPost = queue.poll(); - if(pendingPost == null) { - throw new IllegalStateException("No pending post available"); - } - eventBus.invokeSubscriber(pendingPost); - } - + eventBus.invokeSubscriber(pendingPost); + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/BackgroundPoster.java b/eventbus/src/main/java/org/greenrobot/eventbus/BackgroundPoster.java index 94db640ed..9e9b9e877 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/BackgroundPoster.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/BackgroundPoster.java @@ -24,51 +24,52 @@ import java.util.logging.Level; */ final class BackgroundPoster implements Runnable, Poster { - private final PendingPostQueue queue; - private final EventBus eventBus; + private final PendingPostQueue queue; + private final EventBus eventBus; - private volatile boolean executorRunning; + private volatile boolean executorRunning; - BackgroundPoster(EventBus eventBus) { - this.eventBus = eventBus; - queue = new PendingPostQueue(); + BackgroundPoster(EventBus eventBus) { + this.eventBus = eventBus; + queue = new PendingPostQueue(); + } + + public void enqueue(Subscription subscription, Object event) { + PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); + synchronized (this) { + queue.enqueue(pendingPost); + if (!executorRunning) { + executorRunning = true; + eventBus.getExecutorService().execute(this); + } } + } - public void enqueue(Subscription subscription, Object event) { - PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); - synchronized (this) { - queue.enqueue(pendingPost); - if (!executorRunning) { - executorRunning = true; - eventBus.getExecutorService().execute(this); + @Override + public void run() { + try { + try { + while (true) { + PendingPost pendingPost = queue.poll(1000); + if (pendingPost == null) { + synchronized (this) { + // Check again, this time in synchronized + pendingPost = queue.poll(); + if (pendingPost == null) { + executorRunning = false; + return; + } } + } + eventBus.invokeSubscriber(pendingPost); } + } catch (InterruptedException e) { + eventBus + .getLogger() + .log(Level.WARNING, Thread.currentThread().getName() + " was interrupted", e); + } + } finally { + executorRunning = false; } - - @Override - public void run() { - try { - try { - while (true) { - PendingPost pendingPost = queue.poll(1000); - if (pendingPost == null) { - synchronized (this) { - // Check again, this time in synchronized - pendingPost = queue.poll(); - if (pendingPost == null) { - executorRunning = false; - return; - } - } - } - eventBus.invokeSubscriber(pendingPost); - } - } catch (InterruptedException e) { - eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interrupted", e); - } - } finally { - executorRunning = false; - } - } - + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/EventBus.java b/eventbus/src/main/java/org/greenrobot/eventbus/EventBus.java index 147bb5ffd..b93e0767c 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/EventBus.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/EventBus.java @@ -28,554 +28,583 @@ import java.util.concurrent.ExecutorService; import java.util.logging.Level; /** - * EventBus is a central publish/subscribe event system for Java and Android. - * Events are posted ({@link #post(Object)}) to the bus, which delivers it to subscribers that have a matching handler - * method for the event type. - * To receive events, subscribers must register themselves to the bus using {@link #register(Object)}. - * Once registered, subscribers receive events until {@link #unregister(Object)} is called. - * Event handling methods must be annotated by {@link Subscribe}, must be public, return nothing (void), - * and have exactly one parameter (the event). + * EventBus is a central publish/subscribe event system for Java and Android. Events are posted + * ({@link #post(Object)}) to the bus, which delivers it to subscribers that have a matching handler + * method for the event type. To receive events, subscribers must register themselves to the bus + * using {@link #register(Object)}. Once registered, subscribers receive events until {@link + * #unregister(Object)} is called. Event handling methods must be annotated by {@link Subscribe}, + * must be public, return nothing (void), and have exactly one parameter (the event). * * @author Markus Junginger, greenrobot */ public class EventBus { - /** Log tag, apps may override it. */ - public static String TAG = "EventBus"; + /** Log tag, apps may override it. */ + public static String TAG = "EventBus"; - static volatile EventBus defaultInstance; + static volatile EventBus defaultInstance; - private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); - private static final Map, List>> eventTypesCache = new HashMap<>(); + private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); + private static final Map, List>> eventTypesCache = new HashMap<>(); - private final Map, CopyOnWriteArrayList> subscriptionsByEventType; - private final Map>> typesBySubscriber; - private final Map, Object> stickyEvents; + private final Map, CopyOnWriteArrayList> subscriptionsByEventType; + private final Map>> typesBySubscriber; + private final Map, Object> stickyEvents; - private final ThreadLocal currentPostingThreadState = new ThreadLocal() { + private final ThreadLocal currentPostingThreadState = + new ThreadLocal() { @Override protected PostingThreadState initialValue() { - return new PostingThreadState(); + return new PostingThreadState(); } - }; + }; - // @Nullable - private final MainThreadSupport mainThreadSupport; - // @Nullable - private final Poster mainThreadPoster; - private final BackgroundPoster backgroundPoster; - private final AsyncPoster asyncPoster; - private final SubscriberMethodFinder subscriberMethodFinder; - private final ExecutorService executorService; + // @Nullable + private final MainThreadSupport mainThreadSupport; + // @Nullable + private final Poster mainThreadPoster; + private final BackgroundPoster backgroundPoster; + private final AsyncPoster asyncPoster; + private final SubscriberMethodFinder subscriberMethodFinder; + private final ExecutorService executorService; - private final boolean throwSubscriberException; - private final boolean logSubscriberExceptions; - private final boolean logNoSubscriberMessages; - private final boolean sendSubscriberExceptionEvent; - private final boolean sendNoSubscriberEvent; - private final boolean eventInheritance; + private final boolean throwSubscriberException; + private final boolean logSubscriberExceptions; + private final boolean logNoSubscriberMessages; + private final boolean sendSubscriberExceptionEvent; + private final boolean sendNoSubscriberEvent; + private final boolean eventInheritance; - private final int indexCount; - private final Logger logger; + private final int indexCount; + private final Logger logger; - /** Convenience singleton for apps using a process-wide EventBus instance. */ - public static EventBus getDefault() { - EventBus instance = defaultInstance; + /** Convenience singleton for apps using a process-wide EventBus instance. */ + public static EventBus getDefault() { + EventBus instance = defaultInstance; + if (instance == null) { + synchronized (EventBus.class) { + instance = EventBus.defaultInstance; if (instance == null) { - synchronized (EventBus.class) { - instance = EventBus.defaultInstance; - if (instance == null) { - instance = EventBus.defaultInstance = new EventBus(); - } - } + instance = EventBus.defaultInstance = new EventBus(); } - return instance; + } + } + return instance; + } + + public static EventBusBuilder builder() { + return new EventBusBuilder(); + } + + /** For unit test primarily. */ + public static void clearCaches() { + SubscriberMethodFinder.clearCaches(); + eventTypesCache.clear(); + } + + /** + * Creates a new EventBus instance; each instance is a separate scope in which events are + * delivered. To use a central bus, consider {@link #getDefault()}. + */ + public EventBus() { + this(DEFAULT_BUILDER); + } + + EventBus(EventBusBuilder builder) { + logger = builder.getLogger(); + subscriptionsByEventType = new HashMap<>(); + typesBySubscriber = new HashMap<>(); + stickyEvents = new ConcurrentHashMap<>(); + mainThreadSupport = builder.getMainThreadSupport(); + mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; + backgroundPoster = new BackgroundPoster(this); + asyncPoster = new AsyncPoster(this); + indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0; + subscriberMethodFinder = + new SubscriberMethodFinder( + builder.subscriberInfoIndexes, + builder.strictMethodVerification, + builder.ignoreGeneratedIndex); + logSubscriberExceptions = builder.logSubscriberExceptions; + logNoSubscriberMessages = builder.logNoSubscriberMessages; + sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; + sendNoSubscriberEvent = builder.sendNoSubscriberEvent; + throwSubscriberException = builder.throwSubscriberException; + eventInheritance = builder.eventInheritance; + executorService = builder.executorService; + } + + /** + * Registers the given subscriber to receive events. Subscribers must call {@link + * #unregister(Object)} once they are no longer interested in receiving events. + * + *

Subscribers have event handling methods that must be annotated by {@link Subscribe}. The + * {@link Subscribe} annotation also allows configuration like {@link ThreadMode} and priority. + */ + public void register(Object subscriber) { + if (AndroidDependenciesDetector.isAndroidSDKAvailable() + && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) { + // Crash if the user (developer) has not imported the Android compatibility library. + throw new RuntimeException( + "It looks like you are using EventBus on Android, " + + "make sure to add the \"eventbus\" Android library to your dependencies."); } - public static EventBusBuilder builder() { - return new EventBusBuilder(); + Class subscriberClass = subscriber.getClass(); + List subscriberMethods = + subscriberMethodFinder.findSubscriberMethods(subscriberClass); + synchronized (this) { + for (SubscriberMethod subscriberMethod : subscriberMethods) { + subscribe(subscriber, subscriberMethod); + } + } + } + + // Must be called in synchronized block + private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { + Class eventType = subscriberMethod.eventType; + Subscription newSubscription = new Subscription(subscriber, subscriberMethod); + CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); + if (subscriptions == null) { + subscriptions = new CopyOnWriteArrayList<>(); + subscriptionsByEventType.put(eventType, subscriptions); + } else { + if (subscriptions.contains(newSubscription)) { + throw new EventBusException( + "Subscriber " + subscriber.getClass() + " already registered to event " + eventType); + } } - /** For unit test primarily. */ - public static void clearCaches() { - SubscriberMethodFinder.clearCaches(); - eventTypesCache.clear(); + int size = subscriptions.size(); + for (int i = 0; i <= size; i++) { + if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { + subscriptions.add(i, newSubscription); + break; + } } - /** - * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a - * central bus, consider {@link #getDefault()}. - */ - public EventBus() { - this(DEFAULT_BUILDER); + List> subscribedEvents = typesBySubscriber.get(subscriber); + if (subscribedEvents == null) { + subscribedEvents = new ArrayList<>(); + typesBySubscriber.put(subscriber, subscribedEvents); } + subscribedEvents.add(eventType); - EventBus(EventBusBuilder builder) { - logger = builder.getLogger(); - subscriptionsByEventType = new HashMap<>(); - typesBySubscriber = new HashMap<>(); - stickyEvents = new ConcurrentHashMap<>(); - mainThreadSupport = builder.getMainThreadSupport(); - mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; - backgroundPoster = new BackgroundPoster(this); - asyncPoster = new AsyncPoster(this); - indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0; - subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, - builder.strictMethodVerification, builder.ignoreGeneratedIndex); - logSubscriberExceptions = builder.logSubscriberExceptions; - logNoSubscriberMessages = builder.logNoSubscriberMessages; - sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; - sendNoSubscriberEvent = builder.sendNoSubscriberEvent; - throwSubscriberException = builder.throwSubscriberException; - eventInheritance = builder.eventInheritance; - executorService = builder.executorService; - } - - /** - * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they - * are no longer interested in receiving events. - *

- * Subscribers have event handling methods that must be annotated by {@link Subscribe}. - * The {@link Subscribe} annotation also allows configuration like {@link - * ThreadMode} and priority. - */ - public void register(Object subscriber) { - if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) { - // Crash if the user (developer) has not imported the Android compatibility library. - throw new RuntimeException("It looks like you are using EventBus on Android, " + - "make sure to add the \"eventbus\" Android library to your dependencies."); + if (subscriberMethod.sticky) { + if (eventInheritance) { + // Existing sticky events of all subclasses of eventType have to be considered. + // Note: Iterating over all events may be inefficient with lots of sticky events, + // thus data structure should be changed to allow a more efficient lookup + // (e.g. an additional map storing sub classes of super classes: Class -> List). + Set, Object>> entries = stickyEvents.entrySet(); + for (Map.Entry, Object> entry : entries) { + Class candidateEventType = entry.getKey(); + if (eventType.isAssignableFrom(candidateEventType)) { + Object stickyEvent = entry.getValue(); + checkPostStickyEventToSubscription(newSubscription, stickyEvent); + } } + } else { + Object stickyEvent = stickyEvents.get(eventType); + checkPostStickyEventToSubscription(newSubscription, stickyEvent); + } + } + } - Class subscriberClass = subscriber.getClass(); - List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); - synchronized (this) { - for (SubscriberMethod subscriberMethod : subscriberMethods) { - subscribe(subscriber, subscriberMethod); - } + private void checkPostStickyEventToSubscription( + Subscription newSubscription, Object stickyEvent) { + if (stickyEvent != null) { + // If the subscriber is trying to abort the event, it will fail (event is not tracked in + // posting state) + // --> Strange corner case, which we don't take care of here. + postToSubscription(newSubscription, stickyEvent, isMainThread()); + } + } + + /** + * Checks if the current thread is running in the main thread. If there is no main thread support + * (e.g. non-Android), "true" is always returned. In that case MAIN thread subscribers are always + * called in posting thread, and BACKGROUND subscribers are always called from a background + * poster. + */ + private boolean isMainThread() { + return mainThreadSupport == null || mainThreadSupport.isMainThread(); + } + + public synchronized boolean isRegistered(Object subscriber) { + return typesBySubscriber.containsKey(subscriber); + } + + /** + * Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update + * typesBySubscriber. + */ + private void unsubscribeByEventType(Object subscriber, Class eventType) { + List subscriptions = subscriptionsByEventType.get(eventType); + if (subscriptions != null) { + int size = subscriptions.size(); + for (int i = 0; i < size; i++) { + Subscription subscription = subscriptions.get(i); + if (subscription.subscriber == subscriber) { + subscription.active = false; + subscriptions.remove(i); + i--; + size--; } + } + } + } + + /** Unregisters the given subscriber from all event classes. */ + public synchronized void unregister(Object subscriber) { + List> subscribedTypes = typesBySubscriber.get(subscriber); + if (subscribedTypes != null) { + for (Class eventType : subscribedTypes) { + unsubscribeByEventType(subscriber, eventType); + } + typesBySubscriber.remove(subscriber); + } else { + logger.log( + Level.WARNING, + "Subscriber to unregister was not registered before: " + subscriber.getClass()); + } + } + + /** Posts the given event to the event bus. */ + public void post(Object event) { + PostingThreadState postingState = currentPostingThreadState.get(); + List eventQueue = postingState.eventQueue; + eventQueue.add(event); + + if (!postingState.isPosting) { + postingState.isMainThread = isMainThread(); + postingState.isPosting = true; + if (postingState.canceled) { + throw new EventBusException("Internal error. Abort state was not reset"); + } + try { + while (!eventQueue.isEmpty()) { + postSingleEvent(eventQueue.remove(0), postingState); + } + } finally { + postingState.isPosting = false; + postingState.isMainThread = false; + } + } + } + + /** + * Called from a subscriber's event handling method, further event delivery will be canceled. + * Subsequent subscribers won't receive the event. Events are usually canceled by higher priority + * subscribers (see {@link Subscribe#priority()}). Canceling is restricted to event handling + * methods running in posting thread {@link ThreadMode#POSTING}. + */ + public void cancelEventDelivery(Object event) { + PostingThreadState postingState = currentPostingThreadState.get(); + if (!postingState.isPosting) { + throw new EventBusException( + "This method may only be called from inside event handling methods on the posting thread"); + } else if (event == null) { + throw new EventBusException("Event may not be null"); + } else if (postingState.event != event) { + throw new EventBusException("Only the currently handled event may be aborted"); + } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) { + throw new EventBusException(" event handlers may only abort the incoming event"); } - // Must be called in synchronized block - private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { - Class eventType = subscriberMethod.eventType; - Subscription newSubscription = new Subscription(subscriber, subscriberMethod); - CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); - if (subscriptions == null) { - subscriptions = new CopyOnWriteArrayList<>(); - subscriptionsByEventType.put(eventType, subscriptions); - } else { - if (subscriptions.contains(newSubscription)) { - throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " - + eventType); - } - } + postingState.canceled = true; + } - int size = subscriptions.size(); - for (int i = 0; i <= size; i++) { - if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { - subscriptions.add(i, newSubscription); - break; - } - } - - List> subscribedEvents = typesBySubscriber.get(subscriber); - if (subscribedEvents == null) { - subscribedEvents = new ArrayList<>(); - typesBySubscriber.put(subscriber, subscribedEvents); - } - subscribedEvents.add(eventType); - - if (subscriberMethod.sticky) { - if (eventInheritance) { - // Existing sticky events of all subclasses of eventType have to be considered. - // Note: Iterating over all events may be inefficient with lots of sticky events, - // thus data structure should be changed to allow a more efficient lookup - // (e.g. an additional map storing sub classes of super classes: Class -> List). - Set, Object>> entries = stickyEvents.entrySet(); - for (Map.Entry, Object> entry : entries) { - Class candidateEventType = entry.getKey(); - if (eventType.isAssignableFrom(candidateEventType)) { - Object stickyEvent = entry.getValue(); - checkPostStickyEventToSubscription(newSubscription, stickyEvent); - } - } - } else { - Object stickyEvent = stickyEvents.get(eventType); - checkPostStickyEventToSubscription(newSubscription, stickyEvent); - } - } + /** + * Posts the given event to the event bus and holds on to the event (because it is sticky). The + * most recent sticky event of an event's type is kept in memory for future access by subscribers + * using {@link Subscribe#sticky()}. + */ + public void postSticky(Object event) { + synchronized (stickyEvents) { + stickyEvents.put(event.getClass(), event); } + // Should be posted after it is putted, in case the subscriber wants to remove immediately + post(event); + } - private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { - if (stickyEvent != null) { - // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) - // --> Strange corner case, which we don't take care of here. - postToSubscription(newSubscription, stickyEvent, isMainThread()); - } + /** + * Gets the most recent sticky event for the given type. + * + * @see #postSticky(Object) + */ + public T getStickyEvent(Class eventType) { + synchronized (stickyEvents) { + return eventType.cast(stickyEvents.get(eventType)); } + } - /** - * Checks if the current thread is running in the main thread. - * If there is no main thread support (e.g. non-Android), "true" is always returned. In that case MAIN thread - * subscribers are always called in posting thread, and BACKGROUND subscribers are always called from a background - * poster. - */ - private boolean isMainThread() { - return mainThreadSupport == null || mainThreadSupport.isMainThread(); + /** + * Remove and gets the recent sticky event for the given event type. + * + * @see #postSticky(Object) + */ + public T removeStickyEvent(Class eventType) { + synchronized (stickyEvents) { + return eventType.cast(stickyEvents.remove(eventType)); } + } - public synchronized boolean isRegistered(Object subscriber) { - return typesBySubscriber.containsKey(subscriber); - } - - /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ - private void unsubscribeByEventType(Object subscriber, Class eventType) { - List subscriptions = subscriptionsByEventType.get(eventType); - if (subscriptions != null) { - int size = subscriptions.size(); - for (int i = 0; i < size; i++) { - Subscription subscription = subscriptions.get(i); - if (subscription.subscriber == subscriber) { - subscription.active = false; - subscriptions.remove(i); - i--; - size--; - } - } - } - } - - /** Unregisters the given subscriber from all event classes. */ - public synchronized void unregister(Object subscriber) { - List> subscribedTypes = typesBySubscriber.get(subscriber); - if (subscribedTypes != null) { - for (Class eventType : subscribedTypes) { - unsubscribeByEventType(subscriber, eventType); - } - typesBySubscriber.remove(subscriber); - } else { - logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); - } - } - - /** Posts the given event to the event bus. */ - public void post(Object event) { - PostingThreadState postingState = currentPostingThreadState.get(); - List eventQueue = postingState.eventQueue; - eventQueue.add(event); - - if (!postingState.isPosting) { - postingState.isMainThread = isMainThread(); - postingState.isPosting = true; - if (postingState.canceled) { - throw new EventBusException("Internal error. Abort state was not reset"); - } - try { - while (!eventQueue.isEmpty()) { - postSingleEvent(eventQueue.remove(0), postingState); - } - } finally { - postingState.isPosting = false; - postingState.isMainThread = false; - } - } - } - - /** - * Called from a subscriber's event handling method, further event delivery will be canceled. Subsequent - * subscribers - * won't receive the event. Events are usually canceled by higher priority subscribers (see - * {@link Subscribe#priority()}). Canceling is restricted to event handling methods running in posting thread - * {@link ThreadMode#POSTING}. - */ - public void cancelEventDelivery(Object event) { - PostingThreadState postingState = currentPostingThreadState.get(); - if (!postingState.isPosting) { - throw new EventBusException( - "This method may only be called from inside event handling methods on the posting thread"); - } else if (event == null) { - throw new EventBusException("Event may not be null"); - } else if (postingState.event != event) { - throw new EventBusException("Only the currently handled event may be aborted"); - } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) { - throw new EventBusException(" event handlers may only abort the incoming event"); - } - - postingState.canceled = true; - } - - /** - * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky - * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}. - */ - public void postSticky(Object event) { - synchronized (stickyEvents) { - stickyEvents.put(event.getClass(), event); - } - // Should be posted after it is putted, in case the subscriber wants to remove immediately - post(event); - } - - /** - * Gets the most recent sticky event for the given type. - * - * @see #postSticky(Object) - */ - public T getStickyEvent(Class eventType) { - synchronized (stickyEvents) { - return eventType.cast(stickyEvents.get(eventType)); - } - } - - /** - * Remove and gets the recent sticky event for the given event type. - * - * @see #postSticky(Object) - */ - public T removeStickyEvent(Class eventType) { - synchronized (stickyEvents) { - return eventType.cast(stickyEvents.remove(eventType)); - } - } - - /** - * Removes the sticky event if it equals to the given event. - * - * @return true if the events matched and the sticky event was removed. - */ - public boolean removeStickyEvent(Object event) { - synchronized (stickyEvents) { - Class eventType = event.getClass(); - Object existingEvent = stickyEvents.get(eventType); - if (event.equals(existingEvent)) { - stickyEvents.remove(eventType); - return true; - } else { - return false; - } - } - } - - /** - * Removes all sticky events. - */ - public void removeAllStickyEvents() { - synchronized (stickyEvents) { - stickyEvents.clear(); - } - } - - public boolean hasSubscriberForEvent(Class eventClass) { - List> eventTypes = lookupAllEventTypes(eventClass); - if (eventTypes != null) { - int countTypes = eventTypes.size(); - for (int h = 0; h < countTypes; h++) { - Class clazz = eventTypes.get(h); - CopyOnWriteArrayList subscriptions; - synchronized (this) { - subscriptions = subscriptionsByEventType.get(clazz); - } - if (subscriptions != null && !subscriptions.isEmpty()) { - return true; - } - } - } + /** + * Removes the sticky event if it equals to the given event. + * + * @return true if the events matched and the sticky event was removed. + */ + public boolean removeStickyEvent(Object event) { + synchronized (stickyEvents) { + Class eventType = event.getClass(); + Object existingEvent = stickyEvents.get(eventType); + if (event.equals(existingEvent)) { + stickyEvents.remove(eventType); + return true; + } else { return false; + } } + } - private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { - Class eventClass = event.getClass(); - boolean subscriptionFound = false; - if (eventInheritance) { - List> eventTypes = lookupAllEventTypes(eventClass); - int countTypes = eventTypes.size(); - for (int h = 0; h < countTypes; h++) { - Class clazz = eventTypes.get(h); - subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); - } - } else { - subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); - } - if (!subscriptionFound) { - if (logNoSubscriberMessages) { - logger.log(Level.FINE, "No subscribers registered for event " + eventClass); - } - if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && - eventClass != SubscriberExceptionEvent.class) { - post(new NoSubscriberEvent(this, event)); - } - } + /** Removes all sticky events. */ + public void removeAllStickyEvents() { + synchronized (stickyEvents) { + stickyEvents.clear(); } + } - private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) { + public boolean hasSubscriberForEvent(Class eventClass) { + List> eventTypes = lookupAllEventTypes(eventClass); + if (eventTypes != null) { + int countTypes = eventTypes.size(); + for (int h = 0; h < countTypes; h++) { + Class clazz = eventTypes.get(h); CopyOnWriteArrayList subscriptions; synchronized (this) { - subscriptions = subscriptionsByEventType.get(eventClass); + subscriptions = subscriptionsByEventType.get(clazz); } if (subscriptions != null && !subscriptions.isEmpty()) { - for (Subscription subscription : subscriptions) { - postingState.event = event; - postingState.subscription = subscription; - boolean aborted; - try { - postToSubscription(subscription, event, postingState.isMainThread); - aborted = postingState.canceled; - } finally { - postingState.event = null; - postingState.subscription = null; - postingState.canceled = false; - } - if (aborted) { - break; - } - } - return true; + return true; } - return false; + } } + return false; + } - private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { - switch (subscription.subscriberMethod.threadMode) { - case POSTING: - invokeSubscriber(subscription, event); - break; - case MAIN: - if (isMainThread) { - invokeSubscriber(subscription, event); - } else { - mainThreadPoster.enqueue(subscription, event); - } - break; - case MAIN_ORDERED: - if (mainThreadPoster != null) { - mainThreadPoster.enqueue(subscription, event); - } else { - // temporary: technically not correct as poster not decoupled from subscriber - invokeSubscriber(subscription, event); - } - break; - case BACKGROUND: - if (isMainThread) { - backgroundPoster.enqueue(subscription, event); - } else { - invokeSubscriber(subscription, event); - } - break; - case ASYNC: - asyncPoster.enqueue(subscription, event); - break; - default: - throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); - } + private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { + Class eventClass = event.getClass(); + boolean subscriptionFound = false; + if (eventInheritance) { + List> eventTypes = lookupAllEventTypes(eventClass); + int countTypes = eventTypes.size(); + for (int h = 0; h < countTypes; h++) { + Class clazz = eventTypes.get(h); + subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); + } + } else { + subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); } - - /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */ - private static List> lookupAllEventTypes(Class eventClass) { - synchronized (eventTypesCache) { - List> eventTypes = eventTypesCache.get(eventClass); - if (eventTypes == null) { - eventTypes = new ArrayList<>(); - Class clazz = eventClass; - while (clazz != null) { - eventTypes.add(clazz); - addInterfaces(eventTypes, clazz.getInterfaces()); - clazz = clazz.getSuperclass(); - } - eventTypesCache.put(eventClass, eventTypes); - } - return eventTypes; - } + if (!subscriptionFound) { + if (logNoSubscriberMessages) { + logger.log(Level.FINE, "No subscribers registered for event " + eventClass); + } + if (sendNoSubscriberEvent + && eventClass != NoSubscriberEvent.class + && eventClass != SubscriberExceptionEvent.class) { + post(new NoSubscriberEvent(this, event)); + } } + } - /** Recurses through super interfaces. */ - static void addInterfaces(List> eventTypes, Class[] interfaces) { - for (Class interfaceClass : interfaces) { - if (!eventTypes.contains(interfaceClass)) { - eventTypes.add(interfaceClass); - addInterfaces(eventTypes, interfaceClass.getInterfaces()); - } - } + private boolean postSingleEventForEventType( + Object event, PostingThreadState postingState, Class eventClass) { + CopyOnWriteArrayList subscriptions; + synchronized (this) { + subscriptions = subscriptionsByEventType.get(eventClass); } - - /** - * Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents race conditions - * between {@link #unregister(Object)} and event delivery. Otherwise the event might be delivered after the - * subscriber unregistered. This is particularly important for main thread delivery and registrations bound to the - * live cycle of an Activity or Fragment. - */ - void invokeSubscriber(PendingPost pendingPost) { - Object event = pendingPost.event; - Subscription subscription = pendingPost.subscription; - PendingPost.releasePendingPost(pendingPost); - if (subscription.active) { - invokeSubscriber(subscription, event); - } - } - - void invokeSubscriber(Subscription subscription, Object event) { + if (subscriptions != null && !subscriptions.isEmpty()) { + for (Subscription subscription : subscriptions) { + postingState.event = event; + postingState.subscription = subscription; + boolean aborted; try { - subscription.subscriberMethod.method.invoke(subscription.subscriber, event); - } catch (InvocationTargetException e) { - handleSubscriberException(subscription, event, e.getCause()); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Unexpected exception", e); + postToSubscription(subscription, event, postingState.isMainThread); + aborted = postingState.canceled; + } finally { + postingState.event = null; + postingState.subscription = null; + postingState.canceled = false; } + if (aborted) { + break; + } + } + return true; } + return false; + } - private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) { - if (event instanceof SubscriberExceptionEvent) { - if (logSubscriberExceptions) { - // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log - logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass() - + " threw an exception", cause); - SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event; - logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in " - + exEvent.causingSubscriber, exEvent.throwable); - } + private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { + switch (subscription.subscriberMethod.threadMode) { + case POSTING: + invokeSubscriber(subscription, event); + break; + case MAIN: + if (isMainThread) { + invokeSubscriber(subscription, event); } else { - if (throwSubscriberException) { - throw new EventBusException("Invoking subscriber failed", cause); - } - if (logSubscriberExceptions) { - logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class " - + subscription.subscriber.getClass(), cause); - } - if (sendSubscriberExceptionEvent) { - SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event, - subscription.subscriber); - post(exEvent); - } + mainThreadPoster.enqueue(subscription, event); } + break; + case MAIN_ORDERED: + if (mainThreadPoster != null) { + mainThreadPoster.enqueue(subscription, event); + } else { + // temporary: technically not correct as poster not decoupled from subscriber + invokeSubscriber(subscription, event); + } + break; + case BACKGROUND: + if (isMainThread) { + backgroundPoster.enqueue(subscription, event); + } else { + invokeSubscriber(subscription, event); + } + break; + case ASYNC: + asyncPoster.enqueue(subscription, event); + break; + default: + throw new IllegalStateException( + "Unknown thread mode: " + subscription.subscriberMethod.threadMode); } + } - /** For ThreadLocal, much faster to set (and get multiple values). */ - final static class PostingThreadState { - final List eventQueue = new ArrayList<>(); - boolean isPosting; - boolean isMainThread; - Subscription subscription; - Object event; - boolean canceled; + /** + * Looks up all Class objects including super classes and interfaces. Should also work for + * interfaces. + */ + private static List> lookupAllEventTypes(Class eventClass) { + synchronized (eventTypesCache) { + List> eventTypes = eventTypesCache.get(eventClass); + if (eventTypes == null) { + eventTypes = new ArrayList<>(); + Class clazz = eventClass; + while (clazz != null) { + eventTypes.add(clazz); + addInterfaces(eventTypes, clazz.getInterfaces()); + clazz = clazz.getSuperclass(); + } + eventTypesCache.put(eventClass, eventTypes); + } + return eventTypes; } + } - ExecutorService getExecutorService() { - return executorService; + /** Recurses through super interfaces. */ + static void addInterfaces(List> eventTypes, Class[] interfaces) { + for (Class interfaceClass : interfaces) { + if (!eventTypes.contains(interfaceClass)) { + eventTypes.add(interfaceClass); + addInterfaces(eventTypes, interfaceClass.getInterfaces()); + } } + } - /** - * For internal use only. - */ - public Logger getLogger() { - return logger; + /** + * Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents + * race conditions between {@link #unregister(Object)} and event delivery. Otherwise the event + * might be delivered after the subscriber unregistered. This is particularly important for main + * thread delivery and registrations bound to the live cycle of an Activity or Fragment. + */ + void invokeSubscriber(PendingPost pendingPost) { + Object event = pendingPost.event; + Subscription subscription = pendingPost.subscription; + PendingPost.releasePendingPost(pendingPost); + if (subscription.active) { + invokeSubscriber(subscription, event); } + } - // Just an idea: we could provide a callback to post() to be notified, an alternative would be events, of course... - /* public */interface PostCallback { - void onPostCompleted(List exceptionEvents); + void invokeSubscriber(Subscription subscription, Object event) { + try { + subscription.subscriberMethod.method.invoke(subscription.subscriber, event); + } catch (InvocationTargetException e) { + handleSubscriberException(subscription, event, e.getCause()); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unexpected exception", e); } + } - @Override - public String toString() { - return "EventBus[indexCount=" + indexCount + ", eventInheritance=" + eventInheritance + "]"; + private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) { + if (event instanceof SubscriberExceptionEvent) { + if (logSubscriberExceptions) { + // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log + logger.log( + Level.SEVERE, + "SubscriberExceptionEvent subscriber " + + subscription.subscriber.getClass() + + " threw an exception", + cause); + SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event; + logger.log( + Level.SEVERE, + "Initial event " + + exEvent.causingEvent + + " caused exception in " + + exEvent.causingSubscriber, + exEvent.throwable); + } + } else { + if (throwSubscriberException) { + throw new EventBusException("Invoking subscriber failed", cause); + } + if (logSubscriberExceptions) { + logger.log( + Level.SEVERE, + "Could not dispatch event: " + + event.getClass() + + " to subscribing class " + + subscription.subscriber.getClass(), + cause); + } + if (sendSubscriberExceptionEvent) { + SubscriberExceptionEvent exEvent = + new SubscriberExceptionEvent(this, cause, event, subscription.subscriber); + post(exEvent); + } } + } + + /** For ThreadLocal, much faster to set (and get multiple values). */ + static final class PostingThreadState { + final List eventQueue = new ArrayList<>(); + boolean isPosting; + boolean isMainThread; + Subscription subscription; + Object event; + boolean canceled; + } + + ExecutorService getExecutorService() { + return executorService; + } + + /** For internal use only. */ + public Logger getLogger() { + return logger; + } + + // Just an idea: we could provide a callback to post() to be notified, an alternative would be + // events, of course... + /* public */ interface PostCallback { + void onPostCompleted(List exceptionEvents); + } + + @Override + public String toString() { + return "EventBus[indexCount=" + indexCount + ", eventInheritance=" + eventInheritance + "]"; + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/EventBusBuilder.java b/eventbus/src/main/java/org/greenrobot/eventbus/EventBusBuilder.java index 6df346d69..44aa698dd 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/EventBusBuilder.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/EventBusBuilder.java @@ -23,171 +23,171 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * Creates EventBus instances with custom parameters and also allows to install a custom default EventBus instance. - * Create a new builder using {@link EventBus#builder()}. + * Creates EventBus instances with custom parameters and also allows to install a custom default + * EventBus instance. Create a new builder using {@link EventBus#builder()}. */ @SuppressWarnings("unused") public class EventBusBuilder { - private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool(); + private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool(); - boolean logSubscriberExceptions = true; - boolean logNoSubscriberMessages = true; - boolean sendSubscriberExceptionEvent = true; - boolean sendNoSubscriberEvent = true; - boolean throwSubscriberException; - boolean eventInheritance = true; - boolean ignoreGeneratedIndex; - boolean strictMethodVerification; - ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE; - List> skipMethodVerificationForClasses; - List subscriberInfoIndexes; - Logger logger; - MainThreadSupport mainThreadSupport; + boolean logSubscriberExceptions = true; + boolean logNoSubscriberMessages = true; + boolean sendSubscriberExceptionEvent = true; + boolean sendNoSubscriberEvent = true; + boolean throwSubscriberException; + boolean eventInheritance = true; + boolean ignoreGeneratedIndex; + boolean strictMethodVerification; + ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE; + List> skipMethodVerificationForClasses; + List subscriberInfoIndexes; + Logger logger; + MainThreadSupport mainThreadSupport; - EventBusBuilder() { + EventBusBuilder() {} + + /** Default: true */ + public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) { + this.logSubscriberExceptions = logSubscriberExceptions; + return this; + } + + /** Default: true */ + public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) { + this.logNoSubscriberMessages = logNoSubscriberMessages; + return this; + } + + /** Default: true */ + public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) { + this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent; + return this; + } + + /** Default: true */ + public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) { + this.sendNoSubscriberEvent = sendNoSubscriberEvent; + return this; + } + + /** + * Fails if an subscriber throws an exception (default: false). + * + *

Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, + * you won't miss exceptions during development. + */ + public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) { + this.throwSubscriberException = throwSubscriberException; + return this; + } + + /** + * By default, EventBus considers the event class hierarchy (subscribers to super classes will be + * notified). Switching this feature off will improve posting of events. For simple event classes + * extending Object directly, we measured a speed up of 20% for event posting. For more complex + * event hierarchies, the speed up should be greater than 20%. + * + *

However, keep in mind that event posting usually consumes just a small proportion of CPU + * time inside an app, unless it is posting at high rates, e.g. hundreds/thousands of events per + * second. + */ + public EventBusBuilder eventInheritance(boolean eventInheritance) { + this.eventInheritance = eventInheritance; + return this; + } + + /** + * Provide a custom thread pool to EventBus used for async and background event delivery. This is + * an advanced setting to that can break things: ensure the given ExecutorService won't get stuck + * to avoid undefined behavior. + */ + public EventBusBuilder executorService(ExecutorService executorService) { + this.executorService = executorService; + return this; + } + + /** + * Method name verification is done for methods starting with onEvent to avoid typos; using this + * method you can exclude subscriber classes from this check. Also disables checks for method + * modifiers (public, not static nor abstract). + */ + public EventBusBuilder skipMethodVerificationFor(Class clazz) { + if (skipMethodVerificationForClasses == null) { + skipMethodVerificationForClasses = new ArrayList<>(); } + skipMethodVerificationForClasses.add(clazz); + return this; + } - /** Default: true */ - public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) { - this.logSubscriberExceptions = logSubscriberExceptions; - return this; + /** Forces the use of reflection even if there's a generated index (default: false). */ + public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) { + this.ignoreGeneratedIndex = ignoreGeneratedIndex; + return this; + } + + /** Enables strict method verification (default: false). */ + public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) { + this.strictMethodVerification = strictMethodVerification; + return this; + } + + /** Adds an index generated by EventBus' annotation preprocessor. */ + public EventBusBuilder addIndex(SubscriberInfoIndex index) { + if (subscriberInfoIndexes == null) { + subscriberInfoIndexes = new ArrayList<>(); } + subscriberInfoIndexes.add(index); + return this; + } - /** Default: true */ - public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) { - this.logNoSubscriberMessages = logNoSubscriberMessages; - return this; + /** + * Set a specific log handler for all EventBus logging. + * + *

By default, all logging is via {@code android.util.Log} on Android or System.out on JVM. + */ + public EventBusBuilder logger(Logger logger) { + this.logger = logger; + return this; + } + + Logger getLogger() { + if (logger != null) { + return logger; + } else { + return Logger.Default.get(); } + } - /** Default: true */ - public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) { - this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent; - return this; + MainThreadSupport getMainThreadSupport() { + if (mainThreadSupport != null) { + return mainThreadSupport; + } else if (AndroidComponents.areAvailable()) { + return AndroidComponents.get().defaultMainThreadSupport; + } else { + return null; } + } - /** Default: true */ - public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) { - this.sendNoSubscriberEvent = sendNoSubscriberEvent; - return this; - } - - /** - * Fails if an subscriber throws an exception (default: false). - *

- * Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, you won't miss - * exceptions during development. - */ - public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) { - this.throwSubscriberException = throwSubscriberException; - return this; - } - - /** - * By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified). - * Switching this feature off will improve posting of events. For simple event classes extending Object directly, - * we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be - * greater than 20%. - *

- * However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app, - * unless it is posting at high rates, e.g. hundreds/thousands of events per second. - */ - public EventBusBuilder eventInheritance(boolean eventInheritance) { - this.eventInheritance = eventInheritance; - return this; - } - - - /** - * Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced - * setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior. - */ - public EventBusBuilder executorService(ExecutorService executorService) { - this.executorService = executorService; - return this; - } - - /** - * Method name verification is done for methods starting with onEvent to avoid typos; using this method you can - * exclude subscriber classes from this check. Also disables checks for method modifiers (public, not static nor - * abstract). - */ - public EventBusBuilder skipMethodVerificationFor(Class clazz) { - if (skipMethodVerificationForClasses == null) { - skipMethodVerificationForClasses = new ArrayList<>(); - } - skipMethodVerificationForClasses.add(clazz); - return this; - } - - /** Forces the use of reflection even if there's a generated index (default: false). */ - public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) { - this.ignoreGeneratedIndex = ignoreGeneratedIndex; - return this; - } - - /** Enables strict method verification (default: false). */ - public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) { - this.strictMethodVerification = strictMethodVerification; - return this; - } - - /** Adds an index generated by EventBus' annotation preprocessor. */ - public EventBusBuilder addIndex(SubscriberInfoIndex index) { - if (subscriberInfoIndexes == null) { - subscriberInfoIndexes = new ArrayList<>(); - } - subscriberInfoIndexes.add(index); - return this; - } - - /** - * Set a specific log handler for all EventBus logging. - *

- * By default, all logging is via {@code android.util.Log} on Android or System.out on JVM. - */ - public EventBusBuilder logger(Logger logger) { - this.logger = logger; - return this; - } - - Logger getLogger() { - if (logger != null) { - return logger; - } else { - return Logger.Default.get(); - } - } - - MainThreadSupport getMainThreadSupport() { - if (mainThreadSupport != null) { - return mainThreadSupport; - } else if (AndroidComponents.areAvailable()) { - return AndroidComponents.get().defaultMainThreadSupport; - } else { - return null; - } - } - - /** - * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be - * done only once before the first usage of the default EventBus. - * - * @throws EventBusException if there's already a default EventBus instance in place - */ - public EventBus installDefaultEventBus() { - synchronized (EventBus.class) { - if (EventBus.defaultInstance != null) { - throw new EventBusException("Default instance already exists." + - " It may be only set once before it's used the first time to ensure consistent behavior."); - } - EventBus.defaultInstance = build(); - return EventBus.defaultInstance; - } - } - - /** Builds an EventBus based on the current configuration. */ - public EventBus build() { - return new EventBus(this); + /** + * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' + * values. Must be done only once before the first usage of the default EventBus. + * + * @throws EventBusException if there's already a default EventBus instance in place + */ + public EventBus installDefaultEventBus() { + synchronized (EventBus.class) { + if (EventBus.defaultInstance != null) { + throw new EventBusException( + "Default instance already exists." + + " It may be only set once before it's used the first time to ensure consistent behavior."); + } + EventBus.defaultInstance = build(); + return EventBus.defaultInstance; } + } + /** Builds an EventBus based on the current configuration. */ + public EventBus build() { + return new EventBus(this); + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/EventBusException.java b/eventbus/src/main/java/org/greenrobot/eventbus/EventBusException.java index 20d50e6bc..d505b8eb6 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/EventBusException.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/EventBusException.java @@ -17,24 +17,22 @@ package org.greenrobot.eventbus; /** * An {@link RuntimeException} thrown in cases something went wrong inside EventBus. - * + * * @author Markus - * */ public class EventBusException extends RuntimeException { - private static final long serialVersionUID = -2912559384646531479L; + private static final long serialVersionUID = -2912559384646531479L; - public EventBusException(String detailMessage) { - super(detailMessage); - } + public EventBusException(String detailMessage) { + super(detailMessage); + } - public EventBusException(Throwable throwable) { - super(throwable); - } - - public EventBusException(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); - } + public EventBusException(Throwable throwable) { + super(throwable); + } + public EventBusException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/Logger.java b/eventbus/src/main/java/org/greenrobot/eventbus/Logger.java index e9ec8e885..db292587b 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/Logger.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/Logger.java @@ -20,54 +20,51 @@ import java.util.logging.Level; public interface Logger { - void log(Level level, String msg); + void log(Level level, String msg); - void log(Level level, String msg, Throwable th); + void log(Level level, String msg, Throwable th); - class JavaLogger implements Logger { - protected final java.util.logging.Logger logger; - - public JavaLogger(String tag) { - logger = java.util.logging.Logger.getLogger(tag); - } - - @Override - public void log(Level level, String msg) { - // TODO Replace logged method with caller method - logger.log(level, msg); - } - - @Override - public void log(Level level, String msg, Throwable th) { - // TODO Replace logged method with caller method - logger.log(level, msg, th); - } + class JavaLogger implements Logger { + protected final java.util.logging.Logger logger; + public JavaLogger(String tag) { + logger = java.util.logging.Logger.getLogger(tag); } - class SystemOutLogger implements Logger { - - @Override - public void log(Level level, String msg) { - System.out.println("[" + level + "] " + msg); - } - - @Override - public void log(Level level, String msg, Throwable th) { - System.out.println("[" + level + "] " + msg); - th.printStackTrace(System.out); - } - + @Override + public void log(Level level, String msg) { + // TODO Replace logged method with caller method + logger.log(level, msg); } - class Default { - public static Logger get() { - if (AndroidComponents.areAvailable()) { - return AndroidComponents.get().logger; - } + @Override + public void log(Level level, String msg, Throwable th) { + // TODO Replace logged method with caller method + logger.log(level, msg, th); + } + } - return new SystemOutLogger(); - } + class SystemOutLogger implements Logger { + + @Override + public void log(Level level, String msg) { + System.out.println("[" + level + "] " + msg); } + @Override + public void log(Level level, String msg, Throwable th) { + System.out.println("[" + level + "] " + msg); + th.printStackTrace(System.out); + } + } + + class Default { + public static Logger get() { + if (AndroidComponents.areAvailable()) { + return AndroidComponents.get().logger; + } + + return new SystemOutLogger(); + } + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/MainThreadSupport.java b/eventbus/src/main/java/org/greenrobot/eventbus/MainThreadSupport.java index 22605811c..1a0bc7e6d 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/MainThreadSupport.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/MainThreadSupport.java @@ -16,11 +16,12 @@ package org.greenrobot.eventbus; /** - * Interface to the "main" thread, which can be whatever you like. Typically on Android, Android's main thread is used. + * Interface to the "main" thread, which can be whatever you like. Typically on Android, Android's + * main thread is used. */ public interface MainThreadSupport { - boolean isMainThread(); + boolean isMainThread(); - Poster createPoster(EventBus eventBus); + Poster createPoster(EventBus eventBus); } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/NoSubscriberEvent.java b/eventbus/src/main/java/org/greenrobot/eventbus/NoSubscriberEvent.java index e4da47579..b59e087a4 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/NoSubscriberEvent.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/NoSubscriberEvent.java @@ -17,19 +17,18 @@ package org.greenrobot.eventbus; /** * This Event is posted by EventBus when no subscriber is found for a posted event. - * + * * @author Markus */ public final class NoSubscriberEvent { - /** The {@link EventBus} instance to with the original event was posted to. */ - public final EventBus eventBus; + /** The {@link EventBus} instance to with the original event was posted to. */ + public final EventBus eventBus; - /** The original event that could not be delivered to any subscriber. */ - public final Object originalEvent; - - public NoSubscriberEvent(EventBus eventBus, Object originalEvent) { - this.eventBus = eventBus; - this.originalEvent = originalEvent; - } + /** The original event that could not be delivered to any subscriber. */ + public final Object originalEvent; + public NoSubscriberEvent(EventBus eventBus, Object originalEvent) { + this.eventBus = eventBus; + this.originalEvent = originalEvent; + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/PendingPost.java b/eventbus/src/main/java/org/greenrobot/eventbus/PendingPost.java index 01f474c22..edb4ad928 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/PendingPost.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/PendingPost.java @@ -19,41 +19,40 @@ import java.util.ArrayList; import java.util.List; final class PendingPost { - private final static List pendingPostPool = new ArrayList(); + private static final List pendingPostPool = new ArrayList(); - Object event; - Subscription subscription; - PendingPost next; + Object event; + Subscription subscription; + PendingPost next; - private PendingPost(Object event, Subscription subscription) { - this.event = event; - this.subscription = subscription; - } + private PendingPost(Object event, Subscription subscription) { + this.event = event; + this.subscription = subscription; + } - static PendingPost obtainPendingPost(Subscription subscription, Object event) { - synchronized (pendingPostPool) { - int size = pendingPostPool.size(); - if (size > 0) { - PendingPost pendingPost = pendingPostPool.remove(size - 1); - pendingPost.event = event; - pendingPost.subscription = subscription; - pendingPost.next = null; - return pendingPost; - } - } - return new PendingPost(event, subscription); - } - - static void releasePendingPost(PendingPost pendingPost) { - pendingPost.event = null; - pendingPost.subscription = null; + static PendingPost obtainPendingPost(Subscription subscription, Object event) { + synchronized (pendingPostPool) { + int size = pendingPostPool.size(); + if (size > 0) { + PendingPost pendingPost = pendingPostPool.remove(size - 1); + pendingPost.event = event; + pendingPost.subscription = subscription; pendingPost.next = null; - synchronized (pendingPostPool) { - // Don't let the pool grow indefinitely - if (pendingPostPool.size() < 10000) { - pendingPostPool.add(pendingPost); - } - } + return pendingPost; + } } + return new PendingPost(event, subscription); + } -} \ No newline at end of file + static void releasePendingPost(PendingPost pendingPost) { + pendingPost.event = null; + pendingPost.subscription = null; + pendingPost.next = null; + synchronized (pendingPostPool) { + // Don't let the pool grow indefinitely + if (pendingPostPool.size() < 10000) { + pendingPostPool.add(pendingPost); + } + } + } +} diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/PendingPostQueue.java b/eventbus/src/main/java/org/greenrobot/eventbus/PendingPostQueue.java index 55db529aa..388fc2b52 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/PendingPostQueue.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/PendingPostQueue.java @@ -17,40 +17,39 @@ package org.greenrobot.eventbus; final class PendingPostQueue { - private PendingPost head; - private PendingPost tail; + private PendingPost head; + private PendingPost tail; - synchronized void enqueue(PendingPost pendingPost) { - if (pendingPost == null) { - throw new NullPointerException("null cannot be enqueued"); - } - if (tail != null) { - tail.next = pendingPost; - tail = pendingPost; - } else if (head == null) { - head = tail = pendingPost; - } else { - throw new IllegalStateException("Head present, but no tail"); - } - notifyAll(); + synchronized void enqueue(PendingPost pendingPost) { + if (pendingPost == null) { + throw new NullPointerException("null cannot be enqueued"); } - - synchronized PendingPost poll() { - PendingPost pendingPost = head; - if (head != null) { - head = head.next; - if (head == null) { - tail = null; - } - } - return pendingPost; + if (tail != null) { + tail.next = pendingPost; + tail = pendingPost; + } else if (head == null) { + head = tail = pendingPost; + } else { + throw new IllegalStateException("Head present, but no tail"); } + notifyAll(); + } - synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException { - if (head == null) { - wait(maxMillisToWait); - } - return poll(); + synchronized PendingPost poll() { + PendingPost pendingPost = head; + if (head != null) { + head = head.next; + if (head == null) { + tail = null; + } } + return pendingPost; + } + synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException { + if (head == null) { + wait(maxMillisToWait); + } + return poll(); + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/Poster.java b/eventbus/src/main/java/org/greenrobot/eventbus/Poster.java index 67cfd67c0..b0e5ccb21 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/Poster.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/Poster.java @@ -22,11 +22,11 @@ package org.greenrobot.eventbus; */ public interface Poster { - /** - * Enqueue an event to be posted for a particular subscription. - * - * @param subscription Subscription which will receive the event. - * @param event Event that will be posted to subscribers. - */ - void enqueue(Subscription subscription, Object event); + /** + * Enqueue an event to be posted for a particular subscription. + * + * @param subscription Subscription which will receive the event. + * @param event Event that will be posted to subscribers. + */ + void enqueue(Subscription subscription, Object event); } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/Subscribe.java b/eventbus/src/main/java/org/greenrobot/eventbus/Subscribe.java index ed0b8c82e..f674f68ad 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/Subscribe.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/Subscribe.java @@ -16,7 +16,6 @@ package org.greenrobot.eventbus; - import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -27,18 +26,19 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { - ThreadMode threadMode() default ThreadMode.POSTING; + ThreadMode threadMode() default ThreadMode.POSTING; - /** - * If true, delivers the most recent sticky event (posted with - * {@link EventBus#postSticky(Object)}) to this subscriber (if event available). - */ - boolean sticky() default false; + /** + * If true, delivers the most recent sticky event (posted with {@link + * EventBus#postSticky(Object)}) to this subscriber (if event available). + */ + boolean sticky() default false; - /** Subscriber priority to influence the order of event delivery. - * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before - * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of - * delivery among subscribers with different {@link ThreadMode}s! */ - int priority() default 0; + /** + * Subscriber priority to influence the order of event delivery. Within the same delivery thread + * ({@link ThreadMode}), higher priority subscribers will receive events before others with a + * lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of + * delivery among subscribers with different {@link ThreadMode}s! + */ + int priority() default 0; } - diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberExceptionEvent.java b/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberExceptionEvent.java index ff69f05a1..1b517a26b 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberExceptionEvent.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberExceptionEvent.java @@ -16,29 +16,29 @@ package org.greenrobot.eventbus; /** - * This Event is posted by EventBus when an exception occurs inside a subscriber's event handling method. - * + * This Event is posted by EventBus when an exception occurs inside a subscriber's event handling + * method. + * * @author Markus */ public final class SubscriberExceptionEvent { - /** The {@link EventBus} instance to with the original event was posted to. */ - public final EventBus eventBus; + /** The {@link EventBus} instance to with the original event was posted to. */ + public final EventBus eventBus; - /** The Throwable thrown by a subscriber. */ - public final Throwable throwable; + /** The Throwable thrown by a subscriber. */ + public final Throwable throwable; - /** The original event that could not be delivered to any subscriber. */ - public final Object causingEvent; + /** The original event that could not be delivered to any subscriber. */ + public final Object causingEvent; - /** The subscriber that threw the Throwable. */ - public final Object causingSubscriber; - - public SubscriberExceptionEvent(EventBus eventBus, Throwable throwable, Object causingEvent, - Object causingSubscriber) { - this.eventBus = eventBus; - this.throwable = throwable; - this.causingEvent = causingEvent; - this.causingSubscriber = causingSubscriber; - } + /** The subscriber that threw the Throwable. */ + public final Object causingSubscriber; + public SubscriberExceptionEvent( + EventBus eventBus, Throwable throwable, Object causingEvent, Object causingSubscriber) { + this.eventBus = eventBus; + this.throwable = throwable; + this.causingEvent = causingEvent; + this.causingSubscriber = causingSubscriber; + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethod.java b/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethod.java index 1d78d4799..1f1bc6cd9 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethod.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethod.java @@ -19,50 +19,52 @@ import java.lang.reflect.Method; /** Used internally by EventBus and generated subscriber indexes. */ public class SubscriberMethod { - final Method method; - final ThreadMode threadMode; - final Class eventType; - final int priority; - final boolean sticky; - /** Used for efficient comparison */ - String methodString; + final Method method; + final ThreadMode threadMode; + final Class eventType; + final int priority; + final boolean sticky; + /** Used for efficient comparison */ + String methodString; - public SubscriberMethod(Method method, Class eventType, ThreadMode threadMode, int priority, boolean sticky) { - this.method = method; - this.threadMode = threadMode; - this.eventType = eventType; - this.priority = priority; - this.sticky = sticky; - } + public SubscriberMethod( + Method method, Class eventType, ThreadMode threadMode, int priority, boolean sticky) { + this.method = method; + this.threadMode = threadMode; + this.eventType = eventType; + this.priority = priority; + this.sticky = sticky; + } - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } else if (other instanceof SubscriberMethod) { - checkMethodString(); - SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other; - otherSubscriberMethod.checkMethodString(); - // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6 - return methodString.equals(otherSubscriberMethod.methodString); - } else { - return false; - } + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof SubscriberMethod) { + checkMethodString(); + SubscriberMethod otherSubscriberMethod = (SubscriberMethod) other; + otherSubscriberMethod.checkMethodString(); + // Don't use method.equals because of + // http://code.google.com/p/android/issues/detail?id=7811#c6 + return methodString.equals(otherSubscriberMethod.methodString); + } else { + return false; } + } - private synchronized void checkMethodString() { - if (methodString == null) { - // Method.toString has more overhead, just take relevant parts of the method - StringBuilder builder = new StringBuilder(64); - builder.append(method.getDeclaringClass().getName()); - builder.append('#').append(method.getName()); - builder.append('(').append(eventType.getName()); - methodString = builder.toString(); - } + private synchronized void checkMethodString() { + if (methodString == null) { + // Method.toString has more overhead, just take relevant parts of the method + StringBuilder builder = new StringBuilder(64); + builder.append(method.getDeclaringClass().getName()); + builder.append('#').append(method.getName()); + builder.append('(').append(eventType.getName()); + methodString = builder.toString(); } + } - @Override - public int hashCode() { - return method.hashCode(); - } -} \ No newline at end of file + @Override + public int hashCode() { + return method.hashCode(); + } +} diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethodFinder.java b/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethodFinder.java index 3f2812bcc..3ba2f289c 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethodFinder.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/SubscriberMethodFinder.java @@ -27,256 +27,277 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; class SubscriberMethodFinder { - /* - * In newer class files, compilers may add methods. Those are called bridge or synthetic methods. - * EventBus must ignore both. There modifiers are not public but defined in the Java class file format: - * http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1 - */ - private static final int BRIDGE = 0x40; - private static final int SYNTHETIC = 0x1000; + /* + * In newer class files, compilers may add methods. Those are called bridge or synthetic methods. + * EventBus must ignore both. There modifiers are not public but defined in the Java class file format: + * http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1 + */ + private static final int BRIDGE = 0x40; + private static final int SYNTHETIC = 0x1000; - private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC; - private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>(); + private static final int MODIFIERS_IGNORE = + Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC; + private static final Map, List> METHOD_CACHE = + new ConcurrentHashMap<>(); - private List subscriberInfoIndexes; - private final boolean strictMethodVerification; - private final boolean ignoreGeneratedIndex; + private List subscriberInfoIndexes; + private final boolean strictMethodVerification; + private final boolean ignoreGeneratedIndex; - private static final int POOL_SIZE = 4; - private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]; + private static final int POOL_SIZE = 4; + private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]; - SubscriberMethodFinder(List subscriberInfoIndexes, boolean strictMethodVerification, - boolean ignoreGeneratedIndex) { - this.subscriberInfoIndexes = subscriberInfoIndexes; - this.strictMethodVerification = strictMethodVerification; - this.ignoreGeneratedIndex = ignoreGeneratedIndex; + SubscriberMethodFinder( + List subscriberInfoIndexes, + boolean strictMethodVerification, + boolean ignoreGeneratedIndex) { + this.subscriberInfoIndexes = subscriberInfoIndexes; + this.strictMethodVerification = strictMethodVerification; + this.ignoreGeneratedIndex = ignoreGeneratedIndex; + } + + List findSubscriberMethods(Class subscriberClass) { + List subscriberMethods = METHOD_CACHE.get(subscriberClass); + if (subscriberMethods != null) { + return subscriberMethods; } - List findSubscriberMethods(Class subscriberClass) { - List subscriberMethods = METHOD_CACHE.get(subscriberClass); - if (subscriberMethods != null) { - return subscriberMethods; - } + if (ignoreGeneratedIndex) { + subscriberMethods = findUsingReflection(subscriberClass); + } else { + subscriberMethods = findUsingInfo(subscriberClass); + } + if (subscriberMethods.isEmpty()) { + throw new EventBusException( + "Subscriber " + + subscriberClass + + " and its super classes have no public methods with the @Subscribe annotation"); + } else { + METHOD_CACHE.put(subscriberClass, subscriberMethods); + return subscriberMethods; + } + } + private List findUsingInfo(Class subscriberClass) { + FindState findState = prepareFindState(); + findState.initForSubscriber(subscriberClass); + while (findState.clazz != null) { + findState.subscriberInfo = getSubscriberInfo(findState); + if (findState.subscriberInfo != null) { + SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); + for (SubscriberMethod subscriberMethod : array) { + if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { + findState.subscriberMethods.add(subscriberMethod); + } + } + } else { + findUsingReflectionInSingleClass(findState); + } + findState.moveToSuperclass(); + } + return getMethodsAndRelease(findState); + } + + private List getMethodsAndRelease(FindState findState) { + List subscriberMethods = new ArrayList<>(findState.subscriberMethods); + findState.recycle(); + synchronized (FIND_STATE_POOL) { + for (int i = 0; i < POOL_SIZE; i++) { + if (FIND_STATE_POOL[i] == null) { + FIND_STATE_POOL[i] = findState; + break; + } + } + } + return subscriberMethods; + } + + private FindState prepareFindState() { + synchronized (FIND_STATE_POOL) { + for (int i = 0; i < POOL_SIZE; i++) { + FindState state = FIND_STATE_POOL[i]; + if (state != null) { + FIND_STATE_POOL[i] = null; + return state; + } + } + } + return new FindState(); + } + + private SubscriberInfo getSubscriberInfo(FindState findState) { + if (findState.subscriberInfo != null + && findState.subscriberInfo.getSuperSubscriberInfo() != null) { + SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); + if (findState.clazz == superclassInfo.getSubscriberClass()) { + return superclassInfo; + } + } + if (subscriberInfoIndexes != null) { + for (SubscriberInfoIndex index : subscriberInfoIndexes) { + SubscriberInfo info = index.getSubscriberInfo(findState.clazz); + if (info != null) { + return info; + } + } + } + return null; + } + + private List findUsingReflection(Class subscriberClass) { + FindState findState = prepareFindState(); + findState.initForSubscriber(subscriberClass); + while (findState.clazz != null) { + findUsingReflectionInSingleClass(findState); + findState.moveToSuperclass(); + } + return getMethodsAndRelease(findState); + } + + private void findUsingReflectionInSingleClass(FindState findState) { + Method[] methods; + try { + // This is faster than getMethods, especially when subscribers are fat classes like Activities + methods = findState.clazz.getDeclaredMethods(); + } catch (Throwable th) { + // Workaround for java.lang.NoClassDefFoundError, see + // https://github.com/greenrobot/EventBus/issues/149 + try { + methods = findState.clazz.getMethods(); + } catch ( + LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad... + String msg = "Could not inspect methods of " + findState.clazz.getName(); if (ignoreGeneratedIndex) { - subscriberMethods = findUsingReflection(subscriberClass); + msg += ". Please consider using EventBus annotation processor to avoid reflection."; } else { - subscriberMethods = findUsingInfo(subscriberClass); + msg += + ". Please make this class visible to EventBus annotation processor to avoid reflection."; } - if (subscriberMethods.isEmpty()) { - throw new EventBusException("Subscriber " + subscriberClass - + " and its super classes have no public methods with the @Subscribe annotation"); - } else { - METHOD_CACHE.put(subscriberClass, subscriberMethods); - return subscriberMethods; + throw new EventBusException(msg, error); + } + findState.skipSuperClasses = true; + } + for (Method method : methods) { + int modifiers = method.getModifiers(); + if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 1) { + Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); + if (subscribeAnnotation != null) { + Class eventType = parameterTypes[0]; + if (findState.checkAdd(method, eventType)) { + ThreadMode threadMode = subscribeAnnotation.threadMode(); + findState.subscriberMethods.add( + new SubscriberMethod( + method, + eventType, + threadMode, + subscribeAnnotation.priority(), + subscribeAnnotation.sticky())); + } + } + } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { + String methodName = method.getDeclaringClass().getName() + "." + method.getName(); + throw new EventBusException( + "@Subscribe method " + + methodName + + "must have exactly 1 parameter but has " + + parameterTypes.length); } + } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { + String methodName = method.getDeclaringClass().getName() + "." + method.getName(); + throw new EventBusException( + methodName + + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); + } + } + } + + static void clearCaches() { + METHOD_CACHE.clear(); + } + + static class FindState { + final List subscriberMethods = new ArrayList<>(); + final Map anyMethodByEventType = new HashMap<>(); + final Map subscriberClassByMethodKey = new HashMap<>(); + final StringBuilder methodKeyBuilder = new StringBuilder(128); + + Class subscriberClass; + Class clazz; + boolean skipSuperClasses; + SubscriberInfo subscriberInfo; + + void initForSubscriber(Class subscriberClass) { + this.subscriberClass = clazz = subscriberClass; + skipSuperClasses = false; + subscriberInfo = null; } - private List findUsingInfo(Class subscriberClass) { - FindState findState = prepareFindState(); - findState.initForSubscriber(subscriberClass); - while (findState.clazz != null) { - findState.subscriberInfo = getSubscriberInfo(findState); - if (findState.subscriberInfo != null) { - SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); - for (SubscriberMethod subscriberMethod : array) { - if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { - findState.subscriberMethods.add(subscriberMethod); - } - } - } else { - findUsingReflectionInSingleClass(findState); - } - findState.moveToSuperclass(); - } - return getMethodsAndRelease(findState); + void recycle() { + subscriberMethods.clear(); + anyMethodByEventType.clear(); + subscriberClassByMethodKey.clear(); + methodKeyBuilder.setLength(0); + subscriberClass = null; + clazz = null; + skipSuperClasses = false; + subscriberInfo = null; } - private List getMethodsAndRelease(FindState findState) { - List subscriberMethods = new ArrayList<>(findState.subscriberMethods); - findState.recycle(); - synchronized (FIND_STATE_POOL) { - for (int i = 0; i < POOL_SIZE; i++) { - if (FIND_STATE_POOL[i] == null) { - FIND_STATE_POOL[i] = findState; - break; - } - } + boolean checkAdd(Method method, Class eventType) { + // 2 level check: 1st level with event type only (fast), 2nd level with complete signature + // when required. + // Usually a subscriber doesn't have methods listening to the same event type. + Object existing = anyMethodByEventType.put(eventType, method); + if (existing == null) { + return true; + } else { + if (existing instanceof Method) { + if (!checkAddWithMethodSignature((Method) existing, eventType)) { + // Paranoia check + throw new IllegalStateException(); + } + // Put any non-Method object to "consume" the existing Method + anyMethodByEventType.put(eventType, this); } - return subscriberMethods; + return checkAddWithMethodSignature(method, eventType); + } } - private FindState prepareFindState() { - synchronized (FIND_STATE_POOL) { - for (int i = 0; i < POOL_SIZE; i++) { - FindState state = FIND_STATE_POOL[i]; - if (state != null) { - FIND_STATE_POOL[i] = null; - return state; - } - } - } - return new FindState(); + private boolean checkAddWithMethodSignature(Method method, Class eventType) { + methodKeyBuilder.setLength(0); + methodKeyBuilder.append(method.getName()); + methodKeyBuilder.append('>').append(eventType.getName()); + + String methodKey = methodKeyBuilder.toString(); + Class methodClass = method.getDeclaringClass(); + Class methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass); + if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) { + // Only add if not already found in a sub class + return true; + } else { + // Revert the put, old class is further down the class hierarchy + subscriberClassByMethodKey.put(methodKey, methodClassOld); + return false; + } } - private SubscriberInfo getSubscriberInfo(FindState findState) { - if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { - SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); - if (findState.clazz == superclassInfo.getSubscriberClass()) { - return superclassInfo; - } + void moveToSuperclass() { + if (skipSuperClasses) { + clazz = null; + } else { + clazz = clazz.getSuperclass(); + String clazzName = clazz.getName(); + // Skip system classes, this degrades performance. + // Also we might avoid some ClassNotFoundException (see FAQ for background). + if (clazzName.startsWith("java.") + || clazzName.startsWith("javax.") + || clazzName.startsWith("android.") + || clazzName.startsWith("androidx.")) { + clazz = null; } - if (subscriberInfoIndexes != null) { - for (SubscriberInfoIndex index : subscriberInfoIndexes) { - SubscriberInfo info = index.getSubscriberInfo(findState.clazz); - if (info != null) { - return info; - } - } - } - return null; + } } - - private List findUsingReflection(Class subscriberClass) { - FindState findState = prepareFindState(); - findState.initForSubscriber(subscriberClass); - while (findState.clazz != null) { - findUsingReflectionInSingleClass(findState); - findState.moveToSuperclass(); - } - return getMethodsAndRelease(findState); - } - - private void findUsingReflectionInSingleClass(FindState findState) { - Method[] methods; - try { - // This is faster than getMethods, especially when subscribers are fat classes like Activities - methods = findState.clazz.getDeclaredMethods(); - } catch (Throwable th) { - // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 - try { - methods = findState.clazz.getMethods(); - } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad... - String msg = "Could not inspect methods of " + findState.clazz.getName(); - if (ignoreGeneratedIndex) { - msg += ". Please consider using EventBus annotation processor to avoid reflection."; - } else { - msg += ". Please make this class visible to EventBus annotation processor to avoid reflection."; - } - throw new EventBusException(msg, error); - } - findState.skipSuperClasses = true; - } - for (Method method : methods) { - int modifiers = method.getModifiers(); - if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { - Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length == 1) { - Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); - if (subscribeAnnotation != null) { - Class eventType = parameterTypes[0]; - if (findState.checkAdd(method, eventType)) { - ThreadMode threadMode = subscribeAnnotation.threadMode(); - findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, - subscribeAnnotation.priority(), subscribeAnnotation.sticky())); - } - } - } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { - String methodName = method.getDeclaringClass().getName() + "." + method.getName(); - throw new EventBusException("@Subscribe method " + methodName + - "must have exactly 1 parameter but has " + parameterTypes.length); - } - } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { - String methodName = method.getDeclaringClass().getName() + "." + method.getName(); - throw new EventBusException(methodName + - " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); - } - } - } - - static void clearCaches() { - METHOD_CACHE.clear(); - } - - static class FindState { - final List subscriberMethods = new ArrayList<>(); - final Map anyMethodByEventType = new HashMap<>(); - final Map subscriberClassByMethodKey = new HashMap<>(); - final StringBuilder methodKeyBuilder = new StringBuilder(128); - - Class subscriberClass; - Class clazz; - boolean skipSuperClasses; - SubscriberInfo subscriberInfo; - - void initForSubscriber(Class subscriberClass) { - this.subscriberClass = clazz = subscriberClass; - skipSuperClasses = false; - subscriberInfo = null; - } - - void recycle() { - subscriberMethods.clear(); - anyMethodByEventType.clear(); - subscriberClassByMethodKey.clear(); - methodKeyBuilder.setLength(0); - subscriberClass = null; - clazz = null; - skipSuperClasses = false; - subscriberInfo = null; - } - - boolean checkAdd(Method method, Class eventType) { - // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required. - // Usually a subscriber doesn't have methods listening to the same event type. - Object existing = anyMethodByEventType.put(eventType, method); - if (existing == null) { - return true; - } else { - if (existing instanceof Method) { - if (!checkAddWithMethodSignature((Method) existing, eventType)) { - // Paranoia check - throw new IllegalStateException(); - } - // Put any non-Method object to "consume" the existing Method - anyMethodByEventType.put(eventType, this); - } - return checkAddWithMethodSignature(method, eventType); - } - } - - private boolean checkAddWithMethodSignature(Method method, Class eventType) { - methodKeyBuilder.setLength(0); - methodKeyBuilder.append(method.getName()); - methodKeyBuilder.append('>').append(eventType.getName()); - - String methodKey = methodKeyBuilder.toString(); - Class methodClass = method.getDeclaringClass(); - Class methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass); - if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) { - // Only add if not already found in a sub class - return true; - } else { - // Revert the put, old class is further down the class hierarchy - subscriberClassByMethodKey.put(methodKey, methodClassOld); - return false; - } - } - - void moveToSuperclass() { - if (skipSuperClasses) { - clazz = null; - } else { - clazz = clazz.getSuperclass(); - String clazzName = clazz.getName(); - // Skip system classes, this degrades performance. - // Also we might avoid some ClassNotFoundException (see FAQ for background). - if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || - clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) { - clazz = null; - } - } - } - } - + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/Subscription.java b/eventbus/src/main/java/org/greenrobot/eventbus/Subscription.java index cc0de1e3c..99ee28c8b 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/Subscription.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/Subscription.java @@ -16,33 +16,34 @@ package org.greenrobot.eventbus; final class Subscription { - final Object subscriber; - final SubscriberMethod subscriberMethod; - /** - * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery - * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions. - */ - volatile boolean active; + final Object subscriber; + final SubscriberMethod subscriberMethod; + /** + * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by + * queued event delivery {@link EventBus#invokeSubscriber(PendingPost)} to prevent race + * conditions. + */ + volatile boolean active; - Subscription(Object subscriber, SubscriberMethod subscriberMethod) { - this.subscriber = subscriber; - this.subscriberMethod = subscriberMethod; - active = true; - } + Subscription(Object subscriber, SubscriberMethod subscriberMethod) { + this.subscriber = subscriber; + this.subscriberMethod = subscriberMethod; + active = true; + } - @Override - public boolean equals(Object other) { - if (other instanceof Subscription) { - Subscription otherSubscription = (Subscription) other; - return subscriber == otherSubscription.subscriber - && subscriberMethod.equals(otherSubscription.subscriberMethod); - } else { - return false; - } + @Override + public boolean equals(Object other) { + if (other instanceof Subscription) { + Subscription otherSubscription = (Subscription) other; + return subscriber == otherSubscription.subscriber + && subscriberMethod.equals(otherSubscription.subscriberMethod); + } else { + return false; } + } - @Override - public int hashCode() { - return subscriber.hashCode() + subscriberMethod.methodString.hashCode(); - } -} \ No newline at end of file + @Override + public int hashCode() { + return subscriber.hashCode() + subscriberMethod.methodString.hashCode(); + } +} diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/ThreadMode.java b/eventbus/src/main/java/org/greenrobot/eventbus/ThreadMode.java index b1ff24275..dd9633d85 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/ThreadMode.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/ThreadMode.java @@ -16,53 +16,58 @@ package org.greenrobot.eventbus; /** - * Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus. - * EventBus takes care of threading independently of the posting thread. + * Each subscriber method has a thread mode, which determines in which thread the method is to be + * called by EventBus. EventBus takes care of threading independently of the posting thread. * * @see EventBus#register(Object) */ public enum ThreadMode { - /** - * This is the default. Subscriber will be called directly in the same thread, which is posting the event. Event delivery - * implies the least overhead because it avoids thread switching completely. Thus, this is the recommended mode for - * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers - * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread. - */ - POSTING, + /** + * This is the default. Subscriber will be called directly in the same thread, which is posting + * the event. Event delivery implies the least overhead because it avoids thread switching + * completely. Thus, this is the recommended mode for simple tasks that are known to complete in a + * very short time without requiring the main thread. Event handlers using this mode must return + * quickly to avoid blocking the posting thread, which may be the main thread. + */ + POSTING, - /** - * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is - * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event - * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread. - *

- * If not on Android, behaves the same as {@link #POSTING}. - */ - MAIN, + /** + * On Android, subscriber will be called in Android's main thread (UI thread). If the posting + * thread is the main thread, subscriber methods will be called directly, blocking the posting + * thread. Otherwise the event is queued for delivery (non-blocking). Subscribers using this mode + * must return quickly to avoid blocking the main thread. + * + *

If not on Android, behaves the same as {@link #POSTING}. + */ + MAIN, - /** - * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN}, - * the event will always be queued for delivery. This ensures that the post call is non-blocking. - *

- * If not on Android, behaves the same as {@link #POSTING}. - */ - MAIN_ORDERED, + /** + * On Android, subscriber will be called in Android's main thread (UI thread). Different from + * {@link #MAIN}, the event will always be queued for delivery. This ensures that the post call is + * non-blocking. + * + *

If not on Android, behaves the same as {@link #POSTING}. + */ + MAIN_ORDERED, - /** - * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods - * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single - * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to - * return quickly to avoid blocking the background thread. - *

- * If not on Android, always uses a background thread. - */ - BACKGROUND, + /** + * On Android, subscriber will be called in a background thread. If posting thread is not the main + * thread, subscriber methods will be called directly in the posting thread. If the posting thread + * is the main thread, EventBus uses a single background thread, that will deliver all its events + * sequentially. Subscribers using this mode should try to return quickly to avoid blocking the + * background thread. + * + *

If not on Android, always uses a background thread. + */ + BACKGROUND, - /** - * Subscriber will be called in a separate thread. This is always independent of the posting thread and the - * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should - * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number - * of long-running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus - * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications. - */ - ASYNC -} \ No newline at end of file + /** + * Subscriber will be called in a separate thread. This is always independent of the posting + * thread and the main thread. Posting events never wait for subscriber methods using this mode. + * Subscriber methods should use this mode if their execution might take some time, e.g. for + * network access. Avoid triggering a large number of long-running asynchronous subscriber methods + * at the same time to limit the number of concurrent threads. EventBus uses a thread pool to + * efficiently reuse threads from completed asynchronous subscriber notifications. + */ + ASYNC +} diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidComponents.java b/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidComponents.java index 7138b6d9f..18c5f3d2b 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidComponents.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidComponents.java @@ -5,27 +5,28 @@ import org.greenrobot.eventbus.MainThreadSupport; public abstract class AndroidComponents { - private static final AndroidComponents implementation; + private static final AndroidComponents implementation; - static { - implementation = AndroidDependenciesDetector.isAndroidSDKAvailable() + static { + implementation = + AndroidDependenciesDetector.isAndroidSDKAvailable() ? AndroidDependenciesDetector.instantiateAndroidComponents() : null; - } + } - public static boolean areAvailable() { - return implementation != null; - } + public static boolean areAvailable() { + return implementation != null; + } - public static AndroidComponents get() { - return implementation; - } + public static AndroidComponents get() { + return implementation; + } - public final Logger logger; - public final MainThreadSupport defaultMainThreadSupport; + public final Logger logger; + public final MainThreadSupport defaultMainThreadSupport; - public AndroidComponents(Logger logger, MainThreadSupport defaultMainThreadSupport) { - this.logger = logger; - this.defaultMainThreadSupport = defaultMainThreadSupport; - } + public AndroidComponents(Logger logger, MainThreadSupport defaultMainThreadSupport) { + this.logger = logger; + this.defaultMainThreadSupport = defaultMainThreadSupport; + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidDependenciesDetector.java b/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidDependenciesDetector.java index 1783f1433..4f7e3fb0a 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidDependenciesDetector.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/android/AndroidDependenciesDetector.java @@ -6,43 +6,42 @@ import java.lang.reflect.Method; @SuppressWarnings("TryWithIdenticalCatches") public class AndroidDependenciesDetector { - public static boolean isAndroidSDKAvailable() { + public static boolean isAndroidSDKAvailable() { - try { - Class looperClass = Class.forName("android.os.Looper"); - Method getMainLooper = looperClass.getDeclaredMethod("getMainLooper"); - Object mainLooper = getMainLooper.invoke(null); - return mainLooper != null; - } - catch (ClassNotFoundException ignored) {} - catch (NoSuchMethodException ignored) {} - catch (IllegalAccessException ignored) {} - catch (InvocationTargetException ignored) {} - - return false; + try { + Class looperClass = Class.forName("android.os.Looper"); + Method getMainLooper = looperClass.getDeclaredMethod("getMainLooper"); + Object mainLooper = getMainLooper.invoke(null); + return mainLooper != null; + } catch (ClassNotFoundException ignored) { + } catch (NoSuchMethodException ignored) { + } catch (IllegalAccessException ignored) { + } catch (InvocationTargetException ignored) { } - private static final String ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME = "org.greenrobot.eventbus.android.AndroidComponentsImpl"; + return false; + } - public static boolean areAndroidComponentsAvailable() { + private static final String ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME = + "org.greenrobot.eventbus.android.AndroidComponentsImpl"; - try { - Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME); - return true; - } - catch (ClassNotFoundException ex) { - return false; - } + public static boolean areAndroidComponentsAvailable() { + + try { + Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME); + return true; + } catch (ClassNotFoundException ex) { + return false; } + } - public static AndroidComponents instantiateAndroidComponents() { + public static AndroidComponents instantiateAndroidComponents() { - try { - Class impl = Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME); - return (AndroidComponents) impl.getConstructor().newInstance(); - } - catch (Throwable ex) { - return null; - } + try { + Class impl = Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME); + return (AndroidComponents) impl.getConstructor().newInstance(); + } catch (Throwable ex) { + return null; } + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java b/eventbus/src/main/java/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java index 9020c24b7..7799c520d 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java @@ -23,58 +23,63 @@ import java.lang.reflect.Method; /** Base class for generated subscriber meta info classes created by annotation processing. */ public abstract class AbstractSubscriberInfo implements SubscriberInfo { - private final Class subscriberClass; - private final Class superSubscriberInfoClass; - private final boolean shouldCheckSuperclass; + private final Class subscriberClass; + private final Class superSubscriberInfoClass; + private final boolean shouldCheckSuperclass; - protected AbstractSubscriberInfo(Class subscriberClass, Class superSubscriberInfoClass, - boolean shouldCheckSuperclass) { - this.subscriberClass = subscriberClass; - this.superSubscriberInfoClass = superSubscriberInfoClass; - this.shouldCheckSuperclass = shouldCheckSuperclass; + protected AbstractSubscriberInfo( + Class subscriberClass, + Class superSubscriberInfoClass, + boolean shouldCheckSuperclass) { + this.subscriberClass = subscriberClass; + this.superSubscriberInfoClass = superSubscriberInfoClass; + this.shouldCheckSuperclass = shouldCheckSuperclass; + } + + @Override + public Class getSubscriberClass() { + return subscriberClass; + } + + @Override + public SubscriberInfo getSuperSubscriberInfo() { + if (superSubscriberInfoClass == null) { + return null; } - - @Override - public Class getSubscriberClass() { - return subscriberClass; + try { + return superSubscriberInfoClass.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); } + } - @Override - public SubscriberInfo getSuperSubscriberInfo() { - if(superSubscriberInfoClass == null) { - return null; - } - try { - return superSubscriberInfoClass.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } + @Override + public boolean shouldCheckSuperclass() { + return shouldCheckSuperclass; + } + + protected SubscriberMethod createSubscriberMethod(String methodName, Class eventType) { + return createSubscriberMethod(methodName, eventType, ThreadMode.POSTING, 0, false); + } + + protected SubscriberMethod createSubscriberMethod( + String methodName, Class eventType, ThreadMode threadMode) { + return createSubscriberMethod(methodName, eventType, threadMode, 0, false); + } + + protected SubscriberMethod createSubscriberMethod( + String methodName, Class eventType, ThreadMode threadMode, int priority, boolean sticky) { + try { + Method method = subscriberClass.getDeclaredMethod(methodName, eventType); + return new SubscriberMethod(method, eventType, threadMode, priority, sticky); + } catch (NoSuchMethodException e) { + throw new EventBusException( + "Could not find subscriber method in " + + subscriberClass + + ". Maybe a missing ProGuard rule?", + e); } - - @Override - public boolean shouldCheckSuperclass() { - return shouldCheckSuperclass; - } - - protected SubscriberMethod createSubscriberMethod(String methodName, Class eventType) { - return createSubscriberMethod(methodName, eventType, ThreadMode.POSTING, 0, false); - } - - protected SubscriberMethod createSubscriberMethod(String methodName, Class eventType, ThreadMode threadMode) { - return createSubscriberMethod(methodName, eventType, threadMode, 0, false); - } - - protected SubscriberMethod createSubscriberMethod(String methodName, Class eventType, ThreadMode threadMode, - int priority, boolean sticky) { - try { - Method method = subscriberClass.getDeclaredMethod(methodName, eventType); - return new SubscriberMethod(method, eventType, threadMode, priority, sticky); - } catch (NoSuchMethodException e) { - throw new EventBusException("Could not find subscriber method in " + subscriberClass + - ". Maybe a missing ProGuard rule?", e); - } - } - + } } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java b/eventbus/src/main/java/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java index 3ee4442d5..3423353bf 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java @@ -18,26 +18,29 @@ package org.greenrobot.eventbus.meta; import org.greenrobot.eventbus.SubscriberMethod; /** - * Uses {@link SubscriberMethodInfo} objects to create {@link org.greenrobot.eventbus.SubscriberMethod} objects on demand. + * Uses {@link SubscriberMethodInfo} objects to create {@link + * org.greenrobot.eventbus.SubscriberMethod} objects on demand. */ public class SimpleSubscriberInfo extends AbstractSubscriberInfo { - private final SubscriberMethodInfo[] methodInfos; + private final SubscriberMethodInfo[] methodInfos; - public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) { - super(subscriberClass, null, shouldCheckSuperclass); - this.methodInfos = methodInfos; - } + public SimpleSubscriberInfo( + Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) { + super(subscriberClass, null, shouldCheckSuperclass); + this.methodInfos = methodInfos; + } - @Override - public synchronized SubscriberMethod[] getSubscriberMethods() { - int length = methodInfos.length; - SubscriberMethod[] methods = new SubscriberMethod[length]; - for (int i = 0; i < length; i++) { - SubscriberMethodInfo info = methodInfos[i]; - methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode, - info.priority, info.sticky); - } - return methods; + @Override + public synchronized SubscriberMethod[] getSubscriberMethods() { + int length = methodInfos.length; + SubscriberMethod[] methods = new SubscriberMethod[length]; + for (int i = 0; i < length; i++) { + SubscriberMethodInfo info = methodInfos[i]; + methods[i] = + createSubscriberMethod( + info.methodName, info.eventType, info.threadMode, info.priority, info.sticky); } -} \ No newline at end of file + return methods; + } +} diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfo.java b/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfo.java index 83c1e741d..eb7e4b31c 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfo.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfo.java @@ -19,11 +19,11 @@ import org.greenrobot.eventbus.SubscriberMethod; /** Base class for generated index classes created by annotation processing. */ public interface SubscriberInfo { - Class getSubscriberClass(); + Class getSubscriberClass(); - SubscriberMethod[] getSubscriberMethods(); + SubscriberMethod[] getSubscriberMethods(); - SubscriberInfo getSuperSubscriberInfo(); + SubscriberInfo getSuperSubscriberInfo(); - boolean shouldCheckSuperclass(); + boolean shouldCheckSuperclass(); } diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java b/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java index 2152554ca..e2cf9170a 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java @@ -18,27 +18,26 @@ package org.greenrobot.eventbus.meta; import org.greenrobot.eventbus.ThreadMode; public class SubscriberMethodInfo { - final String methodName; - final ThreadMode threadMode; - final Class eventType; - final int priority; - final boolean sticky; + final String methodName; + final ThreadMode threadMode; + final Class eventType; + final int priority; + final boolean sticky; - public SubscriberMethodInfo(String methodName, Class eventType, ThreadMode threadMode, - int priority, boolean sticky) { - this.methodName = methodName; - this.threadMode = threadMode; - this.eventType = eventType; - this.priority = priority; - this.sticky = sticky; - } + public SubscriberMethodInfo( + String methodName, Class eventType, ThreadMode threadMode, int priority, boolean sticky) { + this.methodName = methodName; + this.threadMode = threadMode; + this.eventType = eventType; + this.priority = priority; + this.sticky = sticky; + } - public SubscriberMethodInfo(String methodName, Class eventType) { - this(methodName, eventType, ThreadMode.POSTING, 0, false); - } + public SubscriberMethodInfo(String methodName, Class eventType) { + this(methodName, eventType, ThreadMode.POSTING, 0, false); + } - public SubscriberMethodInfo(String methodName, Class eventType, ThreadMode threadMode) { - this(methodName, eventType, threadMode, 0, false); - } - -} \ No newline at end of file + public SubscriberMethodInfo(String methodName, Class eventType, ThreadMode threadMode) { + this(methodName, eventType, threadMode, 0, false); + } +} diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/util/AsyncExecutor.java b/eventbus/src/main/java/org/greenrobot/eventbus/util/AsyncExecutor.java index bd9cb365d..6f9b47571 100644 --- a/eventbus/src/main/java/org/greenrobot/eventbus/util/AsyncExecutor.java +++ b/eventbus/src/main/java/org/greenrobot/eventbus/util/AsyncExecutor.java @@ -23,13 +23,14 @@ import java.util.concurrent.Executors; import java.util.logging.Level; /** - * Executes an {@link RunnableEx} using a thread pool. Thrown exceptions are propagated by posting failure events. - * By default, uses {@link ThrowableFailureEvent}. - *

- * Set a custom event type using {@link Builder#failureEventType(Class)}. - * The failure event class must have a constructor with one parameter of type {@link Throwable}. - * If using ProGuard or R8 make sure the constructor of the failure event class is kept, it is accessed via reflection. - * E.g. add a rule like + * Executes an {@link RunnableEx} using a thread pool. Thrown exceptions are propagated by posting + * failure events. By default, uses {@link ThrowableFailureEvent}. + * + *

Set a custom event type using {@link Builder#failureEventType(Class)}. The failure event class + * must have a constructor with one parameter of type {@link Throwable}. If using ProGuard or R8 + * make sure the constructor of the failure event class is kept, it is accessed via reflection. E.g. + * add a rule like + * *

  * -keepclassmembers class com.example.CustomThrowableFailureEvent {
  *     <init>(java.lang.Throwable);
@@ -38,96 +39,96 @@ import java.util.logging.Level;
  */
 public class AsyncExecutor {
 
-    public static class Builder {
-        private Executor threadPool;
-        private Class failureEventType;
-        private EventBus eventBus;
+  public static class Builder {
+    private Executor threadPool;
+    private Class failureEventType;
+    private EventBus eventBus;
 
-        private Builder() {
-        }
+    private Builder() {}
 
-        public Builder threadPool(Executor threadPool) {
-            this.threadPool = threadPool;
-            return this;
-        }
-
-        public Builder failureEventType(Class failureEventType) {
-            this.failureEventType = failureEventType;
-            return this;
-        }
-
-        public Builder eventBus(EventBus eventBus) {
-            this.eventBus = eventBus;
-            return this;
-        }
-
-        public AsyncExecutor build() {
-            return buildForScope(null);
-        }
-
-        public AsyncExecutor buildForScope(Object executionContext) {
-            if (eventBus == null) {
-                eventBus = EventBus.getDefault();
-            }
-            if (threadPool == null) {
-                threadPool = Executors.newCachedThreadPool();
-            }
-            if (failureEventType == null) {
-                failureEventType = ThrowableFailureEvent.class;
-            }
-            return new AsyncExecutor(threadPool, eventBus, failureEventType, executionContext);
-        }
+    public Builder threadPool(Executor threadPool) {
+      this.threadPool = threadPool;
+      return this;
     }
 
-    /** Like {@link Runnable}, but the run method may throw an exception. */
-    public interface RunnableEx {
-        void run() throws Exception;
+    public Builder failureEventType(Class failureEventType) {
+      this.failureEventType = failureEventType;
+      return this;
     }
 
-    public static Builder builder() {
-        return new Builder();
+    public Builder eventBus(EventBus eventBus) {
+      this.eventBus = eventBus;
+      return this;
     }
 
-    public static AsyncExecutor create() {
-        return new Builder().build();
+    public AsyncExecutor build() {
+      return buildForScope(null);
     }
 
-    private final Executor threadPool;
-    private final Constructor failureEventConstructor;
-    private final EventBus eventBus;
-    private final Object scope;
-
-    private AsyncExecutor(Executor threadPool, EventBus eventBus, Class failureEventType, Object scope) {
-        this.threadPool = threadPool;
-        this.eventBus = eventBus;
-        this.scope = scope;
-        try {
-            failureEventConstructor = failureEventType.getConstructor(Throwable.class);
-        } catch (NoSuchMethodException e) {
-            throw new RuntimeException(
-                    "Failure event class must have a constructor with one parameter of type Throwable", e);
-        }
+    public AsyncExecutor buildForScope(Object executionContext) {
+      if (eventBus == null) {
+        eventBus = EventBus.getDefault();
+      }
+      if (threadPool == null) {
+        threadPool = Executors.newCachedThreadPool();
+      }
+      if (failureEventType == null) {
+        failureEventType = ThrowableFailureEvent.class;
+      }
+      return new AsyncExecutor(threadPool, eventBus, failureEventType, executionContext);
     }
+  }
 
-    /** Posts an failure event if the given {@link RunnableEx} throws an Exception. */
-    public void execute(final RunnableEx runnable) {
-        threadPool.execute(() -> {
+  /** Like {@link Runnable}, but the run method may throw an exception. */
+  public interface RunnableEx {
+    void run() throws Exception;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static AsyncExecutor create() {
+    return new Builder().build();
+  }
+
+  private final Executor threadPool;
+  private final Constructor failureEventConstructor;
+  private final EventBus eventBus;
+  private final Object scope;
+
+  private AsyncExecutor(
+      Executor threadPool, EventBus eventBus, Class failureEventType, Object scope) {
+    this.threadPool = threadPool;
+    this.eventBus = eventBus;
+    this.scope = scope;
+    try {
+      failureEventConstructor = failureEventType.getConstructor(Throwable.class);
+    } catch (NoSuchMethodException e) {
+      throw new RuntimeException(
+          "Failure event class must have a constructor with one parameter of type Throwable", e);
+    }
+  }
+
+  /** Posts an failure event if the given {@link RunnableEx} throws an Exception. */
+  public void execute(final RunnableEx runnable) {
+    threadPool.execute(
+        () -> {
+          try {
+            runnable.run();
+          } catch (Exception e) {
+            Object event;
             try {
-                runnable.run();
-            } catch (Exception e) {
-                Object event;
-                try {
-                    event = failureEventConstructor.newInstance(e);
-                } catch (Exception e1) {
-                    eventBus.getLogger().log(Level.SEVERE, "Original exception:", e);
-                    throw new RuntimeException("Could not create failure event", e1);
-                }
-                if (event instanceof HasExecutionScope) {
-                    ((HasExecutionScope) event).setExecutionScope(scope);
-                }
-                eventBus.post(event);
+              event = failureEventConstructor.newInstance(e);
+            } catch (Exception e1) {
+              eventBus.getLogger().log(Level.SEVERE, "Original exception:", e);
+              throw new RuntimeException("Could not create failure event", e1);
             }
+            if (event instanceof HasExecutionScope) {
+              ((HasExecutionScope) event).setExecutionScope(scope);
+            }
+            eventBus.post(event);
+          }
         });
-    }
-
+  }
 }
diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java b/eventbus/src/main/java/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java
index 083bd3942..b8c30bc94 100644
--- a/eventbus/src/main/java/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java
+++ b/eventbus/src/main/java/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java
@@ -24,67 +24,63 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.logging.Level;
 
-
 /**
  * Maps throwables to texts for error dialogs. Use Config to configure the mapping.
- * 
+ *
  * @author Markus
  */
 public class ExceptionToResourceMapping {
 
-    public final Map, Integer> throwableToMsgIdMap;
+  public final Map, Integer> throwableToMsgIdMap;
 
-    public ExceptionToResourceMapping() {
-        throwableToMsgIdMap = new HashMap<>();
-    }
+  public ExceptionToResourceMapping() {
+    throwableToMsgIdMap = new HashMap<>();
+  }
 
-    /** Looks at the exception and its causes trying to find an ID. */
-    public Integer mapThrowable(final Throwable throwable) {
-        Throwable throwableToCheck = throwable;
-        int depthToGo = 20;
+  /** Looks at the exception and its causes trying to find an ID. */
+  public Integer mapThrowable(final Throwable throwable) {
+    Throwable throwableToCheck = throwable;
+    int depthToGo = 20;
 
-        while (true) {
-            Integer resId = mapThrowableFlat(throwableToCheck);
-            if (resId != null) {
-                return resId;
-            } else {
-                throwableToCheck = throwableToCheck.getCause();
-                depthToGo--;
-                if (depthToGo <= 0 || throwableToCheck == throwable || throwableToCheck == null) {
-                    Logger logger = Logger.Default.get();  // No EventBus instance here
-                    logger.log(Level.FINE, "No specific message resource ID found for " + throwable);
-                    // return config.defaultErrorMsgId;
-                    return null;
-                }
-            }
-        }
-
-    }
-
-    /** Mapping without checking the cause (done in mapThrowable). */
-    protected Integer mapThrowableFlat(Throwable throwable) {
-        Class throwableClass = throwable.getClass();
-        Integer resId = throwableToMsgIdMap.get(throwableClass);
-        if (resId == null) {
-            Class closestClass = null;
-            Set, Integer>> mappings = throwableToMsgIdMap.entrySet();
-            for (Entry, Integer> mapping : mappings) {
-                Class candidate = mapping.getKey();
-                if (candidate.isAssignableFrom(throwableClass)) {
-                    if (closestClass == null || closestClass.isAssignableFrom(candidate)) {
-                        closestClass = candidate;
-                        resId = mapping.getValue();
-                    }
-                }
-            }
-
-        }
+    while (true) {
+      Integer resId = mapThrowableFlat(throwableToCheck);
+      if (resId != null) {
         return resId;
+      } else {
+        throwableToCheck = throwableToCheck.getCause();
+        depthToGo--;
+        if (depthToGo <= 0 || throwableToCheck == throwable || throwableToCheck == null) {
+          Logger logger = Logger.Default.get(); // No EventBus instance here
+          logger.log(Level.FINE, "No specific message resource ID found for " + throwable);
+          // return config.defaultErrorMsgId;
+          return null;
+        }
+      }
     }
+  }
 
-    public ExceptionToResourceMapping addMapping(Class clazz, int msgId) {
-        throwableToMsgIdMap.put(clazz, msgId);
-        return this;
+  /** Mapping without checking the cause (done in mapThrowable). */
+  protected Integer mapThrowableFlat(Throwable throwable) {
+    Class throwableClass = throwable.getClass();
+    Integer resId = throwableToMsgIdMap.get(throwableClass);
+    if (resId == null) {
+      Class closestClass = null;
+      Set, Integer>> mappings = throwableToMsgIdMap.entrySet();
+      for (Entry, Integer> mapping : mappings) {
+        Class candidate = mapping.getKey();
+        if (candidate.isAssignableFrom(throwableClass)) {
+          if (closestClass == null || closestClass.isAssignableFrom(candidate)) {
+            closestClass = candidate;
+            resId = mapping.getValue();
+          }
+        }
+      }
     }
+    return resId;
+  }
 
+  public ExceptionToResourceMapping addMapping(Class clazz, int msgId) {
+    throwableToMsgIdMap.put(clazz, msgId);
+    return this;
+  }
 }
diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/util/HasExecutionScope.java b/eventbus/src/main/java/org/greenrobot/eventbus/util/HasExecutionScope.java
index fd20a0dc3..07e693683 100644
--- a/eventbus/src/main/java/org/greenrobot/eventbus/util/HasExecutionScope.java
+++ b/eventbus/src/main/java/org/greenrobot/eventbus/util/HasExecutionScope.java
@@ -17,8 +17,7 @@
 package org.greenrobot.eventbus.util;
 
 public interface HasExecutionScope {
-    Object getExecutionScope();
-
-    void setExecutionScope(Object executionScope);
+  Object getExecutionScope();
 
+  void setExecutionScope(Object executionScope);
 }
diff --git a/eventbus/src/main/java/org/greenrobot/eventbus/util/ThrowableFailureEvent.java b/eventbus/src/main/java/org/greenrobot/eventbus/util/ThrowableFailureEvent.java
index 7707e2895..a279d5fdb 100644
--- a/eventbus/src/main/java/org/greenrobot/eventbus/util/ThrowableFailureEvent.java
+++ b/eventbus/src/main/java/org/greenrobot/eventbus/util/ThrowableFailureEvent.java
@@ -16,42 +16,41 @@
 package org.greenrobot.eventbus.util;
 
 /**
- * A generic failure event, which can be used by apps to propagate thrown exceptions.
- * Used as default failure event by {@link AsyncExecutor}.
+ * A generic failure event, which can be used by apps to propagate thrown exceptions. Used as
+ * default failure event by {@link AsyncExecutor}.
  */
 public class ThrowableFailureEvent implements HasExecutionScope {
-    protected final Throwable throwable;
-    protected final boolean suppressErrorUi;
-    private Object executionContext;
+  protected final Throwable throwable;
+  protected final boolean suppressErrorUi;
+  private Object executionContext;
 
-    public ThrowableFailureEvent(Throwable throwable) {
-        this.throwable = throwable;
-        suppressErrorUi = false;
-    }
+  public ThrowableFailureEvent(Throwable throwable) {
+    this.throwable = throwable;
+    suppressErrorUi = false;
+  }
 
-    /**
-     * @param suppressErrorUi
-     *            true indicates to the receiver that no error UI (e.g. dialog) should now displayed.
-     */
-    public ThrowableFailureEvent(Throwable throwable, boolean suppressErrorUi) {
-        this.throwable = throwable;
-        this.suppressErrorUi = suppressErrorUi;
-    }
+  /**
+   * @param suppressErrorUi true indicates to the receiver that no error UI (e.g. dialog) should now
+   *     displayed.
+   */
+  public ThrowableFailureEvent(Throwable throwable, boolean suppressErrorUi) {
+    this.throwable = throwable;
+    this.suppressErrorUi = suppressErrorUi;
+  }
 
-    public Throwable getThrowable() {
-        return throwable;
-    }
+  public Throwable getThrowable() {
+    return throwable;
+  }
 
-    public boolean isSuppressErrorUi() {
-        return suppressErrorUi;
-    }
+  public boolean isSuppressErrorUi() {
+    return suppressErrorUi;
+  }
 
-    public Object getExecutionScope() {
-        return executionContext;
-    }
+  public Object getExecutionScope() {
+    return executionContext;
+  }
 
-    public void setExecutionScope(Object executionContext) {
-        this.executionContext = executionContext;
-    }
-    
+  public void setExecutionScope(Object executionContext) {
+    this.executionContext = executionContext;
+  }
 }
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Lexer.java b/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Lexer.java
index b418155e5..5ab67745c 100644
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Lexer.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Lexer.java
@@ -164,6 +164,7 @@ public class CPP14Lexer extends Lexer {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\u0093\u05b6\b\1\4"
           + "\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Parser.java b/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Parser.java
index 2d90e02c0..8a0346fff 100644
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Parser.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/cpp/CPP14Parser.java
@@ -367,6 +367,7 @@ public class CPP14Parser extends Parser {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\u0093\u0840\4\2\t"
           + "\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/groovy/GroovyLexer.java b/lexers/src/main/java/com/itsaky/androidide/lexers/groovy/GroovyLexer.java
index 72bde8c84..91939ae8a 100755
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/groovy/GroovyLexer.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/groovy/GroovyLexer.java
@@ -131,6 +131,7 @@ public class GroovyLexer extends Lexer {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2r\u03bf\b\1\4\2\t"
           + "\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaLexer.java b/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaLexer.java
index d5de60a3e..18601f6e3 100755
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaLexer.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaLexer.java
@@ -131,6 +131,7 @@ public class JavaLexer extends Lexer {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2r\u03b9\b\1\4\2\t"
           + "\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaParser.java b/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaParser.java
index d844e0b2c..bf0eb89d3 100755
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaParser.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/java/JavaParser.java
@@ -247,6 +247,7 @@ public class JavaParser extends Parser {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3r\u059f\4\2\t\2\4"
           + "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocLexer.java b/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocLexer.java
index 5ca9f11c3..e95dbb375 100755
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocLexer.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocLexer.java
@@ -32,6 +32,7 @@ public class JavadocLexer extends Lexer {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\16x\b\1\4\2\t\2\4"
           + "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocParser.java b/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocParser.java
index c4f072a60..c291c0850 100755
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocParser.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/javadoc/JavadocParser.java
@@ -62,6 +62,7 @@ public class JavadocParser extends Parser {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\16\u00f2\4\2\t\2"
           + "\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinLexer.java b/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinLexer.java
index c32e028d5..9277f00f0 100644
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinLexer.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinLexer.java
@@ -193,6 +193,7 @@ public class KotlinLexer extends Lexer {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   protected static final DFA[] _decisionToDFA;
   protected static final PredictionContextCache _sharedContextCache = new PredictionContextCache();
   private static final String[] _LITERAL_NAMES = makeLiteralNames();
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinParser.java b/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinParser.java
index a45e6299c..c29c55576 100644
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinParser.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/KotlinParser.java
@@ -373,6 +373,7 @@ public class KotlinParser extends Parser {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   protected static final DFA[] _decisionToDFA;
   protected static final PredictionContextCache _sharedContextCache = new PredictionContextCache();
   private static final String[] _LITERAL_NAMES = makeLiteralNames();
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/UnicodeClasses.java b/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/UnicodeClasses.java
index 129e2d590..6af9e6209 100644
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/UnicodeClasses.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/kotlin/UnicodeClasses.java
@@ -26,6 +26,7 @@ public class UnicodeClasses extends Lexer {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\t\37\b\1\4\2\t\2"
           + "\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\3\2\3\2\3\3\3\3\3\4\3"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLLexer.java b/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLLexer.java
index ed091359b..dfd8a3415 100755
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLLexer.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLLexer.java
@@ -46,6 +46,7 @@ public class XMLLexer extends Lexer {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\34\u0114\b\1\b\1"
           + "\b\1\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t"
diff --git a/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLParser.java b/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLParser.java
index af9fca6a7..c5c19dd8b 100644
--- a/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLParser.java
+++ b/lexers/src/main/java/com/itsaky/androidide/lexers/xml/XMLParser.java
@@ -65,6 +65,7 @@ public class XMLParser extends Parser {
    * @deprecated Use {@link #VOCABULARY} instead.
    */
   @Deprecated public static final String[] tokenNames;
+
   public static final String _serializedATN =
       "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\34p\4\2\t\2\4\3\t"
           + "\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4"
diff --git a/logger/src/main/java/com/itsaky/androidide/utils/ILogger.java b/logger/src/main/java/com/itsaky/androidide/utils/ILogger.java
index ab864037a..c2a8eb9ae 100644
--- a/logger/src/main/java/com/itsaky/androidide/utils/ILogger.java
+++ b/logger/src/main/java/com/itsaky/androidide/utils/ILogger.java
@@ -88,7 +88,7 @@ public abstract class ILogger {
   public static ILogger newInstance(String tag) {
     return createInstance(tag);
   }
-  
+
   public static Priority priority(char priorityChar) {
     for (var priority : Priority.values()) {
       if (priorityChar(priority) == priorityChar) {
diff --git a/logger/src/main/java/com/itsaky/androidide/utils/LogUtils.java b/logger/src/main/java/com/itsaky/androidide/utils/LogUtils.java
index a17259729..1ebf8b9f2 100644
--- a/logger/src/main/java/com/itsaky/androidide/utils/LogUtils.java
+++ b/logger/src/main/java/com/itsaky/androidide/utils/LogUtils.java
@@ -51,14 +51,14 @@ public class LogUtils {
       return true;
     }
   }
-  
+
   public static String preProcessLogTag(String tag) {
     final var regex = "[^a-z-A-Z0-9_.]";
     final var matcher = Pattern.compile(regex).matcher(tag);
     if (matcher.find()) {
       tag = tag.replaceAll(regex, "_");
     }
-    
+
     return tag;
   }
 
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java
index bfe5e1cf6..5147d465a 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java
@@ -282,11 +282,11 @@ public class JavaLanguageServer implements ILanguageServer {
   }
 
   private void startOrRestartAnalyzeTimer() {
-    
+
     if (VMUtils.isJvm()) {
       return;
     }
-    
+
     if (!this.timer.isStarted()) {
       this.timer.start();
     } else {
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/CompileTask.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/CompileTask.java
index 6865fe609..411a07a41 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/CompileTask.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/CompileTask.java
@@ -72,6 +72,5 @@ public class CompileTask implements AutoCloseable {
   }
 
   @Override
-  public void close() {
-  }
+  public void close() {}
 }
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/SynchronizedTask.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/SynchronizedTask.java
index 9c1ade148..a2492e2b6 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/SynchronizedTask.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/SynchronizedTask.java
@@ -104,7 +104,7 @@ public class SynchronizedTask {
       }
       run.run();
     } catch (Throwable err) {
-      if(!CancelChecker.isCancelled(err)) {
+      if (!CancelChecker.isCancelled(err)) {
         LOG.error("An error occurred", err);
       }
       throw err;
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java
index d21f31538..46b10aaf3 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java
@@ -86,7 +86,7 @@ public class Parser {
     if (module != null) {
       FILE_MANAGER = SourceFileManager.forModule(module);
     }
-    
+
     return (JavacTask)
         COMPILER.getTask(
             null,
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CodeFormatProvider.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CodeFormatProvider.java
index 4e29a3054..53df511a9 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CodeFormatProvider.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CodeFormatProvider.java
@@ -57,7 +57,7 @@ public class CodeFormatProvider {
       final StopWatch watch = new StopWatch("Code formatting");
       final String content = params.getContent().toString();
       final Formatter formatter = new Formatter(settings.getFormatterOptions());
-      
+
       if (params.getRange() == Range.NONE) {
         String formatted;
         try {
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java
index e65a02763..f06413f43 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java
@@ -229,7 +229,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
     final boolean endsWithParen = endsWithParen(contents, (int) cursor);
 
     abortIfCancelled();
-    
+
     final CompilationRequest request =
         new CompilationRequest(Collections.singletonList(source), partialRequest);
     SynchronizedTask synchronizedTask = compiler.compile(request);
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/GenerateRecordConstructor.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/GenerateRecordConstructor.java
index 4be3af7ff..7074ee905 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/GenerateRecordConstructor.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/GenerateRecordConstructor.java
@@ -60,12 +60,12 @@ public class GenerateRecordConstructor extends Rewrite {
     LOG.info("Generate default constructor for " + className + "...");
     // TODO this needs to fall back on looking for inner classes and package-private classes
     Path file = compiler.findTypeDeclaration(className);
-    
+
     if (file == CompilerProvider.NOT_FOUND) {
       LOG.warn("Unable to find source file for class:", this.className);
       return CANCELLED;
     }
-    
+
     SynchronizedTask synchronizedTask = compiler.compile(file);
     return synchronizedTask.get(
         task -> {
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/ImplementAbstractMethods.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/ImplementAbstractMethods.java
index f43fb36df..de8fd4065 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/ImplementAbstractMethods.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/ImplementAbstractMethods.java
@@ -85,10 +85,11 @@ public class ImplementAbstractMethods extends Rewrite {
   public Map rewrite(@NonNull CompilerProvider compiler) {
     final Path file = compiler.findTypeDeclaration(this.classFile);
     if (file == CompilerProvider.NOT_FOUND) {
-      LOG.warn("Unable to find source file for class:", this.className, "classFile=", this.classFile);
+      LOG.warn(
+          "Unable to find source file for class:", this.className, "classFile=", this.classFile);
       return CANCELLED;
     }
-    
+
     final SynchronizedTask synchronizedTask = compiler.compile(file);
     return synchronizedTask.get(
         task -> {
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveException.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveException.java
index 326b97af7..9c8f13922 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveException.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveException.java
@@ -50,7 +50,7 @@ public class RemoveException extends Rewrite {
   final String className, methodName;
   final String[] erasedParameterTypes;
   final String exceptionType;
-  
+
   private static final ILogger LOG = ILogger.newInstance("RemoveException");
 
   public RemoveException(
@@ -64,12 +64,12 @@ public class RemoveException extends Rewrite {
   @Override
   public Map rewrite(CompilerProvider compiler) {
     Path file = compiler.findTypeDeclaration(className);
-  
+
     if (file == CompilerProvider.NOT_FOUND) {
       LOG.warn("Unable to find source file for class:", this.className);
       return CANCELLED;
     }
-    
+
     SynchronizedTask synchronizedTask = compiler.compile(file);
     return synchronizedTask.get(
         task -> {
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveMethod.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveMethod.java
index acddba24f..efe0db60a 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveMethod.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/rewrite/RemoveMethod.java
@@ -46,7 +46,7 @@ public class RemoveMethod extends Rewrite {
     if (file == CompilerProvider.NOT_FOUND) {
       return CANCELLED;
     }
-    
+
     return compiler
         .compile(file)
         .get(
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/ASTFixer.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/ASTFixer.java
index 054d1879f..6ae877e5d 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/ASTFixer.java
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/ASTFixer.java
@@ -97,8 +97,7 @@ public class ASTFixer {
     }
   }
 
-  private void fixError(
-      @NonNull Scanner scanner, @NonNull CharSequence content, List edits) {
+  private void fixError(@NonNull Scanner scanner, @NonNull CharSequence content, List edits) {
     int errPos = scanner.errPos();
     if (content.charAt(errPos) == '.' && errPos > 0 && content.charAt(errPos) == '.') {
       if (errPos < content.length() - 1
@@ -136,8 +135,7 @@ public class ASTFixer {
 
     @NonNull
     public static CharSequence applyInsertions(CharSequence content, List edits) {
-      ImmutableList reverseEdits =
-          REVERSE_INSERTION.immutableSortedCopy(edits);
+      ImmutableList reverseEdits = REVERSE_INSERTION.immutableSortedCopy(edits);
 
       StringBuilder sb = new StringBuilder(content);
 
diff --git a/lsp/java/src/main/java/com/squareup/javapoet/LineWrapper.java b/lsp/java/src/main/java/com/squareup/javapoet/LineWrapper.java
index 6244b8318..3625e141d 100644
--- a/lsp/java/src/main/java/com/squareup/javapoet/LineWrapper.java
+++ b/lsp/java/src/main/java/com/squareup/javapoet/LineWrapper.java
@@ -29,6 +29,7 @@ final class LineWrapper {
   private final int columnLimit;
   /** Characters written since the last wrapping space that haven't yet been flushed. */
   private final StringBuilder buffer = new StringBuilder();
+
   private boolean closed;
   /** The number of characters since the most recent newline. Includes both out and the buffer. */
   private int column = 0;
diff --git a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java
index 9970db194..7f9b7cc65 100644
--- a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java
+++ b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java
@@ -57,8 +57,10 @@ import java.util.Collections;
 public class XMLLanguageServer implements ILanguageServer {
 
   public static final String SERVER_ID = "xml";
+
   @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
   public SDKInfo sdkInfo;
+
   private ILanguageClient client;
   private IServerSettings settings;
   private boolean canProvideCompletions = false;
diff --git a/sdk-info/src/main/java/org/eclipse/jdt/internal/compiler/classfmt/ClassFileConstants.java b/sdk-info/src/main/java/org/eclipse/jdt/internal/compiler/classfmt/ClassFileConstants.java
index acc919ae8..9b7d15d85 100755
--- a/sdk-info/src/main/java/org/eclipse/jdt/internal/compiler/classfmt/ClassFileConstants.java
+++ b/sdk-info/src/main/java/org/eclipse/jdt/internal/compiler/classfmt/ClassFileConstants.java
@@ -118,6 +118,7 @@ public final class ClassFileConstants {
   int AccMandated = 0x8000;
   /** Flags in module declaration - since java9 */
   int ACC_OPEN = 0x0020;
+
   int ACC_TRANSITIVE = 0x0020;
   int ACC_STATIC_PHASE = 0x0040;
   int ACC_SYNTHETIC = 0x1000;
@@ -129,6 +130,7 @@ public final class ClassFileConstants {
    */
   int AccAnnotationDefault =
       ASTNode.Bit18; // indicate presence of an attribute  "DefaultValue" (annotation method)
+
   int AccDeprecated = ASTNode.Bit21; // indicate presence of an attribute "Deprecated"
   int Utf8Tag = 1;
   int IntegerTag = 3;
diff --git a/shared/src/main/java/com/itsaky/androidide/utils/ReaderInputStream.java b/shared/src/main/java/com/itsaky/androidide/utils/ReaderInputStream.java
index e34ad8fc4..1d153b8b3 100644
--- a/shared/src/main/java/com/itsaky/androidide/utils/ReaderInputStream.java
+++ b/shared/src/main/java/com/itsaky/androidide/utils/ReaderInputStream.java
@@ -90,6 +90,7 @@ public class ReaderInputStream extends InputStream {
    * transfer data from the decoder to the buffer provided by the caller.
    */
   private final ByteBuffer encoderOut;
+
   private CoderResult lastCoderResult;
   private boolean endOfInput;
 
diff --git a/shared/src/main/java/com/itsaky/androidide/utils/VMUtils.java b/shared/src/main/java/com/itsaky/androidide/utils/VMUtils.java
index cad0a3fc9..d117da98c 100644
--- a/shared/src/main/java/com/itsaky/androidide/utils/VMUtils.java
+++ b/shared/src/main/java/com/itsaky/androidide/utils/VMUtils.java
@@ -50,4 +50,4 @@ public class VMUtils {
       return isJvm = true;
     }
   }
-}
\ No newline at end of file
+}
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IAttribute.java b/xml-inflater/src/main/java/com/itsaky/inflater/IAttribute.java
index e95d77b0c..a2c6fc93f 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/IAttribute.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/IAttribute.java
@@ -38,6 +38,7 @@ public interface IAttribute {
    * further.
    */
   int DO_COMPARE = 2;
+
   Comparator COMPARATOR =
       (first, second) -> {
         var result = compareNull(first, second);
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseView.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseView.java
index 40ac5f948..efc83a58c 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseView.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseView.java
@@ -68,7 +68,9 @@ public abstract class BaseView implements IView {
   public int hashCode() {
     return Objects.hash(
         attributes, attrAdapters, qualifiedName, view, getParent(), stored, isPlaceholder());
-  }  @Override
+  }
+
+  @Override
   public void setParent(IViewGroup parent) {
     this.parent = parent;
   }
@@ -89,13 +91,17 @@ public abstract class BaseView implements IView {
         && Objects.equals(view, baseView.view)
         && Objects.equals(getParent(), baseView.getParent())
         && Objects.equals(stored, baseView.stored);
-  }  public void setPlaceholder(boolean placeholder) {
+  }
+
+  public void setPlaceholder(boolean placeholder) {
     this.isPlaceholder = placeholder;
   }
 
   protected Set getAttributeAdapters() {
     return attrAdapters;
-  }  @Override
+  }
+
+  @Override
   public boolean isPlaceholder() {
     return isPlaceholder;
   }
@@ -216,8 +222,6 @@ public abstract class BaseView implements IView {
     this.attrAdapters.add(adapter);
   }
 
-
-
   @Override
   public void setExtraData(Object data) {
     this.stored = data;
@@ -330,10 +334,6 @@ public abstract class BaseView implements IView {
     return sb.toString();
   }
 
-
-
-
-
   protected void newLine(@NonNull StringBuilder stringBuilder, int indentCount) {
     stringBuilder.append("\n");
     indent(stringBuilder, indentCount);