1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import static com.google.protobuf.Internal.checkNotNull;
34 
35 /**
36  * Dynamically generates a manifest-based (i.e. table-based) schema for a given protobuf message.
37  */
38 @ExperimentalApi
39 final class ManifestSchemaFactory implements SchemaFactory {
40 
41   private final MessageInfoFactory messageInfoFactory;
42 
ManifestSchemaFactory()43   public ManifestSchemaFactory() {
44     this(getDefaultMessageInfoFactory());
45   }
46 
ManifestSchemaFactory(MessageInfoFactory messageInfoFactory)47   private ManifestSchemaFactory(MessageInfoFactory messageInfoFactory) {
48     this.messageInfoFactory = checkNotNull(messageInfoFactory, "messageInfoFactory");
49   }
50 
51   @Override
createSchema(Class<T> messageType)52   public <T> Schema<T> createSchema(Class<T> messageType) {
53     SchemaUtil.requireGeneratedMessage(messageType);
54 
55     MessageInfo messageInfo = messageInfoFactory.messageInfoFor(messageType);
56 
57     // MessageSet has a special schema.
58     if (messageInfo.isMessageSetWireFormat()) {
59       if (GeneratedMessageLite.class.isAssignableFrom(messageType)) {
60         return MessageSetSchema.newSchema(
61             SchemaUtil.unknownFieldSetLiteSchema(),
62             ExtensionSchemas.lite(),
63             messageInfo.getDefaultInstance());
64       }
65       return MessageSetSchema.newSchema(
66           SchemaUtil.proto2UnknownFieldSetSchema(),
67           ExtensionSchemas.full(),
68           messageInfo.getDefaultInstance());
69     }
70 
71     return newSchema(messageType, messageInfo);
72   }
73 
newSchema(Class<T> messageType, MessageInfo messageInfo)74   private static <T> Schema<T> newSchema(Class<T> messageType, MessageInfo messageInfo) {
75     if (GeneratedMessageLite.class.isAssignableFrom(messageType)) {
76       return isProto2(messageInfo)
77           ? MessageSchema.newSchema(
78               messageType,
79               messageInfo,
80               NewInstanceSchemas.lite(),
81               ListFieldSchema.lite(),
82               SchemaUtil.unknownFieldSetLiteSchema(),
83               ExtensionSchemas.lite(),
84               MapFieldSchemas.lite())
85           : MessageSchema.newSchema(
86               messageType,
87               messageInfo,
88               NewInstanceSchemas.lite(),
89               ListFieldSchema.lite(),
90               SchemaUtil.unknownFieldSetLiteSchema(),
91               /* extensionSchema= */ null,
92               MapFieldSchemas.lite());
93     }
94     return isProto2(messageInfo)
95         ? MessageSchema.newSchema(
96             messageType,
97             messageInfo,
98             NewInstanceSchemas.full(),
99             ListFieldSchema.full(),
100             SchemaUtil.proto2UnknownFieldSetSchema(),
101             ExtensionSchemas.full(),
102             MapFieldSchemas.full())
103         : MessageSchema.newSchema(
104             messageType,
105             messageInfo,
106             NewInstanceSchemas.full(),
107             ListFieldSchema.full(),
108             SchemaUtil.proto3UnknownFieldSetSchema(),
109             /* extensionSchema= */ null,
110             MapFieldSchemas.full());
111   }
112 
isProto2(MessageInfo messageInfo)113   private static boolean isProto2(MessageInfo messageInfo) {
114     return messageInfo.getSyntax() == ProtoSyntax.PROTO2;
115   }
116 
getDefaultMessageInfoFactory()117   private static MessageInfoFactory getDefaultMessageInfoFactory() {
118     return new CompositeMessageInfoFactory(
119         GeneratedMessageInfoFactory.getInstance(), getDescriptorMessageInfoFactory());
120   }
121 
122   private static class CompositeMessageInfoFactory implements MessageInfoFactory {
123     private MessageInfoFactory[] factories;
124 
CompositeMessageInfoFactory(MessageInfoFactory... factories)125     CompositeMessageInfoFactory(MessageInfoFactory... factories) {
126       this.factories = factories;
127     }
128 
129     @Override
isSupported(Class<?> clazz)130     public boolean isSupported(Class<?> clazz) {
131       for (MessageInfoFactory factory : factories) {
132         if (factory.isSupported(clazz)) {
133           return true;
134         }
135       }
136       return false;
137     }
138 
139     @Override
messageInfoFor(Class<?> clazz)140     public MessageInfo messageInfoFor(Class<?> clazz) {
141       for (MessageInfoFactory factory : factories) {
142         if (factory.isSupported(clazz)) {
143           return factory.messageInfoFor(clazz);
144         }
145       }
146       throw new UnsupportedOperationException(
147           "No factory is available for message type: " + clazz.getName());
148     }
149   }
150 
151   private static final MessageInfoFactory EMPTY_FACTORY =
152       new MessageInfoFactory() {
153         @Override
154         public boolean isSupported(Class<?> clazz) {
155           return false;
156         }
157 
158         @Override
159         public MessageInfo messageInfoFor(Class<?> clazz) {
160           throw new IllegalStateException("This should never be called.");
161         }
162       };
163 
getDescriptorMessageInfoFactory()164   private static MessageInfoFactory getDescriptorMessageInfoFactory() {
165     try {
166       Class<?> clazz = Class.forName("com.google.protobuf.DescriptorMessageInfoFactory");
167       return (MessageInfoFactory) clazz.getDeclaredMethod("getInstance").invoke(null);
168     } catch (Exception e) {
169       return EMPTY_FACTORY;
170     }
171   }
172 }
173