/* * * * Copyright (C) 2015 CS SI * * * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation; either version 3 of the License, or (at your option) * * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along * * with this program; if not, see http://www.gnu.org/licenses/ * */ package org.esa.snap.ui.tooladapter.dialogs; import com.bc.ceres.binding.PropertyContainer; import com.bc.ceres.binding.PropertyDescriptor; import com.bc.ceres.binding.ValueSet; import com.bc.ceres.binding.converters.ArrayConverter; import com.bc.ceres.binding.validators.NotEmptyValidator; import com.bc.ceres.binding.validators.PatternValidator; import com.bc.ceres.swing.binding.BindingContext; import com.bc.ceres.swing.binding.PropertyEditor; import com.bc.ceres.swing.binding.PropertyEditorRegistry; import com.bc.ceres.swing.binding.internal.TextFieldEditor; import org.esa.snap.core.dataio.ProductIOPlugInManager; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.gpf.GPF; import org.esa.snap.core.gpf.OperatorException; import org.esa.snap.core.gpf.OperatorSpi; import org.esa.snap.core.gpf.descriptor.*; import org.esa.snap.core.gpf.descriptor.template.TemplateEngine; import org.esa.snap.core.gpf.descriptor.template.TemplateException; import org.esa.snap.core.gpf.descriptor.template.TemplateFile; import org.esa.snap.core.gpf.descriptor.template.TemplateType; import org.esa.snap.core.gpf.operators.tooladapter.ToolAdapterConstants; import org.esa.snap.core.gpf.operators.tooladapter.ToolAdapterIO; import org.esa.snap.core.gpf.operators.tooladapter.ToolAdapterOpSpi; import org.esa.snap.modules.ModulePackager; import org.esa.snap.rcp.util.Dialogs; import org.esa.snap.ui.AppContext; import org.esa.snap.ui.ModalDialog; import org.esa.snap.ui.tooladapter.actions.EscapeAction; import org.esa.snap.ui.tooladapter.model.AutoCompleteTextArea; import org.esa.snap.ui.tooladapter.model.OperationType; import org.esa.snap.ui.tooladapter.model.OperatorParametersTable; import org.esa.snap.ui.tooladapter.model.VariablesTable; import org.esa.snap.ui.tooladapter.preferences.ToolAdapterOptionsController; import org.esa.snap.ui.tooladapter.validators.RequiredFieldValidator; import org.esa.snap.utils.AdapterWatcher; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import javax.swing.*; import java.awt.*; import java.awt.event.ItemEvent; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.esa.snap.utils.SpringUtilities.DEFAULT_PADDING; import static org.esa.snap.utils.SpringUtilities.makeCompactGrid; /** * A dialog window used to edit an operator, or to create a new operator. * It shows details of an operator such as: descriptor details (name, alias, label, version, copyright, * authors, description), system variables, preprocessing tool, product writer, tool location, * operator working directory, command line template content, tool output patterns and parameters. * * @author Cosmin Cara */ @NbBundle.Messages({ "CTL_Label_Alias_Text=Alias:", "CTL_Label_UniqueName_Text=Unique Name:", "CTL_Label_Label_Text=Label:", "CTL_Label_Version_Text=Version:", "CTL_Label_Copyright_Text=Copyright:", "CTL_Label_Authors_Text=Authors:", "CTL_Label_Description_Text=Description:", "CTL_Label_MenuLocation_Text=Menu Location:", "CTL_Label_TemplateType_Text=Template Type:", "CTL_Panel_OperatorDescriptor_Text=Operator Descriptor", "CTL_Label_PreprocessingTool_Text=Preprocessing Tool: ", "CTL_Label_WriteBefore_Text=Before Processing Convert To: ", "CTL_Panel_PreProcessing_Border_TitleText=Preprocessing", "CTL_Panel_ConfigParams_Text=Configuration Parameters", "CTL_Label_ToolLocation_Text=Tool Executable: ", "CTL_Label_WorkDir_Text=Working Directory: ", "CTL_Label_CmdLineTemplate_Text=Command Line Template:", "CTL_Panel_OutputPattern_Border_TitleText=Tool Output Patterns", "CTL_Label_ProgressPattern=Numeric Progress Pattern: ", "CTL_Label_ErrorPattern=Error Pattern: ", "CTL_Label_StepPattern=Intermediate Operation Pattern: ", "CTL_Panel_SysVar_Border_TitleText=System Variables", "CTL_Label_RadioButton_ExistingMenus=Existing Menus", "CTL_Label_RadioButton_NewMenu=Create Menu", "Icon_Add=/org/esa/snap/resources/images/icons/Add16.png", "CTL_Panel_OpParams_Border_TitleText=Operator Parameters", "CTL_Button_Export_Text=Export as Module", "CTL_Button_Add_Variable_Text=Add Variable", "CTL_Button_Add_PDVariable_Text=Add Platform-Dependent Variable", "MSG_Export_Complete_Text=The adapter was exported as a NetBeans module in %s", "MSG_Inexistent_Tool_Path_Text=The tool executable does not exist.\n" + "Please specify the location of an existing executable.", "MSG_Inexistent_WorkDir_Text=The working directory does not exist.\n" + "Please specify a valid location.", "MSG_Existing_UniqueName_Text=An operator with the same unique name is already registered.\n" + "Please specify an unique name for the operator.", "MSG_Inexistem_Parameter_Value_Text=The file or folder for parameter %s does not exist.\n" + "Please specify a valid location or change the %s property of the parameter.", "MSG_Wrong_Value_Text=One or more form parameters have invalid values.\n" + "Please correct them before saving the adapter.", "MSG_Wrong_Usage_Array_Text=You have used array notation for source products, but only one product will be used.\n" + "Please correct the problem before saving the adapter.", "MSG_Empty_Variable_Text=The variable %s has no value set", "MSG_Empty_MenuLocation_Text=Value of 'Menu location' cannot be empty", "MSG_Empty_Variable_Key_Text=Empty variable key/name is not allowed" }) public abstract class AbstractAdapterEditor extends ModalDialog { protected static final String MESSAGE_REQUIRED = "This field is required"; protected static final int MIN_WIDTH = 720; protected static final int MIN_HEIGHT = 580; protected static final int MIN_TABBED_WIDTH = 640; protected static final int MIN_TABBED_HEIGHT = 512; protected static int MAX_4K_WIDTH = 4096; protected static int MAX_4K_HEIGHT = 2160; protected ToolAdapterOperatorDescriptor oldOperatorDescriptor; protected ToolAdapterOperatorDescriptor newOperatorDescriptor; protected int newNameIndex = -1; protected PropertyContainer propertyContainer; protected BindingContext bindingContext; protected JTextArea templateContent; protected OperatorParametersTable paramsTable; protected AppContext context; protected Logger logger; protected JTextField customMenuLocation; protected JRadioButton rbMenuNew; public static final String helpID = "sta_editor"; protected int formWidth; protected int controlHeight = 24; protected OperationType currentOperation; protected VariablesTable varTable; public static AbstractAdapterEditor createEditorDialog(AppContext appContext, JDialog parent, ToolAdapterOperatorDescriptor operatorDescriptor, OperationType operation) { AbstractAdapterEditor dialog; if (useTabsForEditorDialog()) { dialog = new ToolAdapterTabbedEditorDialog(appContext, parent, operatorDescriptor, operation); } else { dialog = new ToolAdapterEditorDialog(appContext, parent, operatorDescriptor, operation); } return dialog; } public static AbstractAdapterEditor createEditorDialog(AppContext appContext, JDialog parent, ToolAdapterOperatorDescriptor operatorDescriptor, int newNameIndex, OperationType operation) { AbstractAdapterEditor dialog; if (useTabsForEditorDialog()) { dialog = new ToolAdapterTabbedEditorDialog(appContext, parent, operatorDescriptor, newNameIndex, operation); } else { dialog = new ToolAdapterEditorDialog(appContext, parent, operatorDescriptor, newNameIndex, operation); } return dialog; } private static boolean useTabsForEditorDialog() { //return Boolean.parseBoolean(NbPreferences.forModule(Dialogs.class).get(ToolAdapterOptionsController.PREFERENCE_KEY_TABBED_WINDOW, "false")); return true; } private AbstractAdapterEditor(AppContext appContext, JDialog parent, String title) { super(parent.getOwner(), title, ID_OK_CANCEL_HELP, new Object[] { new JButton(Bundle.CTL_Button_Export_Text()) }, helpID); this.context = appContext; this.logger = Logger.getLogger(ToolAdapterEditorDialog.class.getName()); this.registerButton(ID_OTHER, new JButton(Bundle.CTL_Button_Export_Text())); controlHeight = (int)((getJDialog().getFont().getSize() + 1) * 2); } private AbstractAdapterEditor(AppContext appContext, JDialog parent, ToolAdapterOperatorDescriptor operatorDescriptor) { this(appContext, parent, operatorDescriptor.getAlias()); this.oldOperatorDescriptor = operatorDescriptor; this.newOperatorDescriptor = new ToolAdapterOperatorDescriptor(this.oldOperatorDescriptor); //see if all necessary parameters are present: if (newOperatorDescriptor.getToolParameterDescriptors().stream().filter(p -> p.getName().equals(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_ID)).count() == 0){ TemplateParameterDescriptor parameterDescriptor = new TemplateParameterDescriptor(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_ID, Product[].class); parameterDescriptor.setDescription("Input product"); newOperatorDescriptor.getToolParameterDescriptors().add(parameterDescriptor); } if (newOperatorDescriptor.getToolParameterDescriptors().stream().filter(p -> p.getName().equals(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_FILE)).count() == 0){ TemplateParameterDescriptor parameterDescriptor = new TemplateParameterDescriptor(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_FILE, File[].class); parameterDescriptor.setDescription("Input file"); newOperatorDescriptor.getToolParameterDescriptors().add(parameterDescriptor); } if (newOperatorDescriptor.getToolParameterDescriptors().stream().filter(p -> p.getName().equals(ToolAdapterConstants.TOOL_TARGET_PRODUCT_FILE)).count() == 0){ TemplateParameterDescriptor parameterDescriptor = new TemplateParameterDescriptor(ToolAdapterConstants.TOOL_TARGET_PRODUCT_FILE, File.class); parameterDescriptor.setDescription("Output file"); parameterDescriptor.setNotNull(false); newOperatorDescriptor.getToolParameterDescriptors().add(parameterDescriptor); } else { Optional<ToolParameterDescriptor> result = newOperatorDescriptor.getToolParameterDescriptors() .stream() .filter(p -> p.getName().equals(ToolAdapterConstants.TOOL_TARGET_PRODUCT_FILE)).findFirst(); if (result.isPresent()) { result.get().setDescription("Output file"); } } propertyContainer = PropertyContainer.createObjectBacked(newOperatorDescriptor); ProductIOPlugInManager registry = ProductIOPlugInManager.getInstance(); String[] writers = registry.getAllProductWriterFormatStrings(); Arrays.sort(writers); propertyContainer.getDescriptor(ToolAdapterConstants.PROCESSING_WRITER).setValueSet(new ValueSet(writers)); Set<OperatorSpi> spis = GPF.getDefaultInstance().getOperatorSpiRegistry().getOperatorSpis(); java.util.List<String> toolboxSpis = new ArrayList<>(); spis.stream().filter(p -> (p instanceof ToolAdapterOpSpi) && (p.getOperatorDescriptor().getClass() != AnnotationOperatorDescriptor.class) && !p.getOperatorAlias().equals(oldOperatorDescriptor.getAlias())) .forEach(operator -> toolboxSpis.add(operator.getOperatorDescriptor().getAlias())); toolboxSpis.sort(Comparator.<String>naturalOrder()); propertyContainer.getDescriptor(ToolAdapterConstants.PREPROCESSOR_EXTERNAL_TOOL).setValueSet(new ValueSet(toolboxSpis.toArray(new String[toolboxSpis.size()]))); bindingContext = new BindingContext(propertyContainer); paramsTable = new OperatorParametersTable(newOperatorDescriptor, appContext); varTable = new VariablesTable(newOperatorDescriptor.getVariables(), appContext); } /** * Constructs a new window for editing the operator * @param appContext the application context * @param operatorDescriptor the descriptor of the operator to be edited * @param operation is the type of desired operation: NEW/COPY if the operator was not previously registered (so it is a new operator) and EDIT if the operator was registered and the editing operation is requested */ public AbstractAdapterEditor(AppContext appContext, JDialog parent, ToolAdapterOperatorDescriptor operatorDescriptor, OperationType operation) { this(appContext, parent, operatorDescriptor); this.currentOperation = operation; this.newNameIndex = -1; setContent(createMainPanel()); EscapeAction.register(this.getJDialog()); } /** * Constructs a new window for editing the operator * @param appContext the application context * @param operatorDescriptor the descriptor of the operator to be edited * @param newNameIndex an integer value representing the suffix for the new operator name; if this value is less than 1, the editing operation of the current operator is executed; if the value is equal to or greater than 1, the operator is duplicated and the index value is used to compute the name of the new operator * @param operation is the type of desired operation: NEW/COPY if the operator was not previously registered (so it is a new operator) and EDIT if the operator was registered and the editing operation is requested */ public AbstractAdapterEditor(AppContext appContext, JDialog parent, ToolAdapterOperatorDescriptor operatorDescriptor, int newNameIndex, OperationType operation) { this(appContext, parent, operatorDescriptor); this.newNameIndex = newNameIndex; this.currentOperation = operation; if(this.newNameIndex >= 1) { this.newOperatorDescriptor.setName(this.oldOperatorDescriptor.getName() + ToolAdapterConstants.OPERATOR_GENERATED_NAME_SEPARATOR + this.newNameIndex); this.newOperatorDescriptor.setAlias(this.oldOperatorDescriptor.getAlias() + ToolAdapterConstants.OPERATOR_GENERATED_NAME_SEPARATOR + this.newNameIndex); } setContent(createMainPanel()); EscapeAction.register(this.getJDialog()); } ToolAdapterOperatorDescriptor getUpdatedOperatorDescriptor() { return this.newOperatorDescriptor; } protected abstract JComponent createMainPanel(); protected abstract JPanel createDescriptorPanel(); protected abstract JPanel createVariablesPanel(); protected abstract JPanel createPreProcessingPanel(); protected abstract JPanel createToolInfoPanel(); protected abstract JPanel createPatternsPanel(); protected abstract JPanel createParametersPanel(); protected boolean shouldValidate() { String value = NbPreferences.forModule(Dialogs.class).get(ToolAdapterOptionsController.PREFERENCE_KEY_VALIDATE_PATHS, null); return value == null || Boolean.parseBoolean(value); } @Override protected boolean verifyUserInput() { /* Make sure we have stopped table cell editing for both: VariablesTable and OperatorParametersTable */ varTable.stopVariablesTableEditing(); paramsTable.stopVariablesTableEditing(); /** * Verify the existence of the tool executable */ if (shouldValidate()) { File file = newOperatorDescriptor.getMainToolFileLocation(); if (file == null) { // should not come here unless, somehow, the property value was not set by binding Object value = bindingContext.getBinding(ToolAdapterConstants.MAIN_TOOL_FILE_LOCATION).getPropertyValue(); if (value != null) { file = value instanceof File ? (File) value : new File(value.toString()); } } if (file == null) { Dialogs.showWarning(Bundle.MSG_Inexistent_Tool_Path_Text()); return false; } Path toolLocation = newOperatorDescriptor.resolveVariables(newOperatorDescriptor.getMainToolFileLocation()).toPath(); if (!(Files.exists(toolLocation) && Files.isExecutable(toolLocation))) { Dialogs.showWarning(Bundle.MSG_Inexistent_Tool_Path_Text()); return false; } /** * Verify the existence of the working directory */ File workingDir = newOperatorDescriptor.resolveVariables(newOperatorDescriptor.getWorkingDir()); if (!(workingDir != null && workingDir.exists() && workingDir.isDirectory())) { Dialogs.showWarning(Bundle.MSG_Inexistent_WorkDir_Text()); return false; } } /** * Verify that there is no System Variable without value */ java.util.List<SystemVariable> variables = newOperatorDescriptor.getVariables(); if (variables != null) { for (SystemVariable variable : variables) { String key = variable.getKey(); if (key == null || key.isEmpty()) { Dialogs.showWarning(String.format(Bundle.MSG_Empty_Variable_Key_Text())); return false; } String value = variable.getValue(); if (value == null || value.isEmpty()) { Dialogs.showWarning(String.format(Bundle.MSG_Empty_Variable_Text(), key)); return false; } } } if (shouldValidate()) { /** * Verify the existence of files for File parameter values that are marked as Not Null or Not Empty */ ParameterDescriptor[] parameterDescriptors = newOperatorDescriptor.getParameterDescriptors(); if (parameterDescriptors != null && parameterDescriptors.length > 0) { for (ParameterDescriptor parameterDescriptor : parameterDescriptors) { Class<?> dataType = parameterDescriptor.getDataType(); String defaultValue = parameterDescriptor.getDefaultValue(); if (File.class.isAssignableFrom(dataType) && (parameterDescriptor.isNotNull() || parameterDescriptor.isNotEmpty()) && (defaultValue == null || defaultValue.isEmpty() || !Files.exists(Paths.get(defaultValue)))) { Dialogs.showWarning(String.format(Bundle.MSG_Inexistem_Parameter_Value_Text(), parameterDescriptor.getName(), parameterDescriptor.isNotNull() ? ToolAdapterConstants.NOT_NULL : ToolAdapterConstants.NOT_EMPTY)); return false; } } } } //verify the adapter unique name really is unique if (currentOperation.equals(OperationType.COPY) || currentOperation.equals(OperationType.NEW) || (currentOperation.equals(OperationType.COPY) && !newOperatorDescriptor.getName().equals(oldOperatorDescriptor.getName()))) { if (GPF.getDefaultInstance().getOperatorSpiRegistry().getOperatorSpi(newOperatorDescriptor.getName()) != null) { Dialogs.showWarning(String.format(Bundle.MSG_Existing_UniqueName_Text())); return false; } } return true; } @Override protected void onOK() { if (!verifyUserInput()) { Dialogs.showWarning(Bundle.MSG_Wrong_Value_Text()); this.getJDialog().requestFocus(); } else { String templateContent = this.templateContent.getText(); if (!resolveTemplateProductCount(templateContent)) { Dialogs.showWarning(Bundle.MSG_Wrong_Usage_Array_Text()); this.getJDialog().requestFocus(); } else { Path backupCopy = null; Exception thrown = null; try { backupCopy = ToolAdapterIO.backupOperator(oldOperatorDescriptor); if (newOperatorDescriptor.getSourceProductCount() == 0) { Dialogs.showInformation("The template is not using the parameter $sourceProduct.\nNo source product selection will be available at execution time.", "empty.source.info"); } if (!newOperatorDescriptor.isFromPackage()) { newOperatorDescriptor.setSource(ToolAdapterOperatorDescriptor.SOURCE_USER); } TemplateFile template = new TemplateFile(TemplateEngine.createInstance(newOperatorDescriptor, TemplateType.VELOCITY), newOperatorDescriptor.getAlias() + ToolAdapterConstants.TOOL_VELO_TEMPLATE_SUFIX); template.setContents(templateContent, true); newOperatorDescriptor.setTemplate(template); java.util.List<ToolParameterDescriptor> toolParameterDescriptors = newOperatorDescriptor.getToolParameterDescriptors(); toolParameterDescriptors.stream().filter(param -> paramsTable.getBindingContext().getBinding(param.getName()) != null) .filter(param -> paramsTable.getBindingContext().getBinding(param.getName()).getPropertyValue() != null) .forEach(param -> { Object propertyValue = paramsTable.getBindingContext().getBinding(param.getName()).getPropertyValue(); if (param.isParameter()) { String defaultValueString = ""; if (propertyValue.getClass().isArray()) { defaultValueString = String.join(ArrayConverter.SEPARATOR, Arrays.asList((Object[]) propertyValue).stream().map(Object::toString).collect(Collectors.toList())); } else { defaultValueString = propertyValue.toString(); } param.setDefaultValue(defaultValueString); } }); java.util.List<ToolParameterDescriptor> remParameters = toolParameterDescriptors.stream().filter(param -> (ToolAdapterConstants.TOOL_SOURCE_PRODUCT_ID.equals(param.getName()) || ToolAdapterConstants.TOOL_SOURCE_PRODUCT_FILE.equals(param.getName()))). collect(Collectors.toList()); newOperatorDescriptor.removeParamDescriptors(remParameters); if (rbMenuNew.isSelected()) { String customMenuLocationText = customMenuLocation.getText(); if (customMenuLocationText != null && !customMenuLocationText.isEmpty()) { newOperatorDescriptor.setMenuLocation(customMenuLocationText); } } String menuLocation = newOperatorDescriptor.getMenuLocation(); if (menuLocation != null && !menuLocation.startsWith("Menu/")) { newOperatorDescriptor.setMenuLocation("Menu/" + menuLocation); } AdapterWatcher.INSTANCE.suspend(); ToolAdapterIO.saveAndRegisterOperator(newOperatorDescriptor); AdapterWatcher.INSTANCE.resume(); ToolAdapterIO.deleteFolder(backupCopy); super.setButtonID(ID_OK); super.hide(); } catch (TemplateException tex) { logger.warning(tex.getMessage()); Dialogs.showError("The adapter template contains errors [" + tex.toString() + "]!"); thrown = tex; } catch (Exception e) { logger.warning(e.getMessage()); Dialogs.showError("There was an error on saving the operator; check the disk space and permissions and try again! " + e.toString()); thrown = e; } finally { if (thrown != null) { if (backupCopy != null) { try { ToolAdapterIO.restoreOperator(oldOperatorDescriptor, backupCopy); } catch (IOException e) { logger.severe(e.getMessage()); Dialogs.showError("The operator could not be restored [" + e.getMessage() + "]"); } } } } } } } @Override public int show() { getJDialog().revalidate(); return super.show(); } @Override protected void onOther() { try { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); if (fileChooser.showOpenDialog(getButton(ID_OTHER)) == JFileChooser.APPROVE_OPTION) { File targetFolder = fileChooser.getSelectedFile(); newOperatorDescriptor.setSource(ToolAdapterOperatorDescriptor.SOURCE_PACKAGE); onOK(); ModulePackager.packModule(newOperatorDescriptor, new File(targetFolder, newOperatorDescriptor.getAlias() + ".nbm")); Dialogs.showInformation(String.format(Bundle.MSG_Export_Complete_Text(), targetFolder.getAbsolutePath()), null); } } catch (IOException e) { logger.warning(e.getMessage()); Dialogs.showError(e.getMessage()); } } protected JComponent createCheckboxComponent(String memberName, JComponent toogleComponentEnabled, Boolean value) { PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor(memberName); PropertyEditor editor = PropertyEditorRegistry.getInstance().findPropertyEditor(propertyDescriptor); JComponent editorComponent = editor.createEditorComponent(propertyDescriptor, bindingContext); if (editorComponent instanceof JCheckBox && toogleComponentEnabled != null) { ((JCheckBox) editorComponent).setSelected(value); toogleComponentEnabled.setEnabled(value); ((JCheckBox) editorComponent).addActionListener(e -> toogleComponentEnabled.setEnabled(((JCheckBox) editorComponent).isSelected())); } return editorComponent; } protected void addValidatedTextField(JPanel parent, TextFieldEditor textEditor, String labelText, String propertyName, String validatorRegex) { if(validatorRegex == null || validatorRegex.isEmpty()){ addTextField(parent, textEditor, labelText, propertyName, false); } else { parent.add(new JLabel(labelText)); PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor(propertyName); propertyDescriptor.setValidator(new PatternValidator(Pattern.compile(validatorRegex))); JComponent editorComponent = textEditor.createEditorComponent(propertyDescriptor, bindingContext); editorComponent.setPreferredSize(new Dimension(editorComponent.getPreferredSize().width, controlHeight)); editorComponent.setMaximumSize(new Dimension(editorComponent.getMaximumSize().width, controlHeight)); parent.add(editorComponent); } } protected void addTextField(JPanel parent, TextFieldEditor textEditor, String labelText, String propertyName, boolean isRequired) { parent.add(new JLabel(labelText)); PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor(propertyName); if (isRequired) { propertyDescriptor.setValidator(new NotEmptyValidator()); } JComponent editorComponent = textEditor.createEditorComponent(propertyDescriptor, bindingContext); editorComponent.setPreferredSize(new Dimension(editorComponent.getPreferredSize().width, controlHeight)); editorComponent.setMaximumSize(new Dimension(editorComponent.getMaximumSize().width, controlHeight)); parent.add(editorComponent); } protected void addComboField(JPanel parent, String labelText, String propertyName, boolean isRequired, boolean isEditable) { PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor(propertyName); propertyDescriptor.setNotEmpty(isRequired); PropertyEditor editor = PropertyEditorRegistry.getInstance().findPropertyEditor(propertyDescriptor); JComponent editorComponent = editor.createEditorComponent(propertyDescriptor, bindingContext); editorComponent.setMaximumSize(new Dimension(editorComponent.getMaximumSize().width, controlHeight)); editorComponent.setPreferredSize(new Dimension(editorComponent.getPreferredSize().width, controlHeight)); if (editorComponent instanceof JComboBox) { JComboBox comboBox = (JComboBox)editorComponent; comboBox.setEditable(isEditable); comboBox.setEnabled(isEditable); } parent.add(new JLabel(labelText)); parent.add(editorComponent); } protected void addComboField(JPanel parent, String labelText, String propertyName, java.util.List<String> values, boolean sortValues, boolean isRequired) { parent.add(new JLabel(labelText)); PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor(propertyName); propertyDescriptor.setNotEmpty(isRequired); if (sortValues) { values.sort(Comparator.<String>naturalOrder()); } propertyDescriptor.setValueSet(new ValueSet(values.toArray())); PropertyEditor editor = PropertyEditorRegistry.getInstance().findPropertyEditor(propertyDescriptor); JComponent editorComp = editor.createEditorComponent(propertyDescriptor, bindingContext); if (editorComp instanceof JComboBox) { JComboBox comboBox = (JComboBox)editorComp; comboBox.setEditable(true); } editorComp.setMaximumSize(new Dimension(editorComp.getMaximumSize().width, controlHeight)); customMenuLocation = new JTextField(); customMenuLocation.setInputVerifier(new RequiredFieldValidator(Bundle.MSG_Empty_MenuLocation_Text())); //customMenuLocation.setPreferredSize(new Dimension(250, controlHeight)); customMenuLocation.setEnabled(false); JPanel subPanel = new JPanel(new SpringLayout()); subPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); JRadioButton rbExistingMenu = new JRadioButton(Bundle.CTL_Label_RadioButton_ExistingMenus(), true); rbMenuNew = new JRadioButton(Bundle.CTL_Label_RadioButton_NewMenu()); ButtonGroup rbGroup = new ButtonGroup(); rbGroup.add(rbExistingMenu); rbGroup.add(rbMenuNew); // this radio button should be able to capture focus even when the validator // of the rbMenuNew says otherwise rbExistingMenu.setVerifyInputWhenFocusTarget(false); rbExistingMenu.addItemListener(e -> { editorComp.setEnabled(e.getStateChange() == ItemEvent.SELECTED); customMenuLocation.setEnabled(e.getStateChange() == ItemEvent.DESELECTED); }); subPanel.add(rbExistingMenu); subPanel.add(rbMenuNew); subPanel.add(editorComp); subPanel.add(customMenuLocation); Dimension dimension = new Dimension(parent.getWidth() / 2, controlHeight); editorComp.setPreferredSize(dimension); customMenuLocation.setPreferredSize(dimension); subPanel.setPreferredSize(new Dimension(subPanel.getWidth(), (int)(2.5 * controlHeight))); subPanel.setMaximumSize(new Dimension(subPanel.getWidth(), (int) (2.5 * controlHeight))); makeCompactGrid(subPanel, 2, 2, DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING); parent.add(subPanel); } protected java.util.List<String> getAvailableMenuOptions(FileObject current) { java.util.List<String> resultList = new ArrayList<>(); if (current == null) { current = FileUtil.getConfigRoot().getFileObject("Menu"); } FileObject[] children = current.getChildren(); for (FileObject child : children) { String entry = child.getPath(); if (!(entry.endsWith(".instance") || entry.endsWith(".shadow") || entry.endsWith(".xml"))) { resultList.add(entry); resultList.addAll(getAvailableMenuOptions(child)); } } return resultList; } protected boolean resolveTemplateProductCount(String templateContent) { boolean success = true; if (templateContent.contains(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_ID) || templateContent.contains(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_FILE)) { int idx = templateContent.lastIndexOf(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_ID + "["); if (idx > 0) { String value = templateContent.substring(idx + (ToolAdapterConstants.TOOL_SOURCE_PRODUCT_ID + "[").length(), templateContent.indexOf("]", idx)); int maxNum = Integer.valueOf(value) + 1; if (maxNum > 1) { newOperatorDescriptor.setSourceProductCount(maxNum); } else { success = false; } } else { idx = templateContent.lastIndexOf(ToolAdapterConstants.TOOL_SOURCE_PRODUCT_FILE + "["); if (idx > 0) { String value = templateContent.substring(idx + (ToolAdapterConstants.TOOL_SOURCE_PRODUCT_FILE + "[").length(), templateContent.indexOf("]", idx)); int maxNum = Integer.valueOf(value) + 1; if (maxNum > 1) { newOperatorDescriptor.setSourceProductCount(maxNum); } else { success = false; } } else { newOperatorDescriptor.setSourceProductCount(1); } } } else { newOperatorDescriptor.setSourceProductCount(0); } return success; } protected JTextArea createTemplateEditorField() { boolean useAutocomplete = Boolean.parseBoolean(NbPreferences.forModule(Dialogs.class).get(ToolAdapterOptionsController.PREFERENCE_KEY_AUTOCOMPLETE, "false")); if (useAutocomplete) { templateContent = new AutoCompleteTextArea("", 15, 9); } else { templateContent = new JTextArea("", 15, 9); } try { TemplateFile template; if ( (currentOperation == OperationType.NEW) || (currentOperation == OperationType.COPY) ) { template = oldOperatorDescriptor.getTemplate(); if (template != null) { //templateContent.setText(ToolAdapterIO.readOperatorTemplate(oldOperatorDescriptor.getName())); templateContent.setText(template.getContents()); } } else { template = newOperatorDescriptor.getTemplate(); if (template != null) { //templateContent.setText(ToolAdapterIO.readOperatorTemplate(newOperatorDescriptor.getName())); templateContent.setText(template.getContents()); } } } catch (IOException | OperatorException e) { logger.warning(e.getMessage()); } templateContent.setInputVerifier(new RequiredFieldValidator(MESSAGE_REQUIRED)); if (useAutocomplete && templateContent instanceof AutoCompleteTextArea) { ((AutoCompleteTextArea) templateContent).setAutoCompleteEntries(getAutocompleteEntries()); ((AutoCompleteTextArea) templateContent).setTriggerChar('$'); } return templateContent; } protected java.util.List<String> getAutocompleteEntries() { java.util.List<String> entries = new ArrayList<>(); entries.addAll(newOperatorDescriptor.getVariables().stream().map(SystemVariable::getKey).collect(Collectors.toList())); for (ParameterDescriptor parameterDescriptor : newOperatorDescriptor.getParameterDescriptors()) { entries.add(parameterDescriptor.getName()); } entries.sort(Comparator.<String>naturalOrder()); return entries; } }