/*
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.