/**
* Copyright 2009-2013 Oy Vaadin Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vaadin.addon.jpacontainer.fieldfactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;
import com.vaadin.addon.jpacontainer.EntityContainer;
import com.vaadin.addon.jpacontainer.EntityItem;
import com.vaadin.addon.jpacontainer.EntityItemProperty;
import com.vaadin.addon.jpacontainer.JPAContainer;
import com.vaadin.addon.jpacontainer.util.HibernateUtil;
import com.vaadin.v7.data.Container.Filter;
import com.vaadin.v7.data.Validator.InvalidValueException;
import com.vaadin.v7.data.util.BeanItem;
import com.vaadin.v7.data.util.filter.Compare;
import com.vaadin.event.Action;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;
import com.vaadin.v7.ui.CustomField;
import com.vaadin.v7.ui.DefaultFieldFactory;
import com.vaadin.v7.ui.Form;
import com.vaadin.v7.ui.Table;
import com.vaadin.v7.ui.TableFieldFactory;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class MasterDetailEditor extends CustomField implements Action.Handler {
private final FieldFactory fieldFactory;
private Class<?> referencedType;
final private Action add = new Action(getMasterDetailAddItemCaption());
final private Action remove = new Action(getMasterDetailRemoveItemCaption());
final private Action[] actions = new Action[] { add, remove };
private JPAContainer container;
private Table table;
private String backReferencePropertyId;
private Object masterEntity;
private final Object propertyId;
private final EntityContainer<?> containerForProperty;
private final Object itemId;
/**
* @param containerForProperty
* @param itemId
* @param propertyId
* @param uiContext
*/
public MasterDetailEditor(FieldFactory fieldFactory,
EntityContainer<?> containerForProperty, Object itemId,
Object propertyId, Component uiContext) {
this.fieldFactory = fieldFactory;
this.containerForProperty = containerForProperty;
this.itemId = itemId;
this.propertyId = propertyId;
masterEntity = containerForProperty.getItem(itemId).getEntity();
// this.setConverter(new MasterDetailConverter(containerForProperty,
// itemId, propertyId));
boolean writeThrough = true;
if (uiContext instanceof Form) {
Form f = (Form) uiContext;
writeThrough = f.isBuffered();
}
buildContainer(writeThrough);
setCaption(DefaultFieldFactory.createCaptionByPropertyId(propertyId));
}
private void buildContainer(boolean writeThrough) {
Class<?> masterEntityClass = containerForProperty.getEntityClass();
referencedType = fieldFactory.detectReferencedType(
fieldFactory.getEntityManagerFactory(containerForProperty),
propertyId, masterEntityClass);
container = fieldFactory.createJPAContainerFor(containerForProperty,
referencedType, !writeThrough);
backReferencePropertyId = HibernateUtil.getMappedByProperty(
masterEntity, propertyId.toString());
Filter filter = new Compare.Equal(backReferencePropertyId, masterEntity);
container.addContainerFilter(filter);
}
private void buildTable() {
table = new Table(null, container);
Object[] visibleProperties = fieldFactory
.getVisibleProperties(referencedType);
if (visibleProperties == null) {
List<Object> asList = new ArrayList<Object>(
Arrays.asList(getTable().getVisibleColumns()));
asList.remove("id");
asList.remove(backReferencePropertyId);
visibleProperties = asList.toArray();
}
getTable().setPageLength(5);
getTable().setVisibleColumns(visibleProperties);
getTable().addActionHandler(this);
getTable().setTableFieldFactory(getFieldFactoryForMasterDetailEditor());
getTable().setEditable(true);
getTable().setSelectable(true);
}
protected Table getTable() {
return table;
}
/**
* TODO consider opening and adding parameters like propertyId, master class
* etc
*
* @return
*/
private TableFieldFactory getFieldFactoryForMasterDetailEditor() {
return fieldFactory;
}
protected String getMasterDetailRemoveItemCaption() {
return "Remove";
}
protected String getMasterDetailAddItemCaption() {
return "Add";
}
/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.jpacontainer.fieldfactory.JPAContainerCustomField#getType
* ()
*/
@Override
public Class<?> getType() {
return containerForProperty.getItem(itemId).getItemProperty(propertyId)
.getType();
}
public void handleAction(Action action, Object sender, Object target) {
if (action == add) {
addNew();
} else {
remove(target);
}
}
public Action[] getActions(Object target, Object sender) {
return actions;
}
private void remove(Object itemId) {
if (itemId != null) {
Collection<?> collection = (Collection<?>) getPropertyDataSource()
.getValue();
EntityItem item = container.getItem(itemId);
item.getItemProperty(backReferencePropertyId).setValue(null);
container.removeItem(itemId);
if (isBuffered()) {
collection.remove(item.getEntity());
}
}
}
private void addNew() {
try {
Object newInstance = container.getEntityClass().newInstance();
BeanItem<?> beanItem = new BeanItem(newInstance);
beanItem.getItemProperty(backReferencePropertyId).setValue(
masterEntity);
container.addEntity(newInstance);
if (isBuffered()) {
// TODO need to update the actual property also!?
}
} catch (Exception e) {
Logger.getLogger(getClass().getName()).warning(
"Could not instantiate detail instance "
+ container.getEntityClass().getName());
}
}
@Override
public void commit() throws SourceException, InvalidValueException {
if (!isBuffered()) {
// Update the original collection to contain up to date list of
// referenced entities
((EntityItemProperty) getPropertyDataSource()).getItem().refresh();
Collection c = (Collection) getPropertyDataSource().getValue();
boolean isNew = c == null;
HashSet orphaned = !isNew ? new HashSet(c) : null;
Collection itemIds = container.getItemIds();
for (Object object : itemIds) {
EntityItem item = container.getItem(object);
Object entity = item.getEntity();
if (!isNew) {
orphaned.remove(entity);
}
if (c == null) {
try {
c = MultiSelectConverter
.createNewCollectionForType(containerForProperty
.getItem(itemId)
.getItemProperty(propertyId).getType());
} catch (InstantiationException e) {
throw new SourceException(container, e);
} catch (IllegalAccessException e) {
throw new SourceException(container, e);
}
}
if (isNew || !c.contains(entity)) {
c.add(entity);
}
}
if (!isNew) {
c.removeAll(orphaned);
}
getPropertyDataSource().setValue(c);
} else {
super.commit();
}
}
@Override
protected Component initContent() {
CssLayout vl = new CssLayout();
buildTable();
vl.addComponent(getTable());
CssLayout buttons = new CssLayout();
buttons.addComponent(new Button(getMasterDetailAddItemCaption(),
new ClickListener() {
public void buttonClick(ClickEvent event) {
addNew();
}
}));
buttons.addComponent(new Button(getMasterDetailRemoveItemCaption(),
new ClickListener() {
public void buttonClick(ClickEvent event) {
remove(getTable().getValue());
}
}));
vl.addComponent(buttons);
return vl;
}
}