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
31package com.google.protobuf;
32
33import com.google.protobuf.Descriptors.Descriptor;
34import com.google.protobuf.Descriptors.FieldDescriptor;
35
36import java.io.InputStream;
37import java.io.IOException;
38import 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 */
46public 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   */
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   */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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   */
135  public static Builder newBuilder(Message prototype) {
136    return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype);
137  }
138
139  // -----------------------------------------------------------------
140  // Implementation of Message interface.
141
142  public Descriptor getDescriptorForType() {
143    return type;
144  }
145
146  public DynamicMessage getDefaultInstanceForType() {
147    return getDefaultInstance(type);
148  }
149
150  public Map<FieldDescriptor, Object> getAllFields() {
151    return fields.getAllFields();
152  }
153
154  public boolean hasField(FieldDescriptor field) {
155    verifyContainingType(field);
156    return fields.hasField(field);
157  }
158
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
172  public int getRepeatedFieldCount(FieldDescriptor field) {
173    verifyContainingType(field);
174    return fields.getRepeatedFieldCount(field);
175  }
176
177  public Object getRepeatedField(FieldDescriptor field, int index) {
178    verifyContainingType(field);
179    return fields.getRepeatedField(field, index);
180  }
181
182  public UnknownFieldSet getUnknownFields() {
183    return unknownFields;
184  }
185
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
201  public boolean isInitialized() {
202    return isInitialized(type, fields);
203  }
204
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
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
231  public Builder newBuilderForType() {
232    return new Builder(type);
233  }
234
235  public Builder toBuilder() {
236    return newBuilderForType().mergeFrom(this);
237  }
238
239  /** Verifies that the field is a field of this message. */
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. */
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
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
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
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     */
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
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
327    public Builder clone() {
328      Builder result = new Builder(type);
329      result.fields.mergeFrom(fields);
330      return result;
331    }
332
333    public boolean isInitialized() {
334      return DynamicMessage.isInitialized(type, fields);
335    }
336
337    public Descriptor getDescriptorForType() {
338      return type;
339    }
340
341    public DynamicMessage getDefaultInstanceForType() {
342      return getDefaultInstance(type);
343    }
344
345    public Map<FieldDescriptor, Object> getAllFields() {
346      return fields.getAllFields();
347    }
348
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
360    public boolean hasField(FieldDescriptor field) {
361      verifyContainingType(field);
362      return fields.hasField(field);
363    }
364
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
378    public Builder setField(FieldDescriptor field, Object value) {
379      verifyContainingType(field);
380      fields.setField(field, value);
381      return this;
382    }
383
384    public Builder clearField(FieldDescriptor field) {
385      verifyContainingType(field);
386      fields.clearField(field);
387      return this;
388    }
389
390    public int getRepeatedFieldCount(FieldDescriptor field) {
391      verifyContainingType(field);
392      return fields.getRepeatedFieldCount(field);
393    }
394
395    public Object getRepeatedField(FieldDescriptor field, int index) {
396      verifyContainingType(field);
397      return fields.getRepeatedField(field, index);
398    }
399
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
407    public Builder addRepeatedField(FieldDescriptor field, Object value) {
408      verifyContainingType(field);
409      fields.addRepeatedField(field, value);
410      return this;
411    }
412
413    public UnknownFieldSet getUnknownFields() {
414      return unknownFields;
415    }
416
417    public Builder setUnknownFields(UnknownFieldSet unknownFields) {
418      this.unknownFields = unknownFields;
419      return this;
420    }
421
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. */
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