jxtrand
Utility classes for XML string resources, and referential integrity for string resources.
Features
- Define string resources as XML in a standardized format.
- Developer-friendly message formatting API.
- Maven plugin for generating type-safe string resource accessors; no more
MissingResourceException
errors! - Written in pure Java 17.
- OSGi ready.
- JPMS ready.
- ISC license.
- High-coverage automated test suite.
Motivation
Java exposes string resources that can be localized using the ResourceBundle
class. This can be backed by properties in a line-oriented text format. This
text format is somewhat convenient for developers, but suffers from being
line-oriented and whitespace sensitive; it becomes awkward to add significant
whitespace in strings. Java properties can also be represented in an XML-based
format that handles this better, but there is no built-in support for
loading a ResourceBundle
from an XML file. Additionally, the actual logic
used by the ResourceBundle
class to find property files is confusing and
complex.
Lastly, Java applications using localized strings have the property names as string constants in the code. This means the code is free to drift out of sync with the property files; code can refer to properties that do not exist, and properties can become unused without much in the way of tool support to detect unused properties.
Usage
XML Files
The jxtrand
package provides a convenient abstract class to load resources
from XML property files. For example, an application could define the following
class:
/**
* An example where the resource is exported.
*/
public final class ExampleStrings0 extends JXTAbstractStrings
{
/**
* Construct an example.
*
* @param locale The locale
*
* @throws IOException On I/O errors
*/
public ExampleStrings0(final Locale locale)
throws IOException
{
super(
locale,
ExampleStrings0.class,
"/com/io7m/jxtrand/examples",
"Messages"
);
}
}
The class takes a Locale
as an argument, and will look in the directory
/com/io7m/jxtrand/examples
in the module containing ExampleStrings0.class
;
jxtrand
is module-aware.
Given:
final var lang = locale.getLanguage();
final var country = locale.getCountry();
final var ex = locale.getVariant();
The lookup procedure will look at the following files in order:
/com/io7m/jxtrand/examples/Messages_${lang}_${country}_${ex}.xml
/com/io7m/jxtrand/examples/Messages_${lang}_${country}.xml
/com/io7m/jxtrand/examples/Messages_${lang}.xml
/com/io7m/jxtrand/examples/Messages.xml
Modules
When using jxtrand
in a modular context, the jxtrand
will need to be
able to search, reflectively, for resources inside application modules.
Therefore, those modules need to be open for reflection to jxtrand
. The
following is typically necessary in module descriptors (assuming that
resource files are kept in /com/io7m/example/internal
:
module com.io7m.example
{
opens com.io7m.example.internal
to com.io7m.jxtrand.vanilla;
exports com.io7m.example;
}
Resource Compiler
The jxtrand
package provides a simple resource compiler that can produce
enum
classes from string property files (in XML or line-based formats).
Given a property file such as:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="error.01">Error 1!</entry>
<entry key="example2">Example 2</entry>
</properties>
The compiler can produce an enum
file such as:
public enum Strings implements JXTStringConstantType
{
ERROR_01 {
@Override
public String propertyName()
{
return "error.01";
}
},
EXAMPLE2 {
@Override
public String propertyName()
{
return "example2";
}
}
}
These constants can be used directly with instances of the JXTStringsType
class:
JXTStringsType resources;
String formatted = resources.format(ERROR_01);
Maven Plugin
The jxtrand
package publishes a Maven plugin to perform resource compilation
as part of a build. Use a plugin execution similar to the following:
<build>
<plugins>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>com.io7m.jxtrand.maven_plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>generate-resources-0</id>
<goals>
<goal>generateSources</goal>
</goals>
<configuration>
<inputFile>${project.basedir}/src/main/resources/com/io7m/jxtrand/examples/internal/Messages.xml</inputFile>
<packageName>com.io7m.jxtrand.examples</packageName>
<className>GeneratedStrings</className>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
In the above example, the file src/main/resources/com/io7m/jxtrand/examples/internal/Messages.xml
would be used to generate a Java class com.io7m.jxtrand.examples.GeneratedStrings
. The
class is generated as a plain Java source file and is placed in
${project.build.directory}/generated-sources/jxtrand
by default
(and the jxtrand
directory is registered as a generated source directory
automatically).
Releases & Development Snapshots
Releases
You can subscribe to the atom feed to be notified of project releases.
The most recently released version of the package is 2.1.0.
2.1.0 Release (2024-05-10Z)
- Update nl.jqno.equalsverifier:equalsverifier:3.15.5 → 3.16.1.
- Update org.slf4j:slf4j-api:2.0.10 → 2.0.13.
- Update junit.version:5.10.1 → 5.10.2.
- Update ch.qos.logback:logback-classic:1.4.14 → 1.5.6.
- Update com.github.marschall:memoryfilesystem:2.7.0 → 2.8.0.
- Update org.mockito:mockito-core:5.8.0 → 5.11.0.
- Update org.apache.maven.plugins:maven-plugin-plugin:3.12.0 → 3.13.0.
The compiled artifacts for the release (and all previous releases) are available on Maven Central.
Maven Modules
<dependency> <group>com.io7m.jxtrand</group> <artifactId>com.io7m.jxtrand.api</artifactId> <version>2.1.0</version> </dependency><dependency> <group>com.io7m.jxtrand</group> <artifactId>com.io7m.jxtrand.compiler.api</artifactId> <version>2.1.0</version> </dependency><dependency> <group>com.io7m.jxtrand</group> <artifactId>com.io7m.jxtrand.compiler.basic</artifactId> <version>2.1.0</version> </dependency><dependency> <group>com.io7m.jxtrand</group> <artifactId>com.io7m.jxtrand.examples</artifactId> <version>2.1.0</version> </dependency><dependency> <group>com.io7m.jxtrand</group> <artifactId>com.io7m.jxtrand.maven_plugin</artifactId> <version>2.1.0</version> </dependency><dependency> <group>com.io7m.jxtrand</group> <artifactId>com.io7m.jxtrand.tests</artifactId> <version>2.1.0</version> </dependency><dependency> <group>com.io7m.jxtrand</group> <artifactId>com.io7m.jxtrand.vanilla</artifactId> <version>2.1.0</version> </dependency>
Previous Releases
The changelogs for the most recent previous releases are as follows:
2.0.0 Release (2023-06-24Z)
- Add a resource compiler and Maven plugin.
- Update ch.qos.logback:logback-classic 1.4.7 → 1.4.8.
- Update nl.jqno.equalsverifier:equalsverifier 3.14.2 → 3.14.3.
- Update org.mockito:mockito-core 5.3.1 → 5.4.0.
- Split up interfaces for stronger typing. (Backwards incompatible)
1.1.0 Release (2022-04-09Z)
- Strip trailing slashes from resource directory names (Tickets: 2)
1.0.0 Release (2021-01-25Z)
Development Snapshots
At the time of writing, the current unstable development version of the package is 2.1.1-SNAPSHOT.
Development snapshots may be available in the Central Portal Snapshots repository. Snapshots are published to this repository every time the project is built by the project's continuous integration system, but snapshots do expire after around ninety days and so may or may not be available depending on when a build of the package was last triggered.
Manual
This project does not have any user manuals or other documentation beyond what might be present on the page above.
Sources
This project uses Git to manage source code.
Repository: https://www.github.com/io7m-com/jxtrand
$ git clone --recursive https://www.github.com/io7m-com/jxtrand
Issues
This project uses GitHub Issues to track issues.
License
Copyright © 2023 Mark Raynsford <code@io7m.com> https://www.io7m.com Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.