1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import static com.google.protobuf.Internal.checkNotNull; 11 12 /** 13 * Dynamically generates a manifest-based (i.e. table-based) schema for a given protobuf message. 14 */ 15 @CheckReturnValue 16 @ExperimentalApi 17 final class ManifestSchemaFactory implements SchemaFactory { 18 19 private final MessageInfoFactory messageInfoFactory; 20 ManifestSchemaFactory()21 public ManifestSchemaFactory() { 22 this(getDefaultMessageInfoFactory()); 23 } 24 ManifestSchemaFactory(MessageInfoFactory messageInfoFactory)25 private ManifestSchemaFactory(MessageInfoFactory messageInfoFactory) { 26 this.messageInfoFactory = checkNotNull(messageInfoFactory, "messageInfoFactory"); 27 } 28 29 @Override createSchema(Class<T> messageType)30 public <T> Schema<T> createSchema(Class<T> messageType) { 31 SchemaUtil.requireGeneratedMessage(messageType); 32 33 MessageInfo messageInfo = messageInfoFactory.messageInfoFor(messageType); 34 35 // MessageSet has a special schema. 36 if (messageInfo.isMessageSetWireFormat()) { 37 return useLiteRuntime(messageType) 38 ? MessageSetSchema.newSchema( 39 SchemaUtil.unknownFieldSetLiteSchema(), 40 ExtensionSchemas.lite(), 41 messageInfo.getDefaultInstance()) 42 : MessageSetSchema.newSchema( 43 SchemaUtil.unknownFieldSetFullSchema(), 44 ExtensionSchemas.full(), 45 messageInfo.getDefaultInstance()); 46 } 47 48 return newSchema(messageType, messageInfo); 49 } 50 newSchema(Class<T> messageType, MessageInfo messageInfo)51 private static <T> Schema<T> newSchema(Class<T> messageType, MessageInfo messageInfo) { 52 return useLiteRuntime(messageType) 53 ? MessageSchema.newSchema( 54 messageType, 55 messageInfo, 56 NewInstanceSchemas.lite(), 57 ListFieldSchemas.lite(), 58 SchemaUtil.unknownFieldSetLiteSchema(), 59 allowExtensions(messageInfo) ? ExtensionSchemas.lite() : null, 60 MapFieldSchemas.lite()) 61 : MessageSchema.newSchema( 62 messageType, 63 messageInfo, 64 NewInstanceSchemas.full(), 65 ListFieldSchemas.full(), 66 SchemaUtil.unknownFieldSetFullSchema(), 67 allowExtensions(messageInfo) ? ExtensionSchemas.full() : null, 68 MapFieldSchemas.full()); 69 } 70 allowExtensions(MessageInfo messageInfo)71 private static boolean allowExtensions(MessageInfo messageInfo) { 72 switch (messageInfo.getSyntax()) { 73 case PROTO3: 74 return false; 75 default: 76 return true; 77 } 78 } 79 getDefaultMessageInfoFactory()80 private static MessageInfoFactory getDefaultMessageInfoFactory() { 81 return new CompositeMessageInfoFactory( 82 GeneratedMessageInfoFactory.getInstance(), getDescriptorMessageInfoFactory()); 83 } 84 85 private static class CompositeMessageInfoFactory implements MessageInfoFactory { 86 private MessageInfoFactory[] factories; 87 CompositeMessageInfoFactory(MessageInfoFactory... factories)88 CompositeMessageInfoFactory(MessageInfoFactory... factories) { 89 this.factories = factories; 90 } 91 92 @Override isSupported(Class<?> clazz)93 public boolean isSupported(Class<?> clazz) { 94 for (MessageInfoFactory factory : factories) { 95 if (factory.isSupported(clazz)) { 96 return true; 97 } 98 } 99 return false; 100 } 101 102 @Override messageInfoFor(Class<?> clazz)103 public MessageInfo messageInfoFor(Class<?> clazz) { 104 for (MessageInfoFactory factory : factories) { 105 if (factory.isSupported(clazz)) { 106 return factory.messageInfoFor(clazz); 107 } 108 } 109 throw new UnsupportedOperationException( 110 "No factory is available for message type: " + clazz.getName()); 111 } 112 } 113 114 private static final MessageInfoFactory EMPTY_FACTORY = 115 new MessageInfoFactory() { 116 @Override 117 public boolean isSupported(Class<?> clazz) { 118 return false; 119 } 120 121 @Override 122 public MessageInfo messageInfoFor(Class<?> clazz) { 123 throw new IllegalStateException("This should never be called."); 124 } 125 }; 126 getDescriptorMessageInfoFactory()127 private static MessageInfoFactory getDescriptorMessageInfoFactory() { 128 if (Protobuf.assumeLiteRuntime) { 129 return EMPTY_FACTORY; 130 } 131 try { 132 Class<?> clazz = Class.forName("com.google.protobuf.DescriptorMessageInfoFactory"); 133 return (MessageInfoFactory) clazz.getDeclaredMethod("getInstance").invoke(null); 134 } catch (Exception e) { 135 return EMPTY_FACTORY; 136 } 137 } 138 useLiteRuntime(Class<?> messageType)139 private static boolean useLiteRuntime(Class<?> messageType) { 140 return Protobuf.assumeLiteRuntime || GeneratedMessageLite.class.isAssignableFrom(messageType); 141 } 142 } 143