Important Notice - Forums is archived
To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.
Cloning components
Hi guys,
I need your help implementing a function cloning components. I would like to define templates using the Vaadin Designer and at runtime make deep multiple copies of some components.
public static Component deepCopy(HasComponents component){
try {
Class<?> clazz = component.getClass();
Constructor<?> ctor = clazz.getConstructor();
Component clone = (Component)ctor.newInstance();
ComponentContainer cloner = (ComponentContainer)clone;
BeanUtils.copyProperties(clone, component);
Iterator<Component> it = component.iterator();
while (it.hasNext()){
Component c = it.next();
if (c instanceof ComponentContainer){
cloner.addComponent(deepCopy((HasComponents)c));
} else {
cloner.addComponent(c);
}
}
return cloner;
} catch (Exception e) {
log.error("Error cloning component", e);
return null;
}
}
When I call this function with a HorizontalLayout holding a TextBox and a ComboBox it first clones the TextBox and then crashes on the ComboBox with this error:
java.util.ConcurrentModificationException: null
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966) ~[na:1.8.0]
at java.util.LinkedList$ListItr.next(LinkedList.java:888) ~[na:1.8.0]
at com.eqs.lei.util.VaadinUtils.deepCopy(VaadinUtils.java:53) ~[classes/:na]
Please help
Thanks,
Roland
You are modifying the component list while iterating over it.
I think you must first collect the cloned components in a separated list and then do clear the container and add the new components.
Try something like this
List<Component> clonedComponents = new ArraysList<>(cloner.getComponentCount());
Iterator<Component> it = component.iterator();
while (it.hasNext()){
Component c = it.next();
if (c instanceof ComponentContainer){
clonedComponents.add(deepCopy((HasComponents)c));
} else {
clonedComponents.add(c);
}
}
cloner.removeAllComponents();
cloner.addComponents(clonedComponents.toArray(new Component[clonedComponents.size()]));
HTH
Marco
Thanks for helping, Marco
The following code now somehow clones a Vaadin component, but some attributes are not correct in the clone. The original TextField had a width of 100%, the clone has -1
public class Cloner {
private final static Log log = LogFactory.getLog(Cloner.class);
private List<Pair> tasks;
public Component clone(HasComponents component){
tasks = new ArrayList<Pair>();
Component result = deepCopy(component);
for (Pair pair: tasks){
((ComponentContainer)pair.parent).addComponent(pair.child);
}
return result;
}
private Component deepCopy(Component component){
try {
Class<?> clazz = component.getClass();
Constructor<?> ctor = clazz.getConstructor();
Component clone = (Component)ctor.newInstance();
BeanUtils.copyProperties(clone, component);
if (component instanceof HasComponents){
Iterator<Component> it = ((HasComponents)component).iterator();
while (it.hasNext()){
Component c = it.next();
//tasks.add(new Pair(component, c));
tasks.add(new Pair(clone, deepCopy(c)));
}
}
return clone;
} catch (Exception e) {
log.error("Error cloning component", e);
return null;
}
}
private class Pair {
Pair(Component parent, Component child){
this.parent = parent;
this.child = child;
}
private Component parent;
private Component child;
}
}
I think width is discarded by BeanUtils.copyProperties because it is not a "correct" property.
There setter keeps a String while the getter returns a float.
That seems to be the reason. Why does Vaadin not support Clonable?
Today I tried this approach:
DesignContext dc = Design.read(template);
target.addComponent(dc.getComponentById(template.getId()));
or
DesignContext dc = Design.read(template);
target.addComponent(dc.getRootComponent());
but get this exception:
java.lang.IllegalArgumentException: The class com.vaadin.ui.HorizontalLayout or any of its superclasses do not have an @DesignRoot annotation
at com.vaadin.ui.declarative.Design.read(Design.java:567) ~[vaadin-server-7.7.5.jar:7.7.5]
Great for the community that you also work on weekends :) Thanks again for helping
I think I will give up on cloning. Instead I will waste some resources by reading the designer file again by instantiating an extended design class which just returns my template component.
IMO that would be a cleaner solution rather than cloning.
Good work and let me know about your progress
Best regards
Marco