blob: d0a57906d452d460682a2452d7b6b08497b7a824 [file] [log] [blame]
Jon Skeet60c059b2008-10-23 21:17:56 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// http://github.com/jskeet/dotnet-protobufs/
4// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01005// http://code.google.com/p/protobuf/
6//
Jon Skeet60c059b2008-10-23 21:17:56 +01007// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
Jon Skeet68036862008-10-22 13:30:34 +010010//
Jon Skeet60c059b2008-10-23 21:17:56 +010011// * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// * Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17// * Neither the name of Google Inc. nor the names of its
18// contributors may be used to endorse or promote products derived from
19// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010020//
Jon Skeet60c059b2008-10-23 21:17:56 +010021// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet68036862008-10-22 13:30:34 +010032using System;
33using System.Collections;
34using System.Collections.Generic;
35using System.Text;
36using Google.ProtocolBuffers.Collections;
37using Google.ProtocolBuffers.Descriptors;
38
39namespace Google.ProtocolBuffers {
40 /// <summary>
41 /// TODO(jonskeet): Write summary text.
42 /// </summary>
43 public sealed class UninitializedMessageException : Exception {
44
45 private readonly IList<string> missingFields;
46
47 public UninitializedMessageException(IMessage message)
48 : this(FindMissingFields(message)) {
49 }
50
51 private UninitializedMessageException(IList<string> missingFields)
52 : base(BuildDescription(missingFields)) {
53 this.missingFields = Lists.AsReadOnly(missingFields);
54 }
55
56
57 /// <summary>
58 /// Converts this exception into an InvalidProtocolBufferException.
59 /// When a parsed message is missing required fields, this should be thrown
60 /// instead of UninitializedMessageException.
61 /// </summary>
62 public InvalidProtocolBufferException AsInvalidProtocolBufferException() {
63 return new InvalidProtocolBufferException(Message);
64 }
65
66 /// <summary>
67 /// Constructs the description string for a given list of missing fields.
68 /// </summary>
69 private static string BuildDescription(IEnumerable<string> missingFields) {
70 StringBuilder description = new StringBuilder("Message missing required fields: ");
71 bool first = true;
72 foreach(string field in missingFields) {
73 if (first) {
74 first = false;
75 } else {
76 description.Append(", ");
77 }
78 description.Append(field);
79 }
80 return description.ToString();
81 }
82
83 /// <summary>
84 /// Returns a list of the full "paths" of missing required
85 /// fields in the specified message.
86 /// </summary>
87 private static IList<String> FindMissingFields(IMessage message) {
88 List<String> results = new List<String>();
89 FindMissingFields(message, "", results);
90 return results;
91 }
92
93 /// <summary>
94 /// Recursive helper implementing FindMissingFields.
95 /// </summary>
96 private static void FindMissingFields(IMessage message, String prefix, List<String> results) {
97 foreach (FieldDescriptor field in message.DescriptorForType.Fields) {
98 if (field.IsRequired && !message.HasField(field)) {
99 results.Add(prefix + field.Name);
100 }
101 }
102
103 foreach (KeyValuePair<FieldDescriptor, object> entry in message.AllFields) {
104 FieldDescriptor field = entry.Key;
105 object value = entry.Value;
106
107 if (field.MappedType == MappedType.Message) {
108 if (field.IsRepeated) {
109 int i = 0;
110 foreach (object element in (IEnumerable) value) {
111 FindMissingFields((IMessage) element, SubMessagePrefix(prefix, field, i++), results);
112 }
113 } else {
114 if (message.HasField(field)) {
115 FindMissingFields((IMessage) value, SubMessagePrefix(prefix, field, -1), results);
116 }
117 }
118 }
119 }
120 }
121
122 private static String SubMessagePrefix(String prefix, FieldDescriptor field, int index) {
123 StringBuilder result = new StringBuilder(prefix);
124 if (field.IsExtension) {
125 result.Append('(')
126 .Append(field.FullName)
127 .Append(')');
128 } else {
129 result.Append(field.Name);
130 }
131 if (index != -1) {
132 result.Append('[')
133 .Append(index)
134 .Append(']');
135 }
136 result.Append('.');
137 return result.ToString();
138 }
139 }
140}
141
142
143