View Javadoc
1   /*
2    * #%L
3    * Nuiton Decorator
4    * %%
5    * Copyright (C) 2011 CodeLutin, Tony Chemit
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
21   */
22  package org.nuiton.decorator;
23  
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.Locale;
32  import java.util.Map;
33  
34  /**
35   * A decorator provider for multi-i18n locale.
36   * <p/>
37   * Implements the method {@link #loadDecorators(Locale)} to fill the decorators
38   * availables.
39   * <p/>
40   * Then can obtain decorator via the methods {@code getDecorator(...)}
41   * <p/>
42   *
43   * @author tchemit <chemit@codelutin.com>
44   * @since 2.3
45   */
46  public abstract class DecoratorMulti18nProvider {
47  
48      /** Logger */
49      private static final Log log = LogFactory.getLog(DecoratorProvider.class);
50  
51      /**
52       * Loaded decorators.
53       * <p/>
54       * This map will be lazy loaded as needed via the method
55       * {@link #getDecoratorContexts(Locale, boolean)}.
56       */
57      protected Map<Locale, Collection<DecoratorContext<?>>> decoratorContexts;
58  
59  
60      /**
61       * Load all decorators of the provider for the given {@code locale}.
62       *
63       * @param locale the locale to use to load decorators.
64       */
65      protected abstract void loadDecorators(Locale locale);
66  
67  
68      /**
69       * Obtain a decorator for the given object using the given {@code locale}.
70       *
71       * @param locale user locale
72       * @param object object of decorated object
73       * @param <O>    object of decorated object
74       * @return the decorator or {@code null} if not found
75       */
76      @SuppressWarnings({"unchecked"})
77      public <O> Decorator<O> getDecorator(Locale locale, O object) {
78          return getDecorator(locale, object, null);
79      }
80  
81      /**
82       * Obtain a decorator given a object and an extra name to qualify the
83       * context using the given {@code locale}.
84       *
85       * @param locale user locale
86       * @param object object of decorated object
87       * @param name   extra name to qualify the decorator to use
88       * @param <O>    object of decorated object
89       * @return the decorator or {@code null} if not found
90       */
91      @SuppressWarnings({"unchecked"})
92      public <O> Decorator<O> getDecorator(Locale locale,
93                                           O object,
94                                           String name) {
95          Class<O> k = (Class<O>) object.getClass();
96          return getDecoratorByType(locale, k, name);
97      }
98  
99      /**
100      * Obtain a decorator given a type on the given {@code locale}.
101      *
102      * @param locale user locale
103      * @param type   type of decorated object
104      * @param <O>    type of decorated object
105      * @return the decorator or {@code null} if not found
106      */
107     public <O> Decorator<O> getDecoratorByType(Locale locale,
108                                                Class<O> type) {
109         return getDecoratorByType(locale, type, null);
110     }
111 
112     /**
113      * Obtain a decorator given a type and a extra context name on the given
114      * {@code locale}.
115      *
116      * @param locale user locale
117      * @param type   type of decorated object
118      * @param name   extra name to qualify the decorator to use
119      * @param <O>    type of decorated object
120      * @return the decorator or {@code null} if not found
121      */
122     public <O> Decorator<O> getDecoratorByType(Locale locale,
123                                                Class<O> type,
124                                                String name) {
125         DecoratorContext<O> d = getDecoratorContext(locale, type, name, true);
126         return d == null ? null : d.getDecorator();
127     }
128 
129     public void registerPropertyDecorator(Locale locale,
130                                           Class<?> klass,
131                                           String expression) {
132         registerPropertyDecorator(locale, klass, null, expression);
133     }
134 
135     public void registerJXPathDecorator(Locale locale,
136                                         Class<?> klass, String expression) {
137         registerJXPathDecorator(locale, klass, null, expression);
138     }
139 
140     public void registerMultiJXPathDecorator(Locale locale,
141                                              Class<?> klass,
142                                              String expression,
143                                              String separator,
144                                              String separatorReplacement) {
145         registerMultiJXPathDecorator(locale, klass, null, expression, separator,
146                                      separatorReplacement);
147     }
148 
149     public void registerPropertyDecorator(Locale locale,
150                                           Class<?> klass,
151                                           String name,
152                                           String expression) {
153         Decorator<?> decorator =
154                 DecoratorUtil.newPropertyDecorator(klass, expression);
155         registerDecorator(locale, name, decorator);
156     }
157 
158     public void registerJXPathDecorator(Locale locale,
159                                         Class<?> klass,
160                                         String name,
161                                         String expression) {
162         Decorator<?> decorator =
163                 DecoratorUtil.newJXPathDecorator(klass, expression);
164         registerDecorator(locale, name, decorator);
165     }
166 
167     public void registerMultiJXPathDecorator(Locale locale,
168                                              Class<?> klass,
169                                              String name,
170                                              String expression,
171                                              String separator,
172                                              String separatorReplacement) {
173         Decorator<?> decorator = DecoratorUtil.newMultiJXPathDecorator(
174                 klass, expression, separator, separatorReplacement
175         );
176         registerDecorator(locale, name, decorator);
177     }
178 
179     public void registerDecorator(Locale locale,
180                                   Decorator<?> decorator) {
181         registerDecorator(locale, null, decorator);
182     }
183 
184     /**
185      * Register a new decorator in the cache of the provider.
186      *
187      * @param <T>       type of data decorated
188      * @param locale    the given locale to use for this decorator
189      * @param context   the name decorator
190      * @param decorator the decorator to register
191      */
192     public <T> void registerDecorator(Locale locale,
193                                       String context,
194                                       Decorator<T> decorator) {
195 
196         // obtain the decorator context
197         DecoratorContext<?> result =
198                 getDecoratorContext(locale,
199                                     decorator.getType(),
200                                     context,
201                                     false
202                 );
203 
204         if (result != null) {
205             throw new IllegalArgumentException(
206                     "there is an already register decorator with context " +
207                     result);
208         }
209 
210         DecoratorContext<T> decoratorContext =
211                 new DecoratorContext<T>(context, decorator);
212         if (log.isDebugEnabled()) {
213             log.debug(decoratorContext);
214         }
215         getDecoratorContexts(locale, false).add(decoratorContext);
216     }
217 
218     public void clear() {
219         if (decoratorContexts != null) {
220             decoratorContexts.clear();
221         }
222     }
223 
224     protected Map<Locale, Collection<DecoratorContext<?>>> getDecoratorContexts() {
225         if (decoratorContexts == null) {
226             decoratorContexts = new HashMap<Locale, Collection<DecoratorContext<?>>>();
227         }
228         return decoratorContexts;
229     }
230 
231     protected Collection<DecoratorContext<?>> getDecoratorContexts(Locale locale,
232                                                                    boolean doLoad) {
233 
234         Collection<DecoratorContext<?>> decoratorContexts =
235                 getDecoratorContexts().get(locale);
236 
237         if (decoratorContexts == null) {
238             decoratorContexts = new ArrayList<DecoratorContext<?>>();
239             getDecoratorContexts().put(locale, decoratorContexts);
240             if (doLoad) {
241                 loadDecorators(locale);
242             }
243         }
244         return decoratorContexts;
245     }
246 
247     @SuppressWarnings({"unchecked"})
248     protected <T> DecoratorContext<T> getDecoratorContext(Locale locale,
249                                                           Class<T> type,
250                                                           String context,
251                                                           boolean doLoad) {
252         DecoratorContext<T> result = null;
253 
254         Collection<DecoratorContext<?>> decoratorContexts =
255                 getDecoratorContexts(locale, doLoad);
256 
257         if (decoratorContexts != null) {
258             for (DecoratorContext<?> d : decoratorContexts) {
259                 if (type == null) {
260                     if (d.accept(context)) {
261                         result = (DecoratorContext<T>) d;
262                         break;
263                     }
264                     continue;
265                 }
266                 if (d.accept(type, context)) {
267                     result = (DecoratorContext<T>) d;
268                     break;
269                 }
270             }
271         }
272         return result;
273     }
274 
275     public static class DecoratorContext<T> {
276 
277         /** the context name of the decorator */
278         final String context;
279 
280         /** the decorator */
281         final Decorator<T> decorator;
282 
283         public DecoratorContext(String context, Decorator<T> decorator) {
284             this.context = context;
285             this.decorator = decorator;
286         }
287 
288         public String getContext() {
289             return context;
290         }
291 
292         public Decorator<T> getDecorator() {
293             return decorator;
294         }
295 
296         public Class<T> getType() {
297             return decorator.getType();
298         }
299 
300         public boolean accept(Class<?> type, String context) {
301             boolean accept = getType().isAssignableFrom(type) && accept(context);
302             return accept;
303         }
304 
305         public boolean accept(String context) {
306             return this.context == null && context == null ||
307                    this.context != null && this.context.equals(context);
308         }
309 
310         @Override
311         public String toString() {
312             return super.toString() + "<type: " + getType().getName() +
313                    ", context :" + context + ">";
314         }
315     }
316 }