/**
* Copyright 2012 55 Minutes (http://www.55minutes.com)
*
* Licensed 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 fr.openwide.core.wicket.markup.html.model;
import java.io.Serializable;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IComponentAssignedModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.IWrapModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.lang.Args;
/**
* Selects an appropriate localized string based on whether a count value is
* zero, one or greater than one. This makes it easier to produce singluar and
* plural phrases.
* <p>
* For example, consider the case where we would like to set one of these three
* feedback messages. Notice how the phrasing is slightly different depending on
* the number of items. Furthermore in the latter two examples we would like to
* embed a hyperlink to a bookmarkable page.
* <ul>
* <li>There are no friends in your network.</li>
* <li>There is <u>one friend</u> in your network.</li>
* <li>There are <u>5 friends</u> in your network.</li>
* </ul>
* <p>
* Normally in Wicket you would accomplish this using tedious if/else statements
* and string concatenation, conditionally visible fragments/containers, or the
* incredibly arcane syntax of Java's {@link java.text.ChoiceFormat
* ChoiceFormat} (and note that you'd have to carefully escape the "<" and
* ">" if you want to include HTML markup as we need for the hyperlinks in
* this example). All of these approaches are clumsy, and it would be hard for a
* non-programmer to localize or edit the strings.
* <p>
* You can now use this model in a label (also consider
* {@link fiftyfive.wicket.basic.CountLabel CountLabel} as a shortcut for this
* common use case), or call {@link #getObject getObject()} to get the
* interpolated string value for use in a feedback message.
*
* @since 2.0
*/
public class CountMessageModel extends AbstractReadOnlyModel<String> implements IComponentAssignedModel<String> {
private static final long serialVersionUID = 3717960938783720989L;
private String messageKey;
private IModel<? extends Number> countModel;
/**
* Constructs a CountMessageModel that will choose the appropriate localized
* string from a properties file.
*
* @param messageKey
* The key that will be consulted in the properties file. The key
* will have ".zero", ".one" or ".many" appended to it depending
* on the value of the {@code count}.
* @param component
* The Wicket component that will be used for finding the
* properties file. Usually this is the page or panel where you
* plan to use the model.
* @param count
* The number that will be used in the message. This will be used
* to substitute any <code>${count}</code> expressions in the
* message.
*/
public CountMessageModel(String messageKey, IModel<? extends Number> count) {
super();
Args.notNull(messageKey, "messageKey");
Args.notNull(count, "count");
this.messageKey = messageKey;
this.countModel = count;
}
@Override
public IWrapModel<String> wrapOnAssignment(Component component) {
return new AssignmentWrapper(component);
}
@Override
public void detach() {
this.countModel.detach();
super.detach();
}
@Override
public String getObject() {
// this shouldn't be called in the Wicket environment BUT we sometimes use this in Excel exports
return Application.get().getResourceSettings().getLocalizer().getString(getResourceKey(), null,
Model.of(getPropertySubstitutionBean()));
}
public int getCount() {
Number num = this.countModel.getObject();
return num != null ? num.intValue() : 0;
}
public String getResourceKey() {
StringBuilder sb = new StringBuilder();
sb.append(messageKey);
sb.append(getResourceSuffix());
return sb.toString();
}
/**
* Returns either ".zero", ".one", or ".many" depending on the current value
* of the count model.
*/
public String getResourceSuffix() {
int count = getCount();
if (0 == count) {
return ".zero";
} else if (1 == count) {
return ".one";
} else {
return ".many";
}
}
public PropertySubstitutionBean getPropertySubstitutionBean() {
return new PropertySubstitutionBean(getCount());
}
private static class PropertySubstitutionBean implements Serializable {
private static final long serialVersionUID = 5422751133639920585L;
private int count;
private PropertySubstitutionBean(int count) {
this.count = count;
}
@SuppressWarnings("unused")
private int getCount() {
return count;
}
}
private class AssignmentWrapper extends LoadableDetachableModel<String> implements IWrapModel<String> {
private static final long serialVersionUID = 1L;
private final Component component;
public AssignmentWrapper(Component component) {
this.component = component;
}
/**
* @see org.apache.wicket.model.IWrapModel#getWrappedModel()
*/
@Override
public IModel<String> getWrappedModel() {
return CountMessageModel.this;
}
@Override
protected String load() {
return Application.get().getResourceSettings().getLocalizer()
.getString(CountMessageModel.this.getResourceKey(), component,
Model.of(CountMessageModel.this.getPropertySubstitutionBean()));
}
@Override
protected void onDetach() {
CountMessageModel.this.detach();
}
}
}