All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.util.beans.BinderFactory Maven / Gradle / Ivy

There is a newer version: 3.1
Show newest version
/*
 * #%L
 * Nuiton Utils
 * %%
 * Copyright (C) 2004 - 2010 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nuiton.util.beans;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Factory of {@link Binder}.
 *
 * 

Obtain a new binder

* To obtain a new binder you can use the {@code newBinder(XXX)} methods. * * For example to obtain a mirrored binder (same source and target type) which * will be able to copy all accepting properties, use this code : *
 * Binder<BeanA, BeanA> binder = BinderFactory.newBinder(BeanA.class);
 * 
*

Usage of contextale binder

* It is possible to use different binder for same source and target type, using a * extra context name parameter, like this : *
 * Binder<BeanA, BeanA> binder = BinderFactory.newBinder(BeanA.class, "mycontext");
 * 
* * This is usefull when you register your own binder model in the factory (see * next section) to bind different things from the same type of objects... * *

Register a new binder model

* To register a new binder's model use one of the method {@code registerBinderModel(XXX)}. * * * More documentation will come soon, yu can see the package info javadoc or * unit tests... * * @author Tony Chemit - [email protected] * @since 1.5.3 */ public class BinderFactory { /** Logger. */ private static final Log log = LogFactory.getLog(BinderFactory.class); /** Cache of registred binders indexed by their unique entry */ protected static BindelModelEntryMap binderModels; /** * Gets the registred mirror binder (source type = target type) with no * context name specified. * * @param sourceType the type of source and target * @param the type of source and target * @return the registred binder or {@code null} if not found. */ public static Binder newBinder(Class sourceType) { return newBinder0(sourceType, sourceType, null, Binder.class); } /** * Gets the registred mirror binder (source type = target type) with the * given context name. * * @param sourceType the type of source and target * @param contextName the context's name of the searched binder * @param the type of source and target * @return the registred binder or {@code null} if not found. */ public static Binder newBinder(Class sourceType, String contextName) { return newBinder0(sourceType, sourceType, contextName, Binder.class); } /** * Gets the registred binder given his types with no context name. * * @param sourceType the type of source * @param targetType the type of target * @param the type of source * @param the type of target * @return the registred binder or {@code null} if not found. */ public static Binder newBinder(Class sourceType, Class targetType) { return newBinder0(sourceType, targetType, null, Binder.class); } /** * Gets the registred binder given his types with no context name. * * @param sourceType the type of source * @param targetType the type of target * @param contextName the context's name of the searched binder * @param the type of source * @param the type of target * @return the registred binder or {@code null} if not found. */ public static Binder newBinder(Class sourceType, Class targetType, String contextName) { return newBinder0(sourceType, targetType, contextName, Binder.class); } /** * Gets the registred binder given his types and his context's name. * * @param sourceType the type of source * @param targetType the type of target * @param contextName the context's name of the searched binder * @param binderType type of binder required * @param the type of source * @param the type of target * @param the type of binder * @return the new instanciated binder. */ public static > B newBinder(Class sourceType, Class targetType, String contextName, Class binderType) { B binder = (B) newBinder0(sourceType, targetType, contextName, binderType); return binder; } public static Binder.BinderModel registerBinderModel(BinderModelBuilder binderModelBuilder) throws IllegalArgumentException { Binder.BinderModel model = registerBinderModel(binderModelBuilder, null); return model; } public static Binder.BinderModel registerBinderModel(Binder binder) throws IllegalArgumentException { Binder.BinderModel model = registerBinderModel(binder, null); return model; } public static Binder.BinderModel registerBinderModel(Binder.BinderModel model) throws IllegalArgumentException { registerBinderModel(model, null); return model; } public static Binder.BinderModel registerBinderModel(BinderModelBuilder binderModelBuilder, String contextName) throws IllegalArgumentException { Binder.BinderModel model = binderModelBuilder.getModel(); registerBinderModel(model, contextName); return model; } public static Binder.BinderModel registerBinderModel(Binder binder, String contextName) throws IllegalArgumentException { Binder.BinderModel model = binder.getModel(); registerBinderModel(model, contextName); return model; } public static Binder.BinderModel registerBinderModel(Binder.BinderModel model, String contextName) throws IllegalArgumentException { // check if the given model is not already registred for the given context Binder.BinderModel registredModel = getBinderModels().get(model, contextName); // let's add this model into cache of models BinderModelEntry key = new BinderModelEntry(model, contextName); if (registredModel != null) { // this model is already registred, remove it from cache if (log.isWarnEnabled()) { log.warn("Remove existing binder model from cache : " + toString(registredModel, contextName)); } } // add new model into cache getBinderModels().put(key, model); return model; } /** * Given a {@code model} and a {@code binderType}, instanciate a new binder * and returns it. * * Note: This method will NOT register * the model in the factory. If you want to reuse your model, please use * one of the {@code registerBinderModel(XXX)} method. * * @param model the model used by the binder * @param binderType the type of the binder * @param the source type * @param the target type * @param the type of the binder * @return the new instanciated binder * @since 2.1 */ public static > B newBinder(Binder.BinderModel model, Class binderType) { B binder; try { binder = binderType.getConstructor().newInstance(); } catch (Exception e) { throw new IllegalStateException( "Could not instanciate binder of type " + binderType, e); } binder.setModel(model); return binder; } /** * Clear the cache of registred binder models. * * Note : This is a convienient method for test purposes and should * be used in a normal usage of this provider. */ public static void clear() { if (binderModels != null) { binderModels.clear(); binderModels = null; } } /** * Tells if there is a cached binder model for the given parameters. * * @param sourceType the type of source * @param targetType the type of target * @param contextName the context's name of the searched binder * @param the type of source * @param the type of target * @return {@code true} if there is a cached binder model for the given * parameters, {@code false} otherwise. */ public static boolean isBinderModelExists(Class sourceType, Class targetType, String contextName) { Binder.BinderModel model = getBinderModels().get(sourceType, targetType, contextName); return model != null; } /** * Obtain a cached binder model. * * @param sourceType the type of source * @param targetType the type of target * @param contextName the context's name of the searched binder * @param the type of source * @param the type of target * @return the cached binder model or {@code null} if not found. */ public static Binder.BinderModel getCachedBinderModel(Class sourceType, Class targetType, String contextName) { Binder.BinderModel model = getBinderModels().get(sourceType, targetType, contextName); return model; } protected static BindelModelEntryMap getBinderModels() { if (binderModels == null) { binderModels = new BindelModelEntryMap(); } return binderModels; } protected static String toString(Binder.BinderModel model, String contextName) { return toString(model.getSourceType(), model.getTargetType(), contextName); } protected static String toString(Class sourceType, Class targetType, String contextName) { return "<" + sourceType.getName() + " - " + targetType.getName() + " > [" + contextName + "] "; } /** * Instanciate a new binder given his types and his context's name. * * If the corresponding binder model does not exist, then it will be created * and cached (using the {@link BinderModelBuilder#newDefaultBuilder(Class, Class)} method). * * @param sourceType the type of source * @param targetType the type of target * @param contextName the context's name of the searched binder * @param binderType type of binder required * @param the type of source * @param the type of target * @param the type of binder * @return the new instanciated binder. */ protected static > Binder newBinder0(Class sourceType, Class targetType, String contextName, Class binderType) { // obtain the cached model Binder.BinderModel model = getBinderModels().get(sourceType, targetType, contextName); if (model == null) { // model not yet registred, let's create it if (log.isInfoEnabled()) { log.info("No binder model found for " + toString(sourceType, targetType, contextName) + ", will create a new default one."); } BinderModelBuilder builder = BinderModelBuilder.newDefaultBuilder(sourceType, targetType); // register the new binder model model = registerBinderModel(builder, contextName); } B binder = newBinder(model, binderType); return binder; } public static class BindelModelEntryMap implements Map> { protected final Map> delegate; public BindelModelEntryMap() { delegate = new HashMap>(); } public Binder.BinderModel get(Class source, Class target, String contextName) { Binder.BinderModel result = null; for (BinderModelEntry key : binderModels.keySet()) { if (!key.getSourceType().equals(source)) { continue; } if (!key.getTargetType().equals(target)) { continue; } if (key.getName() == null) { if (contextName != null) { continue; } } else { if (!key.getName().equals(contextName)) { continue; } } result = (Binder.BinderModel) binderModels.get(key); break; } return result; } public Binder.BinderModel get(Binder.BinderModel model, String contextName) { Class source = model.getSourceType(); Class target = model.getTargetType(); Binder.BinderModel result = get(source, target, contextName); return result; } @Override public int size() { return delegate.size(); } @Override public boolean isEmpty() { return delegate.isEmpty(); } @Override public boolean containsKey(Object key) { return delegate.containsKey(key); } @Override public boolean containsValue(Object value) { return delegate.containsValue(value); } @Override public Binder.BinderModel get(Object key) { return delegate.get(key); } public Binder.BinderModel put(BinderModelEntry key, Binder.BinderModel value) { return delegate.put(key, value); } @Override public Binder.BinderModel remove(Object key) { return delegate.remove(key); } public void putAll(Map> m) { delegate.putAll(m); } @Override public void clear() { delegate.clear(); } @Override public Set keySet() { return delegate.keySet(); } @Override public Collection> values() { return delegate.values(); } @Override public Set>> entrySet() { return delegate.entrySet(); } } /** * Definition of an binder model entry (source and target types + context name). * * Note :When no context is specified, we always use a * {@code null} context name. */ public static class BinderModelEntry { protected final Class sourceType; protected final Class targetType; protected final String name; public BinderModelEntry(Class sourceType, Class targetType, String name) { this.sourceType = sourceType; this.targetType = targetType; this.name = name; } public BinderModelEntry(Binder.BinderModel model, String contextName) { this(model.getSourceType(), model.getTargetType(), contextName); } public Class getSourceType() { return sourceType; } public Class getTargetType() { return targetType; } public String getName() { return name; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } BinderModelEntry that = (BinderModelEntry) o; return (name == null ? that.name == null : name.equals(that.name)) && sourceType.equals(that.sourceType) && targetType.equals(that.targetType); } @Override public int hashCode() { int result = sourceType.hashCode(); result = 31 * result + targetType.hashCode(); result = 31 * result + (name != null ? name.hashCode() : 0); return result; } @Override public String toString() { StringBuilder buffer = new StringBuilder("<"); buffer.append(super.toString()); buffer.append(", sourceType: ").append(getSourceType()).append(','); buffer.append(" targetType: ").append(getTargetType()).append(','); buffer.append(" name: ").append(getName()).append('>'); return buffer.toString(); } } }