/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * 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. */ package org.keycloak.connections.mongo.api.types; import java.util.HashMap; import java.util.Map; /** * Registry of mappers, which allow to convert application object to database objects. MapperRegistry is main entry point to be used by application. * Application can create instance of MapperRegistry and then register required Mapper objects. * * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ public class MapperRegistry { // TODO: Thread-safety support (maybe...) // Mappers of Application objects to DB objects private Map<Class<?>, Mapper<?, ?>> appObjectMappers = new HashMap<Class<?>, Mapper<?, ?>>(); // Mappers of DB objects to Application objects private Map<Class<?>, Map<Class<?>, Mapper<?, ?>>> dbObjectMappers = new HashMap<Class<?>, Map<Class<?>, Mapper<?,?>>>(); /** * Add mapper for converting application objects to DB objects * * @param mapper */ public void addAppObjectMapper(Mapper<?, ?> mapper) { appObjectMappers.put(mapper.getTypeOfObjectToConvert(), mapper); } /** * Add mapper for converting DB objects to application objects * * @param mapper */ public void addDBObjectMapper(Mapper<?, ?> mapper) { Class<?> dbObjectType = mapper.getTypeOfObjectToConvert(); Class<?> appObjectType = mapper.getExpectedReturnType(); Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType); if (appObjects == null) { appObjects = new HashMap<Class<?>, Mapper<?, ?>>(); dbObjectMappers.put(dbObjectType, appObjects); } appObjects.put(appObjectType, mapper); } public <S> S convertDBObjectToApplicationObject(MapperContext<Object, S> context) { if (context.getObjectToConvert() == null) { return null; } Object dbObject = context.getObjectToConvert(); Class<?> expectedApplicationObjectType = context.getExpectedReturnType(); Class<?> dbObjectType = dbObject.getClass(); Mapper<Object, S> mapper; Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType); if (appObjects == null) { throw new IllegalArgumentException("Not found any mappers for type " + dbObjectType); } else { if (appObjects.size() == 1) { mapper = (Mapper<Object, S>)appObjects.values().iterator().next(); } else { // Try to find converter for requested application type mapper = (Mapper<Object, S>)getAppConverterForType(context.getExpectedReturnType(), appObjects); } } if (mapper == null) { throw new IllegalArgumentException("Can't found mapper for type " + dbObjectType + " and expectedApplicationType " + expectedApplicationObjectType); } return mapper.convertObject(context); } public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) { if (applicationObject == null) { return null; } Class<?> appObjectType = applicationObject.getClass(); Mapper<Object, S> mapper = (Mapper<Object, S>)getAppConverterForType(appObjectType, appObjectMappers); if (mapper == null) { throw new IllegalArgumentException("Can't found converter for type " + appObjectType + " in registered appObjectMappers"); } if (!expectedDBObjectType.isAssignableFrom(mapper.getExpectedReturnType())) { throw new IllegalArgumentException("Converter " + mapper + " has return type " + mapper.getExpectedReturnType() + " but we need type " + expectedDBObjectType); } return mapper.convertObject(new MapperContext<Object, S>(applicationObject, expectedDBObjectType, null)); } // Try to find converter for given type or all it's supertypes private static Mapper<Object, ?> getAppConverterForType(Class<?> appObjectType, Map<Class<?>, Mapper<?, ?>> appObjectConverters) { Mapper<Object, ?> mapper = (Mapper<Object, ?>)appObjectConverters.get(appObjectType); if (mapper != null) { return mapper; } else { Class<?>[] interfaces = appObjectType.getInterfaces(); for (Class<?> interface1 : interfaces) { mapper = getAppConverterForType(interface1, appObjectConverters); if (mapper != null) { return mapper; } } Class<?> superType = appObjectType.getSuperclass(); if (superType != null) { return getAppConverterForType(superType, appObjectConverters); } else { return null; } } } }