org.apache.wicket.model.StringResourceModel Maven / Gradle / Ivy
Show all versions of org.ops4j.pax.wicket.service Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.model;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Locale;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.Localizer;
import org.apache.wicket.Session;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.resource.loader.ComponentStringResourceLoader;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.string.interpolator.PropertyVariableInterpolator;
/**
* This model class encapsulates the full power of localization support within the Wicket framework.
* It combines the flexible Wicket resource loading mechanism with property expressions, property
* models and standard Java MessageFormat
substitutions. This combination should be
* able to solve any dynamic localization requirement that a project has.
*
* The model should be created with four parameters, which are described in detail below:
*
* - resourceKey - This is the most important parameter as it contains the key that should
* be used to obtain resources from any string resource loaders. This parameter is mandatory: a null
* value will throw an exception. Typically it will contain an ordinary string such as
* "label.username". To add extra power to the key functionality the key may also contain
* a property expression which will be evaluated if the model parameter (see below) is not null.
* This allows keys to be changed dynamically as the application is running. For example, the key
* could be "product.${product.id}" which prior to rendering will call
* model.getObject().getProduct().getId() and substitute this value into the resource key before is
* is passed to the loader.
*
- component - This parameter should be a component that the string resource is relative
* to. In a simple application this will usually be the Page on which the component resides. For
* reusable components/containers that are packaged with their own string resource bundles it should
* be the actual component/container rather than the page. For more information on this please see
* {@link org.apache.wicket.resource.loader.ComponentStringResourceLoader}. The relative component
* may actually be
null
when all resource loading is to be done from a global resource
* loader. However, we recommend that a relative component is still supplied even in these cases in
* order to 'future proof' your application with regards to changing resource loading strategies.
* - model - This parameter is mandatory if either the resourceKey, the found string
* resource (see below) or any of the substitution parameters (see below) contain property
* expressions. Where property expressions are present they will all be evaluated relative to this
* model object. If there are no property expressions present then this model parameter may be
*
null
* - parameters - The parameters parameter allows an array of objects to be passed for
* substitution on the found string resource (see below) using a standard
*
java.text.MessageFormat
object. Each parameter may be an ordinary Object, in which
* case it will be processed by the standard formatting rules associated with
* java.text.MessageFormat
. Alternatively, the parameter may be an instance of
* IModel
in which case the getObject()
method will be applied prior to
* the parameter being passed to the java.text.MessageFormat
. This allows such features
* dynamic parameters that are obtained using a PropertyModel
object or even nested
* string resource models.
*
* As well as the supplied parameters, the found string resource can contain formatting information.
* It may contain property expressions in which case these are evaluated using the model object
* supplied when the string resource model is created. The string resource may also contain
* java.text.MessageFormat
style markup for replacement of parameters. Where a string
* resource contains both types of formatting information then the property expression will be
* applied first.
*
* Example 1
*
* In its simplest form, the model can be used as follows:
*
*
* public MyPage extends WebPage<Void>
* {
* public MyPage(final PageParameters parameters)
* {
* add(new Label("username", new StringResourceModel("label.username", this, null)));
* }
* }
*
*
* Where the resource bundle for the page contains the entry label.username=Username
*
* Example 2
*
* In this example, the resource key is selected based on the evaluation of a property expression:
*
*
* public MyPage extends WebPage<Void>
* {
* public MyPage(final PageParameters parameters)
* {
* WeatherStation ws = new WeatherStation();
* add(new Label("weatherMessage",
* new StringResourceModel("weather.${currentStatus}", this, new Model<String>(ws)));
* }
* }
*
*
* Which will call the WeatherStation.getCurrentStatus() method each time the string resource model
* is used and where the resource bundle for the page contains the entries:
*
*
* weather.sunny=Don't forget sunscreen!
* weather.raining=You might need an umbrella
* weather.snowing=Got your skis?
* weather.overcast=Best take a coat to be safe
*
*
*
* Example 3
*
* In this example the found resource string contains a property expression that is substituted via
* the model:
*
*
* public MyPage extends WebPage<Void>
* {
* public MyPage(final PageParameters parameters)
* {
* WeatherStation ws = new WeatherStation();
* add(new Label("weatherMessage",
* new StringResourceModel("weather.message", this, new Model<String>(ws)));
* }
* }
*
*
* Where the resource bundle contains the entry weather.message=Weather station reports that
* the temperature is ${currentTemperature} ${units}
*
* Example 4
*
* In this example, the use of substitution parameters is employed to format a quite complex message
* string. This is an example of the most complex and powerful use of the string resource model:
*
*
* public MyPage extends WebPage<Void>
* {
* public MyPage(final PageParameters parameters)
* {
* WeatherStation ws = new WeatherStation();
* IModel<WeatherStation> model = new Model<WeatherStation>(ws);
* add(new Label("weatherMessage",
* new StringResourceModel(
* "weather.detail", this, model,
* new Object[]
* {
* new Date(),
* new PropertyModel<?>(model, "currentStatus"),
* new PropertyModel<?>(model, "currentTemperature"),
* new PropertyModel<?>(model, "units")
* }));
* }
* }
*
*
* And where the resource bundle entry is:
*
*
* weather.detail=The report for {0,date}, shows the temperature as {2,number,###.##} {3} \
* and the weather to be {1}
*
*
* @see ComponentStringResourceLoader for additional information especially on the component search
* order
*
* @author Chris Turner
*/
public class StringResourceModel extends LoadableDetachableModel
implements
IComponentAssignedModel
{
private static final long serialVersionUID = 1L;
/** The locale to use. */
private transient Locale locale;
/**
* The localizer to be used to access localized resources and the associated locale for
* formatting.
*/
private transient Localizer localizer;
/** The wrapped model. */
private final IModel> model;
/** Optional parameters. */
private final Object[] parameters;
/** The relative component used for lookups. */
private Component component;
/** The key of message to get. */
private final String resourceKey;
/** The default value of the message. */
private final String defaultValue;
public IWrapModel wrapOnAssignment(Component component)
{
return new AssignmentWrapper(component);
}
private class AssignmentWrapper implements IWrapModel
{
private static final long serialVersionUID = 1L;
private final Component component;
/**
* Construct.
*
* @param component
*/
public AssignmentWrapper(Component component)
{
this.component = component;
}
public void detach()
{
StringResourceModel.this.detach();
}
public String getObject()
{
if (StringResourceModel.this.component != null)
{
return StringResourceModel.this.getObject();
}
else
{
// TODO: Remove this as soon as we can break binary compatibility
StringResourceModel.this.component = component;
String res = StringResourceModel.this.getObject();
StringResourceModel.this.component = null;
return res;
}
}
public void setObject(String object)
{
StringResourceModel.this.setObject(object);
}
public IModel getWrappedModel()
{
return StringResourceModel.this;
}
}
/**
* Construct.
*
* @param resourceKey
* The resource key for this string resource
* @param component
* The component that the resource is relative to
* @param model
* The model to use for property substitutions
* @see #StringResourceModel(String, Component, IModel, Object[])
*/
public StringResourceModel(final String resourceKey, final Component component,
final IModel> model)
{
this(resourceKey, component, model, null, null);
}
/**
* Construct.
*
* @param resourceKey
* The resource key for this string resource
* @param component
* The component that the resource is relative to
* @param model
* The model to use for property substitutions
* @param defaultValue
* The default value if the resource key is not found.
*
* @see #StringResourceModel(String, Component, IModel, Object[])
*/
public StringResourceModel(final String resourceKey, final Component component,
final IModel> model, final String defaultValue)
{
this(resourceKey, component, model, null, defaultValue);
}
/**
* Creates a new string resource model using the supplied parameters.
*
* The relative component parameter should generally be supplied, as without it resources can
* not be obtained from resource bundles that are held relative to a particular component or
* page. However, for application that use only global resources then this parameter may be
* null.
*
* The model parameter is also optional and only needs to be supplied if value substitutions are
* to take place on either the resource key or the actual resource strings.
*
* The parameters parameter is also optional and is used for substitutions.
*
* @param resourceKey
* The resource key for this string resource
* @param component
* The component that the resource is relative to
* @param model
* The model to use for property substitutions
* @param parameters
* The parameters to substitute using a Java MessageFormat object
*/
public StringResourceModel(final String resourceKey, final Component component,
final IModel> model, final Object[] parameters)
{
this(resourceKey, component, model, parameters, null);
}
/**
* Creates a new string resource model using the supplied parameters.
*
* The relative component parameter should generally be supplied, as without it resources can
* not be obtained from resource bundles that are held relative to a particular component or
* page. However, for application that use only global resources then this parameter may be
* null.
*
* The model parameter is also optional and only needs to be supplied if value substitutions are
* to take place on either the resource key or the actual resource strings.
*
* The parameters parameter is also optional and is used for substitutions.
*
* @param resourceKey
* The resource key for this string resource
* @param component
* The component that the resource is relative to
* @param model
* The model to use for property substitutions
* @param parameters
* The parameters to substitute using a Java MessageFormat object
* @param defaultValue
* The default value if the resource key is not found.
*/
public StringResourceModel(final String resourceKey, final Component component,
final IModel> model, final Object[] parameters, final String defaultValue)
{
if (resourceKey == null)
{
throw new IllegalArgumentException("Resource key must not be null");
}
this.resourceKey = resourceKey;
this.component = component;
this.model = model;
this.parameters = parameters;
this.defaultValue = defaultValue;
}
/**
* Construct.
*
* @param resourceKey
* The resource key for this string resource
* @param model
* The model to use for property substitutions
* @see #StringResourceModel(String, Component, IModel, Object[])
*/
public StringResourceModel(final String resourceKey, final IModel> model)
{
this(resourceKey, null, model, null, null);
}
/**
* Construct.
*
* @param resourceKey
* The resource key for this string resource
* @param model
* The model to use for property substitutions
* @param defaultValue
* The default value if the resource key is not found.
*
* @see #StringResourceModel(String, Component, IModel, Object[])
*/
public StringResourceModel(final String resourceKey, final IModel> model,
final String defaultValue)
{
this(resourceKey, null, model, null, defaultValue);
}
/**
* Creates a new string resource model using the supplied parameters.
*
* The model parameter is also optional and only needs to be supplied if value substitutions are
* to take place on either the resource key or the actual resource strings.
*
* The parameters parameter is also optional and is used for substitutions.
*
* @param resourceKey
* The resource key for this string resource
* @param model
* The model to use for property substitutions
* @param parameters
* The parameters to substitute using a Java MessageFormat object
*/
public StringResourceModel(final String resourceKey, final IModel> model,
final Object[] parameters)
{
this(resourceKey, null, model, parameters, null);
}
/**
* Creates a new string resource model using the supplied parameters.
*
* The model parameter is also optional and only needs to be supplied if value substitutions are
* to take place on either the resource key or the actual resource strings.
*
* The parameters parameter is also optional and is used for substitutions.
*
* @param resourceKey
* The resource key for this string resource
* @param model
* The model to use for property substitutions
* @param parameters
* The parameters to substitute using a Java MessageFormat object
* @param defaultValue
* The default value if the resource key is not found.
*/
public StringResourceModel(final String resourceKey, final IModel> model,
final Object[] parameters, final String defaultValue)
{
this(resourceKey, null, model, parameters, defaultValue);
}
/**
* Gets the localizer that is being used by this string resource model.
*
* @return The localizer
*/
public Localizer getLocalizer()
{
if (localizer != null)
{
return localizer;
}
else if (component != null)
{
return component.getLocalizer();
}
else
{
return Application.get().getResourceSettings().getLocalizer();
}
}
/**
* Gets the string currently represented by this string resource model. The string that is
* returned may vary for each call to this method depending on the values contained in the model
* and an the parameters that were passed when this string resource model was created.
*
* @return The string
*/
public final String getString()
{
final Localizer localizer = getLocalizer();
// Make sure we have a localizer before commencing
if (getLocalizer() == null)
{
if (component != null)
{
setLocalizer(component.getLocalizer());
}
else
{
throw new IllegalStateException("No localizer has been set");
}
}
// Make sure we have the locale
if (locale == null)
{
locale = Session.get().getLocale();
}
String value = null;
// Substitute any parameters if necessary
Object[] parameters = getParameters();
if (parameters == null)
{
// Get the string resource, doing any property substitutions as part
// of the get operation
value = localizer.getString(getResourceKey(), component, model, defaultValue);
if (value == null)
{
value = defaultValue;
}
}
else
{
// Get the string resource, doing not any property substitutions
// that has to be done later after MessageFormat
value = localizer.getString(getResourceKey(), component, null, defaultValue);
if (value == null)
{
value = defaultValue;
}
if (value != null)
{
// Build the real parameters
Object[] realParams = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++)
{
if (parameters[i] instanceof IModel)
{
realParams[i] = ((IModel>)parameters[i]).getObject();
}
else if (model != null && parameters[i] instanceof String)
{
realParams[i] = PropertyVariableInterpolator.interpolate(
(String)parameters[i], model.getObject());
}
else
{
realParams[i] = parameters[i];
}
}
if (model != null)
{
// First escape all substitute properties so that message format doesn't try to
// parse that.
value = Strings.replaceAll(value, "${", "$'{'").toString();
}
// Apply the parameters
final MessageFormat format = new MessageFormat(value, component != null
? component.getLocale() : locale);
value = format.format(realParams);
if (model != null)
{
// un escape the substitute properties
value = Strings.replaceAll(value, "$'{'", "${").toString();
// now substitute the properties
value = localizer.substitutePropertyExpressions(component, value, model);
}
}
}
// Return the string resource
return value;
}
/**
* Sets the localizer that is being used by this string resource model. This method is provided
* to allow the default application localizer to be overridden if required.
*
* @param localizer
* The localizer to use
*/
public final void setLocalizer(final Localizer localizer)
{
this.localizer = localizer;
}
/**
* This method just returns debug information, so it won't return the localized string. Please
* use getString() for that.
*
* @return The string for this model object
*/
@Override
public String toString()
{
StringBuffer sb = new StringBuffer("StringResourceModel[");
sb.append("key:");
sb.append(resourceKey);
sb.append(",default:");
sb.append(defaultValue);
sb.append(",params:");
if (parameters != null)
{
sb.append(Arrays.asList(parameters));
}
sb.append("]");
return sb.toString();
}
/**
* Gets the Java MessageFormat substitution parameters.
*
* @return The substitution parameters
*/
protected Object[] getParameters()
{
return parameters;
}
/**
* Gets the resource key for this string resource. If the resource key contains property
* expressions and the model is not null then the returned value is the actual resource key with
* all substitutions undertaken.
*
* @return The (possibly substituted) resource key
*/
protected final String getResourceKey()
{
if (model != null)
{
return PropertyVariableInterpolator.interpolate(resourceKey, model.getObject());
}
else
{
return resourceKey;
}
}
/**
* Gets the string that this string resource model currently represents. The string is returned
* as an object to allow it to be used generically within components.
*
*/
@Override
protected String load()
{
// Initialize information that we need to work successfully
final Session session = Session.get();
if (session != null)
{
locale = session.getLocale();
}
else
{
throw new WicketRuntimeException(
"Cannot attach a string resource model without a Session context because that is required to get a Localizer");
}
return getString();
}
/**
* Detaches from the given session
*/
@Override
protected final void onDetach()
{
// Detach any model
if (model != null)
{
model.detach();
}
// some parameters can be imodels, detach them
if (parameters != null)
{
for (Object parameter : parameters)
{
if (parameter instanceof IModel>)
{
((IModel>)parameter).detach();
}
}
}
// Null out references
localizer = null;
locale = null;
}
@Override
public void setObject(String object)
{
throw new UnsupportedOperationException();
}
}