1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
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 com.google.protobuf.Descriptors.Descriptor;
34 import com.google.protobuf.Descriptors.FieldDescriptor;
35 
36 import java.io.InputStream;
37 import java.io.IOException;
38 import java.util.Map;
39 
40 /**
41  * An implementation of {@link Message} that can represent arbitrary types,
42  * given a {@link Descriptors.Descriptor}.
43  *
44  * @author kenton@google.com Kenton Varda
45  */
46 public final class DynamicMessage extends AbstractMessage {
47   private final Descriptor type;
48   private final FieldSet<FieldDescriptor> fields;
49   private final UnknownFieldSet unknownFields;
50   private int memoizedSize = -1;
51 
52   /**
53    * Construct a {@code DynamicMessage} using the given {@code FieldSet}.
54    */
DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields, UnknownFieldSet unknownFields)55   private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
56                          UnknownFieldSet unknownFields) {
57     this.type = type;
58     this.fields = fields;
59     this.unknownFields = unknownFields;
60   }
61 
62   /**
63    * Get a {@code DynamicMessage} representing the default instance of the
64    * given type.
65    */
getDefaultInstance(Descriptor type)66   public static DynamicMessage getDefaultInstance(Descriptor type) {
67     return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(),
68                               UnknownFieldSet.getDefaultInstance());
69   }
70 
71   /** Parse a message of the given type from the given input stream. */
parseFrom(Descriptor type, CodedInputStream input)72   public static DynamicMessage parseFrom(Descriptor type,
73                                          CodedInputStream input)
74                                          throws IOException {
75     return newBuilder(type).mergeFrom(input).buildParsed();
76   }
77 
78   /** Parse a message of the given type from the given input stream. */
parseFrom( Descriptor type, CodedInputStream input, ExtensionRegistry extensionRegistry)79   public static DynamicMessage parseFrom(
80       Descriptor type,
81       CodedInputStream input,
82       ExtensionRegistry extensionRegistry)
83       throws IOException {
84     return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
85   }
86 
87   /** Parse {@code data} as a message of the given type and return it. */
parseFrom(Descriptor type, ByteString data)88   public static DynamicMessage parseFrom(Descriptor type, ByteString data)
89                                          throws InvalidProtocolBufferException {
90     return newBuilder(type).mergeFrom(data).buildParsed();
91   }
92 
93   /** Parse {@code data} as a message of the given type and return it. */
parseFrom(Descriptor type, ByteString data, ExtensionRegistry extensionRegistry)94   public static DynamicMessage parseFrom(Descriptor type, ByteString data,
95                                          ExtensionRegistry extensionRegistry)
96                                          throws InvalidProtocolBufferException {
97     return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
98   }
99 
100   /** Parse {@code data} as a message of the given type and return it. */
parseFrom(Descriptor type, byte[] data)101   public static DynamicMessage parseFrom(Descriptor type, byte[] data)
102                                          throws InvalidProtocolBufferException {
103     return newBuilder(type).mergeFrom(data).buildParsed();
104   }
105 
106   /** Parse {@code data} as a message of the given type and return it. */
parseFrom(Descriptor type, byte[] data, ExtensionRegistry extensionRegistry)107   public static DynamicMessage parseFrom(Descriptor type, byte[] data,
108                                          ExtensionRegistry extensionRegistry)
109                                          throws InvalidProtocolBufferException {
110     return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
111   }
112 
113   /** Parse a message of the given type from {@code input} and return it. */
parseFrom(Descriptor type, InputStream input)114   public static DynamicMessage parseFrom(Descriptor type, InputStream input)
115                                          throws IOException {
116     return newBuilder(type).mergeFrom(input).buildParsed();
117   }
118 
119   /** Parse a message of the given type from {@code input} and return it. */
parseFrom(Descriptor type, InputStream input, ExtensionRegistry extensionRegistry)120   public static DynamicMessage parseFrom(Descriptor type, InputStream input,
121                                          ExtensionRegistry extensionRegistry)
122                                          throws IOException {
123     return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
124   }
125 
126   /** Construct a {@link Message.Builder} for the given type. */
newBuilder(Descriptor type)127   public static Builder newBuilder(Descriptor type) {
128     return new Builder(type);
129   }
130 
131   /**
132    * Construct a {@link Message.Builder} for a message of the same type as
133    * {@code prototype}, and initialize it with {@code prototype}'s contents.
134    */
newBuilder(Message prototype)135   public static Builder newBuilder(Message prototype) {
136     return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype);
137   }
138 
139   // -----------------------------------------------------------------
140   // Implementation of Message interface.
141 
getDescriptorForType()142   public Descriptor getDescriptorForType() {
143     return type;
144   }
145 
getDefaultInstanceForType()146   public DynamicMessage getDefaultInstanceForType() {
147     return getDefaultInstance(type);
148   }
149 
getAllFields()150   public Map<FieldDescriptor, Object> getAllFields() {
151     return fields.getAllFields();
152   }
153 
hasField(FieldDescriptor field)154   public boolean hasField(FieldDescriptor field) {
155     verifyContainingType(field);
156     return fields.hasField(field);
157   }
158 
getField(FieldDescriptor field)159   public Object getField(FieldDescriptor field) {
160     verifyContainingType(field);
161     Object result = fields.getField(field);
162     if (result == null) {
163       if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
164         result = getDefaultInstance(field.getMessageType());
165       } else {
166         result = field.getDefaultValue();
167       }
168     }
169     return result;
170   }
171 
getRepeatedFieldCount(FieldDescriptor field)172   public int getRepeatedFieldCount(FieldDescriptor field) {
173     verifyContainingType(field);
174     return fields.getRepeatedFieldCount(field);
175   }
176 
getRepeatedField(FieldDescriptor field, int index)177   public Object getRepeatedField(FieldDescriptor field, int index) {
178     verifyContainingType(field);
179     return fields.getRepeatedField(field, index);
180   }
181 
getUnknownFields()182   public UnknownFieldSet getUnknownFields() {
183     return unknownFields;
184   }
185 
isInitialized(Descriptor type, FieldSet<FieldDescriptor> fields)186   private static boolean isInitialized(Descriptor type,
187                                        FieldSet<FieldDescriptor> fields) {
188     // Check that all required fields are present.
189     for (final FieldDescriptor field : type.getFields()) {
190       if (field.isRequired()) {
191         if (!fields.hasField(field)) {
192           return false;
193         }
194       }
195     }
196 
197     // Check that embedded messages are initialized.
198     return fields.isInitialized();
199   }
200 
isInitialized()201   public boolean isInitialized() {
202     return isInitialized(type, fields);
203   }
204 
writeTo(CodedOutputStream output)205   public void writeTo(CodedOutputStream output) throws IOException {
206     if (type.getOptions().getMessageSetWireFormat()) {
207       fields.writeMessageSetTo(output);
208       unknownFields.writeAsMessageSetTo(output);
209     } else {
210       fields.writeTo(output);
211       unknownFields.writeTo(output);
212     }
213   }
214 
getSerializedSize()215   public int getSerializedSize() {
216     int size = memoizedSize;
217     if (size != -1) return size;
218 
219     if (type.getOptions().getMessageSetWireFormat()) {
220       size = fields.getMessageSetSerializedSize();
221       size += unknownFields.getSerializedSizeAsMessageSet();
222     } else {
223       size = fields.getSerializedSize();
224       size += unknownFields.getSerializedSize();
225     }
226 
227     memoizedSize = size;
228     return size;
229   }
230 
newBuilderForType()231   public Builder newBuilderForType() {
232     return new Builder(type);
233   }
234 
toBuilder()235   public Builder toBuilder() {
236     return newBuilderForType().mergeFrom(this);
237   }
238 
239   /** Verifies that the field is a field of this message. */
verifyContainingType(FieldDescriptor field)240   private void verifyContainingType(FieldDescriptor field) {
241     if (field.getContainingType() != type) {
242       throw new IllegalArgumentException(
243         "FieldDescriptor does not match message type.");
244     }
245   }
246 
247   // =================================================================
248 
249   /**
250    * Builder for {@link DynamicMessage}s.
251    */
252   public static final class Builder extends AbstractMessage.Builder<Builder> {
253     private final Descriptor type;
254     private FieldSet<FieldDescriptor> fields;
255     private UnknownFieldSet unknownFields;
256 
257     /** Construct a {@code Builder} for the given type. */
Builder(Descriptor type)258     private Builder(Descriptor type) {
259       this.type = type;
260       this.fields = FieldSet.newFieldSet();
261       this.unknownFields = UnknownFieldSet.getDefaultInstance();
262     }
263 
264     // ---------------------------------------------------------------
265     // Implementation of Message.Builder interface.
266 
clear()267     public Builder clear() {
268       if (fields == null) {
269         throw new IllegalStateException("Cannot call clear() after build().");
270       }
271       fields.clear();
272       return this;
273     }
274 
mergeFrom(Message other)275     public Builder mergeFrom(Message other) {
276       if (other instanceof DynamicMessage) {
277         // This should be somewhat faster than calling super.mergeFrom().
278         DynamicMessage otherDynamicMessage = (DynamicMessage) other;
279         if (otherDynamicMessage.type != type) {
280           throw new IllegalArgumentException(
281             "mergeFrom(Message) can only merge messages of the same type.");
282         }
283         fields.mergeFrom(otherDynamicMessage.fields);
284         mergeUnknownFields(otherDynamicMessage.unknownFields);
285         return this;
286       } else {
287         return super.mergeFrom(other);
288       }
289     }
290 
build()291     public DynamicMessage build() {
292       // If fields == null, we'll throw an appropriate exception later.
293       if (fields != null && !isInitialized()) {
294         throw newUninitializedMessageException(
295           new DynamicMessage(type, fields, unknownFields));
296       }
297       return buildPartial();
298     }
299 
300     /**
301      * Helper for DynamicMessage.parseFrom() methods to call.  Throws
302      * {@link InvalidProtocolBufferException} instead of
303      * {@link UninitializedMessageException}.
304      */
buildParsed()305     private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
306       if (!isInitialized()) {
307         throw newUninitializedMessageException(
308             new DynamicMessage(type, fields, unknownFields))
309           .asInvalidProtocolBufferException();
310       }
311       return buildPartial();
312     }
313 
buildPartial()314     public DynamicMessage buildPartial() {
315       if (fields == null) {
316         throw new IllegalStateException(
317             "build() has already been called on this Builder.");
318       }
319       fields.makeImmutable();
320       DynamicMessage result =
321         new DynamicMessage(type, fields, unknownFields);
322       fields = null;
323       unknownFields = null;
324       return result;
325     }
326 
clone()327     public Builder clone() {
328       Builder result = new Builder(type);
329       result.fields.mergeFrom(fields);
330       return result;
331     }
332 
isInitialized()333     public boolean isInitialized() {
334       return DynamicMessage.isInitialized(type, fields);
335     }
336 
getDescriptorForType()337     public Descriptor getDescriptorForType() {
338       return type;
339     }
340 
getDefaultInstanceForType()341     public DynamicMessage getDefaultInstanceForType() {
342       return getDefaultInstance(type);
343     }
344 
getAllFields()345     public Map<FieldDescriptor, Object> getAllFields() {
346       return fields.getAllFields();
347     }
348 
newBuilderForField(FieldDescriptor field)349     public Builder newBuilderForField(FieldDescriptor field) {
350       verifyContainingType(field);
351 
352       if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
353         throw new IllegalArgumentException(
354           "newBuilderForField is only valid for fields with message type.");
355       }
356 
357       return new Builder(field.getMessageType());
358     }
359 
hasField(FieldDescriptor field)360     public boolean hasField(FieldDescriptor field) {
361       verifyContainingType(field);
362       return fields.hasField(field);
363     }
364 
getField(FieldDescriptor field)365     public Object getField(FieldDescriptor field) {
366       verifyContainingType(field);
367       Object result = fields.getField(field);
368       if (result == null) {
369         if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
370           result = getDefaultInstance(field.getMessageType());
371         } else {
372           result = field.getDefaultValue();
373         }
374       }
375       return result;
376     }
377 
setField(FieldDescriptor field, Object value)378     public Builder setField(FieldDescriptor field, Object value) {
379       verifyContainingType(field);
380       fields.setField(field, value);
381       return this;
382     }
383 
clearField(FieldDescriptor field)384     public Builder clearField(FieldDescriptor field) {
385       verifyContainingType(field);
386       fields.clearField(field);
387       return this;
388     }
389 
getRepeatedFieldCount(FieldDescriptor field)390     public int getRepeatedFieldCount(FieldDescriptor field) {
391       verifyContainingType(field);
392       return fields.getRepeatedFieldCount(field);
393     }
394 
getRepeatedField(FieldDescriptor field, int index)395     public Object getRepeatedField(FieldDescriptor field, int index) {
396       verifyContainingType(field);
397       return fields.getRepeatedField(field, index);
398     }
399 
setRepeatedField(FieldDescriptor field, int index, Object value)400     public Builder setRepeatedField(FieldDescriptor field,
401                                     int index, Object value) {
402       verifyContainingType(field);
403       fields.setRepeatedField(field, index, value);
404       return this;
405     }
406 
addRepeatedField(FieldDescriptor field, Object value)407     public Builder addRepeatedField(FieldDescriptor field, Object value) {
408       verifyContainingType(field);
409       fields.addRepeatedField(field, value);
410       return this;
411     }
412 
getUnknownFields()413     public UnknownFieldSet getUnknownFields() {
414       return unknownFields;
415     }
416 
setUnknownFields(UnknownFieldSet unknownFields)417     public Builder setUnknownFields(UnknownFieldSet unknownFields) {
418       this.unknownFields = unknownFields;
419       return this;
420     }
421 
mergeUnknownFields(UnknownFieldSet unknownFields)422     public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
423       this.unknownFields =
424         UnknownFieldSet.newBuilder(this.unknownFields)
425                        .mergeFrom(unknownFields)
426                        .build();
427       return this;
428     }
429 
430     /** Verifies that the field is a field of this message. */
verifyContainingType(FieldDescriptor field)431     private void verifyContainingType(FieldDescriptor field) {
432       if (field.getContainingType() != type) {
433         throw new IllegalArgumentException(
434           "FieldDescriptor does not match message type.");
435       }
436     }
437   }
438 }
439