/* * Copyright (c) 2012, 2013, Credit Suisse (Anatole Tresch), Werner Keil. * * 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 org.javamoney.format; import org.javamoney.format.tokens.LiteralTokenStyleableItem; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; /** * This class implements a builder that allows creating of {@link ItemFormat} * instances programmatically using a fluent API. The formatting hereby is * modeled by a concatenation of {@link StyleableItemFormatToken} instances. The same * {@link StyleableItemFormatToken} instances also are responsible for implementing the * opposite, parsing, of an item from an input character sequence. Each * {@link StyleableItemFormatToken} gets access to the current parsing location, and the * original and current character input sequence, modeled by the * {@link ItemParseContext}. Finall if parsing of a part failed, a * {@link StyleableItemFormatToken} throws an {@link ItemParseException} describing the * problem. * <p> * This class is not thread-safe and therefore should not be shared among * different threads. * * @param <T> the target type. * @author Anatole Tresch */ public class ItemFormatBuilder<T>{ /** * The tokens to be used for formatting/parsing. */ private List<StyleableItemFormatToken<T>> tokens = new ArrayList<StyleableItemFormatToken<T>>(); /** * The localization configuration. */ private LocalizationContext localizationContext; /** * The target type being parsed/formatted. */ private Class<T> targetType; /** * The item factory to be used. */ private ParseResultFactory<T> parseResultFactory; /** * Creates a new Builder. */ public ItemFormatBuilder(){ } /** * Creates a new Builder. * * @param targetType the target class. */ public ItemFormatBuilder(Class<T> targetType){ if(targetType == null){ throw new IllegalArgumentException("targetType must not be null."); } this.targetType = targetType; } /** * Access the target class, which this formatter can handle with. * * @return the target class, never null. */ public Class<T> getTargetType(){ return this.targetType; } /** * Configure the format with the given {@link LocalizationContext}. * * @param style the localizationStyle to be applied. * @return the builder instance, for chaining. */ public ItemFormatBuilder<T> withStyle(LocalizationContext style){ if(style == null){ throw new IllegalArgumentException("localizationStyle required."); } this.localizationContext = style; return this; } /** * Configure the format with the given target type. * * @param targetType the target type to be applied. * @return the builder instance, for chaining. */ public ItemFormatBuilder<T> withTargetType(Class<T> targetType){ if(targetType == null){ throw new IllegalArgumentException("targetType required."); } this.targetType = targetType; return this; } /** * Add a {@link StyleableItemFormatToken} to the token list. * * @param token the token to add. * @return the builder, for chaining. */ public ItemFormatBuilder<T> append(StyleableItemFormatToken<T> token){ this.tokens.add(token); return this; } /** * Add a {@link StyleableItemFormatToken} to the token list. * * @param token the token to add. * @return the builder, for chaining. */ public ItemFormatBuilder<T> append(String token){ this.tokens.add(new LiteralTokenStyleableItem<T>(token)); return this; } /** * The toal number of tokens. * * @return the number of tokens. */ public int getTokenCount(){ return this.tokens.size(); } /** * Set the item factory used, to of the item parsed from the results in * the {@link ItemParseContext}. * * @param parseResultFactory the {@link ParseResultFactory}. * @return the builder, for chaining. */ public ItemFormatBuilder<T> withItemFactory(ParseResultFactory<T> parseResultFactory){ this.parseResultFactory = parseResultFactory; return this; } /** * Get the configured item factory. * * @return the {@link ParseResultFactory}. */ public ParseResultFactory<T> getParseResultFactory(){ return parseResultFactory; } /** * Clears the builder (tokens, item factory). */ public void clear(){ this.tokens.clear(); this.parseResultFactory = null; } /** * Checks if the builder is ready for creating a {@link ItemFormatBuilder}. * * @return true, if a format instance can be of. * @see #build() */ public boolean isBuildable(){ return this.localizationContext != null && this.targetType != null && !this.tokens.isEmpty(); } /** * Access all the token used for building up this format. * * @return the token used by this formatter, never {@code null}. */ public List<StyleableItemFormatToken<T>> getTokens(){ return Collections.unmodifiableList(this.tokens); } /** * Get the configuring {@link LocalizationContext}. * * @return the localizationStyle instance applied, never null. */ public LocalizationContext getLocalizationContext(){ return this.localizationContext; } /** * This method creates an {@link ItemFormat} based on this instance, hereby * using the given a {@link ParseResultFactory} to extract the item to be returned * from the {@link ItemParseContext}'s results. * * @return the {@link ItemFormat} instance, never null. */ public ItemFormat<T> build(){ if(this.parseResultFactory == null){ return new TokenizedItemFormat<T>(targetType, localizationContext, new DefaultParseResultFactory<T>(targetType), tokens); } return new TokenizedItemFormat<T>(targetType, localizationContext, parseResultFactory, tokens); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString(){ return "BuildableItemFormat [targetType=" + targetType + ", localizationStyle=" + localizationContext + ", tokens=" + tokens + "]"; } /** * Adapter implementation that implements the {@link ItemFormat} interface * based on a {@link ItemFormatBuilder} and a {@link ParseResultFactory}. * * @param <T> the target type * @author Anatole Tresch */ private final static class TokenizedItemFormat<T> implements ItemFormat<T>{ /** * The tokens to be used for formatting/parsing. */ private List<StyleableItemFormatToken<T>> tokens = new ArrayList<StyleableItemFormatToken<T>>(); /** * The localization configuration. */ private LocalizationContext style; /** * The target type being parsed/formatted. */ private Class<T> targetType; /** * The item factory to be used. */ private ParseResultFactory<T> parseResultFactory; /** * Creates a new instance. * * @param buildItemFormat the base buildItemFormat, not null. * @param parseResultFactory the parseResultFactory to be used, not null. */ public TokenizedItemFormat(Class<T> targetType, LocalizationContext style, ParseResultFactory<T> parseResultFactory, StyleableItemFormatToken<T>... tokens){ this(targetType, style, parseResultFactory, Arrays.asList(tokens)); } /** * Creates a new instance. * * @param buildItemFormat the base buildItemFormat, not null. * @param parseResultFactory the parseResultFactory to be used, not null. */ public TokenizedItemFormat(Class<T> targetType, LocalizationContext style, ParseResultFactory<T> parseResultFactory, List<StyleableItemFormatToken<T>> tokens){ if(targetType == null){ throw new IllegalArgumentException("Target Class must not be null."); } if(style == null){ throw new IllegalArgumentException("LocalizationStyle must not be null."); } if(parseResultFactory == null){ throw new IllegalArgumentException("ParseResultFactory must not be null."); } if(tokens == null || tokens.isEmpty()){ throw new IllegalArgumentException("tokens must not be null or empty."); } this.targetType = targetType; this.style = style; this.parseResultFactory = parseResultFactory; this.tokens.addAll(tokens); } /* * (non-Javadoc) * * @see javax.money.format.ItemFormat#getTargetClass() */ @Override public Class<T> getTargetClass(){ return this.targetType; } /* * (non-Javadoc) * * @see javax.money.format.ItemFormat#getLocalizationStyle() */ @Override public LocalizationContext getStyle(){ return this.style; } /** * Print the item into an {@link Appendable}. * * @param appendable the appendable, not null * @param item the item being formatted, not null * @throws IOException forwarded exception thrown by the {@link Appendable}. */ public void print(Appendable appendable, T item, Locale locale) throws IOException{ for(StyleableItemFormatToken<T> token : tokens){ token.print(appendable, item, locale, style); } } /** * Formats the item as {@link String}. * * @param item the item being formatted, not null * @return The formatted String, not null. * @throws ItemFormatException If formatting fails. */ public String format(T item, Locale locale){ StringBuilder builder = new StringBuilder(); try{ print(builder, item, locale); } catch(IOException e){ throw new ItemFormatException("Error foratting of " + item, e); } return builder.toString(); } /** * Parses the input text into an item of type T. * * @param text The input text * @return the item to be parsed. * @throws ItemParseException If parsing failed. */ public T parse(CharSequence text, Locale locale) throws ItemParseException{ ItemParseContext<T> ctx = new ItemParseContext<T>(text, parseResultFactory); for(StyleableItemFormatToken<T> token : tokens){ token.parse(ctx, locale, style); if(ctx.isComplete()){ return ctx.getItem(); } } if(ctx.isComplete()){ return ctx.getItem(); } throw new ItemParseException("Parsing of item of type " + getTargetClass() + " failed from " + ctx); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString(){ return "BuildableItemFormat [targetType=" + targetType + ", localizationStyle=" + style + ", parseResultFactory=" + parseResultFactory + ", tokens=" + tokens + "]"; } } }