aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs
blob: fca689dc08d54f9bae448b6b58636627f49d1956 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 Google.Protobuf.Reflection;

namespace Google.Protobuf.WellKnownTypes
{
    public partial class Any
    {
        private const string DefaultPrefix = "type.googleapis.com";

        // This could be moved to MessageDescriptor if we wanted to, but keeping it here means
        // all the Any-specific code is in the same place.
        private static string GetTypeUrl(MessageDescriptor descriptor, string prefix) =>
            prefix.EndsWith("/") ? prefix + descriptor.FullName : prefix + "/" + descriptor.FullName;

        /// <summary>
        /// Retrieves the type name for a type URL, matching the <see cref="DescriptorBase.FullName"/>
        /// of the packed message type.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is always just the last part of the URL, after the final slash. No validation of 
        /// anything before the trailing slash is performed. If the type URL does not include a slash,
        /// an empty string is returned rather than an exception being thrown; this won't match any types,
        /// and the calling code is probably in a better position to give a meaningful error.
        /// </para>
        /// <para>
        /// There is no handling of fragments or queries  at the moment.
        /// </para>
        /// </remarks>
        /// <param name="typeUrl">The URL to extract the type name from</param>
        /// <returns>The type name</returns>
        public static string GetTypeName(string typeUrl)
        {
            ProtoPreconditions.CheckNotNull(typeUrl, nameof(typeUrl));
            int lastSlash = typeUrl.LastIndexOf('/');
            return lastSlash == -1 ? "" : typeUrl.Substring(lastSlash + 1);
        }

        /// <summary>
        /// Unpacks the content of this Any message into the target message type,
        /// which must match the type URL within this Any message.
        /// </summary>
        /// <typeparam name="T">The type of message to unpack the content into.</typeparam>
        /// <returns>The unpacked message.</returns>
        /// <exception cref="InvalidProtocolBufferException">The target message type doesn't match the type URL in this message</exception>
        public T Unpack<T>() where T : IMessage, new()
        {
            // Note: this doesn't perform as well is it might. We could take a MessageParser<T> in an alternative overload,
            // which would be expected to perform slightly better... although the difference is likely to be negligible.
            T target = new T();
            if (GetTypeName(TypeUrl) != target.Descriptor.FullName)
            {
                throw new InvalidProtocolBufferException(
                    $"Full type name for {target.Descriptor.Name} is {target.Descriptor.FullName}; Any message's type url is {TypeUrl}");
            }
            target.MergeFrom(Value);
            return target;
        }

        /// <summary>
        /// Attempts to unpack the content of this Any message into the target message type,
        /// if it matches the type URL within this Any message.
        /// </summary>
        /// <typeparam name="T">The type of message to attempt to unpack the content into.</typeparam>
        /// <returns><c>true</c> if the message was successfully unpacked; <c>false</c> if the type name didn't match</returns>
        public bool TryUnpack<T>(out T result) where T : IMessage, new()
        {
            // Note: deliberately avoid writing anything to result until the end, in case it's being
            // monitored by other threads. (That would be a bug in the calling code, but let's not make it worse.)
            T target = new T();
            if (GetTypeName(TypeUrl) != target.Descriptor.FullName)
            {
                result = default(T); // Can't use null as there's no class constraint, but this always *will* be null in real usage.
                return false;
            }
            target.MergeFrom(Value);
            result = target;
            return true;
        }

        /// <summary>
        /// Packs the specified message into an Any message using a type URL prefix of "type.googleapis.com".
        /// </summary>
        /// <param name="message">The message to pack.</param>
        /// <returns>An Any message with the content and type URL of <paramref name="message"/>.</returns>
        public static Any Pack(IMessage message) => Pack(message, DefaultPrefix);

        /// <summary>
        /// Packs the specified message into an Any message using the specified type URL prefix.
        /// </summary>
        /// <param name="message">The message to pack.</param>
        /// <param name="typeUrlPrefix">The prefix for the type URL.</param>
        /// <returns>An Any message with the content and type URL of <paramref name="message"/>.</returns>
        public static Any Pack(IMessage message, string typeUrlPrefix)
        {
            ProtoPreconditions.CheckNotNull(message, nameof(message));
            ProtoPreconditions.CheckNotNull(typeUrlPrefix, nameof(typeUrlPrefix));
            return new Any
            {
                TypeUrl = GetTypeUrl(message.Descriptor, typeUrlPrefix),
                Value = message.ToByteString()
            };
        }
    }
}