View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.locators;
17  
18  import java.util.Locale;
19  
20  import net.sf.jguiraffe.di.ClassLoaderProvider;
21  
22  import org.apache.commons.beanutils.ConversionException;
23  import org.apache.commons.beanutils.Converter;
24  
25  /**
26   * <p>
27   * A specialized {@code Converter} implementation dealing with {@link Locator}
28   * objects.
29   * </p>
30   * <p>
31   * This converter implementation is able to transform text representations of
32   * locators into concrete {@link Locator} instances. This is pretty convenient,
33   * especially for bean declarations; here a compact text representation for
34   * locators is much more readable than a verbose declaration of a factory method
35   * invocation.
36   * </p>
37   * <p>
38   * This converter supports most of the standard {@link Locator} implementations
39   * provided by this package. A text representation of a locator starts with a
40   * prefix, followed by a colon. Then the specific data of the concrete
41   * {@link Locator} subclass is appended. The following table lists the supported
42   * prefixes with their meaning and examples:
43   * <table border="1">
44   * <tr>
45   * <th>Prefix</th>
46   * <th>Description</th>
47   * <th>Example</th>
48   * </tr>
49   * <tr>
50   * <td valign="top">classpath</td>
51   * <td>Creates a {@link ClassPathLocator} instance. The data is interpreted as a
52   * resource name which is looked up on the current class path. Optionally, a
53   * class loader name can be provided separated by a semicolon. If this is used,
54   * the corresponding class loader is obtained from the
55   * {@link ClassLoaderProvider}; otherwise, the default class loader is used.</td>
56   * <td>classpath:myresource.properties<br>
57   * classpath:myresource.properties;myClassLoader</td>
58   * </tr>
59   * <tr>
60   * <td valign="top">file</td>
61   * <td>Creates a {@link FileLocator} instance. The data is interpreted as a
62   * relative or absolute file name. It is directly passed to the
63   * {@link FileLocator} instance to be created.</td>
64   * <td>file:target/data.txt</td>
65   * </tr>
66   * <tr>
67   * <td valign="top">url</td>
68   * <td>Creates a {@link URLLocator} instance. The data is interpreted as a URL
69   * in text form. It is directly passed to the {@link URLLocator} instance to be
70   * created.</td>
71   * <td>url:http://www.mydomain.com/image.jpg</td>
72   * </tr>
73   * </table>
74   * </p>
75   * <p>
76   * Prefixes are not case sensitive. If an unknown prefix is encountered, a
77   * {@code ConversionException} is thrown. An instance of this class is
78   * registered as base class converter by the form builder per default. Using the
79   * data type conversion mechanism provided by the dependency injection
80   * framework, it is possible to add further converters for custom
81   * {@link Locator} implementations.
82   * </p>
83   * <p>
84   * This class does not have any internal state. Thus an instance can be shared
85   * between multiple components and called concurrently.
86   * </p>
87   *
88   * @author Oliver Heger
89   * @version $Id: LocatorConverter.java 213 2012-07-14 19:40:51Z oheger $
90   */
91  public class LocatorConverter implements Converter
92  {
93      /**
94       * Constant for the prefix separator. This character is used to separate the
95       * prefix which identifies the type of the locator from its data.
96       */
97      public static final char PREFIX_SEPARATOR = ':';
98  
99      /**
100      * The separator for a class loader name. This character is evaluated for
101      * locator declarations of type classpath.
102      */
103     private static final char CL_SEPARATOR = ';';
104 
105     /** The current class loader provider. */
106     private final ClassLoaderProvider classLoaderProvider;
107 
108     /**
109      * Creates a new instance of {@code LocatorConverter} and initializes it
110      * with the given {@code ClassLoaderProvider}. Class loaders for class path
111      * locators are obtained from this provider.
112      *
113      * @param clp the {@code ClassLoaderProvider}
114      * @since 1.2
115      */
116     public LocatorConverter(ClassLoaderProvider clp)
117     {
118         classLoaderProvider = clp;
119     }
120 
121     /**
122      * Creates a new instance of {@code LocatorConverter} without a class loader
123      * provider. A converter constructed this way cannot resolve any class
124      * loader names. This constructor exists for reasons of backwards
125      * compatibility. It is recommended to always provide a
126      * {@code ClassLoaderProvider}.
127      */
128     public LocatorConverter()
129     {
130         this(null);
131     }
132 
133     /**
134      * Returns the {@code ClassLoaderProvider} used by this converter. This
135      * object is used to determine class loaders when locators for class path
136      * resources are to be created. Result may be <b>null</b> if no
137      * {@code ClassLoaderProvider} has been set.
138      *
139      * @return the current {@code ClassLoaderProvider}
140      * @since 1.2
141      */
142     public ClassLoaderProvider getClassLoaderProvider()
143     {
144         return classLoaderProvider;
145     }
146 
147     /**
148      * Tries to convert the specified object to a {@link Locator}. The
149      * conversion is based on prefixes as described in the class comment.
150      *
151      * @param type the target class
152      * @param value the object to be converted (must not be <b>null</b>)
153      * @return the converted object; this is a concrete implementation of the
154      *         {@link Locator} interface
155      * @throws ConversionException if conversion is not possible
156      */
157     public Object convert(@SuppressWarnings("rawtypes") Class type, Object value)
158     {
159         if (value == null)
160         {
161             throw new ConversionException("Cannot convert null Locator!");
162         }
163 
164         return convertLocator(String.valueOf(value));
165     }
166 
167     /**
168      * Obtains a {@code LocatorRepresentation} instance which corresponds to the
169      * specified prefix extracted from the textual representation of a locator.
170      * If this is not possible, an exception is thrown.
171      *
172      * @param locatorPrefix the prefix identifying the concrete locator type
173      * @return the corresponding {@code LocatorRepresentation} instance
174      * @throws ConversionException if no fitting instance can be found
175      */
176     private LocatorRepresentation getRepresentation(String locatorPrefix)
177     {
178         try
179         {
180             return LocatorRepresentation.valueOf(locatorPrefix
181                     .toUpperCase(Locale.ENGLISH));
182         }
183         catch (IllegalArgumentException iex)
184         {
185             throw new ConversionException("Failed conversion to a Locator! "
186                     + "Unknown prefix: " + locatorPrefix);
187         }
188     }
189 
190     /**
191      * Converts the given textual representation of a locator to a concrete
192      * {@link Locator} instance.
193      *
194      * @param rep the string-based representation of the locator
195      * @return the corresponding {@link Locator} instance
196      * @throws ConversionException if conversion is not possible
197      */
198     private Locator convertLocator(String rep)
199     {
200         int pos = rep.indexOf(PREFIX_SEPARATOR);
201         if (pos < 0)
202         {
203             throw new ConversionException(
204                     "Invalid syntax of a Locator representation: '" + rep
205                             + "'! Cannot find a prefix for the Locator type.");
206         }
207         if (pos == rep.length() - 1)
208         {
209             throw new ConversionException("No data provided for locator: "
210                     + rep);
211         }
212 
213         LocatorRepresentation locRep = getRepresentation(rep.substring(0, pos));
214         try
215         {
216             return locRep.createLocator(rep.substring(pos + 1),
217                     getClassLoaderProvider());
218         }
219         catch (LocatorException lex)
220         {
221             throw new ConversionException("Conversion to Locator failed for "
222                     + rep, lex);
223         }
224     }
225 
226     /**
227      * An internally used enumeration class for the text representations of
228      * locators. This class is used to identify the desired locator type and to
229      * create a corresponding instance.
230      */
231     private static enum LocatorRepresentation
232     {
233         /** Constant for a class path locator. */
234         CLASSPATH
235         {
236             /**
237              * Creates a {@link ClassPathLocator}. The passed in data is
238              * interpreted as the resource name. If it contains a class loader
239              * name (separated by a semicolon), this class loader is obtained
240              * from the given {@code ClassLoaderProvider} and passed to the
241              * newly created {@code Locator}.
242              *
243              * @param data data for the locator
244              * @param clp the {@code ClassLoaderProvider}
245              * @return the new locator instance
246              */
247             @Override
248             public Locator createLocator(String data, ClassLoaderProvider clp)
249             {
250                 String resourceName;
251                 String clName;
252                 int posCLName = data.indexOf(CL_SEPARATOR);
253                 if (posCLName > 0)
254                 {
255                     resourceName = data.substring(0, posCLName);
256                     clName = data.substring(posCLName + 1);
257                 }
258                 else
259                 {
260                     resourceName = data;
261                     clName = null;
262                 }
263                 ClassLoader cl =
264                         (clp != null) ? clp.getClassLoader(clName) : null;
265 
266                 return ClassPathLocator.getInstance(resourceName, cl);
267             }
268         },
269 
270         /** Constant for a file locator. */
271         FILE
272         {
273             /**
274              * Creates a {@link FileLocator}. The passed in data is interpreted
275              * as the file name.
276              *
277              * @param data data for the locator
278              * @param clp the {@code ClassLoaderProvider}
279              * @return the new locator instance
280              */
281             @Override
282             public Locator createLocator(String data, ClassLoaderProvider clp)
283             {
284                 return FileLocator.getInstance(data);
285             }
286         },
287 
288         /** Constant for a URL locator. */
289         URL
290         {
291             /**
292              * Creates a {@link URLLocator}. The passed in data is interpreted
293              * as the text representation of the URL.
294              *
295              * @param data data for the locator
296              * @param clp the {@code ClassLoaderProvider}
297              * @return the new locator instance
298              */
299             @Override
300             public Locator createLocator(String data, ClassLoaderProvider clp)
301             {
302                 return URLLocator.getInstance(data);
303             }
304         };
305 
306         /**
307          * Creates the concrete {@link Locator} implementation represented by
308          * this object. The passed in data was read from the specification
309          * string. It is provided to the locator.
310          *
311          * @param data the data for the new locator
312          * @param clp the provider for class loaders
313          * @return the locator instance
314          */
315         public abstract Locator createLocator(String data,
316                 ClassLoaderProvider clp);
317     }
318 }