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

xdev.ui.DefaultMasterDetailHandler Maven / Gradle / Ivy

/*
 * XDEV Application Framework - XDEV Application Framework
 * Copyright © 2003 XDEV Software (https://xdev.software)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */
package xdev.ui;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JComponent;

import xdev.db.DBConnection;
import xdev.db.DBDataSource;
import xdev.db.DBException;
import xdev.db.Result;
import xdev.db.sql.Condition;
import xdev.db.sql.SELECT;
import xdev.ui.FormularComponent.ValueChangeListener;
import xdev.ui.MasterDetailComponent.DetailHandler;
import xdev.util.logging.LoggerFactory;
import xdev.util.logging.XdevLogger;
import xdev.vt.EntityRelationship;
import xdev.vt.EntityRelationship.Entity;
import xdev.vt.EntityRelationships;
import xdev.vt.KeyValues;
import xdev.vt.VirtualTable;
import xdev.vt.VirtualTable.VirtualTableRow;
import xdev.vt.VirtualTableColumn;


/**
 * Default implementation of {@link MasterDetailHandler}.
 * 

* If you plan to implement your own {@link MasterDetailHandler} this is the * best point to start. *

* * @see MasterDetail#setHandler(MasterDetailHandler) * @see MasterDetailComponent * @author XDEV Software */ public class DefaultMasterDetailHandler implements MasterDetailHandler { /** * Logger instance for this class. */ private static final XdevLogger log = LoggerFactory .getLogger(DefaultMasterDetailHandler.class); protected final static String MASTER_COMPONENT = "MASTER_COMPONENT"; protected final static String DETAIL_COMPONENT = "DETAIL_COMPONENT"; protected final static String WORKING = MasterDetailHandler.class.getName() .concat(".working"); /** * {@inheritDoc} */ @Override public void connect(MasterDetailComponent master, Formular detail) throws MasterDetailException { if(master.isMultiSelect()) { throw new MasterDetailException(master,detail, "The master component does support multiple selection"); } connectImpl(master,detail); } /** * Called from {@link #connect(MasterDetailComponent, Formular)} after * preconditions are checked successfully. * * @param master * the component which operates as master * @param detail * the {@link Formular} which operates as detail view */ protected void connectImpl(final MasterDetailComponent master, final Formular detail) { master.addValueChangeListener(new ValueChangeListener() { @Override public void valueChanged(Object eventObject) { VirtualTableRow row = master.getSelectedVirtualTableRow(); if(row == null) { return; } VirtualTable formularVT = detail.getVirtualTable(); if(formularVT == null) { return; } KeyValues pkValues = new KeyValues(row); row = formularVT.getRow(pkValues); try { if(!Boolean.TRUE.equals(detail.getClientProperty(OFFLINE))) { if(row != null) { row.reload(); } else { if(formularVT.queryAndAppend(pkValues)) { row = formularVT.getLastRow(); } } } if(row != null) { detail.setModel(row); } } catch(Exception e) { log.error(e); } } }); } /** * {@inheritDoc} */ @Override public void connect(MasterDetailComponent master, MasterDetailComponent detail) throws MasterDetailException { if(master.isMultiSelect()) { throw new MasterDetailException(master,detail, "The master component does support multiple selection"); } connectImpl(master,detail); } /** * Called from * {@link #connect(MasterDetailComponent, MasterDetailComponent)} after * preconditions are checked successfully. * * @param master * the component which operates as master * @param detail * the component which operates as detail view */ protected void connectImpl(MasterDetailComponent master, MasterDetailComponent detail) { new MasterDetailComponentConnection(master,detail); } /** * Default implementation for the connection of two * {@link MasterDetailComponent}s. */ protected static class MasterDetailComponentConnection { /** * The master component of this connection. */ protected final MasterDetailComponent master; /** * The detail component of this connection. */ protected final MasterDetailComponent detail; protected MasterDetailComponentConnection(MasterDetailComponent master, MasterDetailComponent detail) { this.master = master; this.detail = detail; ((JComponent)detail).putClientProperty(MASTER_COMPONENT,master); ((JComponent)master).putClientProperty(DETAIL_COMPONENT,detail); master.addValueChangeListener(createMasterHandler()); detail.setDetailHandler(createDetailHandler()); } /** * Factory method for the handler of the master component. * * @return the handler for the master component */ protected ValueChangeListener createMasterHandler() { return new DefaultMasterHandler(); } protected class DefaultMasterHandler implements ValueChangeListener { /** * {@inheritDoc} */ @Override public void valueChanged(Object eventObject) { if(!isWorking(master) && master.getSelectedVirtualTableRow() != null) { try { setWorking(master,true); setWorking(detail,true); updateDetailComponent(); } catch(MasterDetailException mde) { log.error(mde); } finally { setWorking(master,false); setWorking(detail,false); } } } } protected void updateDetailComponent() throws MasterDetailException { MasterDetailInfo info = new MasterDetailInfo(master,detail); Condition condition = getAndCondition(info.detailKeyColumns); updateDetailComponent(condition,info.getMasterKeyValues(),info); clearFollowingDetailComponents(); } protected void updateDetailComponent(Condition condition, Object[] values, MasterDetailInfo info) { detail.updateModel(condition,values); } protected void clearFollowingDetailComponents() { MasterDetailComponent detail = MasterDetailComponentConnection.this.detail; while((detail = getDetail(detail)) != null) { detail.clearModel(); } } /** * Factory method for the handler of the detail component. * * @return the handler for the detail component */ protected DetailHandler createDetailHandler() { return new DefaultDetailHandler(); } protected class DefaultDetailHandler implements DetailHandler { /** * {@inheritDoc} */ @Override public void checkDetailView(Map value) { if(!isWorking(detail)) { try { updateMasterComponents(value.values().toArray()); } catch(Exception e) { log.error(e); } } } } protected void updateMasterComponents(Object[] detailPrimaryKeyValue) throws MasterDetailException, DBException { List stack = new ArrayList(); MasterDetailComponent c = detail; while(c != null) { stack.add(0,c); c = getMaster(c); } DBDataSource dataSource = null; SELECT select = new SELECT(); int max = stack.size() - 2; MasterDetailInfo[] infos = new MasterDetailInfo[max + 1]; for(int i = 0; i <= max; i++) { MasterDetailComponent master = stack.get(i); MasterDetailComponent detail = stack.get(i + 1); MasterDetailInfo info = new MasterDetailInfo(master,detail); infos[i] = info; select.columns((Object[])info.masterKeyColumns); if(i == 0) { dataSource = info.masterVT.getDataSource(); select.FROM(info.masterVT); } select.INNER_JOIN(info.detailVT, getAndCondition(info.detailKeyColumns,info.masterKeyColumns)); if(i == max) { select.WHERE(getAndCondition(info.getDetailPrimaryKeyColumns())); } } DBConnection con = dataSource.openConnection(); try { Result keys = con.query(select,detailPrimaryKeyValue); if(keys.next()) { int col = 0; for(MasterDetailInfo info : infos) { try { setWorking(master,true); setWorking(detail,true); int kc = info.masterKeyColumns.length; Object[] key = new Object[kc]; Map record = new HashMap(); for(int i = 0; i < kc; i++) { key[i] = keys.getObject(col++); record.put(info.masterKeyColumns[i].getName(),key[i]); } info.master.setMasterValue(info.masterVT,record); info.detail.updateModel(getAndCondition(info.detailKeyColumns),key); } finally { setWorking(master,false); setWorking(detail,false); } } } } finally { if(con != null) { con.close(); } } } protected Condition getAndCondition(VirtualTableColumn[] leftSideColumns, VirtualTableColumn[] rightSideColumns) { Condition condition = null; for(int i = 0; i < leftSideColumns.length; i++) { Condition part = leftSideColumns[i].eq(rightSideColumns[i]); if(condition == null) { condition = part; } else { condition = condition.AND(part); } } return condition; } protected Condition getAndCondition(VirtualTableColumn[] columns) { Condition condition = null; for(int i = 0; i < columns.length; i++) { Condition part = columns[i].eq("?"); if(condition == null) { condition = part; } else { condition = condition.AND(part); } } return condition; } protected boolean isWorking(MasterDetailComponent cpn) { return Boolean.TRUE.equals(((JComponent)cpn).getClientProperty(WORKING)); } protected void setWorking(MasterDetailComponent cpn, boolean working) { ((JComponent)cpn).putClientProperty(WORKING,working ? Boolean.TRUE : null); } protected MasterDetailComponent getDetail(MasterDetailComponent master) { Object o = ((JComponent)master).getClientProperty(DETAIL_COMPONENT); if(o != null && o instanceof MasterDetailComponent) { return (MasterDetailComponent)o; } return null; } protected MasterDetailComponent getMaster(MasterDetailComponent detail) { Object o = ((JComponent)detail).getClientProperty(MASTER_COMPONENT); if(o != null && o instanceof MasterDetailComponent) { return (MasterDetailComponent)o; } return null; } } /** * Holds information of a distinct master detail connection. * * @see #getMasterKeyValue() * @see #getDetailPrimaryKeyColumn() */ protected static class MasterDetailInfo { public final MasterDetailComponent master; public final VirtualTable masterVT; public final VirtualTableColumn[] masterKeyColumns; public final MasterDetailComponent detail; public final VirtualTable detailVT; public final VirtualTableColumn[] detailKeyColumns; public final EntityRelationship relation; public final Entity detailEntity; protected MasterDetailInfo(MasterDetailComponent master, MasterDetailComponent detail) throws MasterDetailException { this.master = master; this.detail = detail; masterVT = master.getVirtualTable(); if(masterVT == null) { throw new MasterDetailException(master,detail, "Master component has no VirtualTable"); } VirtualTableColumn[] masterKeys = masterVT.getPrimaryKeyColumns(); if(masterKeys == null || masterKeys.length == 0) { throw new MasterDetailException(master,detail,"Master has no primary key"); } detailVT = detail.getVirtualTable(); if(detailVT == null) { throw new MasterDetailException(master,detail, "Detail component has no VirtualTable"); } relation = EntityRelationships.getModel().getRelationship(masterVT.getName(), VirtualTableColumn.getNamesOf(masterKeys),detailVT.getName()); if(relation == null) { throw new MasterDetailException(master,detail,"No relation found"); } Entity masterEntity = relation.getReferrer(masterVT.getName()); // now get the master columns, to get the right column order (13188) masterKeyColumns = masterVT.getColumns(masterEntity.getColumnNames()); detailEntity = relation.getReferrer(detailVT.getName()); detailKeyColumns = detailVT.getColumns(detailEntity.getColumnNames()); for(int i = 0; i < detailKeyColumns.length; i++) { if(detailKeyColumns[i] == null) { throw new MasterDetailException(master,detail,"Detail column '" + detailEntity.getColumnName(i) + "' not found in relation"); } } } /** * Determines the current selected key of the master component. * * @return the current master key value or null if nothing * is selected * @throws MasterDetailException * if the column count of the master's primary key != 1 */ protected Object[] getMasterKeyValues() throws MasterDetailException { VirtualTableRow selectedRow = master.getSelectedVirtualTableRow(); if(selectedRow == null) { return null; } KeyValues masterKeyValues = new KeyValues(selectedRow); return masterKeyValues.getValues(VirtualTableColumn.getNamesOf(masterKeyColumns)); } /** * Determines the primary key columns of the detail table. * * @return the detail's primary key columns * @throws MasterDetailException * if the detail table has no primary key */ protected VirtualTableColumn[] getDetailPrimaryKeyColumns() throws MasterDetailException { VirtualTableColumn[] detailKeys = detailVT.getPrimaryKeyColumns(); if(detailKeys == null || detailKeys.length == 0) { throw new MasterDetailException(master,detail,"Detail has no primary key"); } return detailKeys; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy