UNDER CONSTRUCTION
Introduction
This page describes everything related to editing data, which is stored in a single file. Some parts are specific for EMF data.
Things are described top/down:
- Selecting the file
The data can be in a fixed file, or can be in any file selected by the user. - Handling the file (EMF specific)
The user should know whether there are changes in the data.
The mechanism provided by EMF is not in line with the normal ‘new’, ‘open’ and ‘save’ operations. Therefore we provide theEMFResourceclass. - The actual data editor
The editor can be used to create new objects or edit existing objects.
You can only save the data if it is valid and changed. This means that all fields have to be valid and at least one field has a different value.
This is supported byObjectControlandObjectControlGroup.
An editor can be created by extending theObjectEditorTemplateclass - ObjectControlGroup
This groupsObjectControls to check on changes and validity of all fields (beingObjectControls) in the editor. - ObjectControl
A control providing one or more JavaFx GUI components for editing an Object.
For anObjectControlthe value is always the type of the field that is being edited. Also the object control provides information about whether something is filled in, whether it is valid and whether it is changed.
Selecting the file?
Data in a fixed file
Define the filename via the properties mechanism, which takes care that the filename is e.g. available via SomeRegistry.dataFilename.
Upon starting the application, the first step is to check whether a filename is specified. If not, show a dialog informing the user that he has to specify a filename, offering the option to open a properties editor.
Any file to be selected by the user
Let the user select the file via a FileChooser.
Handling the file
Use an EMFResource to handle the file.
First create the EMFResource.
Next call load() to read the date from the file. A FileNotFoundException occurs if the file doesn’t exist yet. Catch this exception and if it occurs show the user a dialog asking whether the file should be created or not.
vacationsResource = new EMFResource<>(
VacationsPackage.eINSTANCE,
() -> VacationsFactory.eINSTANCE.createVacations(),
true);
try {
vacations = vacationsResource.load(VacationsRegistry.vacationsFileName);
} catch (FileNotFoundException e) {
LOGGER.severe("File not found: " + e.getMessage());
Alert alert = componentFactory.createYesNoConfirmationDialog(
null,
translationFormatter.formatText("VacationsWindow.alertVacationsFileNotFound.header", VacationsRegistry.vacationsFileName),
TRANSLATIONS.getString("VacationsWindow.alertVacationsFileNotFound.content"));
alert.showAndWait().ifPresent(response -> {
if (response == ButtonType.YES) {
LOGGER.severe("yes, create file");
vacations = vacationsResource.newEObject();
try {
vacationsResource.save(VacationsRegistry.vacationsFileName);
} catch (IOException e1) {
e1.printStackTrace();
}
} else {
LOGGER.severe("no, don't create file");
}
});
}
(Object) Editor
Example step 1
The following picture shows a simple editor for adding or editing a Person (being a former employee of a Company). In this first step we only edit the surname (a String) and retirement date (a Date).

What this editor shows and what is generally needed for an editor:
- For each attribute of the Person:
- There is at least one control to edit the value.
- There is a label that describes the attribute.
- There is an indication of whether the value is mandatory or optional.
- There is an indication to indicate that the value is invalid, or that the value is changed or not.
- Edit mode
If you are editing a new object there will be an ‘Add’ button, if you are editing an existing object, there will be an ‘Update’ button.
So the editor is either in ‘Edit’ mode or in ‘Update’ mode. - Enabling the ‘Add’ button
The ‘Add’ button may only be enabled if all values are valid. - Enabling the ‘Update’ button
The ‘Update’ button may only be enabled if all values are valid and at least one value is changed. - Valid values
An entered value is valid:- if the value is optional and nothing is filled in.
- or the value is a valid value for the attribute.
- ‘New’ button
A ‘New’ button allows the user to start editing a new object. - ‘Cancel’ button
A ‘Cancel’ button allows the user to exit the editor without making any modifications. - Status information
Apart from the status information per attribute, it helps the user when you show a text describing the first invalid control. - Confirmation on unsaved data
If there are unsaved changes in the editor and the user presses ‘New’, ‘Cancel’ or the close window icon, the user shall be asked for confirmation before continuing.
Editor requirements
An editor should provide the following:
- For each attribute of the object:
- one or more controls to show/edit its value
- a label describing the attribute that is shown/edited by the control(s)
- an indication of whether a value is mandatory or optional
- for the currently entered value an indication of whether it is invalid/invalid, changed/unchanged
- At editor level:
- a button to start editing a new object (from scratch)
- an API method to start editing an existing object
- inform the user about unsaved changes
When the ‘new’ button is pressed, upon calling the method to start editing an existing object, or upon closing the editor, it shall be checked whether there are changes which haven’t been saved yet.
If so, the user shall be informed about this and he should be able to cancel the action.
Object Editor strategy
When creating an editor to edit an object you first have to think about the main strategy to use. This section describes an editor for the following strategy:
- The editor can be used to create a new object, or to edit an existing object.
- If the object being edited is null (by default), the editor is in the ‘NEW’ mode. Otherwise it is in ‘EDIT’ mode.
- All information of an object is ‘stored’ in the GUI controls and a number of lists. Together these are referred to as the GUI controls.
When a new object is set (which may be null), the information from the object is stored in the controls. If the object is null, all information is cleared.
Any user changes are only stored in the GUI controls.
Upon ‘add object’ an object is created from the controls, ‘object’ is set to this value and the ‘object ‘ is added to the database.
Upon ‘update’, the ‘object’ is updated from the controls.
Editor overview
An overview of an editor can best be given based on an example. This example the PersonEditor, which is an editor for editing a Person (who is a retired employee of a Company). In this example we use following attributes:
- Surname- a String
- Retirement date – a Date
The following diagram shows an overview of the Person Editor implementation, using the Editor building blocks from the goedegep.jfx.editor package.

