/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ package browser; import java.awt.event.ActionEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreeSelectionModel; import ilog.cpl.IlpEquipment; import ilog.cpl.IlpNetwork; import ilog.cpl.IlpTree; import ilog.cpl.datasource.IlpDataSource; import ilog.cpl.datasource.IlpIdentifierFactory; import ilog.cpl.datasource.IlpMutableDataSource; import ilog.cpl.datasource.structure.IlpChild; import ilog.cpl.datasource.structure.IlpContainer; import ilog.cpl.equipment.IlpEquipmentSelectionModel; import ilog.cpl.graph.background.IlpBackgroundHandlingException; import ilog.cpl.interactor.IlpGesture; import ilog.cpl.interactor.IlpViewActionEvent; import ilog.cpl.interactor.IlpViewInteractor; import ilog.cpl.model.IlpObject; import ilog.cpl.service.IlpURLAccessService; import ilog.cpl.storage.IlpDataSourceLoader; import ilog.cpl.util.selection.IlpObjectSelectionModel; // JTGO import statements import ilog.tgo.model.IltCardItem; import ilog.tgo.model.IltGroup; import ilog.tgo.model.IltNetworkElement; import ilog.tgo.model.IltObject; import ilog.tgo.model.IltShelf; import ilog.tgo.model.IltShelfItem; /** */ public class DrillDownManager extends Object implements TreeSelectionListener { IlpTree treeComponent; IlpNetwork networkComponent; IlpEquipment equipmentComponent; IlpObject currentNetworkRoot; IlpObject currentEquipmentRoot; boolean inDrillDown; PropertyChangeSupport propertyChangeSupport; /** Logger for local access */ protected static Logger log = Logger.getLogger(DrillDownManager.class.getName()); public static final String CURRENT_NETWORK_ROOT = "currentNetworkRoot"; public static final String CURRENT_EQUIPMENT_ROOT = "currentEquipmentRoot"; /** * Construct an instance that manages the given tree and network components. * Both components should share the same datasource. */ public DrillDownManager(IlpTree treeComponent, IlpNetwork networkComponent) { this(treeComponent, networkComponent, null); } /** * Construct an instance that manages the given tree and equipment components. * Both components should share the same datasource. */ public DrillDownManager(IlpTree treeComponent, IlpEquipment equipmentComponent) { this(treeComponent, null, equipmentComponent); } /** * Construct an instance that manages the given tree, network and equipment * components. All three components should share the same datasource. */ public DrillDownManager(IlpTree treeComponent, IlpNetwork networkComponent, IlpEquipment equipmentComponent) { this.treeComponent = treeComponent; this.networkComponent = networkComponent; this.equipmentComponent = equipmentComponent; this.currentNetworkRoot = null; this.currentEquipmentRoot = null; this.inDrillDown = false; propertyChangeSupport = new PropertyChangeSupport(this); TreeSelectionModel treeSelectionModel = treeComponent.getSelectionModel(); // Only allow single selection in the tree, to simplify computation // of corresponding network treeSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); // Add the drill down manager as a selection listener for the tree // Tree selection is handled specifically in this class, and not delegated // to the base class, as selecting an object in the tree triggers a // drill-down treeSelectionModel.addTreeSelectionListener(this); if (networkComponent != null) { IlpViewInteractor networkInteractor = networkComponent.getViewInteractor(); if (networkInteractor != null) networkInteractor.setGestureAction(IlpGesture.BUTTON1_DOUBLE_CLICKED, new DrillDownAction(this)); } } // -------------------------------------------------------------------------------- // Basic accessors and other common functions // -------------------------------------------------------------------------------- /** * Get the tree component associated with this manager. * * @return the tree component. May not be null. */ public IlpTree getTreeComponent() { return this.treeComponent; } /** * Get the network component associated with this manager. * * @return the network component, or null if there is none */ public IlpNetwork getNetworkComponent() { return this.networkComponent; } /** * Get the equipment component associated with this manager. * * @return the equipment component, or null if there is none */ public IlpEquipment getEquipmentComponent() { return this.equipmentComponent; } // Action implementation public class DrillDownAction extends AbstractAction { protected DrillDownManager manager; public DrillDownAction(DrillDownManager manager) { super(); this.manager = manager; } Override public void actionPerformed(ActionEvent e) { if (e instanceof IlpViewActionEvent) { IlpViewActionEvent viewEvent = (IlpViewActionEvent) e; IlpObject ilpObj = viewEvent.getIlpObject(); if (ilpObj != null) { log.log(Level.FINE, "Asked to drill down to: " + ilpObj); inDrillDown = true; showNetworkDetails(ilpObj); showEquipmentDetails(ilpObj); inDrillDown = false; return; } log.log(Level.FINER, "Double-click action detected at " + viewEvent.getPosition() + ", not on IlpObject"); } else log.log(Level.FINER, "Non-CPL action detected"); } } // TreeSelectionListener implementation /** * Listen for a change in the tree selection. When a new node is selected, * change the contents of the network and/or equipment components accordingly. */ Override public void valueChanged(TreeSelectionEvent e) { if (this.inDrillDown) return; IlpObjectSelectionModel selectionModel = (IlpObjectSelectionModel) e.getSource(); IlpObject selectedObject = selectionModel.getSelectedObject(); showNetworkDetails(selectedObject); if (selectedObject != null) showEquipmentDetails(selectedObject); } /** * Returns true if the given object is a root object. By default, return * objects that have no parents in the datasource. */ public boolean isRootObject(IlpObject obj) { IlpChild childItf = treeComponent.getDataSource().getChildInterface(obj); return ((childItf == null) || (childItf.getParent(obj) == null)); } // -------------------------------------------------------------------------------- // Manage drill down for network component // -------------------------------------------------------------------------------- /** * Get the parent object of the equipment currently being displayed. */ public IlpObject getCurrentNetworkRoot() { return this.currentNetworkRoot; } /** * Set the parent object of the network currently being displayed. This method * is protected because it should not be invoked directly. */ protected void setCurrentNetworkRoot(IlpObject root) { IlpObject oldRoot = this.currentNetworkRoot; this.currentNetworkRoot = root; propertyChangeSupport.firePropertyChange(CURRENT_NETWORK_ROOT, oldRoot, root); } public boolean hasSubNetwork(IlpObject obj) { // A more liberal implementation that allows children under non-groups // is also possible return (obj.getIlpClass().isSubClassOf(IltGroup.GetIlpClass())); } public void showNetworkDetails(IlpObject obj) { log.log(Level.FINE, "Asked to set network root object to " + obj); if (networkComponent == null) { log.log(Level.FINE, "Network component is null, doing nothing"); return; } if (obj == null) { showRootNetwork(); } else if (hasSubNetwork(obj)) { // if the object has a sub-network, show it showSubNetwork(obj); } else { // If there is no sub-network, show the // network to which this object belongs showParentNetwork(obj); } } protected void showRootNetwork() { if (getCurrentNetworkRoot() != null) { networkComponent.getAdapter().resetOrigins(); log.log(Level.FINER, "Reset network origins"); // Remove existing background try { networkComponent.removeBackgrounds(); } catch (IlpBackgroundHandlingException ex) { log.log(Level.WARNING, "Could not remove bacgrounds with this exception: " + ex.getLocalizedMessage()); } setCurrentNetworkRoot(null); } else log.log(Level.FINER, "Already showing root network, nothing changed"); } protected void showSubNetwork(IlpObject obj) { if (getCurrentNetworkRoot() != obj) { List<Object> originList = new ArrayList<Object>(); originList.add(obj.getIdentifier()); if (!originList.equals(networkComponent.getAdapter().getOrigins())) { // Second parameter is false, as we do not want to show the // origins themselves, only their children networkComponent.getAdapter().setOrigins(originList, false); log.log(Level.FINER, "Set network component origin to " + obj.getIdentifier()); // Load network configuration String templateBaseName = computeSubNetworkTemplateBaseName(obj); String configName = templateBaseName + "_config.css"; IlpURLAccessService urlService = networkComponent.getContext().getURLAccessService(); try { networkComponent.setStyleSheets(urlService.getFileLocation(configName) != null ? new String[] { IlpNetwork.DefaultConfigurationFileName, configName } : new String[] { IlpNetwork.DefaultConfigurationFileName }); } catch (Exception e) { e.printStackTrace(); } setCurrentNetworkRoot(obj); } } else log.log(Level.FINER, "Already showing sub-network of " + obj.getIdentifier() + ", nothing changed"); } protected void showParentNetwork(IlpObject obj) { IlpObject detailObj = null; if (isRootObject(obj)) { // if the object is a root, show the root network showRootNetwork(); detailObj = obj; } else { IlpDataSource ds = treeComponent.getDataSource(); IlpChild childItf = ds.getChildInterface(obj); IlpObject parent = ds.getObject(childItf.getParent(obj)); // Check if the parent has a subnetwork // If this object is an equipment detail, its parent will // not have a network, so we much check further up the tree if (hasSubNetwork(parent)) { showSubNetwork(parent); detailObj = obj; } else { // Show the next higher level of parent network showParentNetwork(parent); } } if (detailObj != null) { // Select the child object, and make sure it is visble // networkComponent.getSelectionModel().clearSelection(); // networkComponent.ensureVisible(detailObj); // networkComponent.getSelectionModel().addSelectionObject(detailObj); networkComponent.getSelectionModel().setSelectedObjects(Collections.singleton(detailObj)); networkComponent.ensureVisible(detailObj); } } // -------------------------------------------------------------------------------- // Manage drill down for equipment component // -------------------------------------------------------------------------------- /** * Get the parent object of the equipment currently being displayed. */ public IlpObject getCurrentEquipmentRoot() { return this.currentEquipmentRoot; } /** * Set the parent object of the equipment currently being displayed. This * method is protected because it should not be invoked directly. */ protected void setCurrentEquipmentRoot(IlpObject root) { IlpObject oldRoot = this.currentEquipmentRoot; this.currentEquipmentRoot = root; propertyChangeSupport.firePropertyChange(CURRENT_EQUIPMENT_ROOT, oldRoot, root); } public boolean hasEquipmentDetails(IlpObject obj) { return ((obj.getIlpClass() == IltNetworkElement.GetIlpClass()) || obj.getIlpClass().isSubClassOf(IltNetworkElement.GetIlpClass())); } public boolean isEquipmentDetail(IlpObject obj) { return (((obj.getIlpClass() == IltShelf.GetIlpClass()) || obj.getIlpClass().isSubClassOf(IltShelf.GetIlpClass())) || obj.getIlpClass().isSubClassOf(IltShelfItem.GetIlpClass()) || obj.getIlpClass().isSubClassOf(IltCardItem.GetIlpClass())); } protected void showEquipmentDetails(IlpObject obj) { log.log(Level.FINE, "Asked to show equipment details for " + obj); if (equipmentComponent == null) { log.log(Level.FINE, "Equipment component is null, doing nothing"); return; } if (hasEquipmentDetails(obj)) { // if equipment details are available, drill down in the equipment // component. // Second parameter is null, as there is no contained object to select setEquipmentRootObject(obj, null); } else if (isEquipmentDetail(obj)) { // Else if this is an equipment item, show the parent equipment // Second obj parameter is needed to have it selected and centered showParentEquipment(obj, obj); } else // if not equipment related, clear equipment component clearEquipmentDetails(); } protected void setEquipmentRootObject(IlpObject parent, IlpObject detailObj) { if (getCurrentEquipmentRoot() != parent) { setEquipmentOrigin(parent.getIdentifier()); String templateBaseName = computeEquipmentTemplateBaseName(parent); String configName = templateBaseName + "_config.css"; IlpURLAccessService urlService = equipmentComponent.getContext().getURLAccessService(); try { if (urlService.getFileLocation(configName) != null) { log.log(Level.FINER, "Loading config file " + configName); equipmentComponent.setStyleSheets(new String[] { IlpEquipment.DefaultConfigurationFileName, configName }); } else { log.log(Level.FINER, "Config file " + configName + " does not exist"); equipmentComponent.setStyleSheets(new String[] { IlpEquipment.DefaultConfigurationFileName }); } } catch (Exception e) { e.printStackTrace(); } setCurrentEquipmentRoot(parent); } else log.log(Level.FINER, parent.getIdentifier().toString() + " was alreay the root of the equipment view"); // Select the given contained object, and make sure it is visible IlpEquipmentSelectionModel selModel = equipmentComponent.getSelectionModel(); selModel.setSelectedObject(detailObj); if (detailObj != null) { equipmentComponent.ensureVisible(detailObj); } } protected void showParentEquipment(IlpObject obj, IlpObject detailObj) { // Should only be called for objects with a parent IlpDataSource ds = treeComponent.getDataSource(); IlpChild childItf = ds.getChildInterface(obj); IlpObject parent = ds.getObject(childItf.getParent(obj)); if (hasEquipmentDetails(parent)) { // Display the parent setEquipmentRootObject(parent, detailObj); } else if (isEquipmentDetail(parent)) { // recursively try the next higher level showParentEquipment(parent, detailObj); } else { // This should never happen.... clearEquipmentDetails(); } } public void clearEquipmentDetails() { if (!equipmentComponent.getAdapter().isShowingOrigin() || !Collections.EMPTY_LIST.equals(equipmentComponent.getAdapter().getOrigins())) { // Remove existing background try { equipmentComponent.removeBackgrounds(); } catch (IlpBackgroundHandlingException ex) { log.log(Level.WARNING, "Could not remove bacgrounds with this exception: " + ex.getLocalizedMessage()); } // Clear origins equipmentComponent.getAdapter().setOrigins(Collections.emptyList(), true); log.log(Level.FINER, "Cleared equipment component origins"); setCurrentEquipmentRoot(null); } else log.log(Level.FINER, "Equipment component was already empty, nothing changed"); } void setEquipmentOrigin(Object objId) { List<Object> originList = new ArrayList<Object>(); originList.add(objId); if (!originList.equals(equipmentComponent.getAdapter().getOrigins())) { // Second parameter is false, as we do not want to show the // origins themselves, only their children equipmentComponent.getAdapter().setOrigins(originList, false); log.log(Level.FINER, "Set equipment component origin to " + objId); } } // -------------------------------------------------------------------------------- // Manage loading of sub-network and equipment templates // -------------------------------------------------------------------------------- /** * Recursively load sub-networks and equipment details for all nodes in the * datasource that have them. */ void loadChildren() { Collection<IlpObject> rootObjects = treeComponent.getDataSource().getObjects(); Iterator<IlpObject> iter = rootObjects.iterator(); while (iter.hasNext()) loadChildren(iter.next()); } void loadChildren(IlpObject obj) { if (hasSubNetwork(obj)) { loadTemplate(obj, computeSubNetworkTemplateBaseName(obj) + "_template.xml"); // Recursively load children IlpContainer containerItf = treeComponent.getDataSource().getContainerInterface(obj); if (containerItf != null) { Collection<Object> children = containerItf.getChildren(obj); if ((children != null) && (children.size() > 0)) { // Clone list to avoid concurrent modification children = new ArrayList<Object>(children); Iterator<Object> iter = children.iterator(); while (iter.hasNext()) loadChildren(treeComponent.getDataSource().getObject((String) iter.next())); } } } if (hasEquipmentDetails(obj)) { String templateBaseName = computeEquipmentTemplateBaseName(obj); loadTemplate(obj, templateBaseName + "_template.xml"); } } /** * Compute the name of the subnetwork template to load for a given object */ String computeSubNetworkTemplateBaseName(IlpObject obj) { if (obj instanceof IltObject) return ((IltObject) obj).getName(); else { String className = obj.getIlpClass().getName(); return className.substring(className.lastIndexOf('.') + 1); } } /** * Compute the name of the equipment template to load for a given object */ String computeEquipmentTemplateBaseName(IlpObject obj) { String className = obj.getIlpClass().getName(); String shortClassName = className.substring(className.lastIndexOf('.') + 1); if (obj instanceof IltNetworkElement) { IltNetworkElement ne = (IltNetworkElement) obj; String typeName = ne.getType().getName(); return typeName + "_equipment"; } else return shortClassName + "_equipment"; } /** * Load a template file into the datasource under a specific parent */ void loadTemplate(IlpObject obj, String templateFileName) { log.log(Level.FINE, "Loading template " + templateFileName + " for object " + obj.getIdentifier()); final Object parentId = obj.getIdentifier(); // Load the template objects under the parent node, and transform // their IDs so they are unique try { // Create an identifier factory that prepends the parent ID to // all identifiers read from the template, to make them unique IlpIdentifierFactory idFactory = new IlpIdentifierFactory() { Override public Object getIdentifier(Object previousIdentifier) { return parentId.toString() + "/" + previousIdentifier.toString(); } }; // Read an XML file into the data source. // Configure the XML loader to use the ID factory created above, // and to add all objects under the given parent IlpDataSourceLoader loader = new IlpDataSourceLoader(templateFileName, (IlpMutableDataSource) treeComponent.getDataSource()); loader.setIdentifierFactory(idFactory); loader.setParentIdOfRootObjects(parentId); loader.parse(); } catch (java.lang.Exception e) { log.log(Level.WARNING, "Failed to find or parse template " + templateFileName + " for object " + obj.getIdentifier()); System.out.println(e.getMessage()); } } // Property change support public void addPropertyChangeListener(PropertyChangeListener listener) { this.propertyChangeSupport.addPropertyChangeListener(listener); } public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { this.propertyChangeSupport.addPropertyChangeListener(propertyName, listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { this.propertyChangeSupport.removePropertyChangeListener(listener); } public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { this.propertyChangeSupport.removePropertyChangeListener(propertyName, listener); } }