/******************************************************************************* * 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.xml; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext; import org.eclipse.persistence.tools.workbench.framework.resources.ResourceRepository; import org.eclipse.persistence.tools.workbench.framework.ui.dialog.AbstractDialog; import org.eclipse.persistence.tools.workbench.framework.uitools.SwingComponentFactory; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWComplexTypeDefinition; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWElementDeclaration; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWModelGroupDefinition; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWNamedSchemaComponent; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWNamespace; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWSchemaContextComponent; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWXmlSchema; import org.eclipse.persistence.tools.workbench.uitools.Displayable; import org.eclipse.persistence.tools.workbench.uitools.app.AbstractTreeNodeValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.ListValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.PropertyValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.SimpleListValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.SimplePropertyValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.SortedListValueModelAdapter; import org.eclipse.persistence.tools.workbench.uitools.app.TransformationListValueModelAdapter; import org.eclipse.persistence.tools.workbench.uitools.app.TransformationPropertyValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.TreeNodeValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.swing.DocumentAdapter; 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.CollectionTools; /** * This dialog presents a tree of the available contexts * within the provided schema repository. * When the user selects a tree node, the text preview * of the selected context appears in the text field. * <p> * Once the user presses the OK button, clients can * retrieve the selected schema context by calling * #selectedSchemaContext(). This will not be null. * <p> * Here is the layout of the dialog: * <pre> * ________________________________________ * | | * | Title | * |______________________________________| * | ____________________________________ | * | | schema |^| | * | | |-context1 | | | * | | |-context2 | | | * | | | |-subcontext1 | | | * | | |-context3 | | | * | | | | | * | | | | | * | | | | | * | | |v| | * | ------------------------------------ | * | ____________________________________ | * | |(Schema context preview) | | * | ------------------------------------ | * |______________________________________| * | ________ ________ | * | | OK | |Cancel| | * | -------- -------- | * ----------------------------------------</pre> * */ final class SchemaContextChooserDialog extends AbstractDialog { /** Schema repository passed to this dialog */ private SchemaRepositoryValue schemaRepositoryValue; /** PropertyValueModel passed to this dialog within which the schema component is set */ private PropertyValueModel schemaComponentHolder; /** PropertyValueModel used internally to keep track of selected component */ private PropertyValueModel selectedSchemaComponentHolder; /** The tree selection model */ private TreeSelectionModel schemaContextTreeSelectionModel; //***************** Constructors ****************************************** SchemaContextChooserDialog(WorkbenchContext context, SchemaRepositoryValue schemaRepository, PropertyValueModel schemaComponentHolder) { super(context); this.initialize(schemaRepository, schemaComponentHolder); } //***************** Initialization **************************************** protected void initialize() { super.initialize(); // initialize dialog settings this.setTitle(this.resourceRepository().getString("SCHEMA_CONTEXT_CHOOSER_DIALOG.TITLE")); } private void initialize(SchemaRepositoryValue schemaRepository, PropertyValueModel schemaComponentHolder) { this.schemaRepositoryValue = schemaRepository; this.schemaComponentHolder = schemaComponentHolder; this.selectedSchemaComponentHolder = this.buildSelectedSchemaComponentHolder(); this.schemaContextTreeSelectionModel = this.buildSchemaContextTreeSelectionModel(); } private PropertyValueModel buildSelectedSchemaComponentHolder() { return new SimplePropertyValueModel(null); } private TreeSelectionModel buildSchemaContextTreeSelectionModel() { TreeSelectionModel treeSelectionModel = new DefaultTreeSelectionModel(); treeSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); treeSelectionModel.addTreeSelectionListener(this.buildTreeSelectionListener()); return treeSelectionModel; } private TreeSelectionListener buildTreeSelectionListener() { return new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent tse) { SchemaContextChooserDialog.this.schemaContextTreeSelectionChanged(); } }; } protected Component buildMainPanel() { JPanel panel = new JPanel(new GridBagLayout()); panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); panel.setPreferredSize(new Dimension(400, 400)); GridBagConstraints constraints = new GridBagConstraints(); // build the tree constraints.gridx = 0; constraints.gridy = 0; constraints.gridwidth = 2; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 1; constraints.anchor = GridBagConstraints.PAGE_START; constraints.fill = GridBagConstraints.BOTH; constraints.insets = new Insets(0, 0, 0, 0); panel.add(this.buildSchemaContextTreePane(), constraints); // build the preview text field label JLabel schemaContextPreviewLabel = this.buildSchemaContextPreviewLabel(); constraints.gridx = 0; constraints.gridy = 1; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.NONE; constraints.insets = new Insets(5, 0, 0, 5); panel.add(schemaContextPreviewLabel, constraints); // build the preview text field JTextField schemaContextPreviewTextField = this.buildSchemaContextPreviewTextField(); schemaContextPreviewLabel.setLabelFor(schemaContextPreviewTextField); constraints.gridx = 1; constraints.gridy = 1; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 0; constraints.anchor = GridBagConstraints.CENTER; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.insets = new Insets(5, 0, 0, 0); panel.add(schemaContextPreviewTextField, constraints); return panel; } private JScrollPane buildSchemaContextTreePane() { return new JScrollPane(this.buildSchemaContextTree()); } private JTree buildSchemaContextTree() { JTree tree = SwingComponentFactory.buildTree(this.buildSchemaContextTreeModel()); tree.setSelectionModel(this.schemaContextTreeSelectionModel); tree.setCellRenderer(this.buildTreeCellRenderer()); tree.setRootVisible(false); tree.setShowsRootHandles(true); tree.setRowHeight(20); tree.setDoubleBuffered(true); tree.expandRow(0); return tree; } private TreeModelAdapter buildSchemaContextTreeModel() { return new TreeModelAdapter(this.buildSchemaContextTreeRoot()); } private SchemaRepositoryNode buildSchemaContextTreeRoot() { return new SchemaRepositoryNode(this.schemaRepositoryValue); } private TreeCellRenderer buildTreeCellRenderer() { return new DisplayableTreeCellRenderer(); } private JLabel buildSchemaContextPreviewLabel() { JLabel schemaContextPreviewLabel = new JLabel(this.resourceRepository().getString("SCHEMA_CONTEXT_CHOOSER_DIALOG.PREVIEW_TEXT_FIELD")); schemaContextPreviewLabel.setDisplayedMnemonic(this.resourceRepository().getMnemonic("SCHEMA_CONTEXT_CHOOSER_DIALOG.PREVIEW_TEXT_FIELD")); return schemaContextPreviewLabel; } private JTextField buildSchemaContextPreviewTextField() { JTextField textField = new JTextField(); textField.setEditable(false); textField.setEnabled(true); textField.setDocument(this.buildSchemaContextPreviewDocument()); return textField; } private DocumentAdapter buildSchemaContextPreviewDocument() { return new DocumentAdapter(this.buildSelectedSchemaContextStringHolder()); } private PropertyValueModel buildSelectedSchemaContextStringHolder() { return new TransformationPropertyValueModel(this.selectedSchemaComponentHolder) { protected Object transform(Object value) { return SchemaContextComponentDisplayer.displayString( SchemaContextChooserDialog.this.resourceRepository(), (MWSchemaContextComponent) value ); } }; } // **************** Internal ********************************************** private void schemaContextTreeSelectionChanged() { TreePath selectedPath = this.schemaContextTreeSelectionModel.getSelectionPath(); Object newComponent = null; if (selectedPath != null && selectedPath.getLastPathComponent() instanceof SchemaContextComponentNode) { newComponent = ((SchemaContextComponentNode) this.schemaContextTreeSelectionModel.getSelectionPath().getLastPathComponent()).getValue(); } this.selectedSchemaComponentHolder.setValue(newComponent); } public MWNamedSchemaComponent selectedSchemaComponent() { return (MWNamedSchemaComponent) this.selectedSchemaComponentHolder.getValue(); } // **************** AbstractDialog contract ******************************* protected void okConfirmed() { this.schemaComponentHolder.setValue(this.selectedSchemaComponentHolder.getValue()); super.okConfirmed(); } protected String helpTopicId() { return "dialog.schemaContextChooser"; } // **************** Behavior ********************************************** public void show() { if (! this.schemaRepositoryValue.schemas().hasNext()) { this.showErrorDialog(); } else { super.show(); } } private void showErrorDialog() { Component currentWindow = this.currentWindow(); ResourceRepository repo = this.resourceRepository(); String errorTitle = repo.getString("SCHEMA_CONTEXT_CHOOSER_DIALOG.NO_SCHEMAS_LOADED_TITLE"); String errorMessage = repo.getString("SCHEMA_CONTEXT_CHOOSER_DIALOG.NO_SCHEMAS_LOADED_MESSAGE"); JOptionPane.showMessageDialog(currentWindow, errorMessage, errorTitle, JOptionPane.WARNING_MESSAGE); } // **************** Member classes **************************************** private final static class SchemaRepositoryNode extends AbstractTreeNodeValueModel implements Displayable { // **************** Instance variables ************************************ /** The schema repository for this node. */ private SchemaRepositoryValue schemaRepository; /** The children of this node. */ private ListValueModel childrenModel; // **************** Constructors ****************************************** public SchemaRepositoryNode(SchemaRepositoryValue schemaRepository) { super(); this.initialize(schemaRepository); } // **************** Initialization **************************************** private void initialize(SchemaRepositoryValue schemaRepository) { this.schemaRepository = schemaRepository; this.childrenModel = this.buildChildrenModel(); } protected ListValueModel buildChildrenModel() { return new SortedListValueModelAdapter(this.buildSchemaNodesAdapter()); } protected ListValueModel buildSchemaNodesAdapter() { return new TransformationListValueModelAdapter(this.buildSchemasAdapter()) { protected Object transformItem(Object item) { return SchemaRepositoryNode.this.buildSchemaNode((MWXmlSchema) item); } }; } protected ListValueModel buildSchemasAdapter() { return new SimpleListValueModel(CollectionTools.list(this.schemaRepository.schemas())); } protected SchemaNode buildSchemaNode(MWXmlSchema schema) { return new SchemaNode(this, schema); } // **************** ValueModel contract *********************************** public Object getValue() { return this.schemaRepository; } // **************** TreeNodeValueModel contract *************************** public TreeNodeValueModel getParent() { return null; } public ListValueModel getChildrenModel() { return this.childrenModel; } // ********** AbstractTreeNodeValueModel implementation ********** protected void engageValue() {} protected void disengageValue() {} // **************** Comparable contract *********************************** public int compareTo(Object o) { return DEFAULT_COMPARATOR.compare(this, o); } // **************** Displayable contract ********************************** public String displayString() { return null; } public Icon icon() { return null; } } private final static class SchemaNode extends AbstractTreeNodeValueModel implements Displayable { // **************** Instance Variables ************************************ /** The parent of this node */ private SchemaRepositoryNode parent; /** The schema for this node. */ private MWXmlSchema schema; /** The children of this node. */ private ListValueModel childrenModel; // **************** Constructors ****************************************** SchemaNode(SchemaRepositoryNode parent, MWXmlSchema schema) { super(); this.initialize(parent, schema); } // **************** Initialization **************************************** private void initialize(SchemaRepositoryNode parent, MWXmlSchema schema) { this.parent = parent; this.schema = schema; this.childrenModel = this.buildChildrenModel(); } private ListValueModel buildChildrenModel() { return new SortedListValueModelAdapter(this.buildContextComponentNodesAdapter()); } private ListValueModel buildContextComponentNodesAdapter() { return new TransformationListValueModelAdapter(this.buildContextComponentsAdapter()) { protected Object transformItem(Object item) { return SchemaNode.this.buildContextComponentNode((MWNamedSchemaComponent) item); } }; } private ListValueModel buildContextComponentsAdapter() { return new SimpleListValueModel(CollectionTools.list(this.schema.contextComponents())); } private SchemaContextComponentNode buildContextComponentNode(MWNamedSchemaComponent component) { return new SchemaContextComponentNode(this, component); } // **************** ValueModel contract *********************************** public Object getValue() { return this.schema; } // **************** TreeNodeValueModel contract *************************** public TreeNodeValueModel getParent() { return this.parent; } public ListValueModel getChildrenModel() { return this.childrenModel; } // ********** AbstractTreeNodeValueModel implementation ********** protected void engageValue() {} protected void disengageValue() {} // **************** Comparable contract *********************************** public int compareTo(Object o) { return DEFAULT_COMPARATOR.compare(this, o); } // **************** Displayable contract ********************************** public String displayString() { return "schema::" + this.schema.getName(); } public Icon icon() { return null; } } private final static class SchemaContextComponentNode extends AbstractTreeNodeValueModel implements Displayable { // **************** Instance Variables ************************************ /** The parent of this node */ private AbstractTreeNodeValueModel parent; /** The component for this node. */ private MWNamedSchemaComponent component; /** The children of this node. */ private ListValueModel childrenModel; // **************** Constructors ****************************************** SchemaContextComponentNode(AbstractTreeNodeValueModel parent, MWNamedSchemaComponent component) { super(); this.initialize(parent, component); } // **************** Initialization **************************************** private void initialize(AbstractTreeNodeValueModel parent, MWNamedSchemaComponent component) { this.parent = parent; this.component = component; this.childrenModel = this.buildChildrenModel(); } protected ListValueModel buildChildrenModel() { return new TransformationListValueModelAdapter(this.buildContextComponentsAdapter()) { protected Object transformItem(Object item) { return SchemaContextComponentNode.this.buildContextComponentNode((MWNamedSchemaComponent) item); } }; } protected ListValueModel buildContextComponentsAdapter() { return new SimpleListValueModel(CollectionTools.list(this.component.descriptorContextComponents())); } protected SchemaContextComponentNode buildContextComponentNode(MWNamedSchemaComponent component) { return new SchemaContextComponentNode(this, component); } // **************** ValueModel contract *********************************** public Object getValue() { return this.component; } // **************** TreeNodeValueModel contract *************************** public TreeNodeValueModel getParent() { return this.parent; } public ListValueModel getChildrenModel() { return this.childrenModel; } // ********** AbstractTreeNodeValueModel implementation ********** protected void engageValue() {} protected void disengageValue() {} // **************** Comparable contract *********************************** public int compareTo(Object o) { return SchemaContextComponentNodeComparator.compare(this, o); } // **************** Displayable contract ********************************** public String displayString() { return this.component.componentTypeName() + "::" + this.component.qName(); } public Icon icon() { return null; } // **************** Member classes **************************************** private static class SchemaContextComponentNodeComparator { /** * Sort nodes first by namespace (root namespace first, then alphabetically by prefix), * then by node type * then by prefixed name. */ public static int compare(Object o1, Object o2) { SchemaContextComponentNode node1 = (SchemaContextComponentNode) o1; SchemaContextComponentNode node2 = (SchemaContextComponentNode) o2; MWNamedSchemaComponent comp1 = node1.component; MWNamedSchemaComponent comp2 = node2.component; int comparison = compare(comp1.getTargetNamespace(), comp2.getTargetNamespace()); if (comparison == 0) { comparison = compareComponentType(comp1, comp2); } if (comparison == 0) { comparison = comp1.qName().compareTo(comp2.qName()); } return comparison; } private static int compare(MWNamespace thisNamespace, MWNamespace otherNamespace) { if (thisNamespace.isTargetNamespace() && ! otherNamespace.isTargetNamespace()) { return -1; } else if (! thisNamespace.isTargetNamespace() && otherNamespace.isTargetNamespace()) { return 1; } else if(thisNamespace.getNamespacePrefix() != otherNamespace.getNamespacePrefix()) { return thisNamespace.getNamespacePrefix().compareToIgnoreCase(otherNamespace.getNamespacePrefix()); } else { return 0; } } private static int compareComponentType(MWNamedSchemaComponent comp1, MWNamedSchemaComponent comp2) { int rank1, rank2 = 0; if (comp1 instanceof MWElementDeclaration) { rank1 = 1; } else if (comp1 instanceof MWModelGroupDefinition) { rank1 = 2; } else if (comp1 instanceof MWComplexTypeDefinition) { rank1 = 3; } else { rank1 = 4; } if (comp2 instanceof MWElementDeclaration) { rank2 = 1; } else if (comp2 instanceof MWModelGroupDefinition) { rank2 = 2; } else if (comp2 instanceof MWComplexTypeDefinition) { rank2 = 3; } else { rank2 = 4; } return rank1 - rank2; } } } }