/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.test.framework.ui.chooser;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.border.Border;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.eclipse.persistence.tools.workbench.test.framework.TestWorkbenchContext;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext;
import org.eclipse.persistence.tools.workbench.framework.ui.chooser.ClassDescriptionAdapter;
import org.eclipse.persistence.tools.workbench.framework.ui.chooser.ClassDescriptionNode;
import org.eclipse.persistence.tools.workbench.framework.ui.chooser.ClassDescriptionNodeContainer;
import org.eclipse.persistence.tools.workbench.framework.ui.chooser.ClassDescriptionPackagePoolNode;
import org.eclipse.persistence.tools.workbench.framework.ui.chooser.DefaultClassDescriptionAdapter;
import org.eclipse.persistence.tools.workbench.uitools.app.PropertyValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.SimplePropertyValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.ValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.swing.TreeModelAdapter;
import org.eclipse.persistence.tools.workbench.uitools.cell.DisplayableTreeCellRenderer;
import org.eclipse.persistence.tools.workbench.utility.classfile.tools.ClassDependencyGraph;
/**
* This dialog presents three trees to the user:
* - the middle tree holds the nodes in a ClassDependencyGraph
* the user will select one or more of these nodes and the
* left- and right-hand trees will be populated appropriately
* - the left-hand tree holds the nodes that reference the selected nodes
* - the right-hand tree holds the nodes that the selected nodes reference
*/
public class ClassDependencyGraphPanel extends JPanel {
/** this holds the class dependency graph, allowing clients to swap in a new graph */
private PropertyValueModel graphHolder;
/** these are the classes the user has to choose among (the middle tree) */
private PropertyValueModel middleProjectNodeHolder;
private TreeModel middleTreeModel;
private TreeSelectionModel middleTreeSelectionModel;
/** these are the classes that reference the chosen classes (the left-hand tree) */
private JTree referencingTree;
private PropertyValueModel referencingProjectNodeHolder;
private TreeModel referencingTreeModel;
/** these are the classes the chosen classes reference (the right-hand tree) */
private JTree referencedTree;
private PropertyValueModel referencedProjectNodeHolder;
private TreeModel referencedTreeModel;
/** converts the class dependency graph nodes into class names */
private ClassDescriptionAdapter classDependencyGraphNodeAdapter;
private WorkbenchContext context;
// ********** constructors **********
public ClassDependencyGraphPanel(PropertyValueModel graphHolder) {
super();
this.graphHolder = graphHolder;
this.initialize();
}
// ********** initialization **********
private void initialize() {
this.graphHolder.addPropertyChangeListener(ValueModel.VALUE, this.buildGraphHolderListener());
this.context = buildWorkbenchContext();
this.classDependencyGraphNodeAdapter = this.buildClassDependencyGraphNodeAdapter();
this.middleProjectNodeHolder = new SimplePropertyValueModel(this.buildMiddleProjectNode(this.graph()));
this.middleTreeModel = new TreeModelAdapter(this.middleProjectNodeHolder);
this.middleTreeSelectionModel = this.buildMiddleTreeSelectionModel();
this.referencingProjectNodeHolder = new SimplePropertyValueModel(this.buildReferencingProjectNode(Collections.EMPTY_SET));
this.referencingTreeModel = new TreeModelAdapter(this.referencingProjectNodeHolder);
this.referencedProjectNodeHolder = new SimplePropertyValueModel(this.buildReferencedProjectNode(Collections.EMPTY_SET));
this.referencedTreeModel = new TreeModelAdapter(this.referencedProjectNodeHolder);
this.initializeLayout();
}
private PropertyChangeListener buildGraphHolderListener() {
return new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
ClassDependencyGraphPanel.this.graphChanged();
}
};
}
private WorkbenchContext buildWorkbenchContext() {
return new TestWorkbenchContext();
}
private ClassDescriptionAdapter buildClassDependencyGraphNodeAdapter() {
return new DefaultClassDescriptionAdapter() {
public String className(Object classDescription) {
return ((ClassDependencyGraph.Node) classDescription).getClassName();
}
};
}
private ClassDescriptionPackagePoolNode buildMiddleProjectNode(ClassDependencyGraph graph) {
return new ClassDescriptionPackagePoolNode("middle", graph.nodes(), this.getClassDependencyGraphNodeAdapter(), this.context);
}
private ClassDescriptionAdapter getClassDependencyGraphNodeAdapter() {
return this.classDependencyGraphNodeAdapter;
}
private TreeSelectionModel buildMiddleTreeSelectionModel() {
TreeSelectionModel treeSelectionModel = new DefaultTreeSelectionModel();
treeSelectionModel.addTreeSelectionListener(this.buildMiddleTreeSelectionListener());
return treeSelectionModel;
}
private TreeSelectionListener buildMiddleTreeSelectionListener() {
return new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
ClassDependencyGraphPanel.this.middleTreeSelectionChanged(e);
}
};
}
private void initializeLayout() {
Border border = BorderFactory.createEmptyBorder(0, 2, 2, 0);
Font font = new Font("Dialog", Font.PLAIN, 12);
this.setLayout(new BorderLayout());
// build the labels
JPanel labelPanel = new JPanel(new GridLayout(1, 0));
JLabel referencingLabel = new JLabel("Referencing Classes (within Scope)");
referencingLabel.setFont(font);
referencingLabel.setBorder(border);
labelPanel.add(referencingLabel);
JLabel middleLabel = new JLabel("Scope Classes");
middleLabel.setFont(font);
middleLabel.setBorder(border);
labelPanel.add(middleLabel);
JLabel referencedLabel = new JLabel("Referenced Classes");
referencedLabel.setFont(font);
referencedLabel.setBorder(border);
labelPanel.add(referencedLabel);
this.add(labelPanel, BorderLayout.NORTH);
// build the trees
JPanel treePanel = new JPanel(new GridLayout(1, 0));
this.referencingTree = new JTree(this.referencingTreeModel);
this.referencingTree.setCellRenderer(new DisplayableTreeCellRenderer());
this.referencingTree.setRootVisible(false);
this.referencingTree.setShowsRootHandles(true);
this.referencingTree.setRowHeight(20);
this.referencingTree.setDoubleBuffered(true);
referencingLabel.setLabelFor(this.referencingTree);
treePanel.add(new JScrollPane(this.referencingTree));
JTree middleTree = new JTree(this.middleTreeModel);
middleTree.setSelectionModel(this.middleTreeSelectionModel);
middleTree.setCellRenderer(new DisplayableTreeCellRenderer());
middleTree.setRootVisible(false);
middleTree.setShowsRootHandles(true);
middleTree.setRowHeight(20);
middleTree.setDoubleBuffered(true);
middleLabel.setLabelFor(middleTree);
treePanel.add(new JScrollPane(middleTree));
this.referencedTree = new JTree(this.referencedTreeModel);
this.referencedTree.setCellRenderer(new DisplayableTreeCellRenderer());
this.referencedTree.setRootVisible(false);
this.referencedTree.setShowsRootHandles(true);
this.referencedTree.setRowHeight(20);
this.referencedTree.setDoubleBuffered(true);
referencedLabel.setLabelFor(this.referencedTree);
treePanel.add(new JScrollPane(this.referencedTree));
this.add(treePanel, BorderLayout.CENTER);
}
// ********** queries **********
private ClassDependencyGraph graph() {
return (ClassDependencyGraph) this.graphHolder.getValue();
}
// ********** behavior **********
/**
* rebuild the middle tree model
*/
void graphChanged() {
this.middleProjectNodeHolder.setValue(this.buildMiddleProjectNode(this.graph()));
}
/**
* rebuild the outer tree models
*/
void middleTreeSelectionChanged(TreeSelectionEvent e) {
TreePath[] selectedPaths = this.middleTreeSelectionModel.getSelectionPaths();
// if a package is selected, all its classes are considered selected also;
// use a HashSet because there will be duplicates
// if both the class and its containing package are selected
Collection selectedClassNodes = new HashSet();
if (selectedPaths != null) { // when everything is de-selected, this is null
for (int i = selectedPaths.length; i-- > 0; ) {
ClassDescriptionNodeContainer selectedNode = (ClassDescriptionNodeContainer) selectedPaths[i].getLastPathComponent();
selectedNode.addClassDescriptionNodesTo(selectedClassNodes);
}
}
// must force expansion of invisible root nodes so the packages are visible...
ClassDescriptionPackagePoolNode referencingProjectNode = this.buildReferencingProjectNode(selectedClassNodes);
this.referencingProjectNodeHolder.setValue(referencingProjectNode);
this.referencingTree.expandPath(new TreePath(referencingProjectNode));
ClassDescriptionPackagePoolNode referencedProjectNode = this.buildReferencedProjectNode(selectedClassNodes);
this.referencedProjectNodeHolder.setValue(referencedProjectNode);
this.referencedTree.expandPath(new TreePath(referencedProjectNode));
}
private ClassDescriptionPackagePoolNode buildReferencingProjectNode(Collection selectedClassNodes) {
return new ClassDescriptionPackagePoolNode("referencing", this.referencing(selectedClassNodes), this.getClassDependencyGraphNodeAdapter(), this.context);
}
private ClassDescriptionPackagePoolNode buildReferencedProjectNode(Collection selectedClassNodes) {
return new ClassDescriptionPackagePoolNode("referenced", this.referenced(selectedClassNodes), this.getClassDependencyGraphNodeAdapter(), this.context);
}
private Iterator referencing(Collection classNodes) {
Map referencing = new IdentityHashMap();
for (Iterator stream1 = classNodes.iterator(); stream1.hasNext(); ) {
ClassDescriptionNode classNode = (ClassDescriptionNode) stream1.next();
ClassDependencyGraph.Node cdgNode = (ClassDependencyGraph.Node) classNode.getUserClassDescription();
for (Iterator stream2 = cdgNode.referencingNodes(); stream2.hasNext(); ) {
Object next = stream2.next();
referencing.put(next, next); // use as identity set
}
}
return referencing.keySet().iterator();
}
private Iterator referenced(Collection classNodes) {
Map referenced = new IdentityHashMap();
for (Iterator stream1 = classNodes.iterator(); stream1.hasNext(); ) {
ClassDescriptionNode classNode = (ClassDescriptionNode) stream1.next();
ClassDependencyGraph.Node cdgNode = (ClassDependencyGraph.Node) classNode.getUserClassDescription();
for (Iterator stream2 = cdgNode.referencedNodes(); stream2.hasNext(); ) {
Object next = stream2.next();
referenced.put(next, next); // use as identity set
}
}
return referenced.keySet().iterator();
}
}