/*
 This file is part of the software framework JStatCom, 
 a library for building applications for data based analysis.

 Copyright (C) 2000-2005  Markus Kraetzig

 This library 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 2.1 of the
 License, or (at your option) any later version.

 This library 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 library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 USA
 */

package com.jstatcom.model;

import java.awt.Component;
import java.awt.Container;
import java.awt.LayoutManager;
import java.util.Iterator;

import javax.swing.JPanel;

import com.jstatcom.component.Card;
import com.jstatcom.component.JHelpContextMgr;

/**
 * Panel to be used by components that need access to the symbol table
 * hierarchy. The global and local symbol tables can be accessed via
 * global and local.
 * 

* The global symbol table is typically held by a ModelFrame and * stores data that must be shared by all panels belonging to a model frame. *

* The global symbol table is found by traversing the component hierarchy on * addNotify. The next SymbolScope instance is * queried for global. If no parent component is a * SymbolScope, then local is used for * global. *

* The local symbol table is used to store data that is only visible to this * component, but that should be displayed in the SymbolTree to * make it transparent for viewing, saving and debugging. *

* If this component is used in a CardDisplayPanel, subclasses * can overwrite shown to define the behaviour when this panel is * displayed or hidden. This is often a good time to collect user specification * and prepare data that is required by another panel. *

* All ModelPanel instances are registered with * JHelpContextMgr for context sensitive help. The respective * help IDs can be set in an XML file. * * @author Markus Kraetzig * @see com.jstatcom.component.CardDisplayPanel * @see com.jstatcom.model.ModelFrame */ public class ModelPanel extends JPanel implements SymbolScope, Card { private SymbolTable localSymbolTable = null; private SymbolTable preGlobalSymbolTable = null; private SymbolTable preUpperSymbolTable = null; private SymbolScope parentScope = null; // contains all pre_ tables until the panels are shown private static final SymbolTable preParentTable = new SymbolTable("_TMP", null, true); /** * Creates a ModelPanel. */ public ModelPanel() { this(null); } /** * Creates a ModelPanel with a layout manager. * * @param layout */ public ModelPanel(LayoutManager layout) { super(layout); JHelpContextMgr.register(this); } /** * Calls supermethod and finds parent instance of SymbolScope. * If no parent scope can be found, global and local will return the same * SymbolTable, meaning that this component will hold the * global table. */ public void addNotify() { super.addNotify(); if (parentScope == null) parentScope = ModelPanel.findParentScope(this); if (parentScope == null) parentScope = this; // clear all dummy tables if (preGlobalSymbolTable != null) { global(); // inform all childs about change in table updateUpperAfterNotify(); } if (preUpperSymbolTable != null) { upper(); // inform all childs about change in table updateUpperAfterNotify(); } } /** * Gets the local symbol table from the closest parent component that * implements SymbolScope. If there is no parent * implementing SymbolScope, the global, upper and local * symbol table are the same. *

* It might happen that this method is called before this component is added * to the component hierarchy. In this case an initialized symbol table is * returned. If the upper table changed after adding this to the hierarchy, * all symbols from that table are copied to the new upper table and the * reference changes. The remaining table is cleared and will not be * displayed in the SymbolTree anymore. * * @return the upper symbol table * @throws RuntimeException * if global of a parent SymbolScope * returns null */ public SymbolTable upper() { if (parentScope == this) return local(); if (parentScope == null) parentScope = ModelPanel.findParentScope(this); // now parentScope was found, add to true hierarchy if (parentScope != null) { SymbolTable sTable = parentScope.local(); if (sTable == null) throw new RuntimeException("Acquired symbol table was null."); // replace preliminary table if (preUpperSymbolTable != null) { for (Iterator iter = preUpperSymbolTable.iterator(); iter .hasNext();) { // copy all symbols from the dummy table to the real global // table Symbol element = (Symbol) iter.next(); sTable.setSymbol(element); } // also set local symbol table, because parent has changed if (localSymbolTable != null) moveLocalSymbolTable(sTable); preUpperSymbolTable.clear(); preUpperSymbolTable = null; } return sTable; } // return dummy table until parent scope is found if (preUpperSymbolTable == null) preUpperSymbolTable = new SymbolTable(getClass().getName() + "_preUpper", preParentTable, true); return preUpperSymbolTable; } private void moveLocalSymbolTable(SymbolTable sTable) { String name = getName(); if (name == null || name.length() < 1) name = getClass().getName(); SymbolTable newLocalSymbolTable = new SymbolTable(name, sTable); for (Iterator iter = localSymbolTable.iterator(); iter.hasNext();) { // copy all symbols from the dummy // local table to the real local // table Symbol element = (Symbol) iter.next(); newLocalSymbolTable.setSymbol(element); } localSymbolTable.clear(); localSymbolTable = newLocalSymbolTable; } /** * Gets the global symbol table from the closest parent component that * implements SymbolScope. If there is no parent * implementing SymbolScope, the global, upper and local * symbol table are the same. *

* It might happen that this method is called before this component is added * to the component hierarchy. In this case an initialized symbol table is * returned. If the global table changed after adding this to the hierarchy, * all symbols from that table are copied to the new global table and the * reference changes. The remaining table is cleared and will not be * displayed in the SymbolTree anymore. * * @return the global symbol table * @throws RuntimeException * if global of a parent SymbolScope * returns null */ public SymbolTable global() { if (parentScope == this) return local(); if (parentScope == null) parentScope = ModelPanel.findParentScope(this); if (parentScope != null) { SymbolTable sTable = parentScope.global(); if (sTable == null) throw new RuntimeException("Acquired symbol table was null."); if (preGlobalSymbolTable != null) { for (Iterator iter = preGlobalSymbolTable.iterator(); iter .hasNext();) { // copy all symbols from the dummy table to the real global // table Symbol element = (Symbol) iter.next(); sTable.setSymbol(element); } preGlobalSymbolTable.clear(); preGlobalSymbolTable = null; } return sTable; } // return dummy table until parent scope is found if (preGlobalSymbolTable == null) preGlobalSymbolTable = new SymbolTable(getClass().getName() + "_preGlobal", preParentTable, true); return preGlobalSymbolTable; } /** * Gets the local symbol table that is only visible to this panel. The name * of this symbol table is equal to getName. If the name is * empty, the classname is used instead. The parent is the upper symbol * table. * * @return the local symbol table */ public SymbolTable local() { if (localSymbolTable == null) { String name = getName(); if (name == null || name.length() < 1) name = getClass().getName(); if (parentScope == this) localSymbolTable = new SymbolTable(name, null); else localSymbolTable = new SymbolTable(name, upper()); } return localSymbolTable; } /** * Empty default imlementation for Card interface. This * method is only invoked if this component is shown or hidden by a * CardDisplayPanel. * * @param isShown * true if shown in CardDisplayPanel, * false if hidden */ public void shown(boolean isShown) { } /** * Finds the next parent that is a SymbolScope. The result * might be null, if this method is called on components * that are not added to a component hierarchy yet. In that case the method * must be run again on addNotify. *

* The result can be queried for local or global * to access the symbol table. *

* If the calling component does not find a SymbolScope * instance in the hierarchy even after addNotify was called, * is has to deal with the situation by either firing an exception or by * creating a new symbol table. * * @param comp * the component to start traversing the component hierarchy from * @return the component that is instance of SymbolScope * @throws IllegalArgumentException * if (comp == null) */ public static SymbolScope findParentScope(Component comp) { if (comp == null) throw new IllegalArgumentException("Argument was null."); // start from parent comp = comp.getParent(); SymbolScope symbolScope = null; // find next parent that is SymbolScope while (comp != null && symbolScope == null) { if (comp instanceof SymbolScope) { symbolScope = (SymbolScope) comp; break; } comp = comp.getParent(); } return symbolScope; } protected void updateUpperAfterNotify() { SymbolTable upper = upper(); if (localSymbolTable != null && localSymbolTable.getParentTable() != upper) moveLocalSymbolTable(upper); walkContainer(this); } // recursive call to update all child model panels, // also those that are not direct childs private void walkContainer(Container c) { for (int i = 0; i < c.getComponentCount(); i++) { Component cc = c.getComponent(i); if (cc instanceof ModelPanel) ((ModelPanel) cc).updateUpperAfterNotify(); else if (cc instanceof Container) { walkContainer((Container) cc); } } } }

Ads help maintain this website.