/*******************************************************************************
* 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.mappingsplugin.ui.query.relational;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContextHolder;
import org.eclipse.persistence.tools.workbench.framework.ui.view.AbstractPanel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.query.relational.MWBasicExpression;
import org.eclipse.persistence.tools.workbench.mappingsmodel.query.relational.MWCompoundExpression;
import org.eclipse.persistence.tools.workbench.mappingsmodel.query.relational.MWExpression;
import org.eclipse.persistence.tools.workbench.uitools.app.AbstractReadOnlyCollectionValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.CollectionValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.FilteringPropertyValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.PropertyAspectAdapter;
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.swing.ComboBoxModelAdapter;
/**
* The main panel for the ExpressionBuilderDialog.
* Contains the ExpressionTree, buttons for manipulating the CompoundExpressions,
* and a panel to edit the basic expressions
*/
final class ExpressionTreePanel extends AbstractPanel
{
private MWCompoundExpression expression;
private PropertyValueModel selectedExpressionHolder;
private PropertyValueModel selectedCompoundExpressionHolder;
//Models for the ExpressionTree
private ExpressionTree expressionTree;
private DefaultTreeModel expressionTreeModel;
private TreeSelectionModel expressionTreeSelectionModel;
//Logical Operators
public final static String AND = "AND";
public final static String OR = "OR";
public final static String NAND = "NAND";
public final static String NOR = "NOR";
private JButton addButton;
private JButton addNestedButton;
private JButton removeButton;
private JComboBox logicalOperatorComboBox;
private BasicExpressionPanel basicExpressionPanel;
private String baseTopicId;
protected class ExpressionTreeSelectionListener implements TreeSelectionListener {
public void valueChanged(TreeSelectionEvent e) {
if (expressionTreeSelectionModel.getSelectionPath() == null || expressionTreeSelectionModel.getSelectionCount() >1) {
selectedExpressionHolder.setValue(null);
}
else {
ExpressionTreeNode selectedNode = (ExpressionTreeNode) expressionTreeSelectionModel.getSelectionPath().getLastPathComponent();
selectedExpressionHolder.setValue(selectedNode.getUserObject());
}
updateButtons();
}
}
ExpressionTreePanel(WorkbenchContextHolder contextHolder, String baseTopicId) {
super(contextHolder);
this.baseTopicId = baseTopicId;
initialize();
}
private void addBasicExpresion()
{
ExpressionTreeNode node = getSelectedCompoundExpressionNode();
//if nothing is selected we will add to the main compound expression
MWCompoundExpression compoundExpression;
if (node == null)
{
compoundExpression = this.expression;
node = (ExpressionTreeNode)this.expressionTreeModel.getRoot();
}
else
{
compoundExpression = (MWCompoundExpression)node.getUserObject();
}
MWBasicExpression basicExpression = compoundExpression.addBasicExpression();
Enumeration children = node.children();
while (children.hasMoreElements()) {
ExpressionTreeNode treeNode = (ExpressionTreeNode) children.nextElement();
if ( treeNode.getUserObject() == basicExpression) {
expressionTreeSelectionModel.setSelectionPath(new TreePath(treeNode.getPath()));
}
}
}
private void addNestedExpresion()
{
ExpressionTreeNode node = getSelectedCompoundExpressionNode();
MWCompoundExpression compoundExpression = getSelectedCompoundExpression();
//if nothing is selected we will add to the main compound expression
if (compoundExpression == null) {
compoundExpression = this.expression;
node = (ExpressionTreeNode)this.expressionTreeModel.getRoot();
}
MWCompoundExpression addedExpression = compoundExpression.addSubCompoundExpression();
Enumeration children = node.children();
while (children.hasMoreElements()) {
ExpressionTreeNode treeNode = (ExpressionTreeNode) children.nextElement();
if ( treeNode.getUserObject() == addedExpression) {
this.expressionTreeSelectionModel.setSelectionPath(new TreePath(((ExpressionTreeNode)treeNode.getChildAt(0)).getPath()));
}
}
}
private ActionListener buildAddBasicExpresionAction()
{
return new ActionListener() {
public void actionPerformed(ActionEvent ae) {
addBasicExpresion();
}
};
}
private ActionListener buildAddNestedExpressionAction()
{
return new ActionListener() {
public void actionPerformed(ActionEvent ae) {
addNestedExpresion();
}
};
}
private ActionListener buildRemoveExpressionAction()
{
return new ActionListener() {
public void actionPerformed(ActionEvent ae) {
removeSelectedExpressions();
}
};
}
private Iterator allowableOperatorTypes()
{
Collection operatorTypes = new ArrayList();
operatorTypes.add(AND);
operatorTypes.add(OR);
operatorTypes.add(NAND);
operatorTypes.add(NOR);
return operatorTypes.iterator();
}
private MWCompoundExpression getSelectedCompoundExpression()
{
MWExpression selectedExpression = getSelectedExpression();
if (selectedExpression == null)
return null;
//might want to look at this one again once I get to refactoring the expressionTree
//isAssignableFrom is just a replacement for instanceof.
if(MWCompoundExpression.class.isAssignableFrom(selectedExpression.getClass()))
return (MWCompoundExpression) selectedExpression;
else
return ((MWBasicExpression)selectedExpression).getParentCompoundExpression();
}
private ExpressionTreeNode getSelectedCompoundExpressionNode() {
TreePath selectionPath = expressionTreeSelectionModel.getSelectionPath();
if (selectionPath == null)
return null;
while (selectionPath.getPathCount() > 0) {
ExpressionTreeNode lastNode = (ExpressionTreeNode)selectionPath.getLastPathComponent();
//might want to look at this one again once I get to refactoring the expressionTree
//isAssignableFrom is just a replacement for instanceof.
if (MWCompoundExpression.class.isAssignableFrom(lastNode.getUserObject().getClass()))
return lastNode;
selectionPath = selectionPath.getParentPath();
}
return null;
}
private ExpressionTreeNode getSelectedExpressionNode() {
if (this.expressionTreeSelectionModel.getSelectionPath() == null || this.expressionTreeSelectionModel.getSelectionCount() >1)
return null;
return (ExpressionTreeNode)this.expressionTreeSelectionModel.getSelectionPath().getLastPathComponent();
}
private MWExpression getSelectedExpression() {
ExpressionTreeNode selectedNode = getSelectedExpressionNode();
if (selectedNode == null)
return null;
return (MWExpression) selectedNode.getUserObject();
}
private PropertyValueModel buildSelectedCompoundExpressionHolder(PropertyValueModel selectedExpressionHolder) {
return new FilteringPropertyValueModel(selectedExpressionHolder) {
protected boolean accept(Object value) {
return value instanceof MWCompoundExpression;
}
};
}
private void initialize() {
this.selectedExpressionHolder = new SimplePropertyValueModel();
this.selectedCompoundExpressionHolder = buildSelectedCompoundExpressionHolder(selectedExpressionHolder);
setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
this.expressionTreeModel = new DefaultTreeModel( new DefaultMutableTreeNode("RootNode"), true);
expressionTree = new ExpressionTree(this.expressionTreeModel);
addHelpTopicId(expressionTree, baseTopicId);
this.expressionTreeSelectionModel = expressionTree.getSelectionModel();
this.expressionTreeSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
JScrollPane expressionTreePane = new JScrollPane(expressionTree);
expressionTreePane.setPreferredSize(new Dimension(10,10));
expressionTree.addTreeSelectionListener(new ExpressionTreeSelectionListener());
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 1;
constraints.fill = GridBagConstraints.BOTH;
constraints.anchor = GridBagConstraints.CENTER;
constraints.insets = new Insets(5, 5, 5, 5);
add(expressionTreePane, constraints);
JPanel buttonPanel = new JPanel(new GridBagLayout());
this.addButton = new JButton(resourceRepository().getString("ADD_EXPRESSION_BUTTON"));
this.addButton.setMnemonic(resourceRepository().getMnemonic("ADD_EXPRESSION_BUTTON"));
this.addButton.addActionListener(buildAddBasicExpresionAction());
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.insets = new Insets(5, 5, 5, 5);
buttonPanel.add(this.addButton, constraints);
this.addNestedButton = new JButton(resourceRepository().getString("ADD_NESTED_EXPRESSION_BUTTON"));
this.addNestedButton.addActionListener(buildAddNestedExpressionAction());
this.addNestedButton.setMnemonic(resourceRepository().getMnemonic("ADD_NESTED_EXPRESSION_BUTTON"));
constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.insets = new Insets(0, 5, 5, 5);
buttonPanel.add(this.addNestedButton, constraints);
this.removeButton = new JButton(resourceRepository().getString("REMOVE_EXPRESSION_BUTTON"));
this.removeButton.setMnemonic(resourceRepository().getMnemonic("REMOVE_EXPRESSION_BUTTON"));
this.removeButton.addActionListener(buildRemoveExpressionAction());
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.insets = new Insets(0, 5, 5, 5);
buttonPanel.add(this.removeButton, constraints);
JLabel operatorLabel = new JLabel(resourceRepository().getString("OPERATOR_TYPE_COMBO_BOX_LABEL"));
operatorLabel.setDisplayedMnemonic(resourceRepository().getMnemonic("OPERATOR_TYPE_COMBO_BOX_LABEL"));
constraints.gridx = 0;
constraints.gridy = 3;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.insets = new Insets(0, 5, 5, 5);
buttonPanel.add(operatorLabel, constraints);
this.logicalOperatorComboBox = new JComboBox();
this.logicalOperatorComboBox.setModel(buildLogicalOperatorComboBoxModel());
operatorLabel.setLabelFor(logicalOperatorComboBox);
constraints.gridx = 0;
constraints.gridy = 4;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.insets = new Insets(0, 5, 5, 5);
buttonPanel.add(this.logicalOperatorComboBox, constraints);
constraints.gridx = 1;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.NORTHEAST;
constraints.insets = new Insets(5, 5, 5, 5);
add(buttonPanel, constraints);
this.basicExpressionPanel = new BasicExpressionPanel(this.selectedExpressionHolder, getWorkbenchContextHolder());
constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 2;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.BOTH;
constraints.anchor = GridBagConstraints.SOUTH;
constraints.insets = new Insets(5, 5, 5, 5);
add(this.basicExpressionPanel, constraints);
updateButtons();
}
// *********** logical operator ************
private ComboBoxModel buildLogicalOperatorComboBoxModel() {
return new ComboBoxModelAdapter(buildOperatorTypeCollectionHolder(), buildOperatorAdapter());
}
private CollectionValueModel buildOperatorTypeCollectionHolder() {
return new AbstractReadOnlyCollectionValueModel() {
public Object getValue() {
return allowableOperatorTypes();
}
};
}
private PropertyValueModel buildOperatorAdapter() {
return new PropertyAspectAdapter(selectedCompoundExpressionHolder, MWCompoundExpression.OPERATOR_TYPE_PROPERTY) {
protected Object getValueFromSubject() {
if (((MWCompoundExpression) subject).getOperatorType() == MWCompoundExpression.AND)
return AND;
else if (((MWCompoundExpression) subject).getOperatorType() == MWCompoundExpression.OR)
return OR;
else if (((MWCompoundExpression) subject).getOperatorType() == MWCompoundExpression.NAND)
return NAND;
else
return NOR;
}
protected void setValueOnSubject(Object value) {
if (value == AND)
getSelectedCompoundExpression().setOperatorType(MWCompoundExpression.AND);
else if (value == OR)
getSelectedCompoundExpression().setOperatorType(MWCompoundExpression.OR);
else if (value == NAND)
getSelectedCompoundExpression().setOperatorType(MWCompoundExpression.NAND);
else if (value == NOR)
getSelectedCompoundExpression().setOperatorType(MWCompoundExpression.NOR);
}
};
}
protected void disengageListeners() {
CompoundExpressionTreeNode rootNode = (CompoundExpressionTreeNode)expressionTreeModel.getRoot();
rootNode.disengageListeners();
this.expression = null;
}
private void removeSelectedExpressions() {
TreePath[] selectionPaths = this.expressionTreeSelectionModel.getSelectionPaths();
for (int i = 0; i < selectionPaths.length; i++)
{
TreePath path = selectionPaths[i];
ExpressionTreeNode chosenNode = (ExpressionTreeNode) path.getLastPathComponent();
MWExpression chosenExpression = (MWExpression) chosenNode.getUserObject();
MWCompoundExpression parentExpression = chosenExpression.getParentCompoundExpression();
if (parentExpression != null)
{
parentExpression.removeExpression(chosenExpression);
}
else //if parentExpression is null, the root expression has been chosen
//it's parent is a BldrQueryFormat
{
((MWCompoundExpression)chosenExpression).clearExpressions();
}
}
}
void setExpression(MWCompoundExpression expression, int selectionRow)
{
this.expression = expression;
ExpressionTreeNode node = new CompoundExpressionTreeNode(expression);
this.expressionTreeModel.setRoot(node);
node.setModel(expressionTreeModel);
node.initializeChildren();
for (int i = 0; i < selectionRow; i++)
expressionTree.expandRow(i);
expressionTree.setSelectionRow(selectionRow);
expressionTree.scrollRowToVisible(selectionRow);
}
//tree selection event method will call this
private void updateButtons()
{
//if nothing is selected, we will add it directly to the main expression
this.addButton.setEnabled(this.expressionTreeSelectionModel.getSelectionCount() <= 1);
//if nothing is selected, we will add it directly to the main expression
this.addNestedButton.setEnabled(this.expressionTreeSelectionModel.getSelectionCount() <= 1);
this.removeButton.setEnabled(this.expressionTreeSelectionModel.getSelectionCount() >= 1);
this.logicalOperatorComboBox.setEnabled((this.expressionTreeSelectionModel.getSelectionCount() >= 1) && (getSelectedExpression() != null && MWCompoundExpression.class.isAssignableFrom(getSelectedExpression().getClass())));
}
}