/* * Copyright (C) 2011 Red Hat, Inc. and/or its affiliates. * * 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.jboss.errai.databinding.client; import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import org.jboss.errai.common.client.api.Assert; import org.jboss.errai.databinding.client.api.Bindable; /** * Provides access to the generated proxies for {@link Bindable} types. * * @author Christian Sadilek <csadilek@redhat.com> * @author Max Barkley <mbarkley@redhat.com> */ public class BindableProxyFactory { private static Map<Class<?>, BindableProxyProvider> bindableProxyProviders = new HashMap<Class<?>, BindableProxyProvider>(); private static Map<String, Class<?>> bindableTypes = new HashMap<String, Class<?>>(); private static Map<Class<?>, BindableProxyProvider> builtinProxyProviders = new HashMap<Class<?>, BindableProxyProvider>(); private static Map<Object, BindableProxy<?>> proxies = new IdentityHashMap<Object, BindableProxy<?>>(); static { builtinProxyProviders.put(List.class, new BindableProxyProvider() { @SuppressWarnings("unchecked") @Override public BindableProxy<?> getBindableProxy(final Object model) { return new BindableListWrapper<Object>((List<Object>) model); } @Override public BindableProxy<?> getBindableProxy() { return new BindableListWrapper<>(new ArrayList<>()); } }); } /** * Returns a new proxy for the provided model instance. Changes to the proxy's state will result * in updates on the widget given the corresponding property was bound. * * @param <T> * the bindable type * @param model * The model instance to proxy, must not be null. * @return proxy that can be used in place of the model instance. */ @SuppressWarnings("unchecked") public static <T> T getBindableProxy(T model) { Assert.notNull(model); if (model instanceof BindableProxy) return model; BindableProxy<?> proxy = proxies.get(model); if (proxy == null) { final Class<? extends Object> modelClass = (model instanceof List ? List.class : model.getClass()); final BindableProxyProvider proxyProvider = getBindableProxyProvider(modelClass); proxy = proxyProvider.getBindableProxy(model); if (proxy == null) { throw new RuntimeException("No proxy instance provided for bindable type: " + modelClass.getName()); } proxies.put(model, proxy); } return (T) proxy; } /** * Returns a proxy for a newly created model instance of the provided type. Changes to the proxy's * state will result in updates on the component given the corresponding property was bound. * * @param bindableType * the bindable type * @return proxy that can be used in place of the model instance. */ @SuppressWarnings("unchecked") public static <T> T getBindableProxy(Class<T> bindableType) { final BindableProxyProvider proxyProvider = getBindableProxyProvider(bindableType); final BindableProxy<?> proxy = proxyProvider.getBindableProxy(); if (proxy == null) { throw new RuntimeException("No proxy instance provided for bindable type: " + bindableType.getName()); } return (T) proxy; } /** * Returns a proxy for a newly created model instance of the provided type. Changes to the proxy's * state will result in updates on the widget given the corresponding property was bound. * * @param bindableType * The fully qualified name of the bindable type * @return proxy that can be used in place of the model instance. */ public static BindableProxy<?> getBindableProxy(String bindableType) { final Class<?> bindableClass = bindableTypes.get(bindableType); return (BindableProxy<?>) getBindableProxy(bindableClass); } private static BindableProxyProvider getBindableProxyProvider(Class<?> bindableType) { if (bindableProxyProviders.isEmpty()) { throw new RuntimeException("There are no proxy providers for bindable types registered yet."); } BindableProxyProvider proxyProvider = bindableProxyProviders.get(bindableType); if (proxyProvider == null) { proxyProvider = builtinProxyProviders.get(bindableType); } if (proxyProvider == null) { throw new RuntimeException("No proxy provider found for bindable type: " + bindableType.getName()); } return proxyProvider; } /** * Registers a generated bindable proxy. This method is called by the generated * BindableProxyLoader. * * @param proxyType * The bindable type, must not be null. * @param proxyProvider * The proxy provider for the generated bindable proxy, must not be null. */ public static void addBindableProxy(Class<?> proxyType, BindableProxyProvider proxyProvider) { Assert.notNull(proxyType); Assert.notNull(proxyProvider); bindableTypes.put(proxyType.getName(), proxyType); bindableProxyProviders.put(proxyType, proxyProvider); } /** * Remove the cached proxy for the provided model instance. A future lookup will cause the * creation of a new proxy instance. * * @param <T> * the bindable type * @param model * the model instance */ public static <T> void removeCachedProxyForModel(T model) { proxies.remove(model); } /** * Checks if the type of the provided model is bindable. That's the case when a proxy provider has * been generated for that type (the type has been annotated or configured to be bindable). * * @param model * the object to be checked, may be null. * @return true if the object is bindable, otherwise false. */ @SuppressWarnings("unchecked") public static <T> boolean isBindableType(T model) { if (model == null) { return false; } if (model instanceof BindableProxy) { model = (T) ((BindableProxy<T>) model).unwrap(); } final BindableProxyProvider proxyProvider = bindableProxyProviders.get(model.getClass()); return (proxyProvider != null); } }