package org.yamcs.ui; import java.awt.Component; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JTree; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import org.yamcs.xtce.ContainerEntry; import org.yamcs.xtce.Parameter; import org.yamcs.xtce.ParameterEntry; import org.yamcs.xtce.SequenceContainer; import org.yamcs.xtce.SequenceEntry; import org.yamcs.xtce.SpaceSystem; import org.yamcs.xtce.XtceDb; /** * Models an XtceDb with the root as the root SpaceSystem, immediate children * as the SequenceContainers, and leaves as their ParameterEntry instances. * * @author atu * */ public class XtceDbTreeModel implements TreeModel { protected XtceDb xtcedb; protected SpaceSystem root; protected List<TreeModelListener> listeners = new ArrayList<TreeModelListener>(); protected List<SequenceContainer> visibleContainers = new ArrayList<SequenceContainer>(); protected Map<SequenceContainer, List<ParameterEntry>> visibleEntries = new HashMap<SequenceContainer, List<ParameterEntry>>(); public XtceDbTreeModel( XtceDb db ) { xtcedb = db; } /** * Creates the cache where we map the modelled hierarchy to the Xtce * instances. */ protected void createCache() { visibleContainers.clear(); visibleEntries.clear(); if( xtcedb == null ) { return; } for( SequenceContainer sc : xtcedb.getSequenceContainers() ) { visibleContainers.add( sc ); visibleEntries.put(sc, getParameterEntries( sc ) ); } Collections.sort( visibleContainers, new OpsNameComparator() ); } protected List<ParameterEntry> getParameterEntries( Object container ) { List<ParameterEntry> entryList = new ArrayList<ParameterEntry>(); if( container instanceof SequenceContainer ) { for( SequenceEntry se : ((SequenceContainer)container).getEntryList() ) { if( se instanceof ParameterEntry ) { entryList.add( (ParameterEntry)se ); } else if ( se instanceof ContainerEntry ) { entryList.addAll( getParameterEntries( ((ContainerEntry)se).getRefContainer() ) ); } } } return entryList; } @Override public void addTreeModelListener(TreeModelListener l) { listeners.add( l ); } @Override public void removeTreeModelListener(TreeModelListener l) { listeners.remove( l ); } @Override public Object getChild(Object parent, int index) { if( parent == root ) { return visibleContainers.get(index); } if ( parent instanceof SequenceContainer ) { return visibleEntries.get(parent).get(index); } return null; } @Override public int getChildCount(Object parent) { if( parent == root ) { return visibleContainers.size(); } if ( parent instanceof SequenceContainer ) { return visibleEntries.get(parent).size(); } return 0; } @Override public int getIndexOfChild(Object parent, Object child) { if( parent == root ) { return visibleContainers.indexOf( child ); } else if ( parent instanceof SequenceContainer ) { return visibleEntries.get(parent).indexOf( child ); } return -1; } @Override public Object getRoot() { if( root == null ) { if( xtcedb != null ) { root = xtcedb.getRootSpaceSystem(); } createCache(); } return root; } @Override public boolean isLeaf(Object node) { return ( node instanceof ParameterEntry ); } @Override public void valueForPathChanged(TreePath path, Object newValue) { // Not editable } } /** * Extends base class by adding ability to dynamically include or exclude * specific objects. * * Assumes that a SequenceContainer is visible if any of its ParameterEntry * instances are visible, and that the root is always visible. * * Specify always shown objects directly (setAlwaysShown), never shown objects * (setNeverShown), or use a search string which is matched against the * opsname of the object. * * @author atu * */ class FilterableXtceDbTreeModel extends XtceDbTreeModel { private List<String> filterTerms; private List<Object> alwaysShown; private List<Object> neverShown; public boolean allParamsVisibleIfParentVisible = true; public FilterableXtceDbTreeModel(XtceDb db) { super(db); filterTerms = new ArrayList<String>(); alwaysShown = new ArrayList<Object>(); neverShown = new ArrayList<Object>(); } protected void createCache() { visibleContainers.clear(); visibleEntries.clear(); if( xtcedb == null ) { return; } for( SequenceContainer sc : xtcedb.getSequenceContainers() ) { visibleEntries.put(sc, new ArrayList<ParameterEntry>() ); boolean addContainer = isVisible( sc ); for( ParameterEntry pe : getParameterEntries( sc ) ) { if( (allParamsVisibleIfParentVisible && addContainer) || isVisible( pe ) ) { addContainer = true; visibleEntries.get(sc).add(pe); } } if( addContainer ) { visibleContainers.add( sc ); } } Collections.sort( visibleContainers, new OpsNameComparator() ); } public void setFilterText( String filter ) { filterTerms.clear(); for( String term : filter.toLowerCase().split( "\\*" ) ) { filterTerms.add( term ); } createCache(); signalTreeStructureChanged(); } private String getFilterableText( Object o ) { String name = null; if( o instanceof SequenceContainer ) { name = ((SequenceContainer)o).getOpsName().toLowerCase(); } else if ( o instanceof ParameterEntry ) { name = ((ParameterEntry)o).getParameter().getOpsName().toLowerCase(); } else { name = o.toString().toLowerCase(); } return name; } protected boolean isVisible( Object o ) { if( o == root ) { return true; } if( alwaysShown.contains( o ) ) { return true; } if( neverShown.contains( o ) ) { return false; } String haystack = getFilterableText(o); for( String needle : filterTerms ) { if( ! haystack.contains( needle ) ) { return false; } } return true; } public List<Object> getAlwaysShown() { return alwaysShown; } public void setAlwaysShown(List<Object> alwaysShown) { this.alwaysShown = alwaysShown; createCache(); } public List<Object> getNeverShown() { return neverShown; } public void setNeverShown(List<Object> neverShown) { this.neverShown = neverShown; createCache(); } private void signalTreeStructureChanged() { for( TreeModelListener l : listeners ) { l.treeStructureChanged( new TreeModelEvent(this, new Object[]{root}) ); } } } class OpsNameComparator implements Comparator<SequenceContainer> { @Override public int compare( SequenceContainer o1, SequenceContainer o2 ) { return o1.getOpsName().compareTo( o2.getOpsName() ); } } /** * Displays OpsNames for Xtce objects, and the raw type of ParameterEntry * instances in a tooltip. * * @author atu */ class XtceDbCellRenderer extends DefaultTreeCellRenderer { private static final long serialVersionUID = 201211201034L; public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { String tooltipBody = null; if( value instanceof SpaceSystem ) { super.getTreeCellRendererComponent(tree, ((SpaceSystem)value).getOpsName(), selected, expanded, leaf, row, hasFocus); tooltipBody = ( ((SpaceSystem)value).getQualifiedName()+"<br />"+((SpaceSystem)value).getHeader() ); } else if ( value instanceof SequenceContainer ) { super.getTreeCellRendererComponent(tree, ((SequenceContainer)value).getOpsName(), selected, expanded, leaf, row, hasFocus); tooltipBody = ((SequenceContainer)value).getName(); if( ((SequenceContainer)value).getAliasSet() != null ) { tooltipBody += "<br />"+((SequenceContainer)value).getAliasSet().toString(); } } else if ( value instanceof ParameterEntry ) { super.getTreeCellRendererComponent(tree, ((ParameterEntry)value).getParameter().getOpsName(), selected, expanded, leaf, row, hasFocus); Parameter p = ((ParameterEntry)value).getParameter(); tooltipBody = "Type: "+p.getParameterType().toString().replaceFirst( " encoding:", "<br />Encoding:" ); } else if ( value instanceof ContainerEntry ) { super.getTreeCellRendererComponent(tree, "("+((ContainerEntry)value).getLocationInContainerInBits()+") "+((ContainerEntry)value).getRefContainer().getOpsName(), selected, expanded, leaf, row, hasFocus); tooltipBody = ((ContainerEntry)value).getRefContainer().getOpsName(); } else { super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); } if( tooltipBody != null ) { setToolTipText( "<html>"+tooltipBody+"</html>" ); } return this; } }