/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * RangeValueEditor.java * Creation date: (05/10/04 10:14:21 AM) * By: Iulian Radu */ package org.openquark.gems.client.valueentry; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.HashMap; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openquark.cal.compiler.TypeConsApp; import org.openquark.cal.compiler.TypeException; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.compiler.TypeVar; import org.openquark.cal.valuenode.RangeValueNode; import org.openquark.cal.valuenode.ValueNode; import org.openquark.gems.client.GemCutter; import org.openquark.util.ui.UIUtilities; /** * The ValueEditor for Range value nodes. * * This editor contains buttons for specifying range endpoint types, * and two child editors for the nodes of these endpoints. * * The types of the endpoints are required to be instances of the * Prelude.Ord class, as dictated by the range constructor SCs. This * is enforced as follows: * * - on creation of a range type value node: * - if the range type is not specialized (ie: "Range a"), * a node is created, to be specialized by the range editor * - otherwise, only valid orderable types are converted to value nodes * (ie: "Range Double" is valid, whereas "Range (Baz a)" is not) * * - on edit of range value nodes: * - ordering is enforced on the range editor node type (this * is necessary for the case where the node type is "Range a") * - if a context is specified (ie: if the value gem edited is connected), * then the range endpoint types are specialized to the context and Ordered * (the range value node is transmuted when the connection occurs, and * thus enforced to Ord by the previous steps) * - if a context is not specified, then the range value node can be "Ord a => Range a", * and the range endpoint types default to an orderable parametric type ("Ord a => a") * * @author Iulian Radu */ class RangeValueEditor extends StructuredValueEditor { private static final long serialVersionUID = 2032925507494723262L; /** * A custom value editor provider for the RangeValueEditor. * @author Iulian Radu */ public static class RangeValueEditorProvider extends ValueEditorProvider<RangeValueEditor> { public RangeValueEditorProvider(ValueEditorManager valueEditorManager) { super(valueEditorManager); } /** * {@inheritDoc} */ @Override public boolean canHandleValue(ValueNode valueNode, SupportInfo providerSupportInfo) { if (valueNode instanceof RangeValueNode) { return true; } return false; } /** * Returns a new instance of the editor. * @see org.openquark.gems.client.valueentry.ValueEditorProvider#getEditorInstance(ValueEditorHierarchyManager, ValueNode) */ @Override public RangeValueEditor getEditorInstance(ValueEditorHierarchyManager valueEditorHierarchyManager, ValueNode valueNode) { RangeValueEditor editor = new RangeValueEditor(valueEditorHierarchyManager); editor.setOwnerValueNode(valueNode); return editor; } /** * {@inheritDoc} */ @Override public boolean usableForOutput() { return true; } } /** * A listener for child editor commits, updating the value nodes in this editor. * @author Iulian Radu */ private class ChildValueEditorListener extends ValueEditorAdapter { @Override public void valueCommitted(ValueEditorEvent e) { // Retrieve old and new node values ValueNode oldChild = e.getOldValue(); ValueNode newChild = ((ValueEditor) e.getSource()).getValueNode(); if (oldChild.sameValue(newChild)) { return; } // Apply the changes in the child node to the other nodes in this editor // (ie: to the node in the other child editor, and in the parent editor) // // This mechanism allows for changes in type to ripple through the editor hierarchy. Map<ValueNode, TypeExpr> valueNodeToUnconstrainedTypeMap = getValueNodeToUnconstrainedTypeMap(); Map<ValueNode, ValueNode> commitValueMap = valueEditorManager.getValueNodeCommitHelper().getCommitValues(oldChild, newChild, valueNodeToUnconstrainedTypeMap); // The commitValueMap contains new value nodes which reflect the change; we now // have to update the proper editors with these values leftEditor.changeOwnerValue(commitValueMap.get(leftEditor.getOwnerValueNode())); rightEditor.changeOwnerValue(commitValueMap.get(rightEditor.getOwnerValueNode())); replaceValueNode(commitValueMap.get(getValueNode()), true); // Refresh the value panel, to allow for size changes due // to changes in value node type. refreshDisplay(); } } // A type expression representing "Ord a => Range a", enforced by this editor private final TypeConsApp ordRangeType = (TypeConsApp)valueEditorManager.getValueNodeBuilderHelper().getPerspective().getWorkingModuleTypeInfo().getVisibleFunction(RangeValueNode.RangeValueNodeProvider.RANGE_CONSTRUCTION_SC_NAME).getTypeExpr().getTypePieces()[2]; // Icons for the open/closed/unbounded toggle buttons private static final ImageIcon openLIcon = new ImageIcon(GemCutter.class.getResource("/Resources/intervalOpen.gif")); private static final ImageIcon closedLIcon = new ImageIcon(GemCutter.class.getResource("/Resources/intervalClosed.gif")); private static final ImageIcon unboundedLIcon = new ImageIcon(GemCutter.class.getResource("/Resources/arrow.gif")); private static final ImageIcon openRIcon = new ImageIcon(UIUtilities.flipImage(openLIcon.getImage(), UIUtilities.FlipOrientation.Horizontal)); private static final ImageIcon closedRIcon = new ImageIcon(UIUtilities.flipImage(closedLIcon.getImage(), UIUtilities.FlipOrientation.Horizontal)); private static final ImageIcon unboundedRIcon = new ImageIcon(UIUtilities.flipImage(unboundedLIcon.getImage(), UIUtilities.FlipOrientation.Horizontal)); /** Panel containing the value entry UI components */ private final JPanel mainPanel = new JPanel(); /** Value editor for the left endpoint node (set by initializeValue())*/ private ValueEditor leftEditor = null; /** Value editor for the right endpoint node (set by initializeValue())*/ private ValueEditor rightEditor = null; /** Label displayed if no left endpoint present */ private final JLabel leftLabel = new JLabel(ValueEditorMessages.getString("RVE_NoLowerBound")); /** Label displayed if no right endpoint present */ private final JLabel rightLabel = new JLabel(ValueEditorMessages.getString("RVE_NoUpperBound")); /** * The minimum dimension of the left label after border has been applied, * before being resized to match the start editor */ private Dimension leftLabelMinSize = null; /** * The minimum dimension of the right label after border has been applied, * before being resized to match the start editor */ private Dimension rightLabelMinSize = null; /** Button for indicating open interval for the start endpoint */ private final JToggleButton openLToggle = new JToggleButton(); /** Button for indicating closed interval for the start endpoint */ private final JToggleButton closedLToggle = new JToggleButton(); /** Button for indicating no lower bound */ private final JToggleButton unboundLToggle = new JToggleButton(); /** Button for indicating open interval for the end endpoint */ private final JToggleButton openRToggle = new JToggleButton(); /** Button for indicating closed interval for the end endpoint */ private final JToggleButton closedRToggle = new JToggleButton(); /** Button for indicating no upper bound */ private final JToggleButton unboundRToggle = new JToggleButton(); /** * Constructor. * * This method initializes buttons and labels, but does not create * the start/end editors, nor lay out the components. The rest of UI initializing * is done via the initializeValue() method once a value node has been assigned * to this editor. * * @param valueEditorHierarchyManager */ protected RangeValueEditor(ValueEditorHierarchyManager valueEditorHierarchyManager) { super(valueEditorHierarchyManager); setName("RangeValueEditor"); setLayout(new java.awt.BorderLayout()); add(mainPanel, "Center"); setFocusCycleRoot(true); // Initialize labels leftLabel.setBorder(BorderFactory.createEtchedBorder()); leftLabel.setToolTipText(ValueEditorMessages.getString("RVE_Tip_UnboundedLeftInterval")); rightLabel.setBorder(BorderFactory.createEtchedBorder()); rightLabel.setToolTipText(ValueEditorMessages.getString("RVE_Tip_UnboundedRightInterval")); rightLabel.setHorizontalAlignment(SwingConstants.RIGHT); leftLabelMinSize = leftLabel.getMinimumSize(); rightLabelMinSize = rightLabel.getMinimumSize(); // Initialize buttons openLToggle.setIcon(openLIcon); closedLToggle.setIcon(closedLIcon); unboundLToggle.setIcon(unboundedLIcon); openLToggle.setToolTipText(ValueEditorMessages.getString("RVE_Tip_OpenLeftInterval")); closedLToggle.setToolTipText(ValueEditorMessages.getString("RVE_Tip_ClosedLeftInterval")); unboundLToggle.setToolTipText(ValueEditorMessages.getString("RVE_Tip_UnboundedLeftInterval")); openLToggle.setMargin(new Insets(0, 0, 0, 0)); closedLToggle.setMargin(new Insets(0, 0, 0, 0)); unboundLToggle.setMargin(new Insets(0, 0, 0, 0)); ButtonGroup leftGroup = new ButtonGroup(); leftGroup.add(openLToggle); leftGroup.add(closedLToggle); leftGroup.add(unboundLToggle); openRToggle.setIcon(openRIcon); closedRToggle.setIcon(closedRIcon); unboundRToggle.setIcon(unboundedRIcon); openRToggle.setToolTipText(ValueEditorMessages.getString("RVE_Tip_OpenRightInterval")); closedRToggle.setToolTipText(ValueEditorMessages.getString("RVE_Tip_ClosedRightInterval")); unboundRToggle.setToolTipText(ValueEditorMessages.getString("RVE_Tip_UnboundedRightInterval")); openRToggle.setMargin(new Insets(0, 0, 0, 0)); closedRToggle.setMargin(new Insets(0, 0, 0, 0)); unboundRToggle.setMargin(new Insets(0, 0, 0, 0)); ButtonGroup rightGroup = new ButtonGroup(); rightGroup.add(openRToggle); rightGroup.add(closedRToggle); rightGroup.add(unboundRToggle); // Add listeners for showing/hiding editors when 'unbounded' type buttons are toggled unboundLToggle.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { // State changes may occur before editors are created // these do not affect the panel appearance and are ignored. if (leftEditor != null) { boolean startVisible = leftEditor.isVisible(); boolean togglingVisible = (unboundLToggle.getSelectedObjects() == null); if (startVisible != togglingVisible) { leftEditor.setVisible(togglingVisible); leftLabel.setVisible(!togglingVisible); } } } }); unboundRToggle.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { // State changes may occur before editors are created // these do not affect the panel appearance and are ignored if (rightEditor != null) { boolean startVisible = rightEditor.isVisible(); boolean togglingVisible = (unboundRToggle.getSelectedObjects() == null); if (startVisible != togglingVisible) { rightEditor.setVisible(togglingVisible); rightLabel.setVisible(!togglingVisible); } } } }); // Add listeners to revert back to a closed interval if an // 'unbounded endpoint' label is clicked leftLabel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (leftLabel.isEnabled()) { closedLToggle.setSelected(true); } } }); rightLabel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (leftLabel.isEnabled()) { closedRToggle.setSelected(true); } } }); // Make sure that the user's commit and cancel keyboard input is watched. KeyListener keyListener = new ValueEditorKeyListener(); mainPanel.addKeyListener(keyListener); leftLabel.addKeyListener(keyListener); rightLabel.addKeyListener(keyListener); unboundRToggle.addKeyListener(keyListener); closedRToggle.addKeyListener(keyListener); openRToggle.addKeyListener(keyListener); unboundLToggle.addKeyListener(keyListener); closedLToggle.addKeyListener(keyListener); openLToggle.addKeyListener(keyListener); } /** * Commits the editor value node. * @see org.openquark.gems.client.valueentry.ValueEditor#commitValue() */ @Override protected void commitValue() { // The value node of this editor will be updated to contain the value nodes // of the child editors as range endpoints, and the current form from the // editor settings. ValueNode start = leftEditor.getValueNode(); ValueNode end = rightEditor.getValueNode(); ValueNode returnVN = new RangeValueNode(getValueNode().getTypeExpr(), getRangeForm(), start, end); replaceValueNode(returnVN, false); notifyValueCommitted(); } /** * {@inheritDoc} */ @Override public Component getDefaultFocusComponent() { return mainPanel; } /** * Sets the UI components of the main panel. */ private void initializeMainPanel() { mainPanel.setName("IntermediatePanel"); // Set the toggle buttons and their container toolbars KeyListener keyListener = new ValueEditorKeyListener(); JToolBar toolBar1 = new JToolBar(); toolBar1.setFloatable(false); toolBar1.setRollover(true); toolBar1.add(unboundLToggle); toolBar1.add(closedLToggle); toolBar1.add(openLToggle); toolBar1.addKeyListener(keyListener); JToolBar toolBar2 = new JToolBar(); toolBar2.setFloatable(false); toolBar2.setRollover(true); toolBar2.add(openRToggle); toolBar2.add(closedRToggle); toolBar2.add(unboundRToggle); toolBar2.addKeyListener(keyListener); // Initialize the labels and their sizes resizeEndpointLabels(); // Now lay out the items on the main panel mainPanel.removeAll(); mainPanel.setLayout(new GridBagLayout()); { GridBagConstraints constraints = new GridBagConstraints(); constraints.gridx = 0; constraints.gridy = 0; constraints.gridwidth = 2; constraints.weightx = 1.0; constraints.weighty = 0.0; constraints.insets = new Insets(3,3,2,2); constraints.fill = GridBagConstraints.HORIZONTAL; mainPanel.add(leftEditor, constraints); mainPanel.add(leftLabel, constraints); } { GridBagConstraints constraints = new GridBagConstraints(); constraints.gridx = 3; constraints.gridy = 0; constraints.gridwidth = 2; constraints.weightx = 1.0; constraints.weighty = 0.0; constraints.insets = new Insets(3,2,2,3); constraints.fill = GridBagConstraints.HORIZONTAL; mainPanel.add(rightEditor, constraints); mainPanel.add(rightLabel, constraints); } { GridBagConstraints constraints = new GridBagConstraints(); constraints.gridx = 0; constraints.gridy = 1; constraints.weightx = 0.0; constraints.weighty = 1.0; constraints.insets = new Insets(2,3,3,2); constraints.fill = GridBagConstraints.NONE; mainPanel.add(toolBar1, constraints); } { GridBagConstraints constraints = new GridBagConstraints(); constraints.gridx = 4; constraints.gridy = 1; constraints.anchor = GridBagConstraints.LINE_END; constraints.weightx = 1.0; constraints.weighty = 1.0; constraints.insets = new Insets(2,2,3,3); constraints.fill = GridBagConstraints.NONE; mainPanel.add(toolBar2, constraints); } setMaxResizeDimension(new Dimension(2048, getPreferredSize().height)); } /** * Sets the sizes of the endpoint editors and labels, so that label text * is fully visible, and the editors are of at least minimum size. */ private void resizeEndpointLabels() { // Set minimum size on the left label/editor so that neither are squished Dimension editorSize = leftEditor.getPreferredSize(); if (editorSize == null) { editorSize = leftEditor.getMinimumSize(); } Dimension labelSize = leftLabelMinSize; int width = Math.max(editorSize.width, labelSize.width); int height = Math.max(editorSize.height, labelSize.height); leftLabel.setMinimumSize(new Dimension(width, height)); leftEditor.setMinResizeDimension(new Dimension(width, height)); // Set the preferred size of the left label/editor editorSize = leftEditor.getPreferredSize(); labelSize = leftLabel.getMinimumSize(); width = Math.max(editorSize.width, labelSize.width); height = Math.max(editorSize.height, labelSize.height); leftLabel.setPreferredSize(new Dimension(width, height)); leftEditor.setPreferredSize(new Dimension(width, height)); // Set minimum size on the right label/editor editorSize = rightEditor.getPreferredSize(); if (editorSize == null) { editorSize = rightEditor.getMinimumSize(); } labelSize = rightLabelMinSize;//.getMinimumSize(); width = Math.max(editorSize.width, labelSize.width); height = Math.max(editorSize.height, labelSize.height); rightLabel.setMinimumSize(new Dimension(width, height)); rightEditor.setMinResizeDimension(new Dimension(width, height)); // Set the preferred size of the right label/editor editorSize = rightEditor.getPreferredSize(); labelSize = rightLabel.getMinimumSize(); width = Math.max(editorSize.width, labelSize.width); height = Math.max(editorSize.height, labelSize.height); rightLabel.setPreferredSize(new Dimension(width, height)); rightEditor.setPreferredSize(new Dimension(width, height)); } /** * Sets the initial value of this editor. * This method creates the value entry panels for the endpoint nodes, and * initializes the panel displayed by this editor. */ @Override public void setInitialValue() { // The range value editor supports Ordinal Range types, so the // following ensures that the range value node is an instance of // Ordinal prior to being edited. enforceOrdType(); // Now initialize the panel // Create child value entry panels for the left/right endpoints RangeValueNode rangeNode = getRangeValueNode(); leftEditor = makeChildValueEntryPanel(rangeNode.getLeftEndpoint()); rightEditor = makeChildValueEntryPanel(rangeNode.getRightEndpoint()); // Define the context for the child editors. The least constrained type expression // is the expression of the type constructor argument (ie: this is 'Double' if // the editor value node type is 'Range Double') ValueEditorContext context = new ValueEditorContext() { public TypeExpr getLeastConstrainedTypeExpr() { TypeExpr leastConstrainedParentType = getContext().getLeastConstrainedTypeExpr(); if (leastConstrainedParentType instanceof TypeVar) { // Range type is parametric (ie: value gem was not bound to connection) // The editors will handle types unifying with "Ord a => a", the valid type for // of the range constructor. TypeExpr ordType = ordRangeType.getArg(0); return ordType; } else { // The parent is not parametric, so the child must fit the parent constraint. // Here also check and enforce ordering TypeExpr childExpr = ((TypeConsApp)leastConstrainedParentType).getArg(0); TypeExpr newType = null; try { newType = TypeExpr.unify(ordRangeType.getArg(0), childExpr, getValueEditorHierarchyManager().getValueEditorManager().getPerspective().getWorkingModuleTypeInfo()); } catch (TypeException e) { throw new IllegalStateException("RangeValueEditor: Failed to unify type '" + childExpr + "' with '" + ordRangeType + "'."); } return newType; } } }; leftEditor.setContext(context); rightEditor.setContext(context); // Add commit listeners to the child editors ChildValueEditorListener editorListener = new ChildValueEditorListener(); leftEditor.addValueEditorListener(editorListener); rightEditor.addValueEditorListener(editorListener); // Set up the UI components initializeMainPanel(); showRangeForm(); // Set the appropriate editor panel size validate(); Dimension prefSize = getPreferredSize(); setMinResizeDimension(prefSize); setSize(prefSize); } /** * Refresh the editor display by resetting the size of the editor to * match its components. * * @see org.openquark.gems.client.valueentry.ValueEditor#refreshDisplay() */ @Override public void refreshDisplay() { resizeEndpointLabels(); setMinResizeDimension(getPreferredSize()); if (getPreferredSize().width > getSize().width) { setSize(new Dimension(getPreferredSize().width, getSize().height)); } if (getPreferredSize().height > getSize().height) { setSize(new Dimension(getSize().width, getPreferredSize().height)); } } /** * Sets the UI component settings according to the range form * (ie: sets the visibility of the endpoint editors, and selects * the appropriate toggle buttons) */ private void showRangeForm() { RangeValueNode.Form rangeForm = getRangeValueNode().getForm(); // Enable/disable editors leftEditor.setVisible(rangeForm.hasLeftBound()); rightEditor.setVisible(rangeForm.hasRightBound()); // Set left endpoint buttons if (!rangeForm.hasLeftBound()) { unboundLToggle.setSelected(true); } else if (rangeForm.includesLeftBound()) { closedLToggle.setSelected(true); } else { openLToggle.setSelected(true); } // Set right endpoint buttons if (!rangeForm.hasRightBound()) { unboundRToggle.setSelected(true); } else if (rangeForm.includesRightBound()) { closedRToggle.setSelected(true); } else { openRToggle.setSelected(true); } refreshDisplay(); } /** * @return the range form representing the current UI button settings */ private RangeValueNode.Form getRangeForm() { return new RangeValueNode.Form( unboundLToggle.getSelectedObjects() == null, unboundRToggle.getSelectedObjects() == null, closedLToggle.getSelectedObjects() != null, closedRToggle.getSelectedObjects() != null); } /** * {@inheritDoc} */ @Override public void setEditable(boolean editable) { if (leftEditor != null) { leftEditor.setEditable(editable); rightEditor.setEditable(editable); } leftLabel.setEnabled(editable); rightLabel.setEnabled(editable); unboundLToggle.setEnabled(editable); unboundRToggle.setEnabled(editable); openLToggle.setEnabled(editable); openRToggle.setEnabled(editable); closedLToggle.setEnabled(editable); closedRToggle.setEnabled(editable); } /** * @see org.openquark.gems.client.valueentry.ValueEditor#editorActivated() */ @Override public void editorActivated() { valueEditorHierarchyManager.addEditorToHierarchy(leftEditor, this); setResizable(true); } /** * {@inheritDoc} */ @Override protected void handleElementLaunchingEditor() { } /** * @return the range value node currently edited */ public RangeValueNode getRangeValueNode() { return (RangeValueNode)getValueNode(); } /** * Create a child value entry panel to handle the specified value node. * @param valueNode the value node for the value entry panel * @return a value entry panel to be used in this editor */ private ValueEntryPanel makeChildValueEntryPanel(ValueNode valueNode) { ValueEntryPanel childPanel = new ValueEntryPanel(valueEditorHierarchyManager, valueNode); childPanel.setAlwaysShowLaunchButton(false); childPanel.setParentValueEditor(this); return childPanel; } /** * Enforces the type expression of the editor value node to be an instance of Ord. * This type change only occurs when the node has been instantiated with type "Range a"; * when this is not so, the check is enforced in RangeValueNodeProvider. */ private void enforceOrdType() { // Retrieve the default type expression of the parent editor node. // This is retrieved from the type of the supercombinator constructing // the range entity, and will be "Ord a => Range a". TypeExpr ordParametric = ordRangeType; // Now, unify this with the current value node type. This will (1) specialize // a fully parametric type (ie: Range a) to be an instance of Ord, and (2) check that // if the type is already specialized (ex: Range Double), the type is actually orderable // (this check is enforced in RangeValueNode). TypeExpr newType = null; try { newType = TypeExpr.unify(ordParametric, getValueNode().getTypeExpr(), getValueEditorHierarchyManager().getValueEditorManager().getPerspective().getWorkingModuleTypeInfo()); } catch (TypeException e) { throw new IllegalStateException("RangeValueEditor: Failed to unify type '" + getValueNode().getTypeExpr() + "' with '" + ordParametric + "'."); } // Store the ordered type as our type this.replaceValueNode(getValueNode().transmuteValueNode(valueEditorManager.getValueNodeBuilderHelper(), valueEditorManager.getValueNodeTransformer(), newType), false); } /** * Get a map from owner value node to type for ValueEntryPanels for the nodes in this editor. * @return Map from every value node managed by this editor to its least constrained type. */ private Map<ValueNode, TypeExpr> getValueNodeToUnconstrainedTypeMap() { Map<ValueNode, TypeExpr> returnMap = new HashMap<ValueNode, TypeExpr>(); // Calculate the type of the parent value editor node TypeExpr contextType = getContext().getLeastConstrainedTypeExpr(); if (contextType.rootTypeVar() != null) { // ValueGem is not bound to a context, so the type should be the parametric range type // Retrieve the default type expression of the parent editor node. // // This is retrieved from the type of the supercombinator constructing // the range entity, and will be "Ord a => Range a". contextType = ordRangeType; } // Populate the map // With our node and type returnMap.put(getValueNode(), contextType); // With the child editor nodes and types TypeExpr childType = contextType.rootTypeConsApp().getArg(0); returnMap.put(leftEditor.getOwnerValueNode(), childType); returnMap.put(rightEditor.getOwnerValueNode(), childType); return returnMap; } }