The PersonEditor is shown on the bottom left. The editor is a window with a panel where the values of the attributes can be edited, and buttons to e.g. update or add the person.
The common functionality for an editor is provided by the EditorTemplate. So the EventEditor extends this class. The EditorTemplate extends jfxStage, which itself extends Stage. Thus providing the window. It also provides the buttons.
The panel for editing the attributes is an EditPanel. In this case the PersonEditPanel. Again common functionality is provided by a template class: EditPanelTemplate.
For each attribute to be edited, the EditPanel uses an EditorControl. In this case an EditorControlString for the surname and an EditorControl date for the retirement date.
As may be clear from the diagram and the above description, for the creation of Editors, EditPanels, EditorMultiControls and EditorControls the java Template Method Design Pattern is used.
PersonEditor top down description
Using the PersonEditor
A PersonEditor can be used as follows:
- Create an instance of the editor:
PersonEditor personEditor = PersonEditor.newInstance(customization, companyService);
Where:- as always, the first parameter is a CustomizationFx for the GUI customization.
companyServiceis a service which provides a method to add a person to the company data structure.
- Upon creation an editor is in the NEW mode, which means it creates a new instance of the class being edited.
If you want to edit an existing object, call: personEditor.setObject(person) - Show the editor by calling:
personEditor.show()
Any editor should provide a ‘newInstance’ method.
PersonEditor
Overview of the implementation.
- Extend EditorTemplate
The EditorTemplate provides the customized window (icon and title), overall status information and action buttons. - Creating an instance of the editor
The method newInstance(CustomizationFx customization, Consumer<EventInfo> addEventInfotMethod) is used to create a new instance of the editor.
The first parameter is a CustomizationFx for the GUI customization.
The second parameter is a Consumer for adding an new EventInfo object.
The method does two things:- Create an instance of the editor by calling the (private) constructor.
The constructor calls its parents constructor with the customization and the addEventInfotMethod as arguments. - Call performInitialization() on the EditorTemplate.
This will call the methods: configureEditor(), getMainEditPanel()
- Create an instance of the editor by calling the (private) constructor.
- Implement configureEditor()
Method performInitialization() of the EditorTemplate calls this method. In this method call the methods setAddObjectTexts(), setUpdateObjectTexts() and setNewObjectTexts() to customize the texts of the buttons of the editor. - Implement getMainEditPanel()
This method typically creates and returns an EditPanel, in this case an EventEditPanel. - Implement createObject()
This method is e.g. called when the ‘New’ button of the editor is pressed. It just has to create a new instance of the object type being edited. In this case an EventInfo instance.
Design concepts
Id
Mainly for debugging it’s important to be able to identify each object. Therefore the interface EditorComponent provides the methods setId() and getId().
These methods are implemented by EditorComponentAbstract.
Customization
Every GUI component is customized via a CustomizationFx object, which by my convention is the first parameter where needed. Therefore on creating an editor, this is shall be the first parameter and it is passed on to all other objects created by the editor.
Handling changes
At each level (from an editor control to an edit panel) changes are handled in the same way. If there is a change, e.g. because the user types text, the new internal value and status are dertermined, and then any InvalidationListeners are notified. Therefore the interface EditorComponent extends Observable. However only invalidation listeners are supported.
Note: we don’t use Properties for the status information, because listeners are then notified when the first property changes. If they then ask for other status information, this is then not yet updated. Also the strategy used here is more efficient; a client is notified once on a new value and status.
Interfaces
Interface Editor
An editor (like the EventEditor) provides a small interface.
Interface EditorComponent
setId() and getId() to set and get a unique id of the object.
Editor functional topics
In this section we discuss the different topics that the building blocks of an editor have to deal with. This shows their similarities and differences.
Creation and initialization
Editor
- Create the main EditorPanel
EditorPanel
- Create required EditorControls, sub EditorPanels and EditorMultiControls
- Create the editor panel
EditorMultiControl
- Create required EditorControls
EditorControl
- Create required EditorControls (most often just one)
Value(s), Changes
Values change:
- programmatically by calling methods like setValue()
- by the user interacting with GUI controls
Editor
EditorPanel
EditorMultiControl
EditorControl
Status
Status information is needed both within an editor building block and to the users of a building block.
Editor
The status determines what can be done in/with the editor.
- Only if all components are valid the ADD button is activated.
If the ADD button is pressed, a method is called which is set by the client upon creation of the editor. It is done this way because the editor doesn’t know how a new object has to be added. - Only if all components are valid and at the value of at least one component is changed, the UPDATE button is activated.
If the UPDATE button is pressed, the object is changed according the values of all components. - If the NEW button is pressed all components are set to default values.
If the NEW button is pressed, or the user tries to close the Editor, and there is a change in one or more components, the user shall be informed about the unsaved changes and he’s offered the option to cancel his action or ignore the changes.
Where we speak here of ‘all components’ it means all components in the main EditPanel and in its sub EditPanels. So an edit panel has to provide status information:
Invalid – one or more components are invalid
No Changes – all components are valid and none of the components is changed
Changes – all components are valid and at least one component is changed
EditorPanel
EditorMultiControl
EditorControl
GUI elements
Editor
EditorPanel
EditorMultiControl
EditorControl
EventEditPanel
The EventEditPanel is used as an example of creating an edit panel.
- Extend EditPanelTemplate
- Creating an instance of the edit panel
The method newInstance(CustomizationFx customization) is used to create a new instance of the edit panel.
As always, the first parameter is a CustomizationFx for the GUI customization.
The method does two things:- Create an instance of the edit panel by calling the (private) constructor.
The constructor calls its parents constructor with the customization as argument. - Call performInitialization() on the EditPanelTemplate.
- Create an instance of the edit panel by calling the (private) constructor.
Note that what is done here is very similar to what is done for the editor.
ObjectEditorTemplate
This class provides a template for an object editor and it provides some common functionality. So writing an Object Editor starts by extending this class.
The object being edited is typically part of a collection of objects or it is meant to be added to this collection.
Edit mode
The editor is always in one of the modes NEW or EDIT. Upon creation the editor is in NEW mode. In this mode all edit controls are filled with their default values (usually mostly empty). Upon pressing the ‘Add’ button, an object is created, it is filled with the values from the controls, it is added to the collection and the editor switches to the EDIT mode.
Upon calling setObject() with a non null value the editor switches to the EDIT mode, otherwise it switches to the NEW mode.
Layout overview (from top to bottom):
- Title bar, shows the Editor Title.
- The main editor panel.
- A panel with the action buttons.
The items in this panel are right aligned. By default it contains:- Edit status indicator:
- ‘+’ – in NEW mode and the input is valid, so you can add the object
- ‘!’ – At least one control is invalid
- ‘=’ – The controls are valid, but none of the values has changed
- ‘≠’ – in UPDATE mode, the controls are valid and there are changes.
- Cancel button: to quit the editor without making changes. This button is always enabled.
- Add/Update button:
In NEW mode this is an ‘Add’ button, which is only enabled if all controls are valid.
In UPDATE mode this is an ‘Update’ button, which is only enabled if all controls are valid and at least one control has changed. - New button: to start editing a new object. All controls are cleared (or filled with a default value) and the editor switches to NEW mode. This button is always enabled.
- Edit status indicator:
Customization
A CustomizationFx instance is passed to the constructor as the first argument.
As ObjectEditorTemplate extends JfxStage, the CustomizationFx and related ComponentFactoryFx are available to any class extending ObjectEditorTemplate (as customization and componentFactory).
Configuration
The texts and tooltip texts of the action buttons can be set.
Implementing your editor
Create a private constructor and a factory method named newInstance. The factory method has to call performInitialization().
Example:
public static EventsEditor newInstance(CustomizationFx customization, Events events) {
EventsEditor eventsEditor = new EventsEditor(customization, events);
eventsEditor.performInitialization();
return eventsEditor;
}
private EventsEditor(CustomizationFx customization, Events events) {
super(customization, WINDOW_TITLE);
this.events = events;
}
The method performInitialization() will (apart from other things) call the following methods, that your editor will normally implement/override:
- configureEditor()
Override this method to make calls to setAddObjectTexts(), setUpdateObjectTexts() and setNewObjectTexts() for setting the texts for the action buttons.
Example:
@Override
protected void configureEditor() {
setAddObjectTexts("Add event", "Add the event to the events");
setUpdateObjectTexts("Update", "Update the current event");
setNewObjectTexts("New", "Clear the controls to start entering new event data");
}
- createControls()
Create the GUI controls and add them to theobjectControlsGroup, which is provided by the template.
Also here you can perform any other initialization.
Example: - fillControlsWithDefaultValues()
- createEditPanel(rootPane)
- handleChanges()
Examples
The following classes are implementations of ObjectEditorTemplate (in order of increasing complexity):
- goedegep.events.app.guifx.EventsEditor
- goedegep.media.mediadb.trackeditor.guifx.TrackEditor
- goedegep.media.mediadb.albumeditor.guifx.AlbumEditor
This is a complex example, as there are different sub panels for discs. - goedegep.invandprop.app.guifx.InvoiceAndPropertyEditor
This is a tricky example as it is an editor for an Invoice, a Property or both.
ObjectControl (Interface goedegep.jfx.objectcontrols.ObjectControl)
This interface defines a set of methods for GUI controls to show and enter object values.
Many GUI controls, like javafx.scene.control.TextField, represent the value of an object, but the control itself is unaware of the object type. Therefore a set of controls is defined which are object type aware.
For example, if you want to show a LocalDate as text and allow the user to edit this value:
- To show the
LocalDate, it has to be formatted to a String.
In this interface this is taken care of by the methodsetValue(), where the formatting is to be done by the implementing class. - The other way around is even more complicated.
To use the text from theTextFieldto set aLocalDate, you first have to parse the text to create aLocalDate. In this interface this is taken care of by the methodgetValue(), where the parsing is to be done by the implementing class.
But there is more, you want to know whether the user has to enter a value, has entered a value and whether the value is valid. This is discussed below.
Optional
An edit window typically has several controls. A ‘save’ button is often only enabled if all required values are filled in. The isOptional() method can be used to check on this. An implementing class typically provides a constructor with an ‘isOptional‘ parameter.
Filled in
Indicates that something is entered, it may be valid or not. The isFilledIn() method can be used to check on this.
Valid
An edit window typically has several controls. A ‘save’ button is often only enabled if all required values are valid.
Valid is defined as:
- Either: the control is optional and nothing is filled in
- Or: What is filled in can be translated to an object of the type of the control.
The isValid() method can be used to check on this.
The Value
The value (of type T) represented by the control can be set via setValue() and obtained via getValue().
Changes
In an editor you usually provide feedback to the user whether he has changed something or not. Therefore this interface provides support for detecting changes. When the value of the control is programmatically changed (via setValue()) this value is stored as a reference value. Any call to isChanged() returns true if the current value of the control differs from this reference value, otherwise it returns false.
The GUI controls
There is of course always at least one GUI control representing the value, e.g. a TextField. This control can be obtained via getControl().
Besides this there is a status indicator control, which can be obtained via getStatusIndicator().
If an ObjectControl has more GUI controls (like e.g. the ObjectControlFileSelecter) this control will provide extra methods to obtain these controls.
Textual representation
As the control often already represents the value in a textual form, this text can also be obtained via getValueAsFormattedText().
Textual error description
In case of an error an error message can be obtained via getErrorText().
Id
To identify your controls, you can set an Id (just like javafx.scene.Node.setId()). See the methods getId() and setId().
Listeners
This interface extends the javafx.beans.Observable interface. It notifies listeners upon any change in the control.
For convenience there is an extra method removeListeners() to remove all listeners.
How to write an EditorControl
Of course you can simply implement this interface, but in general it is advised to extend the class ObjectControlTemplate and follow the documentation within that class.

