package bluej.pkgmgr;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Window;

import bluej.Config;
import bluej.extensions.SourceType;
import bluej.utility.DialogManager;
import bluej.utility.JavaNames;
import bluej.utility.javafx.HorizontalRadio;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.dialog.DialogPaneAnimateError;
import threadchecker.OnThread;
import threadchecker.Tag;


| Dialog for creating a new class | | @author Justin Tan | @author Michael Kolling | @OnThread(Tag.FXPlatform) class NewClassDialog extends Dialog<NewClassDialog.NewClassInfo>{
| The radio buttons for class, interface, enum, etc | private final ToggleGroup templateButtons;
| Which radio button is associated with which template | private final Map<RadioButton, TemplateInfo> templates = new IdentityHashMap<>();
| The buttons for the source language (Java/Stride) | private final HorizontalRadio<SourceType> language;
| stores restricted windows class filenames | private static List<String> windowsRestrictedWords;
| The field with the class name | private final TextField nameField;
| The dialog pane | private final DialogPaneAnimateError dialogPane;
| Keeps track of whether the field has yet been non-blank. | We don't show an error for empty class name if the class name | has always been empty (unless the user mouses over OK) | private boolean fieldHasHadContent = false;
| The label with the error message. | private final Label errorLabel;
| The information selected in the dialog: class name, | template name and source type. | @OnThread(Tag.Any) public static class NewClassInfo { public final String className; public final String templateName; public final SourceType sourceType; private NewClassInfo(String className, String templateName, SourceType sourceType) { this.templateName = templateName; this.className = className; this.sourceType = sourceType; } }
| Construct a NewClassDialog. | public NewClassDialog(Window parent, SourceType defaultSourceType) { setTitle(Config.getString("pkgmgr.newClass.title")); initOwner(parent); initModality(Modality.WINDOW_MODAL); errorLabel = JavaFXUtil.withStyleClass(new Label(), "dialog-error-label"); dialogPane = new DialogPaneAnimateError(errorLabel, () -> updateOKButton(true)); setDialogPane(dialogPane); Config.addDialogStylesheets(getDialogPane()); getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); VBox mainPanel = new VBox(); JavaFXUtil.addStyleClass(mainPanel, "new-class-dialog"); nameField = new TextField(); nameField.setPromptText(Config.getString("pkgmgr.newClass.prompt")); JavaFXUtil.addChangeListenerPlatform(nameField.textProperty(), s -> { hideError(); updateOKButton(false); }); HBox nameBox = new HBox(new Label(Config.getString("pkgmgr.newClass.label")), nameField); JavaFXUtil.addStyleClass(nameBox, "new-class-dialog-hbox"); nameBox.setAlignment(Pos.BASELINE_LEFT); mainPanel.getChildren().add(nameBox); language = new HorizontalRadio(Arrays.asList(SourceType.Java, SourceType.Stride)); language.select(defaultSourceType); HBox langBox = new HBox(); JavaFXUtil.addStyleClass(langBox, "new-class-dialog-hbox"); langBox.getChildren().add(new Label(Config.getString("pkgmgr.newClass.lang"))); langBox.getChildren().addAll(language.getButtons()); langBox.setAlignment(Pos.BASELINE_LEFT); mainPanel.getChildren().add(langBox); mainPanel.getChildren().add(new Label(Config.getString("pkgmgr.newClass.classType"))); templateButtons = new ToggleGroup(); addClassTypeButtons(parent, mainPanel); mainPanel.getChildren().add(errorLabel); JavaFXUtil.addChangeListenerPlatform(templateButtons.selectedToggleProperty(), toggle -> { hideError(); updateOKButton(false); }); JavaFXUtil.addChangeListenerPlatform(language.selectedProperty(), language -> { hideError(); updateOKButton(false); }); getDialogPane().setContent(mainPanel); setResultConverter(buttonType -> { if (buttonType == ButtonType.OK) { return new NewClassInfo(nameField.getText().trim(), templates.get(templateButtons.getSelectedToggle()).name, language.selectedProperty().get()); } else { return null; } }); updateOKButton(false); setOnShown(e -> Platform.runLater(nameField::requestFocus)); if (Config.makeDialogsResizable()) { setResizable(true); } }
| Each template has a name, and a set of source types for which that | template is available. | @OnThread(Tag.Any) private static class TemplateInfo { private final String name; private final Set<SourceType> sourceTypes = new HashSet<>(); public TemplateInfo(String name, SourceType sourceType) { this.name = name; sourceTypes.add(sourceType); } }
| A list of Templates guarantee uniqueness and order | @OnThread(Tag.Any) private static class TemplatesList { private final List<TemplateInfo> templates = new ArrayList<>(); public void addTemplate(String name, SourceType sourceType) { TemplateInfo template = templates.stream().filter(t -> t.name.equals(name)).findFirst().orElse(null); if (template != null) { template.sourceTypes.add(sourceType); } else { templates.add(new TemplateInfo(name, sourceType)); } } public List getTemplates() { return templates; } }
| Add the class type buttons (defining the class template to be used | to the panel. The templates are defined in the "defs" file. |*/ private void addClassTypeButtons(Window parent, Pane panel) { TemplatesList templates = new TemplatesList(); // first, get templates out of defined templates from bluej.defs | |// (we do this rather than using the directory only to be able to force an order on the templates.) | |addDEFsTemplates(templates, SourceType.Java); | |addDEFsTemplates(templates, SourceType.Stride); | |// next, get templates from files in template directory and merge them in | |addDirectoryTemplates(templates, SourceType.Java, parent); | |// Create a radio button for each template found | |boolean first = true; | |for (TemplateInfo template : templates.getTemplates()) | |{ | |String label = Config.getString("pkgmgr.newClass." + template.name, template.name); RadioButton button = new RadioButton(label); if (first) button.setSelected(true); // select first button.setToggleGroup(templateButtons); | |this.templates.put(button, template); | |panel.getChildren().add(button); | |first = false; | |} | |JavaFXUtil.addChangeListenerPlatform(templateButtons.selectedToggleProperty(), selected -> updateOKButton(false)); | |} | |private void addDEFsTemplates(TemplatesList templates, SourceType sourceType) | |{ | |String templateString = Config.getPropString("bluej.classTemplates." + sourceType.toString().toLowerCase()); StringTokenizer tokenizer = new StringTokenizer(templateString); while (tokenizer.hasMoreTokens()){ templates.addTemplate(tokenizer.nextToken(), sourceType); | |} | |} | |private void addDirectoryTemplates(TemplatesList templates, SourceType sourceType, Window parent) | |{ | |File templateDir = Config.getClassTemplateDir(); | |if ( !templateDir.exists() ) { | |DialogManager.showErrorFX(parent, "error-no-templates"); } else { String templateSuffix = ".tmpl"; int suffixLength = templateSuffix.length(); Arrays.asList(templateDir.list()).forEach(file -> { if (file.endsWith(templateSuffix)) { String templateName = file.substring(0, file.length() - suffixLength); | |templates.addTemplate(templateName, sourceType); | |} | |}); | |} | |} | |/** | Enable/disable the OK button, and set the error label | | @param force True if we want to display a message for the blank class name, | even if it has been blank since the dialog was shown (we do | this when the user mouses over OK). | private void updateOKButton(boolean force) { String newClassName = nameField.getText().trim(); fieldHasHadContent |= !newClassName.equals(""); Toggle selectedToggle = templateButtons.getSelectedToggle(); TemplateInfo info = this.templates.get(selectedToggle); boolean enable = false; SourceType sourceType = language.selectedProperty().get(); Properties localProperties = new Properties(); localProperties.put("LANGUAGE", sourceType.toString()); if (info == null) showError(Config.getString("pkgmgr.newClass.error.noType"), false); else if (((RadioButton)selectedToggle).isDisabled() || !info.sourceTypes.contains(sourceType)) showError(Config.getString("pkgmgr.newClass.error.typeNotAvailable", null, localProperties), false); else if (!JavaNames.isIdentifier(newClassName)) { if (fieldHasHadContent || force) showError(Config.getString("pkgmgr.newClass.error.notValidClassName", null, localProperties), true); } else if (isWindowsRestrictedWord(newClassName)) { if (fieldHasHadContent || force) showError(Config.getString("pkgmgr.newClass.error.windowsRestricted"), true); } else { hideError(); enable = true; } templates.forEach((radio, templateInfo) -> radio.setVisible(templateInfo.sourceTypes.contains(language.selectedProperty().get()))); setOKEnabled(enable); } private void hideError() { errorLabel.setText(""); JavaFXUtil.setPseudoclass("bj-dialog-error", false, nameField); } private void showError(String error, boolean problemIsName) { errorLabel.setText(error); JavaFXUtil.setPseudoclass("bj-dialog-error", problemIsName, nameField); }
| Sets the OK button of the dialog to be enabled (pass true) or not (pass false) | private void setOKEnabled(boolean okEnabled) { dialogPane.getOKButton().setDisable(!okEnabled); }
| Tests for restricted class names (case insensitive) | @param fileName potential class name | @return true if restricted word | private boolean isWindowsRestrictedWord(String fileName) { initialiseRestrictedWordList(); return windowsRestrictedWords.contains(fileName.toUpperCase()); }
| Initialises the list of restricted words | private void initialiseRestrictedWordList() { if (windowsRestrictedWords==null){ windowsRestrictedWords = Arrays.asList("CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"); } } }
top, use, map, class NewClassInfo

.   NewClassInfo
.   NewClassDialog

top, use, map, class TemplateInfo

.   TemplateInfo

top, use, map, class TemplatesList

.   addTemplate
.   getTemplates
.   updateOKButton
.   hideError
.   showError
.   setOKEnabled
.   isWindowsRestrictedWord
.   initialiseRestrictedWordList




318 neLoCode + 62 LoComm