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

org.vaadin.viritin.fields.AbstractElementCollection Maven / Gradle / Ivy

package org.vaadin.viritin.fields;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.vaadin.viritin.BeanBinder;
import org.vaadin.viritin.MBeanFieldGroup;
import org.vaadin.viritin.MBeanFieldGroup.FieldGroupListener;

import com.vaadin.data.Validator;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomField;
import com.vaadin.ui.DefaultFieldFactory;
import com.vaadin.ui.Field;
import com.vaadin.ui.Label;
import com.vaadin.ui.Layout;
import com.vaadin.util.ReflectTools;

/**
 * A superclass for fields suitable for editing collection of referenced objects tied to parent
 * object only. E.g. OneToMany/ElementCollection fields in JPA world.
 * 

* Some features/restrictions: *

    *
  • The field is valid when all elements are valid. *
  • The field is always non buffered *
  • The element type needs to have an empty paremeter constructor or user * must provide an Instantiator. *
* * @author Matti Tahvonen * @param The type in the entity collection. The type must have empty * paremeter constructor or you have to provide Instantiator. */ public abstract class AbstractElementCollection extends CustomField { private static final long serialVersionUID = 7785110162928180695L; public static class ElementAddedEvent extends Component.Event { private static final long serialVersionUID = 2263765199849601501L; private final ET element; public ElementAddedEvent(AbstractElementCollection source, ET element) { super(source); this.element = element; } public ET getElement() { return element; } } public static class ElementRemovedEvent extends Component.Event { private static final long serialVersionUID = 574545902966053269L; private final ET element; public ElementRemovedEvent(AbstractElementCollection source, ET element) { super(source); this.element = element; } public ET getElement() { return element; } } public interface ElementAddedListener extends Serializable { void elementAdded(ElementAddedEvent e); } public interface ElementRemovedListener extends Serializable { void elementRemoved(ElementRemovedEvent e); } private static final Method addedMethod; private static final Method removedMethod; static { addedMethod = ReflectTools.findMethod(ElementAddedListener.class, "elementAdded", ElementAddedEvent.class); removedMethod = ReflectTools.findMethod(ElementRemovedListener.class, "elementRemoved", ElementRemovedEvent.class); } public AbstractElementCollection addElementAddedListener( ElementAddedListener listener) { addListener(ElementAddedEvent.class, listener, addedMethod); return this; } public AbstractElementCollection removeElementAddedListener( ElementAddedListener listener) { removeListener(ElementAddedEvent.class, listener, addedMethod); return this; } public AbstractElementCollection addElementRemovedListener( ElementRemovedListener listener) { addListener(ElementRemovedEvent.class, listener, removedMethod); return this; } public AbstractElementCollection removeElementRemovedListener( ElementRemovedListener listener) { removeListener(ElementRemovedEvent.class, listener, removedMethod); return this; } private Instantiator instantiator; private Instantiator oldEditorInstantiator; private EditorInstantiator newEditorInstantiator; private final Class elementType; private final Class editorType; protected ET newInstance; private final FieldGroupListener fieldGroupListener = new FieldGroupListener() { private static final long serialVersionUID = 2498166986711639744L; @Override public void onFieldGroupChange(MBeanFieldGroup beanFieldGroup) { if (beanFieldGroup.getItemDataSource().getBean() == newInstance) { if (!getFieldGroupFor(newInstance).isValid()) { return; } getAndEnsureValue().add(newInstance); fireEvent(new ElementAddedEvent(AbstractElementCollection.this, newInstance)); setPersisted(newInstance, true); onElementAdded(); } // TODO could optimize for only repainting on changed validity fireValueChange(false); } }; private List visibleProperties; private boolean allowNewItems = true; private boolean allowRemovingItems = true; private boolean allowEditItems = true; public boolean isAllowNewItems() { return allowNewItems; } public boolean isAllowRemovingItems() { return allowRemovingItems; } public boolean isAllowEditItems() { return allowEditItems; } public AbstractElementCollection setAllowEditItems(boolean allowEditItems) { this.allowEditItems = allowEditItems; return this; } public AbstractElementCollection setAllowRemovingItems(boolean allowRemovingItems) { this.allowRemovingItems = allowRemovingItems; return this; } public AbstractElementCollection withCaption(String caption) { setCaption(caption); return this; } @Override public void validate() throws Validator.InvalidValueException { super.validate(); Collection v = getValue(); if(v != null) { for (Object o : v) { for (Field f : getFieldGroupFor((ET) o).getFields()) { f.validate(); } } } } private Collection getAndEnsureValue() { Collection value = getValue(); if (value == null) { if (getPropertyDataSource() == null) { // this should never happen :-) return new HashSet(); } Class fieldType = getPropertyDataSource().getType(); if (fieldType.isInterface()) { if (fieldType == List.class) { value = new ArrayList(); } else { // Set value = new HashSet(); } } else { try { value = (Collection) fieldType.newInstance(); } catch (IllegalAccessException | InstantiationException ex) { throw new RuntimeException( "Could not instantiate the used colleciton type", ex); } } getPropertyDataSource().setValue(value); } return value; } /** * @param allowNewItems true if a new element row should be automatically * added * @return the configured field instance */ public AbstractElementCollection setAllowNewElements( boolean allowNewItems) { this.allowNewItems = allowNewItems; return this; } public interface Instantiator extends Serializable { ET create(); } public interface EditorInstantiator extends Serializable { T create(ET entity); } public AbstractElementCollection(Class elementType, Class formType) { this.elementType = elementType; this.editorType = formType; } public AbstractElementCollection(Class elementType, Instantiator i, Class formType) { this.elementType = elementType; this.instantiator = i; this.editorType = formType; } public Class getElementType() { return elementType; } protected ET createInstance() { if (instantiator != null) { return instantiator.create(); } else { try { return elementType.newInstance(); } catch (IllegalAccessException | InstantiationException ex) { throw new RuntimeException(ex); } } } protected Object createEditorInstance(ET pojo) { if (newEditorInstantiator != null) { return newEditorInstantiator.create(pojo); } else { if (oldEditorInstantiator != null) { return oldEditorInstantiator.create(); } else { try { return editorType.newInstance(); } catch (IllegalAccessException | InstantiationException ex) { throw new RuntimeException(ex); } } } } public EditorInstantiator getNewEditorInstantiator() { return newEditorInstantiator; } public void setNewEditorInstantiator( EditorInstantiator editorInstantiator) { this.newEditorInstantiator = editorInstantiator; } public Instantiator getEditorInstantiator() { return oldEditorInstantiator; } public void setEditorInstantiator( Instantiator editorInstantiator) { this.oldEditorInstantiator = editorInstantiator; } private class EditorStuff implements Serializable { private static final long serialVersionUID = 5132645136059482705L; MBeanFieldGroup bfg; Object editor; private EditorStuff(MBeanFieldGroup editor, Object o) { this.bfg = editor; this.editor = o; } } private final Map pojoToEditor = new IdentityHashMap<>(); protected final MBeanFieldGroup getFieldGroupFor(ET pojo) { EditorStuff es = pojoToEditor.get(pojo); if (es == null) { Object o = createEditorInstance(pojo); MBeanFieldGroup bfg = BeanBinder.bind(pojo, o).withEagerValidation( fieldGroupListener); es = new EditorStuff(bfg, o); // TODO listen for all changes for proper modified/validity changes pojoToEditor.put(pojo, es); } return es.bfg; } protected final Component getComponentFor(ET pojo, String property) { EditorStuff editorsstuff = pojoToEditor.get(pojo); if (editorsstuff == null) { Object o = createEditorInstance(pojo); MBeanFieldGroup bfg = BeanBinder.bind(pojo, o).withEagerValidation( fieldGroupListener); editorsstuff = new EditorStuff(bfg, o); // TODO listen for all changes for proper modified/validity changes pojoToEditor.put(pojo, editorsstuff); } Component c = editorsstuff.bfg.getField(property); if(c == null) { try { // property that is not a property editor field but custom UI "column" java.lang.reflect.Field f = editorType.getDeclaredField(property); f.setAccessible(true); c = (Component) f.get(editorsstuff.editor); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { Logger.getLogger(AbstractElementCollection.class.getName()). log(Level.SEVERE, null, ex); } if(c == null) { c = new Label(""); } } return c; } public void addElement(ET instance) { getAndEnsureValue().add(instance); addInternalElement(instance); fireValueChange(false); fireEvent(new ElementAddedEvent<>(this, instance)); } public void removeElement(ET elemnentToBeRemoved) { removeInternalElement(elemnentToBeRemoved); getAndEnsureValue().remove(elemnentToBeRemoved); fireValueChange(false); fireEvent(new ElementRemovedEvent<>(this, elemnentToBeRemoved)); } public AbstractElementCollection setVisibleProperties( List properties) { visibleProperties = properties; return this; } public List getVisibleProperties() { if (visibleProperties == null) { visibleProperties = new ArrayList<>(); for (java.lang.reflect.Field f : editorType.getDeclaredFields()) { // field order can be counted since jdk6 if (Field.class.isAssignableFrom(f.getType())) { visibleProperties.add(f.getName()); } } } return visibleProperties; } public AbstractElementCollection setVisibleProperties( List properties, List propertyHeaders) { visibleProperties = properties; Iterator it = propertyHeaders.iterator(); for (String prop : visibleProperties) { setPropertyHeader(prop, it.next()); } return this; } private final Map propertyToHeader = new HashMap<>(); public AbstractElementCollection setPropertyHeader(String propertyName, String propertyHeader) { propertyToHeader.put(propertyName, propertyHeader); return this; } protected String getPropertyHeader(String propertyName) { String header = propertyToHeader.get(propertyName); if (header == null) { header = DefaultFieldFactory.createCaptionByPropertyId(propertyName); } return header; } @Override protected Component initContent() { return getLayout(); } @Override protected void setInternalValue(Collection newValue) { super.setInternalValue(newValue); clear(); Collection value = newValue; if (value != null) { for (ET v : value) { addInternalElement(v); } } onElementAdded(); } @Override public Class getType() { return Collection.class; } abstract protected void addInternalElement(ET v); abstract protected void setPersisted(ET v, boolean persisted); abstract protected void removeInternalElement(ET v); abstract protected Layout getLayout(); abstract protected void onElementAdded(); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy