All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.sf.mmm.util.nls.impl.formatter.NlsFormatterChoice Maven / Gradle / Ivy
/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.nls.impl.formatter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import net.sf.mmm.util.date.api.Iso8601UtilLimited;
import net.sf.mmm.util.filter.api.CharFilter;
import net.sf.mmm.util.filter.api.Filter;
import net.sf.mmm.util.filter.base.ConjunctionCharFilter;
import net.sf.mmm.util.filter.base.ListCharFilter;
import net.sf.mmm.util.lang.api.CompareOperator;
import net.sf.mmm.util.lang.api.Conjunction;
import net.sf.mmm.util.nls.api.NlsArgument;
import net.sf.mmm.util.nls.api.NlsArgumentParser;
import net.sf.mmm.util.nls.api.NlsFormatterManager;
import net.sf.mmm.util.nls.api.NlsTemplateResolver;
import net.sf.mmm.util.nls.base.AbstractNlsFormatterPlugin;
import net.sf.mmm.util.nls.base.NlsDependencies;
import net.sf.mmm.util.scanner.base.CharSequenceScanner;
/**
* This is the implementation of {@link net.sf.mmm.util.nls.api.NlsFormatter} for
* {@link net.sf.mmm.util.nls.api.NlsFormatterManager#TYPE_CHOICE choice-format}.
* Examples:
*
*
* {@link net.sf.mmm.util.nls.api.NlsMessage}
* Example result
*
*
* {deleteCount} {deleteCount,choice,(?==1)'files'(else)'file'} deleted.
* 1 file deleted.
*
*
* {flag,choice,(?==true){date}(else){time}}
* 23:59:59
*
*
* Note:
* Literal text in choice format has to be enclosed by single (') or double (") quotes. If you need to use
* such quote inside the text either use a different enclosing quote or use a duplicated quote sign to escape
* the quote. As a single quote is often used in languages such as French or Spanish it is better to escape
* with double quotes.
*
* @author Joerg Hohwiller (hohwille at users.sourceforge.net)
* @since 2.0.0
*/
public final class NlsFormatterChoice extends AbstractNlsFormatterPlugin {
/** The character used to indicate the start of a Choice condition. */
public static final char CONDITION_START = '(';
/** The character used to indicate the end of a Choice condition. */
public static final char CONDITION_END = ')';
/**
* The character used to indicate the variable object of a Choice condition.
*/
public static final char CONDITION_VAR = '?';
/** The value of a Choice condition that matches always. */
public static final String CONDITION_ELSE = "else";
/** The {@link Filter} for {@link #CONDITION_ELSE}. */
private static final Filter FILTER_ELSE = new Condition(null, null);
/**
* The {@link CharFilter} for the {@link CompareOperator#getValue() comparator symbol} .
*/
private static final CharFilter FILTER_COMPARATOR = new ListCharFilter(true, '<', '=', '>', '!');
/** The {@link CharFilter} for the comparator argument. */
private static final CharFilter FILTER_COMPARATOR_ARGUMENT = new ConjunctionCharFilter(Conjunction.OR, CharFilter.LATIN_DIGIT_OR_LETTER_FILTER,
new ListCharFilter(true, '-', '+', '.', ':'));
/** The {@link NlsDependencies} to use. */
private final NlsDependencies nlsDependencies;
/** The {@link Choice}s. */
private final List choices;
/**
* The constructor.
*
* @param scanner is the {@link CharSequenceScanner} pointing to the choice- {@code formatStyle}.
* @param nlsDependencies are the {@link NlsDependencies} to use.
*/
public NlsFormatterChoice(CharSequenceScanner scanner, NlsDependencies nlsDependencies) {
super();
this.nlsDependencies = nlsDependencies;
this.choices = new ArrayList<>();
boolean hasElse = false;
char c = scanner.forceNext();
while ((c == CONDITION_START) && (!hasElse)) {
Choice choice = parseChoice(scanner);
if (choice.condition == FILTER_ELSE) {
hasElse = true;
}
this.choices.add(choice);
c = scanner.forceNext();
}
if (!hasElse) {
throw new NlsFormatterChoiceNoElseConditionException();
}
if (this.choices.size() < 2) {
throw new NlsFormatterChoiceOnlyElseConditionException();
}
scanner.stepBack();
}
/**
* This method parses the {@link Choice}.
*
* @param scanner is the {@link CharSequenceScanner}.
* @return the parsed {@link Choice}.
*/
private Choice parseChoice(CharSequenceScanner scanner) {
Filter condition = parseCondition(scanner);
List segments = new ArrayList<>();
while (scanner.hasNext()) {
int index = scanner.getCurrentIndex();
char c = scanner.peek();
String literal = null;
if ((c == '"') || (c == '\'')) {
scanner.next();
literal = scanner.readUntil(c, false, c);
if (literal == null) {
throw new IllegalArgumentException(scanner.substring(index, scanner.getCurrentIndex()));
}
c = scanner.peek();
}
NlsArgument argument = null;
if (c == NlsArgumentParser.START_EXPRESSION) {
scanner.next();
argument = this.nlsDependencies.getArgumentParser().parse(scanner);
}
if ((argument != null) || (literal != null)) {
segments.add(new Segment(literal, argument));
} else {
break;
}
}
return new Choice(condition, segments);
}
/**
* This method parses the {@link Condition}.
*
* @param scanner is the {@link CharSequenceScanner}.
* @return the parsed {@link Condition} or {@link #FILTER_ELSE} in case of {@link #CONDITION_ELSE}.
*/
private Filter parseCondition(CharSequenceScanner scanner) {
int index = scanner.getCurrentIndex();
Filter condition;
if (scanner.expect(CONDITION_VAR)) {
// variable choice
String symbol = scanner.readWhile(FILTER_COMPARATOR);
CompareOperator comparator = CompareOperator.fromValue(symbol);
if (comparator == null) {
throw new IllegalArgumentException(symbol);
}
Object comparatorArgument = parseComparatorArgument(scanner);
condition = new Condition(comparator, comparatorArgument);
} else if (scanner.expect(CONDITION_ELSE, false)) {
condition = FILTER_ELSE;
} else {
throw new IllegalArgumentException(scanner.substring(index, scanner.getCurrentIndex()));
}
if (!scanner.expect(CONDITION_END)) {
throw new IllegalArgumentException(scanner.substring(index, scanner.getCurrentIndex()));
}
return condition;
}
/**
* This method parses the {@link Condition#comparatorArgument comparator argument}.
*
* @param scanner is the {@link CharSequenceScanner}.
* @return the parsed comparator argument.
*/
private Object parseComparatorArgument(CharSequenceScanner scanner) {
int index = scanner.getCurrentIndex();
Object comparatorArgument;
char c = scanner.forcePeek();
if ((c == '"') || (c == '\'')) {
scanner.next();
comparatorArgument = scanner.readUntil(c, false, c);
} else {
String argument = scanner.readWhile(FILTER_COMPARATOR_ARGUMENT);
if (argument.length() == 0) {
throw new IllegalArgumentException(scanner.substring(index, scanner.getCurrentIndex()));
}
if ("null".equals(argument)) {
comparatorArgument = null;
} else if (argument.matches(Iso8601UtilLimited.PATTERN_STRING_ALL)) {
comparatorArgument = this.nlsDependencies.getIso8601Util().parseDate(argument);
} else if (Boolean.TRUE.toString().equals(argument)) {
comparatorArgument = Boolean.TRUE;
} else if (Boolean.FALSE.toString().equals(argument)) {
comparatorArgument = Boolean.FALSE;
} else {
// double vs. date?
comparatorArgument = Double.valueOf(argument);
}
}
return comparatorArgument;
}
@Override
public void format(Object object, Locale locale, Map arguments, NlsTemplateResolver resolver, Appendable buffer) throws IOException {
for (Choice choice : this.choices) {
if (choice.condition.accept(object)) {
for (Segment segment : choice.segments) {
buffer.append(segment.literal);
if (segment.argument != null) {
this.nlsDependencies.getArgumentFormatter().format(segment.argument, locale, arguments, resolver, buffer);
}
}
return;
}
}
buffer.append(toString());
}
@Override
public String getType() {
return NlsFormatterManager.TYPE_CHOICE;
}
@Override
public String getStyle() {
return null;
}
/**
* @return the {@link List} of {@link Choice}s. Do not modify.
*/
public List getChoices() {
return this.choices;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(NlsFormatterManager.TYPE_CHOICE);
sb.append(",");
for (Choice choice : this.choices) {
sb.append(choice);
}
return sb.toString();
}
/**
* This inner class represents a single choice.
*/
public static final class Choice {
/** The condition that determines when the choice applies. */
private final Filter condition;
/** The segments */
private final List segments;
/**
* The constructor.
*
* @param condition is the {@link #condition}.
* @param segments is the {@link List} of {@link Segment}s.
*/
private Choice(Filter condition, List segments) {
super();
this.condition = condition;
this.segments = segments;
}
/**
* @return the condition that {@link Filter#accept(Object) determines} if this {@link Choice} shall match.
*/
public Filter getCondition() {
return this.condition;
}
/**
* @return {@code true} if {@link #getCondition() coniditon} is '(else)', {@code false} otherwise.
*/
public boolean isElse() {
return (this.condition == FILTER_ELSE);
}
/**
* @return the {@link List} of {@link Segment}s. Do not modify this {@link List}.
*/
public List getSegments() {
return this.segments;
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append(this.condition);
for (Segment segment : this.segments) {
buffer.append('\'');
buffer.append(segment.literal.replace("'", "''"));
buffer.append('\'');
if (segment.argument != null) {
buffer.append(segment.argument);
}
}
return buffer.toString();
}
}
/**
* This inner class represents a single segment of a {@link Choice}.
*/
public static class Segment {
/** The literal result. */
private final String literal;
/**
* The {@link NlsArgument} to use as result or {@code null} if this is the last {@link Segment}.
*/
private final NlsArgument argument;
/**
* The constructor.
*
* @param literal is the literal (prefix).
* @param argument is the {@link NlsArgument} or {@code null} if this is the last {@link Segment}.
*/
public Segment(String literal, NlsArgument argument) {
super();
if (literal == null) {
this.literal = "";
} else {
this.literal = literal;
}
this.argument = argument;
}
/**
* @return the literal (static text).
*/
public String getLiteral() {
return this.literal;
}
/**
* @return the {@link NlsArgument} (dynamic parameter).
*/
public NlsArgument getArgument() {
return this.argument;
}
}
/**
* This inner class represents a single choice.
*/
private static class Condition implements Filter {
/** The {@link CompareOperator}. */
private final CompareOperator comparator;
/** The argument for the {@link #comparator}. */
private final Object comparatorArgument;
/**
* The constructor.
*
* @param comparator is the {@link #comparator}.
* @param comparatorArgument is the {@link #comparatorArgument}.
*/
public Condition(CompareOperator comparator, Object comparatorArgument) {
super();
this.comparator = comparator;
this.comparatorArgument = comparatorArgument;
}
@Override
public boolean accept(Object value) {
if (this.comparator == null) {
return true;
}
return this.comparator.eval(value, this.comparatorArgument);
}
@Override
public String toString() {
if (this.comparator == null) {
return "(else)";
}
StringBuilder buffer = new StringBuilder();
buffer.append("(?");
buffer.append(this.comparator.getValue());
buffer.append(this.comparatorArgument);
buffer.append(")");
return buffer.toString();
}
}
}