• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2  *
3  * This program and the accompanying materials are made available under
4  * the terms of the Common Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
6  *
7  * $Id: IProperties.java,v 1.1.1.1.2.1 2004/07/08 10:52:10 vlad_r Exp $
8  */
9 package com.vladium.util;
10 
11 import java.io.PrintStream;
12 import java.io.PrintWriter;
13 import java.util.ArrayList;
14 import java.util.Enumeration;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Properties;
19 import java.util.Set;
20 import java.util.TreeSet;
21 
22 // ----------------------------------------------------------------------------
23 /**
24  * @author Vlad Roubtsov, (C) 2003
25  */
26 public
27 interface IProperties
28 {
29     // public: ................................................................
30 
31     /**
32      * An IMapper is a stateless hook for mapping a arbitrary property key
33      * to another (useful, for example, for property aliasing and defaulting).
34      * Each IMapper must be completely stateless and could be shared between
35      * multiple IProperties instances (and invoked from multiple concurrent threads).
36      */
37     interface IMapper
38     {
getMappedKey(final String key)39         String getMappedKey (final String key);
40 
41     } // end of nested interface
42 
43 
getProperty(String key)44     String getProperty (String key);
getProperty(String key, String dflt)45     String getProperty (String key, String dflt);
isOverridden(String key)46     boolean isOverridden (String key);
47 
copy()48     IProperties copy ();
properties()49     Iterator /* String */ properties ();
toProperties()50     Properties toProperties ();
51     /**
52      * @param prefix [may not be null]
53      */
toAppArgsForm(final String prefix)54     String [] toAppArgsForm (final String prefix);
isEmpty()55     boolean isEmpty ();
list(PrintStream out)56     void list (PrintStream out);
list(PrintWriter out)57     void list (PrintWriter out);
58 
setProperty(String key, String value)59     String setProperty (String key, String value);
60 
61 
62     abstract class Factory
63     {
64         /**
65          * Creates an empty IProperties set with an optional property mapper.
66          *
67          * @param mapper [may be null]
68          * @return an empty property set [never null]
69          */
create(final IMapper mapper)70         public static IProperties create (final IMapper mapper)
71         {
72             return new PropertiesImpl (null, mapper);
73         }
74 
75         /**
76          * Converts a java.util.Properties instance to an IProperties instance,
77          * with an optional property mapper. Note that 'properties' content is
78          * shallowly cloned, so the result can be mutated independently from
79          * the input.
80          *
81          * @param properties [may not be null]
82          * @param mapper [may be null]
83          * @return a property set based on 'properties' [never null]
84          */
wrap(final Properties properties, final IMapper mapper)85         public static IProperties wrap (final Properties properties, final IMapper mapper)
86         {
87             final HashMap map = new HashMap ();
88 
89             // always use propertyNames() for traversing java.util.Properties:
90 
91             for (Enumeration names = properties.propertyNames (); names.hasMoreElements (); )
92             {
93                 final String n = (String) names.nextElement ();
94                 final String v = properties.getProperty (n);
95 
96                 map.put (n, v);
97             }
98 
99             return new PropertiesImpl (map, mapper); // note: map is a defensive clone
100         }
101 
102         /**
103          * Combines two property sets by creating a property set that chains 'overrides'
104          * to 'base' for property delegation. Note that 'overrides' are cloned
105          * defensively and so the result can be mutated independently of both inputs.
106          *
107          * @param overrides [may be null]
108          * @param base [may be null]
109          * @return [never null; an empty property set with a null mapper is created
110          * if both inputs are null]
111          */
combine(final IProperties overrides, final IProperties base)112         public static IProperties combine (final IProperties overrides, final IProperties base)
113         {
114             final IProperties result = overrides != null
115                 ? overrides.copy ()
116                 : create (null);
117 
118             // [assertion: result != null]
119 
120             ((PropertiesImpl) result).getLastProperties ().setDelegate ((PropertiesImpl) base);
121 
122             return result;
123         }
124 
125         /*
126          * Not MT-safe
127          */
128         private static final class PropertiesImpl implements IProperties, Cloneable
129         {
130             // ACCESSORS (IProperties):
131 
getProperty(final String key)132             public String getProperty (final String key)
133             {
134                 return getProperty (key, null);
135             }
136 
getProperty(final String key, final String dflt)137             public String getProperty (final String key, final String dflt)
138             {
139                 String value = (String) m_valueMap.get (key);
140 
141                 // first, try to delegate horizontally:
142                 if ((value == null) && (m_mapper != null))
143                 {
144                     final String mappedKey = m_mapper.getMappedKey (key);
145 
146                     if (mappedKey != null)
147                         value = (String) m_valueMap.get (mappedKey);
148                 }
149 
150                 // next, try to delegate vertically:
151                 if ((value == null) && (m_delegate != null))
152                 {
153                     value = m_delegate.getProperty (key, null);
154                 }
155 
156                 return value != null ? value : dflt;
157             }
158 
isOverridden(final String key)159             public boolean isOverridden (final String key)
160             {
161                 return m_valueMap.containsKey (key);
162             }
163 
copy()164             public IProperties copy ()
165             {
166                 final PropertiesImpl _clone;
167                 try
168                 {
169                     _clone = (PropertiesImpl) super.clone ();
170                 }
171                 catch (CloneNotSupportedException cnse)
172                 {
173                     throw new Error (cnse.toString ()); // never happens
174                 }
175 
176                 _clone.m_valueMap = (HashMap) m_valueMap.clone ();
177                 _clone.m_unmappedKeySet = null;
178 
179                 // note: m_mapper field is not cloned by design (mappers are stateless/shareable)
180 
181                 PropertiesImpl scan = _clone;
182                 for (PropertiesImpl delegate = m_delegate; delegate != null; delegate = delegate.m_delegate)
183                 {
184                     final PropertiesImpl _delegateClone;
185                     try
186                     {
187                         _delegateClone = (PropertiesImpl) delegate.clone ();
188                     }
189                     catch (CloneNotSupportedException cnse)
190                     {
191                         throw new Error (cnse.toString ()); // never happens
192                     }
193 
194                     // note that the value map needs to be cloned not only for the
195                     // outermost IProperties set but for the inner ones as well
196                     // (to prevent independent mutation in them from showing through)
197 
198                     _delegateClone.m_valueMap = (HashMap) delegate.m_valueMap.clone ();
199                     _delegateClone.m_unmappedKeySet = null; // ensure that the last delegate will have this field reset
200 
201                     scan.setDelegate (_delegateClone);
202                     scan = _delegateClone;
203                 }
204 
205                 return _clone;
206             }
207 
properties()208             public Iterator /* String */ properties ()
209             {
210                 return unmappedKeySet ().iterator ();
211             }
212 
toProperties()213             public Properties toProperties ()
214             {
215                 final Properties result = new Properties ();
216 
217                 for (Iterator i = properties (); i.hasNext (); )
218                 {
219                     final String n = (String) i.next ();
220                     final String v = getProperty (n);
221 
222                     result.setProperty (n, v);
223                 }
224 
225                 return result;
226             }
227 
isEmpty()228             public boolean isEmpty ()
229             {
230                 return m_valueMap.isEmpty () && ((m_delegate == null) || ((m_delegate != null) && m_delegate.isEmpty ()));
231             }
232 
toAppArgsForm(final String prefix)233             public String [] toAppArgsForm (final String prefix)
234             {
235                 if (isEmpty ())
236                     return IConstants.EMPTY_STRING_ARRAY;
237                 else
238                 {
239                     if (prefix == null)
240                         throw new IllegalArgumentException ("null input: prefix");
241 
242                     final List _result = new ArrayList ();
243                     for (Iterator names = properties (); names.hasNext (); )
244                     {
245                         final String name = (String) names.next ();
246                         final String value = getProperty (name, "");
247 
248                         _result.add (prefix.concat (name).concat ("=").concat (value));
249                     }
250 
251                     final String [] result = new String [_result.size ()];
252                     _result.toArray (result);
253 
254                     return result;
255                 }
256             }
257 
list(final PrintStream out)258             public void list (final PrintStream out)
259             {
260                 if (out != null)
261                 {
262                     for (Iterator i = properties (); i.hasNext (); )
263                     {
264                         final String n = (String) i.next ();
265                         final String v = getProperty (n);
266 
267                         out.println (n + ":\t[" + v + "]");
268                     }
269                 }
270             }
271 
list(final PrintWriter out)272             public void list (final PrintWriter out)
273             {
274                 if (out != null)
275                 {
276                     for (Iterator i = properties (); i.hasNext (); )
277                     {
278                         final String n = (String) i.next ();
279                         final String v = getProperty (n);
280 
281                         out.println (n + ":\t[" + v + "]");
282                     }
283                 }
284             }
285 
286             // MUTATORS:
287 
setProperty(final String key, final String value)288             public String setProperty (final String key, final String value)
289             {
290                 if (value == null)
291                     throw new IllegalArgumentException ("null input: value");
292 
293                 m_unmappedKeySet = null;
294 
295                 return (String) m_valueMap.put (key, value);
296             }
297 
298 
PropertiesImpl(final HashMap values, final IMapper mapper)299             PropertiesImpl (final HashMap values, final IMapper mapper)
300             {
301                 m_mapper = mapper;
302                 m_valueMap = values != null ? values : new HashMap ();
303 
304                 m_delegate = null;
305             }
306 
307 
unmappedKeySet()308             Set unmappedKeySet ()
309             {
310                 Set result = m_unmappedKeySet;
311                 if (result == null)
312                 {
313                     result = new TreeSet ();
314                     result.addAll (m_valueMap.keySet ());
315                     if (m_delegate != null)
316                         result.addAll (m_delegate.unmappedKeySet ());
317 
318                     m_unmappedKeySet = result;
319                     return result;
320                 }
321 
322                 return result;
323             }
324 
325             // ACCESSORS:
326 
getLastProperties()327             PropertiesImpl getLastProperties ()
328             {
329                 PropertiesImpl result = this;
330 
331                 for (PropertiesImpl delegate = m_delegate; delegate != null; delegate = delegate.m_delegate)
332                 {
333                     // this does not detect all possible cycles:
334                     if (delegate == this)
335                         throw new IllegalStateException ("cyclic delegation detected");
336 
337                     result = delegate;
338                 }
339 
340                 return result;
341             }
342 
343             // MUTATORS:
344 
setDelegate(final PropertiesImpl delegate)345             void setDelegate (final PropertiesImpl delegate)
346             {
347                 m_delegate = delegate;
348 
349                 m_unmappedKeySet = null;
350             }
351 
352 
353             private final IMapper m_mapper;
354             private /*final*/ HashMap m_valueMap; // never null
355 
356             private PropertiesImpl m_delegate;
357             private transient Set m_unmappedKeySet;
358 
359         } // end of nested class
360 
361     } // end of nested class
362 
363     // protected: .............................................................
364 
365     // package: ...............................................................
366 
367     // private: ...............................................................
368 
369 } // end of class
370 // ----------------------------------------------------------------------------