mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
[Gradle] Format java source code
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ public class SimpleIconTextAdapter extends IconTextAdapter<IconTextListItem> {
|
||||
@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<IconTextListItem> {
|
||||
public String getItemText(int index) {
|
||||
return getItemAt(index).getText();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class WizardTemplateAdapter extends RecyclerView.Adapter<WizardTemplateAd
|
||||
|
||||
private final List<ProjectTemplate> mItems = new ArrayList<>();
|
||||
private OnItemClickListener mListener;
|
||||
|
||||
public WizardTemplateAdapter() {}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ public class FileTreeViewHolder extends TreeNode.BaseNodeViewHolder<File> {
|
||||
} else {
|
||||
chevronIcon = R.drawable.ic_chevron_right;
|
||||
}
|
||||
|
||||
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeImageTransform());
|
||||
binding.filetreeChevron.setImageResource(chevronIcon);
|
||||
binding
|
||||
|
||||
@@ -74,7 +74,7 @@ public abstract class BaseValueEditorFragment extends Fragment {
|
||||
if (child != null) {
|
||||
scrollView.addView(child);
|
||||
}
|
||||
|
||||
|
||||
return scrollView;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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));
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -36,99 +36,89 @@ public class WizardViewModel extends AndroidViewModel {
|
||||
public void createTemplatesList() {
|
||||
TaskExecutor.executeAsync(this::createTemplates);
|
||||
}
|
||||
|
||||
|
||||
private Void createTemplates() {
|
||||
List<ProjectTemplate> 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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -189,6 +189,7 @@ public abstract class Doc {
|
||||
private final RealOrImaginary realOrImaginary;
|
||||
private final Indent plusIndentCommentsBefore;
|
||||
private final Optional<Indent> breakAndIndentTrailingComment;
|
||||
|
||||
private Token(
|
||||
Input.Token token,
|
||||
RealOrImaginary realOrImaginary,
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ public abstract class InputOutput {
|
||||
private static final DiscreteDomain<Integer> INTEGERS = DiscreteDomain.integers();
|
||||
/** The {@link Range}s of the tokens or comments lying on each line, in any part. */
|
||||
protected final List<Range<Integer>> ranges = new ArrayList<>();
|
||||
|
||||
private ImmutableList<String> lines = ImmutableList.of();
|
||||
|
||||
/**
|
||||
|
||||
+1
@@ -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<Tok> toks;
|
||||
private final String lineSeparator;
|
||||
|
||||
+1
@@ -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;
|
||||
|
||||
+1
@@ -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;
|
||||
|
||||
|
||||
+1
@@ -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;
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ public final class Replacement {
|
||||
|
||||
private final Range<Integer> replaceRange;
|
||||
private final String replacementString;
|
||||
|
||||
private Replacement(Range<Integer> replaceRange, String replacementString) {
|
||||
this.replaceRange = checkNotNull(replaceRange, "Null replaceRange");
|
||||
this.replacementString = checkNotNull(replacementString, "Null replacementString");
|
||||
|
||||
+1
@@ -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);
|
||||
}
|
||||
|
||||
+1
@@ -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;
|
||||
|
||||
+1
@@ -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);
|
||||
|
||||
+1
@@ -55,6 +55,7 @@ public class NBNames {
|
||||
public static void preRegister(Context context) {
|
||||
context.put(nbNamesKey, (Context.Factory<NBNames>) NBNames::new);
|
||||
}
|
||||
|
||||
protected NBNames(Context context) {
|
||||
Names n = Names.instance(context);
|
||||
|
||||
|
||||
+1
@@ -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;
|
||||
|
||||
+1
@@ -41,6 +41,7 @@ public class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
|
||||
private final Type elementType;
|
||||
private final TypeAdapter<E> elementTypeAdapter;
|
||||
private final Supplier<Collection<E>> constructor;
|
||||
|
||||
public CollectionTypeAdapter(
|
||||
Gson gson,
|
||||
Type elementType,
|
||||
|
||||
+2
@@ -42,9 +42,11 @@ public class EitherTypeAdapter<L, R> extends TypeAdapter<Either<L, R>> {
|
||||
protected final EitherTypeArgument<R> right;
|
||||
protected final Predicate<JsonElement> leftChecker;
|
||||
protected final Predicate<JsonElement> rightChecker;
|
||||
|
||||
public EitherTypeAdapter(Gson gson, TypeToken<Either<L, R>> typeToken) {
|
||||
this(gson, typeToken, null, null);
|
||||
}
|
||||
|
||||
public EitherTypeAdapter(
|
||||
Gson gson,
|
||||
TypeToken<Either<L, R>> typeToken,
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ public class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
|
||||
private final Map<String, T> nameToConstant = new HashMap<>();
|
||||
private final Map<Integer, T> valueToConstant = new HashMap<>();
|
||||
private final Map<T, Integer> constantToValue = new HashMap<>();
|
||||
|
||||
EnumTypeAdapter(Class<T> classOfT) throws IllegalAccessException {
|
||||
try {
|
||||
Field valueField = classOfT.getDeclaredField(VALUE_FIELD_NAME);
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ public class JsonElementTypeAdapter extends TypeAdapter<Object> {
|
||||
|
||||
private final Gson gson;
|
||||
private final TypeAdapter<JsonElement> adapter;
|
||||
|
||||
public JsonElementTypeAdapter(Gson gson) {
|
||||
this.gson = gson;
|
||||
this.adapter = gson.getAdapter(JsonElement.class);
|
||||
|
||||
+1
@@ -57,6 +57,7 @@ public class MessageTypeAdapter extends TypeAdapter<Message> {
|
||||
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;
|
||||
|
||||
@@ -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$
|
||||
|
||||
+1
@@ -84,6 +84,7 @@ public enum XmlAttributeSortOrder {
|
||||
return attr1.getName().compareTo(attr2.getName());
|
||||
}
|
||||
};
|
||||
|
||||
public final String key;
|
||||
|
||||
XmlAttributeSortOrder(String key) {
|
||||
|
||||
@@ -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<String> IMAGE_EXTENSIONS =
|
||||
ImmutableList.of(DOT_PNG, DOT_9PNG, DOT_GIF, DOT_JPEG, DOT_JPG, DOT_BMP, DOT_WEBP);
|
||||
/** For use by {@link #getLineSeparator()} */
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+9
-9
@@ -36,19 +36,19 @@ import org.json.JSONObject;
|
||||
*
|
||||
* <p>Examples: {@code # Empty: []
|
||||
*
|
||||
* <p><p><p><p><p><p><p># Single row: [[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]
|
||||
* <p><p><p><p><p><p><p><p># Single row: [[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]
|
||||
*
|
||||
* <p><p><p><p><p><p><p># 2 row: [['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'],
|
||||
* <p><p><p><p><p><p><p><p># 2 row: [['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'],
|
||||
* ['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN']]
|
||||
*
|
||||
* <p><p><p><p><p><p><p># 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}} ]]
|
||||
* <p><p><p><p><p><p><p><p># 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}} ]]
|
||||
*
|
||||
* <p><p><p><p><p><p><p>}
|
||||
* <p><p><p><p><p><p><p><p>}
|
||||
*
|
||||
* <p>Aliases are also allowed for the keys that you can pass as {@code extraKeyAliasMap}. Check
|
||||
* {@link VirtualKeysConstants#CONTROL_CHARS_ALIASES}.
|
||||
|
||||
+2
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String> 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -12,6 +12,7 @@ final class GestureAndScaleRecognizer {
|
||||
private final ScaleGestureDetector mScaleDetector;
|
||||
final Listener mListener;
|
||||
boolean isAfterLongPress;
|
||||
|
||||
public GestureAndScaleRecognizer(Context context, Listener listener) {
|
||||
mListener = listener;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<Class<?>> skipMethodVerificationForClasses;
|
||||
List<SubscriberInfoIndex> 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<Class<?>> skipMethodVerificationForClasses;
|
||||
List<SubscriberInfoIndex> 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).
|
||||
*
|
||||
* <p>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%.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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).
|
||||
* <p/>
|
||||
* 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%.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,41 +19,40 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class PendingPost {
|
||||
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
|
||||
private static final List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return method.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
|
||||
private static final int MODIFIERS_IGNORE =
|
||||
Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
|
||||
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
private List<SubscriberInfoIndex> subscriberInfoIndexes;
|
||||
private final boolean strictMethodVerification;
|
||||
private final boolean ignoreGeneratedIndex;
|
||||
private List<SubscriberInfoIndex> 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<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
|
||||
boolean ignoreGeneratedIndex) {
|
||||
this.subscriberInfoIndexes = subscriberInfoIndexes;
|
||||
this.strictMethodVerification = strictMethodVerification;
|
||||
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
|
||||
SubscriberMethodFinder(
|
||||
List<SubscriberInfoIndex> subscriberInfoIndexes,
|
||||
boolean strictMethodVerification,
|
||||
boolean ignoreGeneratedIndex) {
|
||||
this.subscriberInfoIndexes = subscriberInfoIndexes;
|
||||
this.strictMethodVerification = strictMethodVerification;
|
||||
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
|
||||
}
|
||||
|
||||
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
|
||||
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
|
||||
if (subscriberMethods != null) {
|
||||
return subscriberMethods;
|
||||
}
|
||||
|
||||
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
|
||||
List<SubscriberMethod> 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<SubscriberMethod> 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<SubscriberMethod> getMethodsAndRelease(FindState findState) {
|
||||
List<SubscriberMethod> 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<SubscriberMethod> 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<SubscriberMethod> subscriberMethods = new ArrayList<>();
|
||||
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
|
||||
final Map<String, Class> 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<SubscriberMethod> 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<SubscriberMethod> getMethodsAndRelease(FindState findState) {
|
||||
List<SubscriberMethod> 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<SubscriberMethod> 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<SubscriberMethod> subscriberMethods = new ArrayList<>();
|
||||
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
|
||||
final Map<String, Class> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>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
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+29
-30
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<? extends SubscriberInfo> superSubscriberInfoClass;
|
||||
private final boolean shouldCheckSuperclass;
|
||||
private final Class subscriberClass;
|
||||
private final Class<? extends SubscriberInfo> superSubscriberInfoClass;
|
||||
private final boolean shouldCheckSuperclass;
|
||||
|
||||
protected AbstractSubscriberInfo(Class subscriberClass, Class<? extends SubscriberInfo> superSubscriberInfoClass,
|
||||
boolean shouldCheckSuperclass) {
|
||||
this.subscriberClass = subscriberClass;
|
||||
this.superSubscriberInfoClass = superSubscriberInfoClass;
|
||||
this.shouldCheckSuperclass = shouldCheckSuperclass;
|
||||
protected AbstractSubscriberInfo(
|
||||
Class subscriberClass,
|
||||
Class<? extends SubscriberInfo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
public SubscriberMethodInfo(String methodName, Class<?> eventType, ThreadMode threadMode) {
|
||||
this(methodName, eventType, threadMode, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
* <p>
|
||||
* 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}.
|
||||
*
|
||||
* <p>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
|
||||
*
|
||||
* <pre>
|
||||
* -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+45
-49
@@ -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<Class<? extends Throwable>, Integer> throwableToMsgIdMap;
|
||||
public final Map<Class<? extends Throwable>, 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<? extends Throwable> throwableClass = throwable.getClass();
|
||||
Integer resId = throwableToMsgIdMap.get(throwableClass);
|
||||
if (resId == null) {
|
||||
Class<? extends Throwable> closestClass = null;
|
||||
Set<Entry<Class<? extends Throwable>, Integer>> mappings = throwableToMsgIdMap.entrySet();
|
||||
for (Entry<Class<? extends Throwable>, Integer> mapping : mappings) {
|
||||
Class<? extends Throwable> 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<? extends Throwable> clazz, int msgId) {
|
||||
throwableToMsgIdMap.put(clazz, msgId);
|
||||
return this;
|
||||
/** Mapping without checking the cause (done in mapThrowable). */
|
||||
protected Integer mapThrowableFlat(Throwable throwable) {
|
||||
Class<? extends Throwable> throwableClass = throwable.getClass();
|
||||
Integer resId = throwableToMsgIdMap.get(throwableClass);
|
||||
if (resId == null) {
|
||||
Class<? extends Throwable> closestClass = null;
|
||||
Set<Entry<Class<? extends Throwable>, Integer>> mappings = throwableToMsgIdMap.entrySet();
|
||||
for (Entry<Class<? extends Throwable>, Integer> mapping : mappings) {
|
||||
Class<? extends Throwable> candidate = mapping.getKey();
|
||||
if (candidate.isAssignableFrom(throwableClass)) {
|
||||
if (closestClass == null || closestClass.isAssignableFrom(candidate)) {
|
||||
closestClass = candidate;
|
||||
resId = mapping.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resId;
|
||||
}
|
||||
|
||||
public ExceptionToResourceMapping addMapping(Class<? extends Throwable> clazz, int msgId) {
|
||||
throwableToMsgIdMap.put(clazz, msgId);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
package org.greenrobot.eventbus.util;
|
||||
|
||||
public interface HasExecutionScope {
|
||||
Object getExecutionScope();
|
||||
|
||||
void setExecutionScope(Object executionScope);
|
||||
Object getExecutionScope();
|
||||
|
||||
void setExecutionScope(Object executionScope);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,11 +282,11 @@ public class JavaLanguageServer implements ILanguageServer {
|
||||
}
|
||||
|
||||
private void startOrRestartAnalyzeTimer() {
|
||||
|
||||
|
||||
if (VMUtils.isJvm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!this.timer.isStarted()) {
|
||||
this.timer.start();
|
||||
} else {
|
||||
|
||||
@@ -72,6 +72,5 @@ public class CompileTask implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
public void close() {}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -86,7 +86,7 @@ public class Parser {
|
||||
if (module != null) {
|
||||
FILE_MANAGER = SourceFileManager.forModule(module);
|
||||
}
|
||||
|
||||
|
||||
return (JavacTask)
|
||||
COMPILER.getTask(
|
||||
null,
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+2
-2
@@ -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 -> {
|
||||
|
||||
+3
-2
@@ -85,10 +85,11 @@ public class ImplementAbstractMethods extends Rewrite {
|
||||
public Map<Path, TextEdit[]> 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 -> {
|
||||
|
||||
@@ -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<Path, TextEdit[]> 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 -> {
|
||||
|
||||
@@ -46,7 +46,7 @@ public class RemoveMethod extends Rewrite {
|
||||
if (file == CompilerProvider.NOT_FOUND) {
|
||||
return CANCELLED;
|
||||
}
|
||||
|
||||
|
||||
return compiler
|
||||
.compile(file)
|
||||
.get(
|
||||
|
||||
@@ -97,8 +97,7 @@ public class ASTFixer {
|
||||
}
|
||||
}
|
||||
|
||||
private void fixError(
|
||||
@NonNull Scanner scanner, @NonNull CharSequence content, List<Edit> edits) {
|
||||
private void fixError(@NonNull Scanner scanner, @NonNull CharSequence content, List<Edit> 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<Edit> edits) {
|
||||
ImmutableList<Edit> reverseEdits =
|
||||
REVERSE_INSERTION.immutableSortedCopy(edits);
|
||||
ImmutableList<Edit> reverseEdits = REVERSE_INSERTION.immutableSortedCopy(edits);
|
||||
|
||||
StringBuilder sb = new StringBuilder(content);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user