Java tutorial
/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.libreplan.web.orders.materials; import static org.libreplan.web.I18nHelper._; import java.math.BigDecimal; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.LogFactory; import org.libreplan.business.materials.entities.Material; import org.libreplan.business.materials.entities.MaterialAssignment; import org.libreplan.business.materials.entities.MaterialCategory; import org.libreplan.business.materials.entities.UnitType; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.web.common.Util; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.SuspendNotAllowedException; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Grid; import org.zkoss.zul.Label; import org.zkoss.zul.Listbox; import org.zkoss.zul.Listcell; import org.zkoss.zul.Listitem; import org.zkoss.zul.ListitemRenderer; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Row; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.Tab; import org.zkoss.zul.Tree; import org.zkoss.zul.TreeModel; import org.zkoss.zul.Treecell; import org.zkoss.zul.Treeitem; import org.zkoss.zul.TreeitemRenderer; import org.zkoss.zul.Treerow; import org.zkoss.zul.Vbox; import org.zkoss.zul.api.Decimalbox; import org.zkoss.zul.api.Textbox; import org.zkoss.zul.impl.MessageboxDlg; /** * @author scar Gonzlez Fernndez <ogonzalez@igalia.com> * */ public abstract class AssignedMaterialsController<T, A> extends GenericForwardComposer { private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(AssignedMaterialsController.class); private Tree categoriesTree; private Tree allCategoriesTree; private Grid gridMaterials; private Listbox lbFoundMaterials; private Textbox txtSearchMaterial; private Tab tbAssignedMaterials; private Vbox assignmentsBox; protected abstract IAssignedMaterialsModel<T, A> getModel(); @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); getModel().loadUnitTypes(); createAssignmentsBoxComponent(assignmentsBox); } protected abstract void createAssignmentsBoxComponent(Component parent); public void openWindow(T element) { initializeEdition(element); prepareCategoriesTree(); prepareAllCategoriesTree(); Util.createBindingsFor(self); Util.reloadBindings(self); } protected abstract void initializeEdition(T orderElement); /** * Delay initialization of categories tree till user clicks on Materials tab * * Initializing model and renderer properties directly in ZUL resulted in calling the renderer * more times than actually needed, resulting in noticeable lack of performance */ private void prepareCategoriesTree() { if (categoriesTree.getTreeitemRenderer() == null) { categoriesTree.setTreeitemRenderer(getMaterialCategoryWithUnitsAndPriceRenderer()); } categoriesTree.setModel(getMaterialCategories()); } public abstract TreeModel getMaterialCategories(); private void prepareAllCategoriesTree() { if (allCategoriesTree.getTreeitemRenderer() == null) { allCategoriesTree.setTreeitemRenderer(getMaterialCategoryRenderer()); } allCategoriesTree.setModel(getAllMaterialCategories()); } public abstract TreeModel getAllMaterialCategories(); public abstract BigDecimal getTotalUnits(); public abstract BigDecimal getTotalPrice(); /** * On selecting category, refresh {@link MaterialAssignment} associated with * selected {@link MaterialCategory} */ public void refreshMaterialAssigments() { final List<A> materials = getAssignedMaterials(); gridMaterials.setModel(new SimpleListModel(materials)); reloadGridMaterials(); } public List<A> getAssignedMaterials() { final Treeitem treeitem = categoriesTree.getSelectedItem(); return getAssignedMaterials(treeitem); } private List<A> getAssignedMaterials(Treeitem treeitem) { final MaterialCategory materialCategory = (treeitem != null) ? (MaterialCategory) treeitem.getValue() : null; return getAssignedMaterials(materialCategory); } public List<A> getAssignedMaterials(MaterialCategory materialCategory) { return getModel().getAssignedMaterials(materialCategory); } /** * On changing total price, recalculate unit price and refresh categories tree * * @param row */ public void updateTotalPrice(Row row) { final A materialAssignment = (A) row.getValue(); reloadGridMaterials(); refreshTotalPriceAndTotalUnits(materialAssignment); } protected abstract Double getTotalPrice(A materialAssignment); /** * Refresh categoriesTree since it shows totalUnits and totalPrice as well */ private void refreshTotalPriceAndTotalUnits(A materialAssignment) { final Treeitem item = findMaterialCategoryInTree(getCategory(materialAssignment), categoriesTree); if (item != null) { // Reload categoriesTree categoriesTree.setModel(getMaterialCategories()); } } private Treeitem findMaterialCategoryInTree(MaterialCategory category, Tree tree) { for (Iterator i = tree.getItems().iterator(); i.hasNext();) { Treeitem treeitem = (Treeitem) i.next(); final MaterialCategory materialCategory = (MaterialCategory) treeitem.getValue(); if (category.equals(materialCategory)) { return treeitem; } } return null; } private MaterialCategory getCategory(A assignment) { return getMaterial(assignment).getCategory(); } /** * Search materials on pressing search button */ public void searchMaterials() { final String text = txtSearchMaterial.getValue(); final MaterialCategory materialCategory = getSelectedCategory(allCategoriesTree); getModel().searchMaterials(text, materialCategory); Util.reloadBindings(lbFoundMaterials); } /** * Returns {@link MaterialCategory} associated with selected {@link Treeitem} in {@link Tree} * * @param tree * @return */ private MaterialCategory getSelectedCategory(Tree tree) { final Treeitem treeitem = tree.getSelectedItem(); return (treeitem != null) ? (MaterialCategory) treeitem.getValue() : null; } /** * Get materials found on latest search * * @return */ public List<Material> getMatchingMaterials() { return getModel().getMatchingMaterials(); } /** * Assigns a list of selected {@link Material} to current {@link OrderElement} */ public void assignSelectedMaterials() { Set<Material> materials = getSelectedMaterials(); if (materials.isEmpty()) { return; } for (Material each : materials) { getModel().addMaterialAssignment(each); } categoriesTree.clearSelection(); tbAssignedMaterials.setSelected(true); lbFoundMaterials.clearSelection(); Util.reloadBindings(categoriesTree); reloadGridMaterials(); } private Set<Material> getSelectedMaterials() { Set<Material> result = new HashSet<Material>(); final Set<Listitem> listitems = lbFoundMaterials.getSelectedItems(); for (Listitem each : listitems) { final Material material = (Material) each.getValue(); result.add(material); } return result; } public void clearSelectionCategoriesTree() { categoriesTree.clearSelection(); reloadGridMaterials(); } private void reloadGridMaterials() { if (gridMaterials != null) { Util.reloadBindings(gridMaterials); } } public void clearSelectionAllCategoriesTree() { allCategoriesTree.clearSelection(); retrieveAllMaterials(); Util.reloadBindings(lbFoundMaterials); } private void retrieveAllMaterials() { getModel().searchMaterials("", null); } public MaterialCategoryRenderer getMaterialCategoryRenderer() { return new MaterialCategoryRenderer(); } private class MaterialCategoryRenderer implements TreeitemRenderer { /** * Copied verbatim from org.zkoss.zul.Tree; */ @Override public void render(Treeitem ti, Object node) { final MaterialCategory materialCategory = (MaterialCategory) node; Label lblName = new Label(materialCategory.getName()); Treerow tr = null; ti.setValue(node); if (ti.getTreerow() == null) { tr = new Treerow(); tr.setParent(ti); ti.setOpen(true); // Expand node } else { tr = ti.getTreerow(); tr.getChildren().clear(); } // Add category name Treecell cellName = new Treecell(); cellName.addEventListener("onClick", new EventListener() { @Override public void onEvent(Event event) { getModel().searchMaterials("", materialCategory); Util.reloadBindings(lbFoundMaterials); } }); lblName.setParent(cellName); cellName.setParent(tr); } } public MaterialCategoryWithUnitsAndPriceRenderer getMaterialCategoryWithUnitsAndPriceRenderer() { return new MaterialCategoryWithUnitsAndPriceRenderer(); } private class MaterialCategoryWithUnitsAndPriceRenderer implements TreeitemRenderer { /** * Copied verbatim from org.zkoss.zul.Tree; */ @Override public void render(Treeitem ti, Object node) { final MaterialCategory materialCategory = (MaterialCategory) node; Label lblName = new Label(materialCategory.getName()); Label lblUnits = new Label(getUnits(materialCategory).toString()); Label lblPrice = new Label(getPrice(materialCategory).toString() + getCurrencySymbol()); Treerow tr = null; ti.setValue(node); if (ti.getTreerow() == null) { tr = new Treerow(); tr.setParent(ti); ti.setOpen(true); // Expand node } else { tr = ti.getTreerow(); tr.getChildren().clear(); } // Add category name Treecell cellName = new Treecell(); lblName.setParent(cellName); cellName.setParent(tr); // Add total assigned material units in category Treecell cellUnits = new Treecell(); lblUnits.setParent(cellUnits); cellUnits.setParent(tr); // Add total price for assigned materials in category Treecell cellPrice = new Treecell(); lblPrice.setParent(cellPrice); cellPrice.setParent(tr); } private BigDecimal getUnits(MaterialCategory materialCategory) { return getModel().getUnits(materialCategory); } private BigDecimal getPrice(MaterialCategory materialCategory) { return getModel().getPrice(materialCategory); } } /** * On clicking remove {@link MaterialAssignment}, shows dialog for * confirming removing selected element * * @param materialAssignment */ public void showRemoveMaterialAssignmentDlg(A materialAssignment) { try { int status = Messagebox.show( _("Delete item {0}. Are you sure?", getMaterial(materialAssignment).getCode()), _("Delete"), Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION); if (Messagebox.OK == status) { removeMaterialAssignment(materialAssignment); } } catch (InterruptedException e) { throw new RuntimeException(e); } } protected abstract Material getMaterial(A materialAssignment); private void removeMaterialAssignment(A materialAssignment) { getModel().removeMaterialAssignment(materialAssignment); reloadGridMaterials(); reloadTree(categoriesTree); } private void reloadTree(Tree tree) { final Treeitem treeitem = tree.getSelectedItem(); if (treeitem != null) { final MaterialCategory materialCategory = (MaterialCategory) treeitem.getValue(); tree.setModel(getMaterialCategories()); locateAndSelectMaterialCategory(tree, materialCategory); } else { tree.setModel(getMaterialCategories()); reloadGridMaterials(); } } private boolean locateAndSelectMaterialCategory(Tree tree, MaterialCategory materialCategory) { Treeitem treeitem = findTreeItemByMaterialCategory(tree.getRoot(), materialCategory); if (treeitem != null) { treeitem.setSelected(true); return true; } return false; } @SuppressWarnings("unchecked") private Treeitem findTreeItemByMaterialCategory(Component node, MaterialCategory materialCategory) { if (node instanceof Treeitem) { final Treeitem treeitem = (Treeitem) node; final MaterialCategory _materialCategory = (MaterialCategory) treeitem.getValue(); if (_materialCategory.getId().equals(materialCategory.getId())) { return treeitem; } } for (Iterator i = node.getChildren().iterator(); i.hasNext();) { Object obj = i.next(); if (obj instanceof Component) { Treeitem treeitem = findTreeItemByMaterialCategory((Component) obj, materialCategory); if (treeitem != null) { return treeitem; } } } return null; } /** * On clicking Split button, shows dialog for splitting selected * {@link MaterialAssignment} into two * * @param materialAssignment */ @SuppressWarnings("unchecked") public void showSplitMaterialAssignmentDlg(A materialAssignment) { MessageboxDlg dialogSplitAssignment; final String message = _("Do you want to split the material assignment {0}?", getMaterial(materialAssignment).getCode()); Map args = new HashMap(); args.put("message", message); args.put("title", _("Split new assignment")); args.put("OK", Messagebox.OK); args.put("CANCEL", Messagebox.CANCEL); args.put("icon", Messagebox.QUESTION); dialogSplitAssignment = (MessageboxDlg) Executions .createComponents("/orders/_splitMaterialAssignmentDlg.zul", self, args); Decimalbox dbUnits = (Decimalbox) dialogSplitAssignment.getFellowIfAny("dbUnits"); dbUnits.setValue(getUnits(materialAssignment)); try { dialogSplitAssignment.doModal(); int status = dialogSplitAssignment.getResult(); if (Messagebox.OK == status) { splitMaterialAssignment(materialAssignment, dbUnits.getValue()); } } catch (SuspendNotAllowedException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } } /** * Creates a new {@link MaterialAssignment} out of materialAssignment, but * setting its units attribute to units. * * materialAssignment passed as parameter decreases its units attribute in units * * @param materialAssignment * @param units */ private void splitMaterialAssignment(A materialAssignment, BigDecimal units) { A newAssignment = copyFrom(materialAssignment); BigDecimal currentUnits = getUnits(materialAssignment); if (units.compareTo(currentUnits) > 0) { units = currentUnits; currentUnits = BigDecimal.ZERO; } else { currentUnits = currentUnits.subtract(units); } setUnits(newAssignment, units); setUnits(materialAssignment, currentUnits); getModel().addMaterialAssignment(newAssignment); reloadGridMaterials(); } protected abstract void setUnits(A assignment, BigDecimal units); protected abstract A copyFrom(A assignment); protected abstract BigDecimal getUnits(A assignment); private UnitTypeListRenderer unitTypeListRenderer = new UnitTypeListRenderer(); public List<UnitType> getUnitTypes() { return getModel().getUnitTypes(); } public void selectUnitType(Component self) { Listitem selectedItem = ((Listbox) self).getSelectedItem(); UnitType unitType = (UnitType) selectedItem.getValue(); Material material = (Material) ((Row) self.getParent()).getValue(); material.setUnitType(unitType); } public UnitTypeListRenderer getRenderer() { return unitTypeListRenderer; } /** * RowRenderer for a @{UnitType} element * @author Susana Montes Pedreira <smontes@wirelessgalicia.com> */ public class UnitTypeListRenderer implements ListitemRenderer { @Override public void render(Listitem listItem, Object data) { final UnitType unitType = (UnitType) data; listItem.setValue(unitType); Listcell listCell = new Listcell(unitType.getMeasure()); listItem.appendChild(listCell); Listbox listbox = listItem.getListbox(); Component parent = listbox.getParent(); if (parent instanceof Row) { Object assigment = (Object) ((Row) parent).getValue(); if (getModel().isCurrentUnitType(assigment, unitType)) { listItem.getListbox().setSelectedItem(listItem); } return; } if (parent instanceof Listcell) { Material material = (Material) ((Listitem) (parent.getParent())).getValue(); if (isCurrentUnitType(material, unitType)) { listItem.getListbox().setSelectedItem(listItem); } } } } private boolean isCurrentUnitType(Material material, UnitType unitType) { return ((material != null) && (material.getUnitType() != null) && (unitType.getId().equals(material.getUnitType().getId()))); } public String getCurrencySymbol() { return Util.getCurrencySymbol(); } public String getMoneyFormat() { return Util.getMoneyFormat(); } }