/** * Display value entry controls for all unbound arguments. Precondition: the target must be set. */ private void displayArgumentControls() { TableTopPanel tableTopPanel = getTableTopPanel(); // Build the list of value entry panels and place them onto the TableTop // Get the list of unbound arguments List<Gem.PartInput> argParts = targetDisplayedGem.getTargetArguments(); // see if it's ok to use the cached argument values boolean canUseCachedArgs = canUseCachedArguments(); // Figure out the types of the argument panels. Note that we must account for cached values int numArgs = argParts.size(); // make copies of the input types. TypeExpr[] inputTypes = new TypeExpr[numArgs]; for (int i = 0; i < numArgs; i++) { // Get this input, and its type Gem.PartInput inputPart = argParts.get(i); inputTypes[i] = inputPart.getType(); } TypeExpr[] specializedInputTypes; if (canUseCachedArgs) { // this is the array of types cached for each argument. If there is no cached type, we just // use the argument type. TypeExpr[] cachedTypes = new TypeExpr[numArgs]; for (int i = 0; i < numArgs; ++i) { // What we do next depends on whether we use cached sink values Gem.PartInput inputPart = argParts.get(i); ValueNode cachedVN = gemCutter.getTableTop().getCachedValue(inputPart); if (cachedVN != null) { cachedTypes[i] = cachedVN.getTypeExpr(); } } try { ModuleTypeInfo contextModuleTypeInfo = gemCutter.getPerspective().getWorkingModuleTypeInfo(); specializedInputTypes = TypeExpr.patternMatchPieces(cachedTypes, inputTypes, contextModuleTypeInfo); } catch (TypeException te) { throw new IllegalStateException("Error reusing cached args."); } } else { specializedInputTypes = TypeExpr.copyTypeExprs(inputTypes); } ValueEditorManager valueEditorManager = gemCutter.getValueEditorManager(); ValueEditorDirector valueEditorDirector = valueEditorManager.getValueEditorDirector(); Map<PartInput, ValueEditor> inputToEditorMap = new LinkedHashMap<PartInput, ValueEditor>(); // Step through args, create a new value input panel for each, with the correct value type for (int i = 0; i < numArgs; i++) { // Get the gem and input final Gem.PartInput inputPart = argParts.get(i); final Gem inputGem = inputPart.getGem(); int argumentNumber = inputPart.getInputNum(); QualifiedName scName = null; if (inputGem instanceof FunctionalAgentGem) { scName = ((FunctionalAgentGem) inputGem).getName(); } // What we do next depends on whether we use cached sink values ValueNode cachedVN; final ValueEditor editor; if (canUseCachedArgs && (cachedVN = gemCutter.getTableTop().getCachedValue(inputPart)) != null) { // use cached sink value to generate the VEP // get a copy of the cached VN but with the new type expr ValueNode newVN = cachedVN.copyValueNode(); // instantiate the VEP with the new VN (which has the old cached value) editor = valueEditorDirector.getRootValueEditor( valueEditorHierarchyManager, newVN, scName, argumentNumber, new GemCutterMetadataRunner(gemCutter, inputGem)); } else { // don't use cached sink value. Generate the default VEP for this sink. TypeExpr inputType = specializedInputTypes[i]; editor = valueEditorDirector.getRootValueEditor( valueEditorHierarchyManager, inputType, scName, argumentNumber, new GemCutterMetadataRunner(gemCutter, inputGem)); } // If this is a value entry panel then set the name of the input for use in tooltips if (editor instanceof ValueEntryPanel) { ((ValueEntryPanel) editor) .setArgumentName(inputPart.getArgumentName().getCompositeName()); } // Position the editor next to the connection point. positionEditor(editor, inputPart); // Now add the editor to the table top. tableTopPanel.add(editor, 0); // Add to the list of active entry panels valueEditorHierarchyManager.addTopValueEditor(editor); // Update the context and editor map editor.setContext( new ValueEditorContext() { public TypeExpr getLeastConstrainedTypeExpr() { return inputPart.getType(); } }); inputToEditorMap.put(inputPart, editor); // If the editor resizes because it's value changes, make sure it stays aligned to the // connection editor.addComponentListener( new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { positionEditor((ValueEditor) e.getComponent(), inputPart); } }); } // Must prevent ValueGems from being editable. tableTopPanel.setValueGemsEnabled(false); // Need to activate the value editor hierarchy to ensure correct highlighting. valueEditorHierarchyManager.activateCurrentEditor(); // Set the context and type switch listener for each new editor. ArgumentValueCommitter argumentValueCommitter = new ArgumentValueCommitter(valueEditorManager, inputToEditorMap); for (final ValueEditor argumentEditor : getArgumentPanels()) { argumentEditor.addValueEditorListener(argumentValueCommitter); } // May need to show argument controls. if (numArgs > 0) { gemCutter.showArgumentControls(inputToEditorMap); gemCutter.getResetAction().setEnabled(true); } else { gemCutter.getResetAction().setEnabled(false); } }