| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
The Dirigent project is a compact message formatting framework. It can be used similar to Java's Formatter system or printf-like systems. These systems seem to be made for programmers, but those working on these messages usually don't have programming experience and that should not be necessary. Dirigent focused providing the maximum of context to those how need it: Translators. All with the goal to improve translation quality in the long run. All macros (our term for text placeholders) don't use numbers or some weird %-syntax, but simple readable names that describe their content and fit into the message.
Beyond that, all macros are completely locale aware and with advanced features like custom formatters, additional context, message builders and post processors, the final output can be customized to any application. From simply inserting strings into a message all the way to complex object trees that carry consistent text styling information and other metadata.
A macro is a special sequence of letters which is replaced with an input parameter from Dirigent using a Formatter. It must be placed within a string message. Furthermore it's possible to specify more than one macro in a single message. In this context macros follow this syntax:
{[[<position>:]type[#<label>][:<args>]]} or just {[position]}
Where [...] denote optional parts and <...> denote required parts.
Ensuing from the syntax here are the possibilities:
The label as well as the arguments are allowed to contain any character except :, } and \ which have to be escaped using \. A formatter name follows this rule but must respect the label separator # additionally. An argument also must pay attention to the value separator = which is used to separate the name of an argument from the respective value.
The Dirigent process can be started by calling one of the methods Dirigent#compose(Context, String, Object...) or Dirigent#compose(String, Object...). The latter one will create an empty context and call the first method. The process consists of three independent steps:
The Dirigent process can be started with a special compose context. This context includes information for the formatter and post processor which can be evaluated by them. The context is expandable dynamically. Specific entries relate to a specific ContextProperty. This framework provides entries for a Locale, a TimeZone and a Currency within the static context of the Contexts helper class. Every ContextProperty contains a DefaultProvider which is used for getting a default value of the property if it isn't specified. To create a PropertyMapping, which is necessary to create a compose context, the method ContextProperty#with(T) can be used. The creation of a new context should be done by using the Contexts class. Besides a few properties it provides methods for creating contexts.
Formatter are needed to format the messages input value. By default the Dirigent instance only has a default formatter, but macros having a name can't be processed. For that reason the Dirigent instance has a method called registerFormatter(Formatter) which must be used to register a formatter formatting macros with specific names. Furthermore the default formatter can be overwritten by providing it at the Dirigent constructor.
Formatter is an abstract class providing the functionality to handle post processors already. Every implementation must implement the method getNames returning a set of strings representing the macro names which will be handled by the formatter, the method isApplicable(Object) checks whether the specified object can be handled by this formatter and the method format(T, Context, Arguments) returns a component representing the actual formatting result. The type parameter T represents the message's input parameter. Context is the compose context and Arguments contains the arguments of the macro. The available implementations AbstractFormatter and ReflectedFormatter help implementing formatters faster. The AbstractFormatter can be used to handle a specific object type like Integer or Date. The object type is read from the generic type of the class which is used for the implementation of the isApplicable(Object) method. Furthermore a method for getNames exists as well. The names must be provided as constructor parameters. The ReflectedFormatter uses annotations to get the details. An implementation class must have the @Names annotation at the class definition. Additionally it can provide several format methods having an object parameter and optionally a compose context and an arguments object. The methods must be marked with the Format annotation. The ReflectedFormatter checks the input parameter types and looks for the format implementation to use at runtime.
In addition to the described formatter, there are also ConstantFormatters. A constant formatter is special formatter type which doesn't consume any message input values. Instead it only uses the context and the macro arguments to produce its output.
The Dirigent framework provides a few formatters already. They all provide default macro names. Those can be changed by providing new names at the constructor.
The StringFormatter can be used to format any object by calling the String#valueOf(Object) method. This formatter is also used as the default formatter. It is possible to provide one of the flags uppercase or lowercase to return the uppercase or lowercase version of the string. The formatter only has one default name which is string.
Example:
The NumberFormatter can be used to format any Number object. This is done by using a NumberFormat. By default this class uses the NumberFormat#getInstance method to format the number.
This behaviour can be changed by providing arguments. The parameter format lets you specify an individual format which creates a DecimalFormat instance. Additionally it's possible to specify one of the flags integer, currency, percent. These result in of the methods NumberFormat#getIntegerInstance, NumberFormat#getCurrencyInstance and NumberFormat#getPercentInstance.
Every instance will be created with the value of the property Contexts.LOCALE which is specified in the compose context. Furthermore the value of Contexts.CURRENCY will be considered and used as the Currency of the NumberFormat.
In addition it's possible to change the default behaviour of this formatter by specifying one of the modes INTEGER, CURRENCY and PERCENT at the constructor. This causes that it represents the respective flag automatically.
The default names are number, decimal, double and float.
Example:
The IntegerFormatter overwrites the NumberFormat and sets the mode to INTEGER. The default names are integer, long, short, amount and count.
Example:
The CurrencyFormatter overwrites the NumberFormat and sets the mode to CURRENCY. The default names are currency, money and finance.
Example:
The PercentFormatter overwrites the NumberFormat and sets the mode to PERCENT. The default names are percent and percentage.
Example:
The DateTimeFormatter can be used to format a Date object. This is done by using a DateFormat. The behaviour of this class must be specified using one of the modes DATE_TIME, DATE or TIME. The first mode is responsible for formatting a date to a date and the time. The second mode only returns the date part and the last mode represents the time. In contrary to the NumberFormatter here the mode must be specified to archive proper results. By default the DATE_TIME mode is used.
The DATE_TIME mode loads the formatter with the DateFormat.getDateTimeInstance method. As styles it uses the default style. Of course this can be changed by specifying parameters. Does the macro contain one of the flags short, medium, long or full the related style will be used for both the date and the time. Additionally it's possible to style the two date parts separately. This can be done with the parameters date and time with a value which is similar to the flags.
The other two modes DATE and TIME result in a DateFormat loaded with DateFormat.getDateInstance or DateFormat.getTimeInstance. The macro arguments work the same way. Of course here it doesn't make sense to provide one of the parameters date and time, the flag is enough, nevertheless it's possible.
Furthermore the format parameter is supported as well. The format results in a respective SimpleDateFormat which doesn't care about the actual mode of the formatter.
Every instance will be created with the value of the property Contexts.LOCALE which is specified in the compose context. Furthermore the value of Contexts.TIMEZONE will be considered and used as the TimeZone of the DateFormat.
The formatter only has one default name datetime.
Example: assuming the date object represents May 25, 2017 3:13:21 PM UTC
The DateFormatter overwrites the DateTimeFormatter and sets the mode to DATE. The default name is date.
Example:
The TimeFormatter overwrites the DateTimeFormatter and sets the mode to TIME. The default name is time.
Example:
The StaticTextFormatter is a constant formatter which doesn't consume an input parameter. Instead it only writes the text of the first argument directly to the message. This could be used to indicate text parts which shouldn't be formatted for example. The default name of the formatter is text.
Example:
Pro tip: With post processors, which are described in the next section, this static text can be styled in a special way. For example it could be displayed bold or italic. This might be a use-case as well.
A post processor can be used to manipulate a macro after it was created by a formatter or by the Dirigent instance. Therefore the interface PostProcessor provides a method process(Component, Context, Arguments) getting the created component, the current compose context and the arguments of the macro. The result of the method is a component again. The input component will be replaced with the output component. They're allowed to be the same object of course.
An example use-case would be to add a color option to every macro which allows to set the text color of the resulting text. Formatter specific post processors could be used to adapt pre-existing formatters to new component types.
The registration of post processors has handled on two levels:
Post processors, just like formatters, may return custom component implementations, but note that own implementations might require specific handling in the MessageBuilder, so you have to overwrite it. Instead, using a TextComponent (or the implementation Text) or a ComponentGroup might be enough as well. ComponentGroups allow arbitrary nesting of components and as such are very powerful.
The Dirigent frameworks provides a WrappingPostProcessor wrapping an input component with static components using a ComponentGroup.
| Back | FazBrowse Home | New Git URL |