Skip to content

Commit 797cb7b

Browse files
vkryachkoBrian Chen
authored and
Brian Chen
committed
Add polymorphic encoder registration support. (#1019)
This allows clients to configure DataEncoders in a way that does not couple them to a particular output format, e.g. json. This mechanism will be used for auto-generated encoders in the future.
1 parent 6558de9 commit 797cb7b

File tree

5 files changed

+160
-6
lines changed

5 files changed

+160
-6
lines changed

encoders/firebase-encoders/api.txt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,27 @@ package com.google.firebase.encoders {
3636

3737
}
3838

39+
package com.google.firebase.encoders.config {
40+
41+
public interface Configurator {
42+
method public void configure(@NonNull com.google.firebase.encoders.config.EncoderConfig<?>);
43+
}
44+
45+
public interface EncoderConfig<T extends com.google.firebase.encoders.config.EncoderConfig<T>> {
46+
method @NonNull public <U> T registerEncoder(@NonNull Class<U>, @NonNull com.google.firebase.encoders.ObjectEncoder<? super U>);
47+
method @NonNull public <U> T registerEncoder(@NonNull Class<U>, @NonNull com.google.firebase.encoders.ValueEncoder<? super U>);
48+
}
49+
50+
}
51+
3952
package com.google.firebase.encoders.json {
4053

41-
public final class JsonDataEncoderBuilder {
54+
public final class JsonDataEncoderBuilder implements com.google.firebase.encoders.config.EncoderConfig<com.google.firebase.encoders.json.JsonDataEncoderBuilder> {
4255
ctor public JsonDataEncoderBuilder();
4356
method @NonNull public com.google.firebase.encoders.DataEncoder build();
44-
method @NonNull public <T> com.google.firebase.encoders.json.JsonDataEncoderBuilder registerEncoder(@NonNull Class<T>, @NonNull com.google.firebase.encoders.ObjectEncoder<T>);
45-
method @NonNull public <T> com.google.firebase.encoders.json.JsonDataEncoderBuilder registerEncoder(@NonNull Class<T>, @NonNull com.google.firebase.encoders.ValueEncoder<T>);
57+
method @NonNull public com.google.firebase.encoders.json.JsonDataEncoderBuilder configureWith(@NonNull com.google.firebase.encoders.config.Configurator);
58+
method @NonNull public <T> com.google.firebase.encoders.json.JsonDataEncoderBuilder registerEncoder(@NonNull Class<T>, @NonNull com.google.firebase.encoders.ObjectEncoder<? super T>);
59+
method @NonNull public <T> com.google.firebase.encoders.json.JsonDataEncoderBuilder registerEncoder(@NonNull Class<T>, @NonNull com.google.firebase.encoders.ValueEncoder<? super T>);
4660
}
4761

4862
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.encoders.config;
16+
17+
import androidx.annotation.NonNull;
18+
19+
/**
20+
* Callback that is accepted by encoder builders to configure them.
21+
*
22+
* <p>Example:
23+
*
24+
* <pre>{@code
25+
* DataEncoder encoder = new JsonDataEncoderBuilder()
26+
* .configureWith(cfg ->
27+
* cfg.registerEncoder(Foo.class, new FooEncoder())
28+
* .registerEncoder(Bar.class, new BarEncoder()))
29+
* .build();
30+
* }</pre>
31+
*/
32+
public interface Configurator {
33+
void configure(@NonNull EncoderConfig<?> configuration);
34+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.encoders.config;
16+
17+
import androidx.annotation.NonNull;
18+
import com.google.firebase.encoders.ObjectEncoder;
19+
import com.google.firebase.encoders.ValueEncoder;
20+
21+
/**
22+
* Implemented by concrete {@link com.google.firebase.encoders.DataEncoder} builders.
23+
*
24+
* <p>Used by clients to configure encoders without coupling to a particular encoder format.
25+
*/
26+
public interface EncoderConfig<T extends EncoderConfig<T>> {
27+
@NonNull
28+
<U> T registerEncoder(@NonNull Class<U> type, @NonNull ObjectEncoder<? super U> encoder);
29+
30+
@NonNull
31+
<U> T registerEncoder(@NonNull Class<U> type, @NonNull ValueEncoder<? super U> encoder);
32+
}

encoders/firebase-encoders/src/main/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import com.google.firebase.encoders.ObjectEncoder;
2222
import com.google.firebase.encoders.ValueEncoder;
2323
import com.google.firebase.encoders.ValueEncoderContext;
24+
import com.google.firebase.encoders.config.Configurator;
25+
import com.google.firebase.encoders.config.EncoderConfig;
2426
import java.io.IOException;
2527
import java.io.StringWriter;
2628
import java.io.Writer;
@@ -32,7 +34,7 @@
3234
import java.util.Map;
3335
import java.util.TimeZone;
3436

35-
public final class JsonDataEncoderBuilder {
37+
public final class JsonDataEncoderBuilder implements EncoderConfig<JsonDataEncoderBuilder> {
3638

3739
private final Map<Class<?>, ObjectEncoder<?>> objectEncoders = new HashMap<>();
3840
private final Map<Class<?>, ValueEncoder<?>> valueEncoders = new HashMap<>();
@@ -63,8 +65,9 @@ public JsonDataEncoderBuilder() {
6365
}
6466

6567
@NonNull
68+
@Override
6669
public <T> JsonDataEncoderBuilder registerEncoder(
67-
@NonNull Class<T> clazz, @NonNull ObjectEncoder<T> objectEncoder) {
70+
@NonNull Class<T> clazz, @NonNull ObjectEncoder<? super T> objectEncoder) {
6871
if (objectEncoders.containsKey(clazz)) {
6972
throw new IllegalArgumentException("Encoder already registered for " + clazz.getName());
7073
}
@@ -73,15 +76,22 @@ public <T> JsonDataEncoderBuilder registerEncoder(
7376
}
7477

7578
@NonNull
79+
@Override
7680
public <T> JsonDataEncoderBuilder registerEncoder(
77-
@NonNull Class<T> clazz, @NonNull ValueEncoder<T> encoder) {
81+
@NonNull Class<T> clazz, @NonNull ValueEncoder<? super T> encoder) {
7882
if (valueEncoders.containsKey(clazz)) {
7983
throw new IllegalArgumentException("Encoder already registered for " + clazz.getName());
8084
}
8185
valueEncoders.put(clazz, encoder);
8286
return this;
8387
}
8488

89+
@NonNull
90+
public JsonDataEncoderBuilder configureWith(@NonNull Configurator config) {
91+
config.configure(this);
92+
return this;
93+
}
94+
8595
@NonNull
8696
public DataEncoder build() {
8797
return new DataEncoder() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.encoders.json;
16+
17+
import static com.google.common.truth.Truth.assertThat;
18+
19+
import androidx.test.ext.junit.runners.AndroidJUnit4;
20+
import com.google.firebase.encoders.DataEncoder;
21+
import com.google.firebase.encoders.EncodingException;
22+
import com.google.firebase.encoders.ObjectEncoderContext;
23+
import com.google.firebase.encoders.ValueEncoderContext;
24+
import java.util.Collections;
25+
import org.junit.Test;
26+
import org.junit.runner.RunWith;
27+
28+
@RunWith(AndroidJUnit4.class)
29+
public class JsonDataEncoderBuilderTests {
30+
static class Foo {}
31+
32+
@Test
33+
public void configureWith_shouldCorrectlyRegisterObjectEncoder() throws EncodingException {
34+
DataEncoder encoder =
35+
new JsonDataEncoderBuilder()
36+
.configureWith(
37+
cfg ->
38+
cfg.registerEncoder(
39+
Foo.class,
40+
(Foo s, ObjectEncoderContext ctx) -> {
41+
ctx.add("foo", "value");
42+
}))
43+
.build();
44+
45+
assertThat(encoder.encode(new Foo())).isEqualTo("{\"foo\":\"value\"}");
46+
}
47+
48+
@Test
49+
public void configureWith_shouldCorrectlyRegisterValueEncoder() throws EncodingException {
50+
DataEncoder encoder =
51+
new JsonDataEncoderBuilder()
52+
.configureWith(
53+
cfg ->
54+
cfg.registerEncoder(
55+
Foo.class,
56+
(Foo s, ValueEncoderContext ctx) -> {
57+
ctx.add("value");
58+
}))
59+
.build();
60+
61+
assertThat(encoder.encode(Collections.singletonMap("foo", new Foo())))
62+
.isEqualTo("{\"foo\":\"value\"}");
63+
}
64+
}

0 commit comments

Comments
 (0)