1 package edu.jiangxin.apktoolbox.convert.protobuf.supervised;
2
3 import com.google.protobuf.Descriptors;
4 import com.google.protobuf.DynamicMessage;
5 import com.google.protobuf.InvalidProtocolBufferException;
6 import com.google.protobuf.util.JsonFormat;
7
8 import java.io.IOException;
9 import java.io.UncheckedIOException;
10 import java.nio.file.Files;
11 import java.nio.file.Path;
12 import java.util.Comparator;
13 import java.util.Objects;
14 import java.util.Optional;
15
16 public final class ProtoToJson {
17 public static ProtoToJson fromCache(final DescriptorCache cache) {
18 Objects.requireNonNull(cache);
19
20 return new ProtoToJson(cache);
21 }
22
23 public static ProtoToJson fromEmptyCache() {
24 return new ProtoToJson(DescriptorCache.emptyCache());
25 }
26
27 private final DescriptorCache cache;
28 private final JsonFormat.Printer printer = JsonFormat.printer();
29
30 private ProtoToJson(final DescriptorCache cache) {
31 this.cache = Objects.requireNonNull(cache);
32 }
33
34 public DescriptorCache getCache() {
35 return cache;
36 }
37
38 public String toJson(final Path messageFile) {
39 Objects.requireNonNull(messageFile);
40
41 try {
42 return toJson(Files.readAllBytes(messageFile), (String) null);
43 } catch (final IOException e) {
44 throw new UncheckedIOException(e);
45 }
46 }
47
48 public String toJson(final byte[] messageRaw) {
49 Objects.requireNonNull(messageRaw);
50
51 return toJson(messageRaw, (String) null);
52 }
53
54 public String toJson(final Path messageFile, final String messageTypeName) {
55 Objects.requireNonNull(messageFile);
56
57 try {
58 return toJson(Files.readAllBytes(messageFile), messageTypeName);
59 } catch (final IOException e) {
60 throw new UncheckedIOException(e);
61 }
62 }
63
64 public String toJson(final byte[] messageRaw, final String messageTypeName) {
65 Objects.requireNonNull(messageRaw);
66
67 if (messageTypeName == null) {
68 final Optional<DynamicMessage> message = parseWithBestMatchingDescriptor(messageRaw);
69 return toJson(message.orElseThrow(RuntimeException::new));
70 }
71
72 final Optional<Descriptors.Descriptor> descriptor = cache.getByTypeName(messageTypeName);
73 return toJson(messageRaw, descriptor.orElseThrow(() -> new RuntimeException(messageTypeName)));
74 }
75
76 @SuppressWarnings("WeakerAccess")
77 public String toJson(final byte[] messageRaw, final Descriptors.Descriptor descriptor) {
78 Objects.requireNonNull(messageRaw);
79 Objects.requireNonNull(descriptor);
80
81 try {
82 return toJson(DynamicMessage.parseFrom(descriptor, messageRaw));
83 } catch (final InvalidProtocolBufferException e) {
84 throw new RuntimeException(e);
85 }
86 }
87
88 @SuppressWarnings("WeakerAccess")
89 public String toJson(final DynamicMessage message) {
90 Objects.requireNonNull(message);
91
92 try {
93 return printer.print(message);
94 } catch (final InvalidProtocolBufferException e) {
95 throw new RuntimeException(e);
96 }
97 }
98
99 private Optional<DynamicMessage> parseWithBestMatchingDescriptor(final byte[] messageRaw) {
100 Objects.requireNonNull(messageRaw);
101
102 return cache.getDescriptors()
103 .stream()
104 .map(descriptor -> {
105 try {
106 return DynamicMessage.parseFrom(descriptor, messageRaw);
107 } catch (final InvalidProtocolBufferException e) {
108
109 return null;
110 }
111 })
112 .filter(Objects::nonNull)
113 .min(Comparator.comparingInt(message -> message.getUnknownFields()
114 .asMap()
115 .size()));
116 }
117 }