package ws.antonov.config.provider; import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.Message; import com.google.protobuf.TextFormat; import com.googlecode.protobuf.format.CouchDBFormat; import com.googlecode.protobuf.format.JavaPropsFormat; import com.googlecode.protobuf.format.JsonFormat; import com.googlecode.protobuf.format.XmlFormat; import ws.antonov.config.api.provider.ConfigProvider; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; /** * Copyright 2011 Alex Antonov * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @author aantonov * @since 0.1 */ public abstract class AbstractConfigProvider implements ConfigProvider { private ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); private static final ConcurrentHashMap<Class, Method> newBuilderMethodCache = new ConcurrentHashMap<Class, Method>(); protected static boolean isProtobuf23; protected static Class extensionRegistryClass; static { try { extensionRegistryClass = Class.forName("com.google.protobuf.ExtensionRegistryLite"); isProtobuf23 = true; } catch (ClassNotFoundException e) { extensionRegistryClass = com.google.protobuf.ExtensionRegistry.class; isProtobuf23 = false; } } protected AbstractConfigProvider() { this(null); } protected AbstractConfigProvider(ExtensionRegistry extensionRegistry) { if (extensionRegistry != null) this.extensionRegistry = extensionRegistry; } /** * This method takes a <code>Message</code> class and determines what <code>Message.Builder</code> needs to be instantiated. * After that there is a chained call to convertMessage(builder, contentType, data) is done to populate the builder with the data. * The returned builder object is not finalized, and can be further appended, if need be to override the values or augment them. * @param returnType * @param contentType * @param data * @return * @throws Exception * @see ws.antonov.config.provider.AbstractConfigProvider#convertMessage(com.google.protobuf.Message.Builder, ws.antonov.config.provider.AbstractConfigProvider.ContentType, java.io.InputStream) */ protected Message.Builder convertMessage(Class<? extends Message> returnType, ContentType contentType, InputStream data) throws Exception { Message.Builder builder; Method m = newBuilderMethodCache.get(returnType); if (m == null) { m = returnType.getMethod("newBuilder"); newBuilderMethodCache.put(returnType, m); } builder = (Message.Builder) m.invoke(returnType); return convertMessage(builder, contentType, data); } protected Message.Builder convertMessage(Message.Builder builder, ContentType contentType, InputStream data) throws Exception { switch (contentType) { case JSON: return populateJsonMessage(builder, data); case COUCHDB: return populateCouchDBMessage(builder, data); case PROPS: return populatePropertiesMessage(builder, data); case TEXT: return populateTextMessage(builder, data); case XML: return populateXmlMessage(builder, data); case PROTOBUF: default: return populateBinaryMessage(builder, data); } } protected Message.Builder populateXmlMessage(Message.Builder builder, InputStream data) throws Exception { XmlFormat.merge(new InputStreamReader(data), builder); return builder; } protected Message.Builder populateTextMessage(Message.Builder builder, InputStream data) throws Exception { TextFormat.merge(new InputStreamReader(data), builder); return builder; } protected Message.Builder populatePropertiesMessage(Message.Builder builder, InputStream data) throws Exception { JavaPropsFormat.merge(new InputStreamReader(data), builder); return builder; } protected Message.Builder populateJsonMessage(Message.Builder builder, InputStream data) throws Exception { JsonFormat.merge(new InputStreamReader(data), builder); return builder; } protected Message.Builder populateCouchDBMessage(Message.Builder builder, InputStream data) throws Exception { CouchDBFormat.merge(new InputStreamReader(data), builder); return builder; } protected Message.Builder populateBinaryMessage(Message.Builder builder, InputStream data) throws Exception { if (data != null && data.available() > 0) { builder.mergeFrom(data, extensionRegistry); } return builder; } public enum ContentType { PROTOBUF, JSON, XML, TEXT, PROPS, COUCHDB } }