package splar.apps.generator; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.util.LinkedList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.BevelBorder; import javax.swing.border.Border; import javax.swing.border.TitledBorder; public class FMGeneratorGUI extends JFrame implements ActionListener { /** * */ private static final long serialVersionUID = 3698662440079433924L; private static final String remarkMessage = "This software generates 3-CNF Feature Models, i.e., models in which the " + "cross-tree constraints (CTC) are represented by a Random 3-CNF Formula" ; private static final String initialMessage = "This software generates 3-CNF Feature Models. For details please see publication below:" + "\n\n M. Mendonca, A. Wasowski, K. Czarnecki: SAT-Based Analysis of Feature Models is Easy, Proceedings of SPLC 2009, San Francisco, USA" + "\n\n" + "IMPORTANT: The models are generated based on the parameters indicated above." + "It is important to note that some of these parameters are conflicting. For instance, if the clause density is to high " + "it might not be possible to generate consistent models. Also, conflicts can cause a significant delay in the generation " + "process as the system will attempt unproductively to produce a model that matches the specified parameters." + "The user should try to adjust parameters to avoid such scenarios." + "\n\n" + "Your suggestions for improvement and feedback are important. Contact us at http://www.splot-research.org"; private Color foreColor1 = new Color(50,50,250); private Color foreColor2 = new Color(250,50,50); private Color errorBgColor = new Color(250,50,50); private Font font1 = new Font("Default", Font.BOLD, 16); private Font font2 = new Font("Default", Font.BOLD, 12); JTextField collectionNameTextField = new JTextField("MyCollection"); JTextField collectionSizeTextField = new JTextField("10"); JTextField collectionPathTextField = new JTextField("c:\\my_feature_models\\"); JTextField featureTreeSizeTextField = new JTextField("100"); JTextField featureTreeMandatoryTextField = new JTextField("25"); JTextField featureTreeOptionalTextField = new JTextField("25"); JTextField featureTreeAlternativeTextField = new JTextField("25"); JTextField featureTreeExclusiveTextField = new JTextField("25"); JTextField featureTreeMinBranchingFactorTextField = new JTextField("1"); JTextField featureTreeMaxBranchingFactorTextField = new JTextField("6"); JTextField featureTreeMaxFeatureGroupSizeTextField = new JTextField("6"); JTextField ctcrTextField = new JTextField("20"); // JTextField ctcrToleranceTextField = new JTextField("5"); JTextField ctcClauseDensityTextField = new JTextField("1.0"); String[] data = {"Generate CONSISTENT models ONLY", "Generate INCONSISTENT models ONLY", "I DON'T CARE"}; JComboBox consistentyList = new JComboBox(data); JButton generateButton = new JButton("Generate"); JButton cancelButton = new JButton("Cancel"); JLabel messagesLabel = new JLabel(">> <<"); JTextArea messageOutputTextArea = new JTextArea(initialMessage); private List<FMGeneratorGUIListener> listeners; public FMGeneratorGUI(String title) { super(title); init(); } protected void init() { listeners = new LinkedList<FMGeneratorGUIListener>(); buildGUI(); addTooltips(); generateButton.addActionListener(this); cancelButton.addActionListener(this); } private void addTooltips() { collectionNameTextField.setToolTipText("What is the name of this collection of models (used as a prefix to name generated files)?"); collectionSizeTextField.setToolTipText("How many feature models do you want to generate in this collection?"); collectionPathTextField.setToolTipText("What is the path for saving your models? "); featureTreeSizeTextField.setToolTipText("What is the desired size for generated models?"); featureTreeMandatoryTextField.setToolTipText("What is the percentage of mandatory features?"); featureTreeOptionalTextField.setToolTipText("What is the percentage of optional features?"); featureTreeAlternativeTextField.setToolTipText("What is the percentage of alternative (OR) features?"); featureTreeExclusiveTextField.setToolTipText("What is the percentage of exclusive (XOR) features?"); featureTreeMinBranchingFactorTextField.setToolTipText("What is the minimum branching factor (# of children per parent node)?"); featureTreeMaxBranchingFactorTextField.setToolTipText("What is the maximum branching factor (# of children per parent node)?"); featureTreeMaxFeatureGroupSizeTextField.setToolTipText("What is the maximum number of grouped features in a Feature Group?"); ctcrTextField.setToolTipText("What percentage of the features in the feature tree should be considered to build the Random 3-CNF formula?"); // ctcrToleranceTextField.setToolTipText("How much do you accept the CTCR to differ from the desired value (e.g., for 5% just type 5)?"); ctcClauseDensityTextField.setToolTipText("What is the desired clause density for the Random 3-CNF formula (density = #clauses / #features)?"); consistentyList.setToolTipText("What is the desired consistency for your models?"); } private Border createTitleBorder(String title) { return BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED), title, TitledBorder.LEFT, TitledBorder.TOP, font1, foreColor1); } private JPanel generateRemarkPanel() { JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); JTextArea textArea = new JTextArea(remarkMessage); textArea.setEditable(false); textArea.setLineWrap(true); textArea.setForeground(foreColor1); textArea.setFont(font1); textArea.setWrapStyleWord(true); textArea.setRows(2); JPanel tempPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); tempPanel.setBackground(Color.white); JLabel label = new JLabel("Hover mouse over textfields to see tooltip descriptions"); label.setForeground(foreColor2); label.setFont(font2); tempPanel.add(label); panel.add(textArea, BorderLayout.CENTER); panel.add(tempPanel, BorderLayout.SOUTH); return panel; } public void buildGUI() { setLayout( new BorderLayout()); // Collection Information JPanel collectionPanel = new JPanel( new GridLayout(3,1)); collectionPanel.setBorder(createTitleBorder("Collection Information")); JPanel panel1 = new JPanel(new BorderLayout()); panel1.add(new JLabel("Name: "), BorderLayout.WEST); panel1.add( collectionNameTextField, BorderLayout.CENTER); JPanel panel2 = new JPanel(new BorderLayout()); panel2.add(new JLabel("Size: "), BorderLayout.WEST); panel2.add(collectionSizeTextField, BorderLayout.CENTER); JPanel panel3 = new JPanel(new BorderLayout()); panel3.add(new JLabel("Output Directory: "), BorderLayout.WEST); panel3.add( collectionPathTextField, BorderLayout.CENTER); collectionPanel.add(panel1); collectionPanel.add(panel2); collectionPanel.add(panel3); // Feature Tree Information JPanel featureTreePanel = new JPanel( new GridLayout(8,1)); featureTreePanel.setBorder(createTitleBorder("Feature Tree Information")); JPanel panel4 = new JPanel(new BorderLayout()); panel4.add(new JLabel("Size (# of features) [>1]: "), BorderLayout.WEST); panel4.add( featureTreeSizeTextField, BorderLayout.CENTER); JPanel panel5 = new JPanel(new BorderLayout()); panel5.add(new JLabel("% of Mandatory features [0-100]: "), BorderLayout.WEST); panel5.add( featureTreeMandatoryTextField, BorderLayout.CENTER); JPanel panel6 = new JPanel(new BorderLayout()); panel6.add(new JLabel("% of Optional features [0-100]: "), BorderLayout.WEST); panel6.add( featureTreeOptionalTextField, BorderLayout.CENTER); JPanel panel7 = new JPanel(new BorderLayout()); panel7.add(new JLabel("% of Alternative (OR) features [0-100]: "), BorderLayout.WEST); panel7.add( featureTreeAlternativeTextField, BorderLayout.CENTER); JPanel panel8 = new JPanel(new BorderLayout()); panel8.add(new JLabel("% of Exclusive (XOR) features [0-100]: "), BorderLayout.WEST); panel8.add( featureTreeExclusiveTextField, BorderLayout.CENTER); JPanel panel9 = new JPanel(new BorderLayout()); panel9.add(new JLabel("Minimum Branching Factor [>= 0]: "), BorderLayout.WEST); panel9.add( featureTreeMinBranchingFactorTextField, BorderLayout.CENTER); JPanel panel10 = new JPanel(new BorderLayout()); panel10.add(new JLabel("Maximum Branching Factor [>= Minimum factor]: "), BorderLayout.WEST); panel10.add( featureTreeMaxBranchingFactorTextField, BorderLayout.CENTER); JPanel panel11 = new JPanel(new BorderLayout()); panel11.add(new JLabel("Maximum Size for Feature Groups [> 1]: "), BorderLayout.WEST); panel11.add( featureTreeMaxFeatureGroupSizeTextField, BorderLayout.CENTER); featureTreePanel.add(panel4); featureTreePanel.add(panel5); featureTreePanel.add(panel6); featureTreePanel.add(panel7); featureTreePanel.add(panel8); featureTreePanel.add(panel9); featureTreePanel.add(panel10); featureTreePanel.add(panel11); // Cross-tree Constraints Information JPanel crosstreePanel = new JPanel( new GridLayout(3,1)); crosstreePanel.setBorder(createTitleBorder("Cross-Tree Constraints Information (Random 3-CNF Formula)")); JPanel panel12 = new JPanel(new BorderLayout()); panel12.add(new JLabel("% of Feature Tree Variables To Be Considered [0-100]: "), BorderLayout.WEST); panel12.add( ctcrTextField, BorderLayout.CENTER); // JPanel panel13 = new JPanel(new BorderLayout()); // panel13.add(new JLabel("CTCR Tolerance[0-100]: "), BorderLayout.WEST); // panel13.add( ctcrToleranceTextField, BorderLayout.CENTER); JPanel panel14 = new JPanel(new BorderLayout()); panel14.add(new JLabel("Clause Density [>= 0.0]: "), BorderLayout.WEST); panel14.add( ctcClauseDensityTextField, BorderLayout.CENTER); JPanel panel15 = new JPanel(new BorderLayout()); panel15.add(new JLabel("Model Consistency: "), BorderLayout.WEST); panel15.add( consistentyList, BorderLayout.CENTER); crosstreePanel.add(panel12); // crosstreePanel.add(panel13); crosstreePanel.add(panel14); crosstreePanel.add(panel15); JPanel buttonPanel = new JPanel(new GridLayout(1,3)); buttonPanel.add( generateButton); buttonPanel.add( cancelButton); cancelButton.setEnabled(false); JPanel centerPanel = new JPanel( new BorderLayout()); JPanel tempPanel = new JPanel(); tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.PAGE_AXIS)); JPanel errorMessagesPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); errorMessagesPanel.add(messagesLabel); tempPanel.add(collectionPanel); tempPanel.add(featureTreePanel); tempPanel.add(crosstreePanel); tempPanel.add(errorMessagesPanel); tempPanel.add(buttonPanel); JScrollPane pane = new JScrollPane(messageOutputTextArea); centerPanel.add(tempPanel, BorderLayout.NORTH); centerPanel.add(pane, BorderLayout.CENTER); messageOutputTextArea.setEditable(false); messageOutputTextArea.setLineWrap(true); messageOutputTextArea.setWrapStyleWord(true); add( generateRemarkPanel(), BorderLayout.NORTH); add( centerPanel, BorderLayout.CENTER); // add( new JPanel(), BorderLayout.CENTER); } public String getCollectionName() { return collectionNameTextField.getText().trim(); } public int getCollectionSize() { return Integer.parseInt(collectionSizeTextField.getText().trim()); } public String getCollectionPath() { return collectionPathTextField.getText().trim(); } public int getFeatureTreeSize() { return Integer.parseInt(featureTreeSizeTextField.getText().trim()); } public int getFeatureTreeMandatoryPercentage() { return Integer.parseInt(featureTreeMandatoryTextField.getText().trim()); } public int getFeatureTreeOptionalPercentage() { return Integer.parseInt(featureTreeOptionalTextField.getText().trim()); } public int getFeatureTreeExclusiveORPercentage() { return Integer.parseInt(featureTreeExclusiveTextField.getText().trim()); } public int getFeatureTreeInclusiveORPercentage() { return Integer.parseInt(featureTreeAlternativeTextField.getText().trim()); } public int getFeatureTreeMinBranchingFactor() { return Integer.parseInt(featureTreeMinBranchingFactorTextField.getText().trim()); } public int getFeatureTreeMaxBranchingFactor() { return Integer.parseInt(featureTreeMaxBranchingFactorTextField.getText().trim()); } public int getFeatureTreeMaxFeatureGroupSize() { return Integer.parseInt(featureTreeMaxFeatureGroupSizeTextField.getText().trim()); } public int getCTCR() { return Integer.parseInt(ctcrTextField.getText().trim()); } // public int getCTCRange() { // return Integer.parseInt(ctcrToleranceTextField.getText().trim()); // } public float getCTCClauseDensity() { return Float.parseFloat(ctcClauseDensityTextField.getText().trim()); } public int getConsistentyOption() { return (consistentyList.getSelectedIndex() == 0) ? 1 : ((consistentyList.getSelectedIndex() == 1) ? -1 : 0); } public void actionPerformed(ActionEvent event) { if ( event.getSource() == generateButton ) { validParametersState(); clearErrorMessage(); if ( validateParameters() ) { FMGeneratorGUIEvent gEvent = new FMGeneratorGUIEvent(this); for( FMGeneratorGUIListener listener : listeners ) { listener.startGeneration(gEvent); } } } else if ( event.getSource() == cancelButton ) { for( FMGeneratorGUIListener listener : listeners ) { listener.cancelGeneration(); } } } public void addListener(FMGeneratorGUIListener listener) { listeners.add(listener); } public void disableGeneration() { generateButton.setEnabled(false); } public void enableGeneration() { generateButton.setEnabled(true); } public void disableCancelGeneration() { cancelButton.setEnabled(false); } public void enableCancelGeneration() { cancelButton.setEnabled(true); } public void addMessage(String message) { messageOutputTextArea.append(message + "\r\n"); } public void clearMessages() { messageOutputTextArea.setText(""); } public void setMessage(String message) { messagesLabel.setText(message); messagesLabel.setFont(font2); messagesLabel.setForeground(errorBgColor); } private void clearErrorMessage() { messagesLabel.setText(""); } private void validParametersState() { collectionNameTextField.setBackground(Color.white); collectionSizeTextField.setBackground(Color.white); collectionPathTextField.setBackground(Color.white); featureTreeSizeTextField.setBackground(Color.white); featureTreeMandatoryTextField.setBackground(Color.white); featureTreeOptionalTextField.setBackground(Color.white); featureTreeAlternativeTextField.setBackground(Color.white); featureTreeExclusiveTextField.setBackground(Color.white); featureTreeMinBranchingFactorTextField.setBackground(Color.white); featureTreeMaxBranchingFactorTextField.setBackground(Color.white); featureTreeMaxFeatureGroupSizeTextField.setBackground(Color.white); ctcrTextField.setBackground(Color.white); // ctcrToleranceTextField.setBackground(Color.white); ctcClauseDensityTextField.setBackground(Color.white); consistentyList.setBackground(Color.white); } private boolean validateParameters() { collectionNameTextField.getText().trim(); if ( collectionNameTextField.getText().trim().isEmpty() ) { setMessage("Collection name cannot be empty"); collectionNameTextField.setBackground(errorBgColor); return false; } try { if (Integer.parseInt(collectionSizeTextField.getText().trim()) < 1 ) { throw new Exception(); } } catch (Exception e) { setMessage("Collection size must be a positive integer"); collectionSizeTextField.setBackground(errorBgColor); return false; } File file = new File(collectionPathTextField.getText().trim()); if ( collectionPathTextField.getText().trim().isEmpty() || !file.exists() ) { setMessage("Collection path must point to a valid directory path"); collectionPathTextField.setBackground(errorBgColor); return false; } try { if( Integer.parseInt(featureTreeSizeTextField.getText().trim()) < 1) { throw new Exception(); } } catch (Exception e) { setMessage("Feature tree size must be a positive integer"); featureTreeSizeTextField.setBackground(errorBgColor); return false; } try { if( Integer.parseInt(featureTreeMandatoryTextField.getText().trim()) < 0 || Integer.parseInt(featureTreeMandatoryTextField.getText().trim()) > 100) { throw new Exception(); } } catch (Exception e) { setMessage("% of mandatory features must be between 0 and 100"); featureTreeMandatoryTextField.setBackground(errorBgColor); return false; } try { if( Integer.parseInt(featureTreeOptionalTextField.getText().trim()) < 0 || Integer.parseInt(featureTreeOptionalTextField.getText().trim()) > 100) { throw new Exception(); } } catch (Exception e) { setMessage("% of optional features must be between 0 and 100"); featureTreeOptionalTextField.setBackground(errorBgColor); return false; } try { if( Integer.parseInt(featureTreeExclusiveTextField.getText().trim()) < 0 || Integer.parseInt(featureTreeExclusiveTextField.getText().trim()) > 100) { throw new Exception(); } } catch (Exception e) { setMessage("% of exclusive (XOR) features must be between 0 and 100"); featureTreeExclusiveTextField.setBackground(errorBgColor); return false; } try { if( Integer.parseInt(featureTreeAlternativeTextField.getText().trim()) < 0 || Integer.parseInt(featureTreeAlternativeTextField.getText().trim()) > 100) { throw new Exception(); } } catch (Exception e) { setMessage("% of alternative (OR) features must be between 0 and 100"); featureTreeAlternativeTextField.setBackground(errorBgColor); return false; } try { if ( Integer.parseInt(featureTreeMinBranchingFactorTextField.getText().trim()) < 0 ) { throw new Exception(); } } catch (Exception e) { setMessage("Minimum branching factor must be greater or equal to 0"); featureTreeMinBranchingFactorTextField.setBackground(errorBgColor); return false; } try { if ( Integer.parseInt(featureTreeMaxBranchingFactorTextField.getText().trim()) < Integer.parseInt(featureTreeMinBranchingFactorTextField.getText().trim()) ) { throw new Exception(); } } catch (Exception e) { setMessage("Maximum branching factor must be greater than minimum factor"); featureTreeMaxBranchingFactorTextField.setBackground(errorBgColor); return false; } try { if ( Integer.parseInt(featureTreeMaxFeatureGroupSizeTextField.getText().trim()) < 0 ) { throw new Exception(); } } catch (Exception e) { setMessage("Maximum group size must be a positive integer"); featureTreeMaxFeatureGroupSizeTextField.setBackground(errorBgColor); return false; } try { if( Integer.parseInt(ctcrTextField.getText().trim()) < 0 || Integer.parseInt(ctcrTextField.getText().trim()) > 100) { throw new Exception(); } } catch (Exception e) { setMessage("CTCR % must be between 0 and 100"); ctcrTextField.setBackground(errorBgColor); return false; } // try { // if( Integer.parseInt(ctcrToleranceTextField.getText().trim()) < 0 || Integer.parseInt(ctcrToleranceTextField.getText().trim()) > 20) { // throw new Exception(); // } // } catch (Exception e) { // setMessage("CTCR tolerance % must be between 0 and 20 (e.g., 10)"); // ctcrToleranceTextField.setBackground(errorBgColor); // return false; // } try { if ( Float.parseFloat(ctcClauseDensityTextField.getText().trim()) < 0 ) { throw new Exception(); } } catch (Exception e) { setMessage("CTC Clause Density must be a non-negative float number (e.g., 0.5, 1.3)"); ctcClauseDensityTextField.setBackground(errorBgColor); return false; } clearErrorMessage(); return true; } }