org.nuiton.util.beans.BinderFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nuiton-utils Show documentation
Show all versions of nuiton-utils Show documentation
Library of usefull classes to be used in any project.
/*
* #%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 extends BinderModelEntry, ? extends Binder.BinderModel, ?>> 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();
}
}
}