blob: e35c945b13d38f41880c380f44948165c2035c50 [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
Jon Skeetcb8644d2009-06-17 16:09:22 +010056
57 /// <summary>
58 /// Returns a read-only list of human-readable names of
59 /// required fields missing from this message. Each name
60 /// is a full path to a field, e.g. "foo.bar[5].baz"
61 /// </summary>
62 public IList<string> MissingFields {
63 get { return missingFields; }
64 }
Jon Skeet68036862008-10-22 13:30:34 +010065
66 /// <summary>
67 /// Converts this exception into an InvalidProtocolBufferException.
68 /// When a parsed message is missing required fields, this should be thrown
69 /// instead of UninitializedMessageException.
70 /// </summary>
71 public InvalidProtocolBufferException AsInvalidProtocolBufferException() {
72 return new InvalidProtocolBufferException(Message);
73 }
74
75 /// <summary>
76 /// Constructs the description string for a given list of missing fields.
77 /// </summary>
78 private static string BuildDescription(IEnumerable<string> missingFields) {
79 StringBuilder description = new StringBuilder("Message missing required fields: ");
80 bool first = true;
81 foreach(string field in missingFields) {
82 if (first) {
83 first = false;
84 } else {
85 description.Append(", ");
86 }
87 description.Append(field);
88 }
89 return description.ToString();
90 }
91
92 /// <summary>
93 /// Returns a list of the full "paths" of missing required
94 /// fields in the specified message.
95 /// </summary>
96 private static IList<String> FindMissingFields(IMessage message) {
97 List<String> results = new List<String>();
98 FindMissingFields(message, "", results);
99 return results;
100 }
101
102 /// <summary>
103 /// Recursive helper implementing FindMissingFields.
104 /// </summary>
105 private static void FindMissingFields(IMessage message, String prefix, List<String> results) {
106 foreach (FieldDescriptor field in message.DescriptorForType.Fields) {
107 if (field.IsRequired && !message.HasField(field)) {
108 results.Add(prefix + field.Name);
109 }
110 }
111
112 foreach (KeyValuePair<FieldDescriptor, object> entry in message.AllFields) {
113 FieldDescriptor field = entry.Key;
114 object value = entry.Value;
115
116 if (field.MappedType == MappedType.Message) {
117 if (field.IsRepeated) {
118 int i = 0;
119 foreach (object element in (IEnumerable) value) {
120 FindMissingFields((IMessage) element, SubMessagePrefix(prefix, field, i++), results);
121 }
122 } else {
123 if (message.HasField(field)) {
124 FindMissingFields((IMessage) value, SubMessagePrefix(prefix, field, -1), results);
125 }
126 }
127 }
128 }
129 }
130
131 private static String SubMessagePrefix(String prefix, FieldDescriptor field, int index) {
132 StringBuilder result = new StringBuilder(prefix);
133 if (field.IsExtension) {
134 result.Append('(')
135 .Append(field.FullName)
136 .Append(')');
137 } else {
138 result.Append(field.Name);
139 }
140 if (index != -1) {
141 result.Append('[')
142 .Append(index)
143 .Append(']');
144 }
145 result.Append('.');
146 return result.ToString();
147 }
148 }
149}