#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;
#if !LITE
using Google.ProtocolBuffers.Collections;
using Google.ProtocolBuffers.Descriptors;
#endif
namespace Google.ProtocolBuffers
{
///
/// TODO(jonskeet): Write summary text.
///
public sealed class UninitializedMessageException : Exception
{
private readonly IList missingFields;
private UninitializedMessageException(IList missingFields)
: base(BuildDescription(missingFields))
{
this.missingFields = new List(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();
}
///
/// For Lite exceptions that do not known how to enumerate missing fields
///
public UninitializedMessageException(IMessageLite message)
: base(String.Format("Message {0} is missing required fields", message.GetType()))
{
missingFields = new List();
}
#if !LITE
public UninitializedMessageException(IMessage message)
: this(FindMissingFields(message))
{
}
///
/// 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)
{
if (element is IMessage)
{
FindMissingFields((IMessage) element, SubMessagePrefix(prefix, field, i++), results);
}
else
{
results.Add(prefix + field.Name);
}
}
}
else
{
if (message.HasField(field))
{
if (value is IMessage)
{
FindMissingFields((IMessage) value, SubMessagePrefix(prefix, field, -1), results);
}
else
{
results.Add(prefix + field.Name);
}
}
}
}
}
}
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();
}
#endif
}
}