Failing editor tests
This commit is contained in:
		
							parent
							
								
									3069432bc2
								
							
						
					
					
						commit
						646e708c82
					
				@ -1,4 +1,16 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "javaClassName": "io.noties.markwon.app.samples.html.InspectHtmlTextSample",
 | 
			
		||||
    "id": "20210201140501",
 | 
			
		||||
    "title": "Inspect text",
 | 
			
		||||
    "description": "Inspect text content of a `HTML` node",
 | 
			
		||||
    "artifacts": [
 | 
			
		||||
      "HTML"
 | 
			
		||||
    ],
 | 
			
		||||
    "tags": [
 | 
			
		||||
      "HTML"
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "javaClassName": "io.noties.markwon.app.samples.image.HugeImageSample",
 | 
			
		||||
    "id": "20210118165230",
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,57 @@
 | 
			
		||||
package io.noties.markwon.app.samples.html
 | 
			
		||||
 | 
			
		||||
import android.text.style.URLSpan
 | 
			
		||||
import io.noties.markwon.Markwon
 | 
			
		||||
import io.noties.markwon.MarkwonVisitor
 | 
			
		||||
import io.noties.markwon.app.sample.Tags
 | 
			
		||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
 | 
			
		||||
import io.noties.markwon.html.HtmlPlugin
 | 
			
		||||
import io.noties.markwon.html.HtmlTag
 | 
			
		||||
import io.noties.markwon.html.MarkwonHtmlRenderer
 | 
			
		||||
import io.noties.markwon.html.TagHandler
 | 
			
		||||
import io.noties.markwon.sample.annotations.MarkwonArtifact
 | 
			
		||||
import io.noties.markwon.sample.annotations.MarkwonSampleInfo
 | 
			
		||||
 | 
			
		||||
@MarkwonSampleInfo(
 | 
			
		||||
  id = "20210201140501",
 | 
			
		||||
  title = "Inspect text",
 | 
			
		||||
  description = "Inspect text content of a `HTML` node",
 | 
			
		||||
  artifacts = [MarkwonArtifact.HTML],
 | 
			
		||||
  tags = [Tags.html]
 | 
			
		||||
)
 | 
			
		||||
class InspectHtmlTextSample : MarkwonTextViewSample() {
 | 
			
		||||
  override fun render() {
 | 
			
		||||
    val md = """
 | 
			
		||||
      <p>lorem ipsum</p>
 | 
			
		||||
      <div class="custom-youtube-player">https://www.youtube.com/watch?v=abcdefgh</div>
 | 
			
		||||
    """.trimIndent()
 | 
			
		||||
 | 
			
		||||
    val markwon = Markwon.builder(context)
 | 
			
		||||
      .usePlugin(HtmlPlugin.create {
 | 
			
		||||
        it.addHandler(DivHandler())
 | 
			
		||||
      })
 | 
			
		||||
      .build()
 | 
			
		||||
 | 
			
		||||
    markwon.setMarkdown(textView, md)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  class DivHandler : TagHandler() {
 | 
			
		||||
    override fun handle(visitor: MarkwonVisitor, renderer: MarkwonHtmlRenderer, tag: HtmlTag) {
 | 
			
		||||
      val attr = tag.attributes()["class"] ?: return
 | 
			
		||||
      if (attr.contains(CUSTOM_CLASS)) {
 | 
			
		||||
        val text = visitor.builder().substring(tag.start(), tag.end())
 | 
			
		||||
        visitor.builder().setSpan(
 | 
			
		||||
          URLSpan(text),
 | 
			
		||||
          tag.start(),
 | 
			
		||||
          tag.end()
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun supportedTags(): Collection<String> = setOf("div")
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
      const val CUSTOM_CLASS = "custom-youtube-player"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -94,6 +94,28 @@ public class MarkwonEditorUtilsTest {
 | 
			
		||||
        assertMatched(input, strike, "~~", 3, 11);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void delimited_triple_asterisks() {
 | 
			
		||||
        final String input = "***italic bold bold***";
 | 
			
		||||
 | 
			
		||||
        final Match bold = findDelimited(input, 0, "**", "__");
 | 
			
		||||
        final Match em = findDelimited(input, 0, "*", "_");
 | 
			
		||||
 | 
			
		||||
        assertMatched(input, bold, "**", 0, input.length());
 | 
			
		||||
        assertMatched(input, em, "*", 0, input.length());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void delimited_triple_asterisks_2() {
 | 
			
		||||
        final String input = "***italic bold* bold**";
 | 
			
		||||
 | 
			
		||||
        final Match bold = findDelimited(input, 0, "**", "__");
 | 
			
		||||
        final Match em = findDelimited(input, 0, "*", "_");
 | 
			
		||||
 | 
			
		||||
        assertMatched(input, bold, "**", 0, input.length());
 | 
			
		||||
        assertMatched(input, em, "*", 0, 15);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void assertMatched(
 | 
			
		||||
            @NonNull String input,
 | 
			
		||||
            @Nullable Match match,
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
import androidx.annotation.VisibleForTesting;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ExecutorService;
 | 
			
		||||
@ -77,12 +78,6 @@ class AsyncDrawableLoaderImpl extends AsyncDrawableLoader {
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private Future<?> execute(@NonNull final AsyncDrawable asyncDrawable) {
 | 
			
		||||
 | 
			
		||||
        // todo: more efficient DefaultMediaDecoder... BitmapFactory.decodeStream is a bit not optimal
 | 
			
		||||
        //      for big images for sure. We _could_ introduce internal Drawable that will check for
 | 
			
		||||
        //      image bounds (but we will need to cache inputStream in order to inspect and optimize
 | 
			
		||||
        //      input image...)
 | 
			
		||||
 | 
			
		||||
        return executorService.submit(new Runnable() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
@ -113,17 +108,26 @@ class AsyncDrawableLoaderImpl extends AsyncDrawableLoader {
 | 
			
		||||
 | 
			
		||||
                            final ImageItem.WithDecodingNeeded withDecodingNeeded = imageItem.getAsWithDecodingNeeded();
 | 
			
		||||
 | 
			
		||||
                            MediaDecoder mediaDecoder = mediaDecoders.get(withDecodingNeeded.contentType());
 | 
			
		||||
                            // @since $SNAPSHOT; close input stream
 | 
			
		||||
                            try {
 | 
			
		||||
                                MediaDecoder mediaDecoder = mediaDecoders.get(withDecodingNeeded.contentType());
 | 
			
		||||
 | 
			
		||||
                            if (mediaDecoder == null) {
 | 
			
		||||
                                mediaDecoder = defaultMediaDecoder;
 | 
			
		||||
                            }
 | 
			
		||||
                                if (mediaDecoder == null) {
 | 
			
		||||
                                    mediaDecoder = defaultMediaDecoder;
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                            if (mediaDecoder != null) {
 | 
			
		||||
                                drawable = mediaDecoder.decode(withDecodingNeeded.contentType(), withDecodingNeeded.inputStream());
 | 
			
		||||
                            } else {
 | 
			
		||||
                                // throw that no media decoder is found
 | 
			
		||||
                                throw new IllegalStateException("No media-decoder is found: " + destination);
 | 
			
		||||
                                if (mediaDecoder != null) {
 | 
			
		||||
                                    drawable = mediaDecoder.decode(withDecodingNeeded.contentType(), withDecodingNeeded.inputStream());
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    // throw that no media decoder is found
 | 
			
		||||
                                    throw new IllegalStateException("No media-decoder is found: " + destination);
 | 
			
		||||
                                }
 | 
			
		||||
                            } finally {
 | 
			
		||||
                                try {
 | 
			
		||||
                                    withDecodingNeeded.inputStream().close();
 | 
			
		||||
                                } catch (IOException e) {
 | 
			
		||||
                                    Log.e("MARKWON-IMAGE", "Error closing inputStream", e);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            drawable = imageItem.getAsWithResult().result();
 | 
			
		||||
 | 
			
		||||
@ -33,141 +33,156 @@ import java.util.Collections;
 | 
			
		||||
 */
 | 
			
		||||
public class DefaultDownScalingMediaDecoder extends MediaDecoder {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Values {@code <= 0} are ignored, a dimension is considered to be not restrained any limit in such case
 | 
			
		||||
   */
 | 
			
		||||
  @NonNull
 | 
			
		||||
  public static DefaultDownScalingMediaDecoder create(int maxWidth, int maxHeight) {
 | 
			
		||||
    return create(Resources.getSystem(), maxWidth, maxHeight);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @NonNull
 | 
			
		||||
  public static DefaultDownScalingMediaDecoder create(
 | 
			
		||||
    @NonNull Resources resources,
 | 
			
		||||
    int maxWidth,
 | 
			
		||||
    int maxHeight
 | 
			
		||||
  ) {
 | 
			
		||||
    return new DefaultDownScalingMediaDecoder(resources, maxWidth, maxHeight);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final Resources resources;
 | 
			
		||||
  private final int maxWidth;
 | 
			
		||||
  private final int maxHeight;
 | 
			
		||||
 | 
			
		||||
  private DefaultDownScalingMediaDecoder(@NonNull Resources resources, int maxWidth, int maxHeight) {
 | 
			
		||||
    this.resources = resources;
 | 
			
		||||
    this.maxWidth = maxWidth;
 | 
			
		||||
    this.maxHeight = maxHeight;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // https://android.jlelse.eu/loading-large-bitmaps-efficiently-in-android-66826cd4ad53
 | 
			
		||||
  @NonNull
 | 
			
		||||
  @Override
 | 
			
		||||
  public Drawable decode(@Nullable String contentType, @NonNull InputStream inputStream) {
 | 
			
		||||
 | 
			
		||||
    final File file = writeToTempFile(inputStream);
 | 
			
		||||
    try {
 | 
			
		||||
 | 
			
		||||
      final BitmapFactory.Options options = new BitmapFactory.Options();
 | 
			
		||||
      options.inJustDecodeBounds = true;
 | 
			
		||||
 | 
			
		||||
      BitmapFactory
 | 
			
		||||
        .decodeStream(readFile(file), null, options);
 | 
			
		||||
 | 
			
		||||
      options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
 | 
			
		||||
      options.inJustDecodeBounds = false;
 | 
			
		||||
 | 
			
		||||
      final Bitmap bitmap = BitmapFactory
 | 
			
		||||
        .decodeStream(readFile(file), null, options);
 | 
			
		||||
 | 
			
		||||
      return new BitmapDrawable(resources, bitmap);
 | 
			
		||||
    } finally {
 | 
			
		||||
      // we no longer need the temporary file
 | 
			
		||||
      //noinspection ResultOfMethodCallIgnored
 | 
			
		||||
      file.delete();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @NonNull
 | 
			
		||||
  private static File writeToTempFile(@NonNull InputStream inputStream) {
 | 
			
		||||
    final File file;
 | 
			
		||||
    try {
 | 
			
		||||
      file = File.createTempFile("markwon", null);
 | 
			
		||||
    } catch (IOException e) {
 | 
			
		||||
      throw new IllegalStateException(e);
 | 
			
		||||
    /**
 | 
			
		||||
     * Values {@code <= 0} are ignored, a dimension is considered to be not restrained any limit in such case
 | 
			
		||||
     */
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static DefaultDownScalingMediaDecoder create(int maxWidth, int maxHeight) {
 | 
			
		||||
        return create(Resources.getSystem(), maxWidth, maxHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final OutputStream outputStream;
 | 
			
		||||
    try {
 | 
			
		||||
      outputStream = new BufferedOutputStream(new FileOutputStream(file, false));
 | 
			
		||||
    } catch (FileNotFoundException e) {
 | 
			
		||||
      throw new IllegalStateException(e);
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static DefaultDownScalingMediaDecoder create(
 | 
			
		||||
            @NonNull Resources resources,
 | 
			
		||||
            int maxWidth,
 | 
			
		||||
            int maxHeight
 | 
			
		||||
    ) {
 | 
			
		||||
        return new DefaultDownScalingMediaDecoder(resources, maxWidth, maxHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final byte[] buffer = new byte[1024 * 8];
 | 
			
		||||
    int length;
 | 
			
		||||
    try {
 | 
			
		||||
      while ((length = inputStream.read(buffer)) > 0) {
 | 
			
		||||
        outputStream.write(buffer, 0, length);
 | 
			
		||||
      }
 | 
			
		||||
    } catch (IOException e) {
 | 
			
		||||
      throw new IllegalStateException(e);
 | 
			
		||||
    } finally {
 | 
			
		||||
      try {
 | 
			
		||||
        outputStream.close();
 | 
			
		||||
      } catch (IOException e) {
 | 
			
		||||
        // ignored
 | 
			
		||||
      }
 | 
			
		||||
    private final Resources resources;
 | 
			
		||||
    private final int maxWidth;
 | 
			
		||||
    private final int maxHeight;
 | 
			
		||||
 | 
			
		||||
    private DefaultDownScalingMediaDecoder(@NonNull Resources resources, int maxWidth, int maxHeight) {
 | 
			
		||||
        this.resources = resources;
 | 
			
		||||
        this.maxWidth = maxWidth;
 | 
			
		||||
        this.maxHeight = maxHeight;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return file;
 | 
			
		||||
  }
 | 
			
		||||
    // https://android.jlelse.eu/loading-large-bitmaps-efficiently-in-android-66826cd4ad53
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public Drawable decode(@Nullable String contentType, @NonNull InputStream inputStream) {
 | 
			
		||||
 | 
			
		||||
  @NonNull
 | 
			
		||||
  private static InputStream readFile(@NonNull File file) {
 | 
			
		||||
    try {
 | 
			
		||||
      return new BufferedInputStream(new FileInputStream(file));
 | 
			
		||||
    } catch (FileNotFoundException e) {
 | 
			
		||||
      throw new IllegalStateException(e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
        final File file = writeToTempFile(inputStream);
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
  // see: https://developer.android.com/topic/performance/graphics/load-bitmap.html#load-bitmap
 | 
			
		||||
  private static int calculateInSampleSize(@NonNull BitmapFactory.Options options, int maxWidth, int maxHeight) {
 | 
			
		||||
    final int w = options.outWidth;
 | 
			
		||||
    final int h = options.outHeight;
 | 
			
		||||
            final BitmapFactory.Options options = new BitmapFactory.Options();
 | 
			
		||||
            options.inJustDecodeBounds = true;
 | 
			
		||||
 | 
			
		||||
    final boolean hasMaxWidth = maxWidth > 0;
 | 
			
		||||
    final boolean hasMaxHeight = maxHeight > 0;
 | 
			
		||||
            // initial result when obtaining bounds is discarded
 | 
			
		||||
            decode(file, options);
 | 
			
		||||
 | 
			
		||||
    final int inSampleSize;
 | 
			
		||||
    if (hasMaxWidth && hasMaxHeight) {
 | 
			
		||||
      // minimum of both
 | 
			
		||||
      inSampleSize = Math.min(calculateInSampleSize(w, maxWidth), calculateInSampleSize(h, maxHeight));
 | 
			
		||||
    } else if (hasMaxWidth) {
 | 
			
		||||
      inSampleSize = calculateInSampleSize(w, maxWidth);
 | 
			
		||||
    } else if (hasMaxHeight) {
 | 
			
		||||
      inSampleSize = calculateInSampleSize(h, maxHeight);
 | 
			
		||||
    } else {
 | 
			
		||||
      // else no sampling, as we have no dimensions to base our calculations on
 | 
			
		||||
      inSampleSize = 1;
 | 
			
		||||
            options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
 | 
			
		||||
            options.inJustDecodeBounds = false;
 | 
			
		||||
 | 
			
		||||
            final Bitmap bitmap = decode(file, options);
 | 
			
		||||
 | 
			
		||||
            return new BitmapDrawable(resources, bitmap);
 | 
			
		||||
        } finally {
 | 
			
		||||
            // we no longer need the temporary file
 | 
			
		||||
            //noinspection ResultOfMethodCallIgnored
 | 
			
		||||
            file.delete();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return inSampleSize;
 | 
			
		||||
  }
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private static File writeToTempFile(@NonNull InputStream inputStream) {
 | 
			
		||||
        final File file;
 | 
			
		||||
        try {
 | 
			
		||||
            file = File.createTempFile("markwon", null);
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new IllegalStateException(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
  private static int calculateInSampleSize(int actual, int max) {
 | 
			
		||||
    int inSampleSize = 1;
 | 
			
		||||
    final int half = actual / 2;
 | 
			
		||||
    while ((half / inSampleSize) > max) {
 | 
			
		||||
      inSampleSize *= 2;
 | 
			
		||||
        final OutputStream outputStream;
 | 
			
		||||
        try {
 | 
			
		||||
            outputStream = new BufferedOutputStream(new FileOutputStream(file, false));
 | 
			
		||||
        } catch (FileNotFoundException e) {
 | 
			
		||||
            throw new IllegalStateException(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final byte[] buffer = new byte[1024 * 8];
 | 
			
		||||
        int length;
 | 
			
		||||
        try {
 | 
			
		||||
            while ((length = inputStream.read(buffer)) > 0) {
 | 
			
		||||
                outputStream.write(buffer, 0, length);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new IllegalStateException(e);
 | 
			
		||||
        } finally {
 | 
			
		||||
            try {
 | 
			
		||||
                outputStream.close();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                // ignored
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return file;
 | 
			
		||||
    }
 | 
			
		||||
    return inSampleSize;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @NonNull
 | 
			
		||||
  @Override
 | 
			
		||||
  public Collection<String> supportedTypes() {
 | 
			
		||||
    return Collections.emptySet();
 | 
			
		||||
  }
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private static Bitmap decode(@NonNull File file, @NonNull BitmapFactory.Options options) {
 | 
			
		||||
        final InputStream is = readFile(file);
 | 
			
		||||
        // not yet, still min SDK is 16
 | 
			
		||||
        try {
 | 
			
		||||
            return BitmapFactory.decodeStream(is, null, options);
 | 
			
		||||
        } finally {
 | 
			
		||||
            try {
 | 
			
		||||
                is.close();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                // ignored
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private static InputStream readFile(@NonNull File file) {
 | 
			
		||||
        try {
 | 
			
		||||
            return new BufferedInputStream(new FileInputStream(file));
 | 
			
		||||
        } catch (FileNotFoundException e) {
 | 
			
		||||
            throw new IllegalStateException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // see: https://developer.android.com/topic/performance/graphics/load-bitmap.html#load-bitmap
 | 
			
		||||
    private static int calculateInSampleSize(@NonNull BitmapFactory.Options options, int maxWidth, int maxHeight) {
 | 
			
		||||
        final int w = options.outWidth;
 | 
			
		||||
        final int h = options.outHeight;
 | 
			
		||||
 | 
			
		||||
        final boolean hasMaxWidth = maxWidth > 0;
 | 
			
		||||
        final boolean hasMaxHeight = maxHeight > 0;
 | 
			
		||||
 | 
			
		||||
        final int inSampleSize;
 | 
			
		||||
        if (hasMaxWidth && hasMaxHeight) {
 | 
			
		||||
            // minimum of both
 | 
			
		||||
            inSampleSize = Math.min(calculateInSampleSize(w, maxWidth), calculateInSampleSize(h, maxHeight));
 | 
			
		||||
        } else if (hasMaxWidth) {
 | 
			
		||||
            inSampleSize = calculateInSampleSize(w, maxWidth);
 | 
			
		||||
        } else if (hasMaxHeight) {
 | 
			
		||||
            inSampleSize = calculateInSampleSize(h, maxHeight);
 | 
			
		||||
        } else {
 | 
			
		||||
            // else no sampling, as we have no dimensions to base our calculations on
 | 
			
		||||
            inSampleSize = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return inSampleSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int calculateInSampleSize(int actual, int max) {
 | 
			
		||||
        int inSampleSize = 1;
 | 
			
		||||
        final int half = actual / 2;
 | 
			
		||||
        while ((half / inSampleSize) > max) {
 | 
			
		||||
            inSampleSize *= 2;
 | 
			
		||||
        }
 | 
			
		||||
        return inSampleSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public Collection<String> supportedTypes() {
 | 
			
		||||
        return Collections.emptySet();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user