package com.nominanuda.zen.stereotype; import static com.nominanuda.zen.common.Ex.EX; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; import java.util.Locale; import java.util.UUID; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import com.nominanuda.zen.common.Check; public interface Copier<T> { /** * @param t * @return the copy x such as that x.equals(t); if the object is immutable * than can return t itself so that x == t. If t is <code>null</code> * the method returns <code>null</code> */ T copy(@Nullable T t); /** * @return the basic {@link Copier} that can copy {@link Copyable}s and returns the * supplied parameter if it is a basic java immutable type or carries the {@link Immutable} * annotation. Also {@link Cloneable}s are supported if they expose the {@link Object#clone()} method * as public */ static <X> Copier<X> basic() { return new Copier<X>() { @Override public X copy(X x) throws IllegalArgumentException { if(x == null) { return null; } else if(x instanceof Copyable) { return ((Copyable)x).copyCast(); } Class<?> clazz = x.getClass(); if(clazz.isAnnotationPresent(Immutable.class)) { return x; } else if(Check.isInstanceOf(x, String.class, Number.class, Locale.class, UUID.class, URL.class, URI.class)) { return x; } else if(x instanceof Cloneable) { Method cloneMethod; try { cloneMethod = clazz.getMethod("clone", new Class[]{}); } catch (NoSuchMethodException | SecurityException e1) { throw new IllegalArgumentException( "don't know how to copy an object of type "+ x.getClass().getName()); } if(cloneMethod != null) { try { @SuppressWarnings("unchecked") X res = (X)cloneMethod.invoke(x); return res; } catch (IllegalAccessException | InvocationTargetException e) { throw EX.uncheckedExecution(e); } } else {//defensive programming <:P throw new IllegalArgumentException( "don't know how to copy an object of type "+ x.getClass().getName()); } } else { throw new IllegalArgumentException( "don't know how to copy an object of type "+ x.getClass().getName()); } } }; } /** * * @return a zero copier that always returns the supplier parameter */ static <X> Copier<X> unsafeNoOp() { return new Copier<X>() { @Override public X copy(X t) { return t; } }; } }