MessageLiteToString.java
Go to the documentation of this file.
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import java.lang.reflect.Method;
34 import java.lang.reflect.Modifier;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.TreeSet;
41 
43 final class MessageLiteToString {
44 
45  private static final String LIST_SUFFIX = "List";
46  private static final String BUILDER_LIST_SUFFIX = "OrBuilderList";
47  private static final String MAP_SUFFIX = "Map";
48  private static final String BYTES_SUFFIX = "Bytes";
49 
58  static String toString(MessageLite messageLite, String commentString) {
59  StringBuilder buffer = new StringBuilder();
60  buffer.append("# ").append(commentString);
61  reflectivePrintWithIndent(messageLite, buffer, 0);
62  return buffer.toString();
63  }
64 
71  private static void reflectivePrintWithIndent(
72  MessageLite messageLite, StringBuilder buffer, int indent) {
73  // Build a map of method name to method. We're looking for methods like getFoo(), hasFoo(),
74  // getFooList() and getFooMap() which might be useful for building an object's string
75  // representation.
76  Map<String, Method> nameToNoArgMethod = new HashMap<String, Method>();
77  Map<String, Method> nameToMethod = new HashMap<String, Method>();
78  Set<String> getters = new TreeSet<String>();
79  for (Method method : messageLite.getClass().getDeclaredMethods()) {
80  nameToMethod.put(method.getName(), method);
81  if (method.getParameterTypes().length == 0) {
82  nameToNoArgMethod.put(method.getName(), method);
83 
84  if (method.getName().startsWith("get")) {
85  getters.add(method.getName());
86  }
87  }
88  }
89 
90  for (String getter : getters) {
91  String suffix = getter.replaceFirst("get", "");
92  if (suffix.endsWith(LIST_SUFFIX)
93  && !suffix.endsWith(BUILDER_LIST_SUFFIX)
94  // Sometimes people have fields named 'list' that aren't repeated.
95  && !suffix.equals(LIST_SUFFIX)) {
96  String camelCase =
97  suffix.substring(0, 1).toLowerCase()
98  + suffix.substring(1, suffix.length() - LIST_SUFFIX.length());
99  // Try to reflectively get the value and toString() the field as if it were repeated. This
100  // only works if the method names have not been proguarded out or renamed.
101  Method listMethod = nameToNoArgMethod.get(getter);
102  if (listMethod != null && listMethod.getReturnType().equals(List.class)) {
103  printField(
104  buffer,
105  indent,
106  camelCaseToSnakeCase(camelCase),
107  GeneratedMessageLite.invokeOrDie(listMethod, messageLite));
108  continue;
109  }
110  }
111  if (suffix.endsWith(MAP_SUFFIX)
112  // Sometimes people have fields named 'map' that aren't maps.
113  && !suffix.equals(MAP_SUFFIX)) {
114  String camelCase =
115  suffix.substring(0, 1).toLowerCase()
116  + suffix.substring(1, suffix.length() - MAP_SUFFIX.length());
117  // Try to reflectively get the value and toString() the field as if it were a map. This only
118  // works if the method names have not been proguarded out or renamed.
119  Method mapMethod = nameToNoArgMethod.get(getter);
120  if (mapMethod != null
121  && mapMethod.getReturnType().equals(Map.class)
122  // Skip the deprecated getter method with no prefix "Map" when the field name ends with
123  // "map".
124  && !mapMethod.isAnnotationPresent(Deprecated.class)
125  // Skip the internal mutable getter method.
126  && Modifier.isPublic(mapMethod.getModifiers())) {
127  printField(
128  buffer,
129  indent,
130  camelCaseToSnakeCase(camelCase),
131  GeneratedMessageLite.invokeOrDie(mapMethod, messageLite));
132  continue;
133  }
134  }
135 
136  Method setter = nameToMethod.get("set" + suffix);
137  if (setter == null) {
138  continue;
139  }
140  if (suffix.endsWith(BYTES_SUFFIX)
141  && nameToNoArgMethod.containsKey(
142  "get" + suffix.substring(0, suffix.length() - "Bytes".length()))) {
143  // Heuristic to skip bytes based accessors for string fields.
144  continue;
145  }
146 
147  String camelCase = suffix.substring(0, 1).toLowerCase() + suffix.substring(1);
148 
149  // Try to reflectively get the value and toString() the field as if it were optional. This
150  // only works if the method names have not been proguarded out or renamed.
151  Method getMethod = nameToNoArgMethod.get("get" + suffix);
152  Method hasMethod = nameToNoArgMethod.get("has" + suffix);
153  // TODO(dweis): Fix proto3 semantics.
154  if (getMethod != null) {
155  Object value = GeneratedMessageLite.invokeOrDie(getMethod, messageLite);
156  final boolean hasValue =
157  hasMethod == null
158  ? !isDefaultValue(value)
159  : (Boolean) GeneratedMessageLite.invokeOrDie(hasMethod, messageLite);
160  // TODO(dweis): This doesn't stop printing oneof case twice: value and enum style.
161  if (hasValue) {
162  printField(buffer, indent, camelCaseToSnakeCase(camelCase), value);
163  }
164  continue;
165  }
166  }
167 
168  if (messageLite instanceof GeneratedMessageLite.ExtendableMessage) {
169  Iterator<Map.Entry<GeneratedMessageLite.ExtensionDescriptor, Object>> iter =
170  ((GeneratedMessageLite.ExtendableMessage<?, ?>) messageLite).extensions.iterator();
171  while (iter.hasNext()) {
172  Map.Entry<GeneratedMessageLite.ExtensionDescriptor, Object> entry = iter.next();
173  printField(buffer, indent, "[" + entry.getKey().getNumber() + "]", entry.getValue());
174  }
175  }
176 
177  if (((GeneratedMessageLite<?, ?>) messageLite).unknownFields != null) {
178  ((GeneratedMessageLite<?, ?>) messageLite).unknownFields.printWithIndent(buffer, indent);
179  }
180  }
181 
182  private static boolean isDefaultValue(Object o) {
183  if (o instanceof Boolean) {
184  return !((Boolean) o);
185  }
186  if (o instanceof Integer) {
187  return ((Integer) o) == 0;
188  }
189  if (o instanceof Float) {
190  return ((Float) o) == 0f;
191  }
192  if (o instanceof Double) {
193  return ((Double) o) == 0d;
194  }
195  if (o instanceof String) {
196  return o.equals("");
197  }
198  if (o instanceof ByteString) {
199  return o.equals(ByteString.EMPTY);
200  }
201  if (o instanceof MessageLite) { // Can happen in oneofs.
202  return o == ((MessageLite) o).getDefaultInstanceForType();
203  }
204  if (o instanceof java.lang.Enum<?>) { // Catches oneof enums.
205  return ((java.lang.Enum<?>) o).ordinal() == 0;
206  }
207 
208  return false;
209  }
210 
221  static final void printField(StringBuilder buffer, int indent, String name, Object object) {
222  if (object instanceof List<?>) {
223  List<?> list = (List<?>) object;
224  for (Object entry : list) {
225  printField(buffer, indent, name, entry);
226  }
227  return;
228  }
229  if (object instanceof Map<?, ?>) {
230  Map<?, ?> map = (Map<?, ?>) object;
231  for (Map.Entry<?, ?> entry : map.entrySet()) {
232  printField(buffer, indent, name, entry);
233  }
234  return;
235  }
236 
237  buffer.append('\n');
238  for (int i = 0; i < indent; i++) {
239  buffer.append(' ');
240  }
241  buffer.append(name);
242 
243  if (object instanceof String) {
244  buffer.append(": \"").append(TextFormatEscaper.escapeText((String) object)).append('"');
245  } else if (object instanceof ByteString) {
246  buffer.append(": \"").append(TextFormatEscaper.escapeBytes((ByteString) object)).append('"');
247  } else if (object instanceof GeneratedMessageLite) {
248  buffer.append(" {");
249  reflectivePrintWithIndent((GeneratedMessageLite<?, ?>) object, buffer, indent + 2);
250  buffer.append("\n");
251  for (int i = 0; i < indent; i++) {
252  buffer.append(' ');
253  }
254  buffer.append("}");
255  } else if (object instanceof Map.Entry<?, ?>) {
256  buffer.append(" {");
257  Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
258  printField(buffer, indent + 2, "key", entry.getKey());
259  printField(buffer, indent + 2, "value", entry.getValue());
260  buffer.append("\n");
261  for (int i = 0; i < indent; i++) {
262  buffer.append(' ');
263  }
264  buffer.append("}");
265  } else {
266  buffer.append(": ").append(object.toString());
267  }
268  }
269 
270  private static final String camelCaseToSnakeCase(String camelCase) {
271  StringBuilder builder = new StringBuilder();
272  for (int i = 0; i < camelCase.length(); i++) {
273  char ch = camelCase.charAt(i);
274  if (Character.isUpperCase(ch)) {
275  builder.append("_");
276  }
277  builder.append(Character.toLowerCase(ch));
278  }
279  return builder.toString();
280  }
281 }
java::lang
name
GLuint const GLchar * name
Definition: glcorearb.h:3055
java::lang::reflect::Method
Map
Definition: ruby/ext/google/protobuf_c/protobuf.h:442
indent
static int indent(upb_textprinter *p)
Definition: php/ext/google/protobuf/upb.c:8400
java::lang::reflect
map
zval * map
Definition: php/ext/google/protobuf/encode_decode.c:473
testing::internal::Double
FloatingPoint< double > Double
Definition: gtest-internal.h:429
testing::internal::Float
FloatingPoint< float > Float
Definition: gtest-internal.h:428
buffer
Definition: buffer_processor.h:43
i
int i
Definition: gmock-matchers_test.cc:764
java
ch
char ch
Definition: gmock-matchers_test.cc:3871
f
GLfloat f
Definition: glcorearb.h:3964
value
GLsizei const GLfloat * value
Definition: glcorearb.h:3093
Method
Definition: api.pb.h:285
google::protobuf::method
const Descriptor::ReservedRange const EnumValueDescriptor method
Definition: src/google/protobuf/descriptor.h:1973


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:56