{"id":37688588,"url":"https://github.com/albertus82/jface-utils","last_synced_at":"2026-01-16T12:38:42.149Z","repository":{"id":32322294,"uuid":"35897545","full_name":"albertus82/jface-utils","owner":"albertus82","description":"Java SWT/JFace Utility Library including a Preferences Framework, Lightweight HTTP Server and macOS support.","archived":false,"fork":false,"pushed_at":"2026-01-10T15:41:45.000Z","size":37990,"stargazers_count":14,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-11T02:10:17.687Z","etag":null,"topics":["http-server","java","jface","macos","mqtt","preferences","swt"],"latest_commit_sha":null,"homepage":"https://mvnrepository.com/artifact/io.github.albertus82/jface-utils","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/albertus82.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2015-05-19T17:28:10.000Z","updated_at":"2026-01-10T15:41:49.000Z","dependencies_parsed_at":"2025-12-30T06:01:28.884Z","dependency_job_id":null,"html_url":"https://github.com/albertus82/jface-utils","commit_stats":null,"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"purl":"pkg:github/albertus82/jface-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertus82%2Fjface-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertus82%2Fjface-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertus82%2Fjface-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertus82%2Fjface-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/albertus82","download_url":"https://codeload.github.com/albertus82/jface-utils/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertus82%2Fjface-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478708,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http-server","java","jface","macos","mqtt","preferences","swt"],"created_at":"2026-01-16T12:38:41.255Z","updated_at":"2026-01-16T12:38:42.141Z","avatar_url":"https://github.com/albertus82.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"JFaceUtils\r\n==========\r\n\r\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.albertus82/jface-utils)](https://mvnrepository.com/artifact/io.github.albertus82/jface-utils)\r\n[![Build status](https://github.com/albertus82/jface-utils/actions/workflows/build.yml/badge.svg)](https://github.com/albertus82/jface-utils/actions)\r\n[![Known Vulnerabilities](https://snyk.io/test/github/albertus82/jface-utils/badge.svg?targetFile=pom.xml)](https://snyk.io/test/github/albertus82/jface-utils?targetFile=pom.xml)\r\n\r\n### Java SWT/JFace Utility Library including a Preferences Framework\r\n\r\nThis library is meant to support the development of small footprint Java desktop applications with graphical user interface. Some non GUI utility classes are also included, aiming to improve some basic Java features such as logging, configuration, I/O and the lightweight HTTP server.\r\n\r\n## Usage\r\n\r\n### Maven\r\n\r\n\u003e :warning: Starting from version 18.0.0, the **Maven Group ID** and the **Java base package name** changed from `it.albertus` to `io.github.albertus82` (to know the reasons behind this change, see [Choosing your Coordinates - The Central Repository Documentation](https://central.sonatype.org/publish/requirements/coordinates/)).\r\n\r\nAdd the following element to your project's `pom.xml` file:\r\n\r\n```xml\r\n\u003cdependency\u003e\r\n    \u003cgroupId\u003eio.github.albertus82\u003c/groupId\u003e\r\n    \u003cartifactId\u003ejface-utils\u003c/artifactId\u003e\r\n    \u003cversion\u003e20.2.1\u003c/version\u003e\r\n\u003c/dependency\u003e\r\n```\r\n\r\n### Manual download\r\n\r\nYou can download the JARs from the [releases page](https://github.com/Albertus82/JFaceUtils/releases/latest).\r\n\r\n## The Preferences Framework\r\n\r\nThe creation of a **preferences dialog** to manage the configuration of a SWT/JFace application can be an annoying and time consuming task: you have to create every single field; these fields could be a lot, so you might want to split them across multiple pages. Moreover, the basic JFace's `FieldEditor` classes aren't very flexible.\r\n\r\nThis framework allows you to create a complete preferences dialog by writing only two `enum`s, and includes several customizable `FieldEditor` classes with localization support and other useful features.\r\n\r\n![Screenshot](https://cloud.githubusercontent.com/assets/8672431/19839894/0b8fe44a-9eeb-11e6-8ff4-c3b4f321c2b8.png)\r\n\r\n### Getting started\r\n\r\nIn order to open a preferences dialog, you must instantiate a [`Preferences`](src/main/java/io/github/albertus82/jface/preference/Preferences.java) object and invoke one of its `openDialog` methods (e.g. from a `SelectionListener`). The [`Preferences`](src/main/java/io/github/albertus82/jface/preference/Preferences.java) constructors take three or four arguments:\r\n* [`IPageDefinition[]`](src/main/java/io/github/albertus82/jface/preference/page/IPageDefinition.java): definitions of the pages that will contain the preference items;\r\n* [`IPreference[]`](src/main/java/io/github/albertus82/jface/preference/IPreference.java): the preference items;\r\n* [`IPreferencesCallback`](src/main/java/io/github/albertus82/jface/preference/IPreferencesCallback.java): the object that updates the application properties;\r\n* `Image[]`: icons used for the preference dialogs (optional).\r\n\r\nA convenient approach may be to implement [`IPageDefinition`](src/main/java/io/github/albertus82/jface/preference/page/IPageDefinition.java) and [`IPreference`](src/main/java/io/github/albertus82/jface/preference/IPreference.java) interfaces using enums, like in the following code examples.\r\n\r\n#### Page definition enum\r\n\r\nThis is a very simple example of enum that implements [`IPageDefinition`](src/main/java/io/github/albertus82/jface/preference/page/IPageDefinition.java):\r\n\r\n```java\r\npublic enum MyPageDefinition implements IPageDefinition {\r\n\r\n    TEXT(new PageDefinitionDetailsBuilder().nodeId(\"text\").label(\"Text\").build()),\r\n    TEXT_NUMERIC(new PageDefinitionDetailsBuilder().nodeId(\"text.numeric\").parent(TEXT).label(\"Numeric\").build()),\r\n    COMBO(new PageDefinitionDetailsBuilder().nodeId(\"combo\").label(\"Combo\").build()),\r\n    COMBO_NUMERIC(new PageDefinitionDetailsBuilder().nodeId(\"combo.numeric\").parent(COMBO).label(\"Numeric\").build()),\r\n    PAGE(new PageDefinitionDetailsBuilder().nodeId(\"page\").label(\"Page\").build()),\r\n    VARIOUS(new PageDefinitionDetailsBuilder().nodeId(\"various\").label(\"Various\").build());\r\n\r\n    private PageDefinitionDetails pageDefinitionDetails;\r\n\r\n    MyPageDefinition() {\r\n        this(new PageDefinitionDetailsBuilder().build());\r\n    }\r\n\r\n    MyPageDefinition(PageDefinitionDetails pageDefinitionDetails) {\r\n        this.pageDefinitionDetails = pageDefinitionDetails;\r\n    }\r\n\r\n    @Override\r\n    public String getNodeId() {\r\n        return pageDefinitionDetails.getNodeId();\r\n    }\r\n\r\n    @Override\r\n    public String getLabel() {\r\n        return pageDefinitionDetails.getLabel().get();\r\n    }\r\n\r\n    @Override\r\n    public Class\u003c? extends BasePreferencePage\u003e getPageClass() {\r\n        return pageDefinitionDetails.getPageClass();\r\n    }\r\n\r\n    @Override\r\n    public IPageDefinition getParent() {\r\n        return pageDefinitionDetails.getParent();\r\n    }\r\n\r\n    @Override\r\n    public ImageDescriptor getImage() {\r\n        return pageDefinitionDetails.getImage();\r\n    }\r\n\r\n}\r\n```\r\n\r\nYou can surely improve this code, for example introducing localization and autodetermining `nodeId` values using the enum names. This example makes use of [`PageDefinitionDetails`](src/main/java/io/github/albertus82/jface/preference/page/PageDefinitionDetails.java) helper class and its builder.\r\n\r\n#### Preference enum\r\n\r\nThis is a simple example of enum that implements [`IPreference`](src/main/java/io/github/albertus82/jface/preference/IPreference.java):\r\n\r\n```java\r\npublic enum MyPreference implements IPreference {\r\n\r\n    STRING(new PreferenceDetailsBuilder(MyPageDefinition.TEXT).name(\"string\").label(\"String\").defaultValue(\"Hello World!\").build(), new FieldEditorDetailsBuilder(DefaultStringFieldEditor.class).build()),\r\n    WRAP_STRING(new PreferenceDetailsBuilder(MyPageDefinition.TEXT).name(\"wrapString\").label(\"Wrap String\").defaultValue(\"Long text here.\").build(), new FieldEditorDetailsBuilder(WrapStringFieldEditor.class).build()),\r\n\r\n    INTEGER(new PreferenceDetailsBuilder(MyPageDefinition.TEXT_NUMERIC).name(\"integer\").label(\"Integer\").defaultValue(12345).build(), new FieldEditorDetailsBuilder(EnhancedIntegerFieldEditor.class).emptyStringAllowed(true).numberMinimum(-67890).build()),\r\n    DOUBLE(new PreferenceDetailsBuilder(MyPageDefinition.TEXT_NUMERIC).name(\"double\").label(\"Double\").defaultValue(24680.13579).build(), new FieldEditorDetailsBuilder(DoubleFieldEditor.class).emptyStringAllowed(true).build()),\r\n\r\n    COMBO(new PreferenceDetailsBuilder(MyPageDefinition.COMBO).name(\"combo\").label(\"Combo\").defaultValue(\"value 1\").build(), new FieldEditorDetailsBuilder(DefaultComboFieldEditor.class).labelsAndValues(new StaticLabelsAndValues(\"Label 1\", \"value 1\").put(\"Label 2\", \"value 2\")).build()),\r\n    VALIDATED_COMBO(new PreferenceDetailsBuilder(MyPageDefinition.COMBO).name(\"validatedCombo\").label(\"Validated Combo\").defaultValue(\"value 5\").build(), new FieldEditorDetailsBuilder(ValidatedComboFieldEditor.class).labelsAndValues(new StaticLabelsAndValues(\"Label 5\", \"value 5\")).emptyStringAllowed(false).build()),\r\n\r\n    FLOAT_COMBO(new PreferenceDetailsBuilder(MyPageDefinition.COMBO_NUMERIC).separate().name(\"floatCombo\").label(\"Float Combo\").defaultValue(123.456f).build(), new FieldEditorDetailsBuilder(FloatComboFieldEditor.class).labelsAndValues(new StaticLabelsAndValues(\"float\", 1)).emptyStringAllowed(true).numberValidRange(-10000, 20000).build()),\r\n    BIGDECIMAL_COMBO(new PreferenceDetailsBuilder(MyPageDefinition.COMBO_NUMERIC).name(\"bigDecimalCombo\").label(\"BigDecimal Combo\").defaultValue(67890.12345).build(), new FieldEditorDetailsBuilder(BigDecimalComboFieldEditor.class).labelsAndValues(new StaticLabelsAndValues(\"BigDecimal Value\", -10.5).put(\"invalid\", 1000000)).emptyStringAllowed(false).numberValidRange(-1000, 100000).textLimit(20).build()),\r\n\r\n    BOOLEAN(new PreferenceDetailsBuilder(MyPageDefinition.VARIOUS).name(\"boolean\").label(\"Boolean\").defaultValue(false).build(), new FieldEditorDetailsBuilder(DefaultBooleanFieldEditor.class).build()),\r\n    COLOR(new PreferenceDetailsBuilder(MyPageDefinition.VARIOUS).name(\"color\").label(\"Color\").defaultValue(\"255,0,0\").build(), new FieldEditorDetailsBuilder(ColorFieldEditor.class).build()),\r\n    PASSWORD(new PreferenceDetailsBuilder(MyPageDefinition.VARIOUS).name(\"password\").label(\"Password\").build(), new FieldEditorDetailsBuilder(PasswordFieldEditor.class).build()),\r\n    DATE(new PreferenceDetailsBuilder(MyPageDefinition.VARIOUS).name(\"date\").label(\"Date\").defaultValue(\"24/12/2015\").build(), new FieldEditorDetailsBuilder(DateFieldEditor.class).datePattern(\"dd/MM/yyyy\").dateFrom(new GregorianCalendar(2010, Calendar.JANUARY, 1).getTime()).style(SWT.DROP_DOWN).build()),\r\n\r\n    EMAIL(new PreferenceDetailsBuilder(MyPageDefinition.PAGE).name(\"emails\").label(\"Emails\").build(), new FieldEditorDetailsBuilder(EmailAddressesListEditor.class).build()),\r\n    URI(new PreferenceDetailsBuilder(MyPageDefinition.PAGE).name(\"uris\").label(\"URIs\").build(), new FieldEditorDetailsBuilder(UriListEditor.class).build());\r\n\r\n    private static final FieldEditorFactory fieldEditorFactory = new FieldEditorFactory();\r\n\r\n    private PreferenceDetails preferenceDetails;\r\n    private FieldEditorDetails fieldEditorDetails;\r\n\r\n    MyPreference(PreferenceDetails preferenceDetails, FieldEditorDetails fieldEditorDetails) {\r\n        this.preferenceDetails = preferenceDetails;\r\n        this.fieldEditorDetails = fieldEditorDetails;\r\n    }\r\n\r\n    @Override\r\n    public String getName() {\r\n        return preferenceDetails.getName();\r\n    }\r\n\r\n    @Override\r\n    public String getLabel() {\r\n        return preferenceDetails.getLabel().get();\r\n    }\r\n\r\n    @Override\r\n    public IPageDefinition getPageDefinition() {\r\n        return preferenceDetails.getPageDefinition();\r\n    }\r\n\r\n    @Override\r\n    public String getDefaultValue() {\r\n        return preferenceDetails.getDefaultValue();\r\n    }\r\n\r\n    @Override\r\n    public IPreference getParent() {\r\n        return preferenceDetails.getParent();\r\n    }\r\n\r\n    @Override\r\n    public boolean isRestartRequired() {\r\n        return preferenceDetails.isRestartRequired();\r\n    }\r\n\r\n    @Override\r\n    public boolean isSeparate() {\r\n        return preferenceDetails.isSeparate();\r\n    }\r\n\r\n    @Override\r\n    public IPreference[] getChildren() {\r\n        Set\u003cMyPreference\u003e preferences = EnumSet.noneOf(MyPreference.class);\r\n        for (MyPreference item : MyPreference.values()) {\r\n            if (this.equals(item.getParent())) {\r\n                preferences.add(item);\r\n            }\r\n        }\r\n        return preferences.toArray(new IPreference[] {});\r\n    }\r\n\r\n    @Override\r\n    public FieldEditor createFieldEditor(Composite parent) {\r\n        return fieldEditorFactory.createFieldEditor(getName(), getLabel(), parent, fieldEditorDetails);\r\n    }\r\n}\r\n```\r\n\r\nYou can surely improve this code, for example introducing localization and autodetermining `name` values using the enum names. This example makes use of [`PreferenceDetails`](src/main/java/io/github/albertus82/jface/preference/PreferenceDetails.java) and [`FieldEditorDetails`](src/main/java/io/github/albertus82/jface/preference/FieldEditorDetails.java) helper classes and their respective builders.\r\n\r\n#### Callback object\r\n\r\nThe interface [`IPreferencesCallback`](src/main/java/io/github/albertus82/jface/preference/IPreferencesCallback.java) declares two methods:\r\n* **`getFileName`**: must return the path and name of your configuration file.\r\n* **`reload`**: must **reload the configuration file and update your in-memory configuration properties**, so that your application can see the updated values. This method is invoked automatically when necessary (callback).\r\n\r\nYou can manually implement [`IPreferencesCallback`](src/main/java/io/github/albertus82/jface/preference/IPreferencesCallback.java) or use/extend [`PropertiesConfiguration`](src/main/java/io/github/albertus82/util/config/PropertiesConfiguration.java) or [`Configuration`](src/main/java/io/github/albertus82/util/config/Configuration.java) depending on your needs.\r\n\r\n\r\n### [`FieldEditorFactory`](src/main/java/io/github/albertus82/jface/preference/FieldEditorFactory.java)\r\n\r\nThe [`FieldEditorFactory`](src/main/java/io/github/albertus82/jface/preference/FieldEditorFactory.java) helps you to create `FieldEditor` objects, as you saw in the previous [`IPreference`](src/main/java/io/github/albertus82/jface/preference/IPreference.java) example.\r\n\r\nBy default, custom values are presented in *bold* format. If you don't like this behaviour, you can disable it invoking the `setBoldCustomValues(boolean)` method of [`FieldEditorFactory`](src/main/java/io/github/albertus82/jface/preference/FieldEditorFactory.java):\r\n\r\n```java\r\npublic enum MyPreference implements IPreference {\r\n\r\n    /* Enum values... */\r\n\r\n    private static final FieldEditorFactory fieldEditorFactory = new FieldEditorFactory();\r\n\r\n    static {\r\n        fieldEditorFactory.setBoldCustomValues(false);\r\n    }\r\n}\r\n```\r\n\r\n#### Extension\r\n\r\nIf you need to create your custom `FieldEditor` classes, you can extend [`FieldEditorFactory`](src/main/java/io/github/albertus82/jface/preference/FieldEditorFactory.java) to add support for these new objects:\r\n\r\n```java\r\npublic class MyFieldEditorFactory extends FieldEditorFactory {\r\n\r\n    @Override\r\n    public FieldEditor createFieldEditor(String name, String label, Composite parent, FieldEditorDetails details) {\r\n        Class\u003c? extends FieldEditor\u003e type = details.getFieldEditorClass();\r\n\r\n        if (MyCustomFieldEditor.class.equals(type)) {\r\n            return new MyCustomFieldEditor(name, label, parent);\r\n        }\r\n        if (AnotherCustomFieldEditor.class.equals(type)) {\r\n            return new AnotherCustomFieldEditor(name, label, details.getLabelsAndValues().toArray(), parent);\r\n        }\r\n\r\n        return super.createFieldEditor(name, label, parent, details);\r\n    }\r\n}\r\n```\r\n\r\nAfter that, you can use your new factory instead of the default one.\r\n\r\n## macOS integration with [`CocoaUIEnhancer`](src/main/java/io/github/albertus82/jface/cocoa/CocoaUIEnhancer.java)\r\n\r\nThe [`CocoaUIEnhancer`](src/main/java/io/github/albertus82/jface/cocoa/CocoaUIEnhancer.java) class provides a hook to connect the **Preferences**, **About** and **Quit** menu items of the **macOS** application menu.\r\nThis is a modified version of the [`CocoaUIEnhancer`](http://www.transparentech.com/files/CocoaUIEnhancer.java) class available at [TransparenTech](http://www.transparentech.com/opensource/cocoauienhancer), and it is released under the Eclipse Public License ([EPL](https://www.eclipse.org/legal/epl-v10.html)).\r\n\r\nIn order to better integrate your JFace application with macOS, you should first call the following static methods of `Display` before its creation:\r\n\r\n```java\r\nDisplay.setAppName(\"My JFace Application\");\r\nDisplay.setAppVersion(\"1.2.3\");\r\n```\r\n\r\nNext, you can use [`CocoaUIEnhancer`](src/main/java/io/github/albertus82/jface/cocoa/CocoaUIEnhancer.java) before opening the shell:\r\n\r\n```java\r\nif (Util.isCocoa()) {\r\n    new CocoaUIEnhancer(getShell().getDisplay()).hookApplicationMenu(new CloseListener(), new AboutListener(), new PreferencesListener());\r\n}\r\n```\r\n\r\nThe `hookApplicationMenu` method is overloaded in order to accept **SWT Listeners** or **JFace Actions** for **About** and **Preferences** functions. When one argument is `null`, then the respective menu item will be disabled; so, for instance, if your application does not have a preferences management, you can pass `null` in place of `preferencesListener` or `preferencesAction` and the **Preferences...** menu item will be grayed out.\r\n\r\n## SWT Closeable Resources\r\n\r\nSWT uses operating system resources to deliver its native graphics and widget functionality. These resources should be freed when no longer needed, and the traditional way to do it is calling the `dispose()` method on the objects that represent the resources, however this approach may be error prone.\r\n\r\nNow, if you need to instantiate a `Widget`, `Resource` (like a `GC`), `Device` or `Clipboard` and you want to make sure that its resources will be released after use, you can use the `Closeable` wrappers available in the `io.github.albertus82.jface.closeable` package with a *try-for-resources* statement. The wrapped object will be disposed automatically after the `try` block like any other *closeable* resource, so you will not have to invoke its `dispose()` method.\r\n\r\n### Example\r\n\r\n#### Without `Closeable` wrapper - *not recommended*:\r\n\r\n```java\r\nGC gc = null;\r\ntry {\r\n    gc = new GC(canvas);\r\n    Rectangle canvasBounds = canvas.getBounds();\r\n    gc.fillRectangle(0, 0, canvasBounds.width, canvasBounds.height);\r\n}\r\nfinally {\r\n    if (gc != null)\r\n        gc.dispose();\r\n}\r\n```\r\n\r\n#### *try-for-resources* with `Closeable` wrapper:\r\n\r\n```java\r\ntry (CloseableResource\u003cGC\u003e wrapper = new CloseableResource\u003c\u003e(new GC(canvas))) {\r\n    GC gc = wrapper.getResource();\r\n    Rectangle canvasBounds = canvas.getBounds();\r\n    gc.fillRectangle(0, 0, canvasBounds.width, canvasBounds.height);\r\n}\r\n```\r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falbertus82%2Fjface-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falbertus82%2Fjface-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falbertus82%2Fjface-utils/lists"}