addFactory method for MarkwonSpansFactory

This commit is contained in:
Dimitry Ivanov 2019-04-19 13:39:20 +03:00
parent 740ff1013c
commit 7f3f3368be
4 changed files with 121 additions and 0 deletions

View File

@ -34,6 +34,15 @@ public interface MarkwonSpansFactory {
@NonNull
<N extends Node> Builder setFactory(@NonNull Class<N> node, @Nullable SpanFactory factory);
/**
* Helper method to add a {@link SpanFactory} for a Node. This method will merge existing
* {@link SpanFactory} with the specified one.
*
* @since 3.0.1-SNAPSHOT
*/
@NonNull
<N extends Node> Builder addFactory(@NonNull Class<N> node, @NonNull SpanFactory factory);
/**
* Can be useful when <em>enhancing</em> an already defined SpanFactory with another one.
*/

View File

@ -5,8 +5,10 @@ import android.support.annotation.Nullable;
import org.commonmark.node.Node;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -52,6 +54,27 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory {
return this;
}
@NonNull
@Override
public <N extends Node> Builder addFactory(@NonNull Class<N> node, @NonNull SpanFactory factory) {
// if there is no factory registered for this node -> just add it
final SpanFactory existing = factories.get(node);
if (existing == null) {
factories.put(node, factory);
} else {
// existing span factory can be of CompositeSpanFactory at this point -> append to it
if (existing instanceof CompositeSpanFactory) {
((CompositeSpanFactory) existing).factories.add(factory);
} else {
// if it's not composite at this point -> make it
final CompositeSpanFactory compositeSpanFactory =
new CompositeSpanFactory(existing, factory);
factories.put(node, compositeSpanFactory);
}
}
return this;
}
@Nullable
@Override
public <N extends Node> SpanFactory getFactory(@NonNull Class<N> node) {
@ -74,4 +97,28 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory {
return new MarkwonSpansFactoryImpl(Collections.unmodifiableMap(factories));
}
}
static class CompositeSpanFactory implements SpanFactory {
final List<SpanFactory> factories;
CompositeSpanFactory(@NonNull SpanFactory first, @NonNull SpanFactory second) {
this.factories = new ArrayList<>(3);
this.factories.add(first);
this.factories.add(second);
}
@Nullable
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
// please note that we do not check it factory itself returns an array of spans,
// as this behaviour is supported now (previously we supported only a single-level array)
final int length = factories.size();
final Object[] out = new Object[length];
for (int i = 0; i < length; i++) {
out[i] = factories.get(i).getSpans(configuration, props);
}
return out;
}
}
}

View File

@ -13,13 +13,18 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@ -87,4 +92,58 @@ public class MarkwonSpansFactoryImplTest {
assertNull(factory.get(type));
}
}
@Test
public void composite_span_factory() {
// validate that composite span factory returns (calls) all span-factories
final SpanFactory first = mock(SpanFactory.class);
final SpanFactory second = mock(SpanFactory.class);
final MarkwonSpansFactoryImpl.CompositeSpanFactory factory =
new MarkwonSpansFactoryImpl.CompositeSpanFactory(first, second);
final Object spans = factory.getSpans(mock(MarkwonConfiguration.class), mock(RenderProps.class));
assertNotNull(spans);
assertTrue(spans.getClass().isArray());
assertEquals(2, ((Object[]) spans).length);
verify(first, times(1)).getSpans(any(MarkwonConfiguration.class), any(RenderProps.class));
verify(second, times(1)).getSpans(any(MarkwonConfiguration.class), any(RenderProps.class));
}
@Test
public void builder_add_factory() {
// here is what we should validate:
// * if we call addFactory and there is none already -> supplied factory
// * if there is
// * * if not composite -> make composite
// * * if composite -> add to it
final MarkwonSpansFactoryImpl.BuilderImpl builder = new MarkwonSpansFactoryImpl.BuilderImpl();
final SpanFactory first = mock(SpanFactory.class);
final SpanFactory second = mock(SpanFactory.class);
final SpanFactory third = mock(SpanFactory.class);
final Class<Node> node = Node.class;
// assert none yet
assertNull(builder.getFactory(node));
// add first, none yet -> it should be added without modifications
builder.addFactory(node, first);
assertEquals(first, builder.getFactory(node));
// add second -> composite factory will be created
builder.addFactory(node, second);
final MarkwonSpansFactoryImpl.CompositeSpanFactory compositeSpanFactory =
(MarkwonSpansFactoryImpl.CompositeSpanFactory) builder.getFactory(node);
assertNotNull(compositeSpanFactory);
assertEquals(Arrays.asList(first, second), compositeSpanFactory.factories);
builder.addFactory(node, third);
assertEquals(compositeSpanFactory, builder.getFactory(node));
assertEquals(Arrays.asList(first, second, third), compositeSpanFactory.factories);
}
}

View File

@ -156,6 +156,12 @@ public class CorePluginTest {
return this;
}
@NonNull
@Override
public <N extends Node> MarkwonSpansFactory.Builder addFactory(@NonNull Class<N> node, @NonNull SpanFactory factory) {
throw new RuntimeException();
}
@Nullable
@Override
public <N extends Node> SpanFactory getFactory(@NonNull Class<N> node) {