001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.shared.value.renderer;
034:
035: import com.flexive.shared.FxContext;
036: import com.flexive.shared.FxLanguage;
037: import com.flexive.shared.security.UserTicket;
038: import com.flexive.shared.structure.FxSelectListItem;
039: import com.flexive.shared.value.*;
040:
041: import java.text.DateFormat;
042: import java.text.NumberFormat;
043: import java.util.Date;
044: import java.util.List;
045: import java.util.ArrayList;
046: import java.util.Collections;
047: import java.util.concurrent.ConcurrentHashMap;
048: import java.util.concurrent.ConcurrentMap;
049:
050: import org.apache.commons.lang.StringUtils;
051:
052: /**
053: * Factory for FxValueRenderer. A FxValueRenderer provides a transparent way of formatting
054: * any FxValue object in a given language.
055: *
056: * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
057: * @version $Rev: 181 $
058: */
059: public class FxValueRendererFactory {
060:
061: private static final ConcurrentMap<FxLanguage, FxValueRendererImpl> renderers = new ConcurrentHashMap<FxLanguage, FxValueRendererImpl>();
062: /** Internal fallback default language for locale-agnostic formatters */
063: static final FxLanguage DEFAULT = new FxLanguage(-1,
064: FxLanguage.DEFAULT.getIso2digit(), new FxString(
065: "Default fallback formatter"), true);
066:
067: /**
068: * FxDate formatter.
069: */
070: private static class FxDateFormatter implements
071: FxValueFormatter<Date, FxDate> {
072: public String format(FxDate value, Date translation,
073: FxLanguage outputLanguage) {
074: return DateFormat.getDateInstance(DateFormat.MEDIUM,
075: outputLanguage.getLocale()).format(translation);
076: }
077: }
078:
079: /**
080: * FxDateTime formatter.
081: */
082: private static class FxDateTimeFormatter implements
083: FxValueFormatter<Date, FxDateTime> {
084: public String format(FxDateTime container, Date value,
085: FxLanguage outputLanguage) {
086: return DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
087: DateFormat.MEDIUM, outputLanguage.getLocale())
088: .format(value);
089: }
090: }
091:
092: /**
093: * FxDouble formatter.
094: */
095: private static class FxDoubleFormatter implements
096: FxValueFormatter<Double, FxDouble> {
097: public String format(FxDouble value, Double translation,
098: FxLanguage outputLanguage) {
099: return NumberFormat.getNumberInstance(
100: outputLanguage.getLocale()).format(translation);
101: }
102: }
103:
104: /**
105: * FxFloat formatter.
106: */
107: private static class FxFloatFormatter implements
108: FxValueFormatter<Float, FxFloat> {
109: public String format(FxFloat value, Float translation,
110: FxLanguage outputLanguage) {
111: return NumberFormat.getNumberInstance(
112: outputLanguage.getLocale()).format(translation);
113: }
114: }
115:
116: /**
117: * FxSelectOne formatter
118: */
119: private static class FxSelectOneFormatter implements
120: FxValueFormatter<FxSelectListItem, FxSelectOne> {
121: public String format(FxSelectOne value,
122: FxSelectListItem translation, FxLanguage outputLanguage) {
123: return translation.getLabel().getBestTranslation(
124: outputLanguage);
125: }
126: }
127:
128: /**
129: * FxSelectMany formatter
130: */
131: private static class FxSelectManyFormatter implements
132: FxValueFormatter<SelectMany, FxSelectMany> {
133: public String format(FxSelectMany value,
134: SelectMany translation, FxLanguage outputLanguage) {
135: final List<String> out = new ArrayList<String>(translation
136: .getSelected().size());
137: for (FxSelectListItem item : translation.getSelected()) {
138: out.add(item.getLabel().getBestTranslation(
139: outputLanguage));
140: }
141: Collections.sort(out); // sort by label
142: return StringUtils.join(out.iterator(), ", ");
143: }
144: }
145:
146: /**
147: * Generic object formatter for all types that are not explicitly covered.
148: */
149: private static class ObjectFormatter implements FxValueFormatter {
150: public String format(FxValue value, Object translation,
151: FxLanguage outputLanguage) {
152: return translation != null ? translation.toString()
153: : "null";
154: }
155: }
156:
157: static {
158: addRenderer(DEFAULT, FxDate.class, new FxDateFormatter());
159: addRenderer(DEFAULT, FxDateTime.class,
160: new FxDateTimeFormatter());
161: addRenderer(DEFAULT, FxDouble.class, new FxDoubleFormatter());
162: addRenderer(DEFAULT, FxFloat.class, new FxFloatFormatter());
163: addRenderer(DEFAULT, FxSelectOne.class,
164: new FxSelectOneFormatter());
165: addRenderer(DEFAULT, FxSelectMany.class,
166: new FxSelectManyFormatter());
167: //noinspection unchecked
168: addRenderer(DEFAULT, FxValue.class, new ObjectFormatter());
169: }
170:
171: /**
172: * Return a <code>FxValueRenderer</code> instance for the user's current language.
173: *
174: * @return a <code>FxValueRenderer</code> instance for the user's current language.
175: */
176: public static FxValueRenderer getInstance() {
177: final UserTicket ticket = FxContext.get().getTicket();
178: return getInstance(ticket != null ? ticket.getLanguage() : null);
179: }
180:
181: /**
182: * Returns a <code>FxValueRenderer</code> instance for the given language.
183: * If <code>language</code> is null, the default renderer is returned.
184: *
185: * @param language the target language. Both the output formatting and the value to be
186: * rendered may depend on the renderer's language.
187: * @return a <code>FxValueRenderer</code> instance for the given language.
188: */
189: public static FxValueRenderer getInstance(FxLanguage language) {
190: if (language == null) {
191: // default renderer always exists
192: return renderers.get(DEFAULT);
193: }
194: if (!renderers.containsKey(language)) {
195: renderers.putIfAbsent(language, new FxValueRendererImpl(
196: language));
197: }
198: return renderers.get(language);
199: }
200:
201: /**
202: * Return the default FxValue formatter for the given FxValue subclass.
203: *
204: * @param valueType class of the value to be formatted
205: * @return the default FxValue formatter for the given FxValue subclass.
206: */
207: public static FxValueFormatter getDefaultFormatter(Class valueType) {
208: // this works because the addRenderer methods are bounded
209: //noinspection unchecked
210: return renderers.get(DEFAULT).get(valueType);
211: }
212:
213: private static <DT, T extends FxValue<DT, T>> void addRenderer(
214: FxLanguage language, Class<T> valueType,
215: FxValueFormatter<DT, T> formatter) {
216: renderers.putIfAbsent(language, new FxValueRendererImpl(
217: language));
218: renderers.get(language).put(valueType, formatter);
219: }
220: }
|