Starting
with version 2.0, OmniFaces provides in the org.omnifaces.util.copier
package a set of utilities dedicated for copying objects. In OmniFaces, you can
see it at work in <o:validateBean> source code, but you can used it
in your projects whenever you need to copy objects (e.g. copy Java beans).
The centerpiece
is represented by the Copier interface, which declare a single method named, copy().
This interface acts as a simple contract for keeping everything unitary, and it
should be implemented by classes that know how to copy an object:
package
org.omnifaces.util.copier;
public
interface Copier {
Object copy(Object object);
}
This
interface is implemented by four classes; each of them defines a copy strategy
that is capable to return a copy of an object that satisfies certain
requirements. In addition, OmniFaces provides a fifth class (strategy) that
represents a straightforward invocation of these four strategies (invoke them
in a preset order until one succeeds).
CloneCopier
Copier
that copies an object using the Cloneable facility.
public class
CloneCopier implements Copier {
private static final String ERROR_CANT_CLONE
=
"Can not clone object of type %s
since it doesn't implement Cloneable";
@Override
public Object copy(Object object) {
if (!(object instanceof Cloneable)) {
throw new
IllegalStateException(format(ERROR_CANT_CLONE, object.getClass()));
}
try {
Method cloneMethod = getMethod(object,
"clone");
if (!cloneMethod.isAccessible()) {
cloneMethod.setAccessible(true);
}
return cloneMethod.invoke(object);
} catch (SecurityException |
IllegalAccessException | IllegalArgumentException | InvocationTargetException
e) {
throw new IllegalStateException(e);
}
}
private Method getMethod(Object object,
String name) {
for (Class<?> c = object.getClass();
c != null; c = c.getSuperclass()) {
for
(Method method : c.getDeclaredMethods()) {
if (method.getName().equals(name)) {
return method;
}
}
}
return null;
}
}
The source
code of CloneCopier
reveals that the object that can be copied via this Copier must implement Cloneable
interface, and provide a clone() method. If these two mandatory
conditions are met, then CloneCopier uses the Java Reflection API to
invoke the corresponding clone() method.
Usage -
let's suppose the below simple bean that is Cloneable:
public class
PlayerCloneable implements Cloneable {
private String name;
private String surname;
private int rank;
public PlayerCloneable(){
}
// This method calls Object's clone().
PlayerCloneable getClone() throws
CloneNotSupportedException {
//
call clone in Object.
return
(PlayerCloneable) super.clone();
}
// getters and seeters
}
Now, the
object to copy is the following instance of PlayerCloneable:
PlayerCloneable
player = new PlayerCloneable();
// set
object properties
And, a copy
of it can be obtained as:
CloneCopier cloneCopier = new
CloneCopier();
PlayerCloneable player_clone =
(PlayerCloneable) cloneCopier.copy(player);
SerializationCopier
Copier
that copies an object by serializing and subsequently de-serializing it again.
As per the platform serialization rules, the object and all its non transient
dependencies have to implement the Serializable interface.
public class
SerializationCopier implements Copier {
@Override
public Object copy(Object object) {
if (!(object instanceof Serializable)) {
throw
new IllegalStateException("Can't copy object of type " +
object.getClass() + " since it doesn't implement Serializable");
}
try {
ByteArrayOutputStream
outputStream = new ByteArrayOutputStream();
new
ObjectOutputStream(outputStream).writeObject(object);
return new ObjectInputStream(new
ByteArrayInputStream(outputStream.toByteArray())).readObject();
}
catch (IOException | ClassNotFoundException e) {
throw
new IllegalStateException(e);
}
}
}
The source
code of SerializationCopier
reveals that the object that can be copied via this Copier must implement Serializable
interface. If this mandatory condition is met, then OmniFaces uses ObjectOutputStream#writeObject()
to serialize the object to be copied. Further, it read back the object (deserialize)
via ObjectInputStream#readObject()
method.
Usage -
let's suppose the below simple bean that is Serializable:
public class
PlayerSerializable implements Serializable {
private String name;
private String surname;
private int rank;
public PlayerSerializable(){
}
// getters and setters
}
Now, the
object to copy is the following instance of PlayerSerializable:
PlayerSerializable
player = new PlayerSerializable();
// set
object properties
And, a copy
of it can be obtained as:
SerializationCopier
serializationCopier = new SerializationCopier();
PlayerSerializable player_serializable
= (PlayerSerializable) serializationCopier.copy(player);
CopyCtorCopier
Copier
that copies an object using its copy constructor.
public class
CopyCtorCopier implements Copier {
@Override
public Object copy(Object object) {
try {
Constructor<?
extends Object> copyConstructor =
object.getClass().getConstructor(object.getClass());
return copyConstructor.newInstance(object);
}
catch (NoSuchMethodException | SecurityException |
InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw
new IllegalStateException(e);
}
}
}
The source
code of CopyCtorCopier
reveals that the object that can be copied via this Copier
must provide a constructor that takes an object of the same type as the object
that's to be constructed. This constructor then initializes itself using the
values of this other instance.
Usage -
let's suppose the below simple bean:
public class
PlayerCopyCtor {
private String name;
private String surname;
private int rank;
public PlayerCopyCtor() {
}
public PlayerCopyCtor(PlayerCopyCtor playerCopyCtor) {
this.name = playerCopyCtor.getName();
this.surname = playerCopyCtor.getSurname();
this.rank = playerCopyCtor.getRank();
}
// getters and setters
}
Now, the
object to copy is the following instance of PlayerCopyCtor:
PlayerCopyCtor
player = new PlayerCopyCtor();
// set
object properties
And, a copy
of it can be obtained as:
CopyCtorCopier copyCtorCopier =
new CopyCtorCopier();
PlayerCopyCtor player_copyctor =
(PlayerCopyCtor) copyCtorCopier.copy(player);
NewInstanceCopier
Copier
that doesn't actually copy an object fully, but just returns a new instance of
the same type. This Copier will not preserves the object
properties!
public class
NewInstanceCopier implements Copier {
@Override
public Object copy(Object object) {
try {
return
object.getClass().newInstance();
} catch (InstantiationException |
IllegalAccessException e) {
throw
new IllegalStateException(e);
}
}
}
The source
code of NewInstanceCopier
reveals that the object that can be copied via this Copier must implement a public default constructor.
Usage -
let's suppose the below simple bean:
public class
PlayerNewInstance {
private String name;
private String surname;
private int rank;
public PlayerNewInstance() {
}
// getters and setters
}
Now, the
object to copy is the following instance of PlayerNewInstance:
PlayerPartial
player = new PlayerPartial();
// set
object properties
And, a copy
of it can be obtained as:
NewInstanceCopier
newInstanceCopier = new NewInstanceCopier();
PlayerNewInstance
player_newinstance = (PlayerNewInstance) newInstanceCopier.copy(player);
Complete
source code on GitHub.
Note
When you don't want to explicitly use one of the above Copiers, you can
simply use the MultiStrategyCopier,
which will invoke these Copiers, one by one, starting with CloneCopier,
continue with SerializationCopier
and CopyCtorCopier,
and finish with NewInstanceCopier.
Whenever one succeeds, the process stops.
MultiStrategyCopier
multiStrategyCopier = new MultiStrategyCopier();
MultiStrategyCopier copied =
(MultiStrategyCopier) multiStrategyCopier.copy(object_to_copy);
If the OmniFaces
Copiers
doesn't satisfy your needs, then you can choose to come with your own Copier
(for example, if you may need a partial object (only a subset of the original
object properties)).
Niciun comentariu :
Trimiteți un comentariu