#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://github.com/jskeet/dotnet-protobufs/
// Original C++/Java/Python code:
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Collections;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers {
///
/// TODO(jonskeet): Write summary text.
///
public sealed class UninitializedMessageException : Exception {
private readonly IList missingFields;
public UninitializedMessageException(IMessage message)
: this(FindMissingFields(message)) {
}
private UninitializedMessageException(IList missingFields)
: base(BuildDescription(missingFields)) {
this.missingFields = Lists.AsReadOnly(missingFields);
}
///
/// Returns a read-only list of human-readable names of
/// required fields missing from this message. Each name
/// is a full path to a field, e.g. "foo.bar[5].baz"
///
public IList MissingFields {
get { return missingFields; }
}
///
/// Converts this exception into an InvalidProtocolBufferException.
/// When a parsed message is missing required fields, this should be thrown
/// instead of UninitializedMessageException.
///
public InvalidProtocolBufferException AsInvalidProtocolBufferException() {
return new InvalidProtocolBufferException(Message);
}
///
/// Constructs the description string for a given list of missing fields.
///
private static string BuildDescription(IEnumerable missingFields) {
StringBuilder description = new StringBuilder("Message missing required fields: ");
bool first = true;
foreach(string field in missingFields) {
if (first) {
first = false;
} else {
description.Append(", ");
}
description.Append(field);
}
return description.ToString();
}
///
/// Returns a list of the full "paths" of missing required
/// fields in the specified message.
///
private static IList FindMissingFields(IMessage message) {
List results = new List();
FindMissingFields(message, "", results);
return results;
}
///
/// Recursive helper implementing FindMissingFields.
///
private static void FindMissingFields(IMessage message, String prefix, List results) {
foreach (FieldDescriptor field in message.DescriptorForType.Fields) {
if (field.IsRequired && !message.HasField(field)) {
results.Add(prefix + field.Name);
}
}
foreach (KeyValuePair entry in message.AllFields) {
FieldDescriptor field = entry.Key;
object value = entry.Value;
if (field.MappedType == MappedType.Message) {
if (field.IsRepeated) {
int i = 0;
foreach (object element in (IEnumerable) value) {
FindMissingFields((IMessage) element, SubMessagePrefix(prefix, field, i++), results);
}
} else {
if (message.HasField(field)) {
FindMissingFields((IMessage) value, SubMessagePrefix(prefix, field, -1), results);
}
}
}
}
}
private static String SubMessagePrefix(String prefix, FieldDescriptor field, int index) {
StringBuilder result = new StringBuilder(prefix);
if (field.IsExtension) {
result.Append('(')
.Append(field.FullName)
.Append(')');
} else {
result.Append(field.Name);
}
if (index != -1) {
result.Append('[')
.Append(index)
.Append(']');
}
result.Append('.');
return result.ToString();
}
}
}