Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
peersimgrid release 1.0
[simgrid.git] / contrib / psg / src / peersim / config / ConfigContainer.java
1 /*
2  * Copyright (c) 2003-2005 The BISON Project
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  *
17  */
18
19 package peersim.config;
20
21 import java.lang.reflect.*;
22 import java.util.*;
23 import org.lsmp.djep.groupJep.*;
24
25 /**
26  * This class is the container for the configuration data used in
27  * {@link Configuration}; see that class for more information.
28  */
29 public class ConfigContainer
30 {
31
32 // =================== static fields =================================
33 // ===================================================================
34
35 /** Symbolic constant for no debug */
36 private static final int DEBUG_NO = 0;
37
38 /** Symbolic constant for regular debug */
39 private static final int DEBUG_REG = 1;
40
41 /** Symbolic constant for extended debug */
42 private static final int DEBUG_CONTEXT = 2;
43
44 //========================== fields =================================
45 //===================================================================
46
47 /**
48  * The properties object that stores all configuration information.
49  */
50 private Properties config;
51
52 /**
53  * Map associating string protocol names to the numeric protocol
54  * identifiers. The protocol names are understood without prefix.
55  */
56 private Map<String, Integer> protocols;
57
58 /**
59  * The maximum depth that can be reached when analyzing expressions. This
60  * value can be substituted by setting the configuration parameter
61  * PAR_MAXDEPTH.
62  */
63 private int maxdepth;
64
65 /** Debug level */
66 private int debugLevel;
67
68 /**
69  * If true, no exception is thrown. Instead, an error is printed and the
70  * Configuration tries to return a reasonable return value
71  */
72 private boolean check = false;
73
74 // =================== initialization ================================
75 // ===================================================================
76
77 public ConfigContainer(Properties config, boolean check)
78 {
79         this.config = config;
80         this.check = check;
81         maxdepth = getInt(Configuration.PAR_MAXDEPTH, Configuration.DEFAULT_MAXDEPTH);
82
83         // initialize protocol id-s
84         protocols = new HashMap<String, Integer>();
85         String[] prots = getNames(Configuration.PAR_PROT);// they're returned in correct order
86         for (int i = 0; i < prots.length; ++i) {
87                 protocols.put(prots[i].substring(Configuration.PAR_PROT.length() + 1), Integer.valueOf(i));
88         }
89         String debug = config.getProperty(Configuration.PAR_DEBUG);
90         if (Configuration.DEBUG_EXTENDED.equals(debug))
91                 debugLevel = DEBUG_CONTEXT;
92         else if (Configuration.DEBUG_FULL.equals(debug)) {
93                 Map<String, String> map = new TreeMap<String, String>();
94                 Enumeration e = config.propertyNames();
95                 while (e.hasMoreElements()) {
96                         String name = (String) e.nextElement();
97                         String value = config.getProperty(name);
98                         map.put(name, value);
99                 }
100                 Iterator i = map.keySet().iterator();
101                 while (i.hasNext()) {
102                         String name = (String) i.next();
103                         System.err.println("DEBUG " + name
104                                         + ("".equals(map.get(name)) ? "" : " = " + map.get(name)));
105                 }
106         } else if (debug != null) {
107                 debugLevel = DEBUG_REG;
108         } else {
109                 debugLevel = DEBUG_NO;
110         }
111 }
112
113 // =================== static public methods =========================
114 // ===================================================================
115
116 /**
117  * @return true if and only if name is a specified (existing) property.
118  */
119 public boolean contains(String name)
120 {
121         boolean ret = config.containsKey(name);
122         debug(name, "" + ret);
123         return ret;
124 }
125
126 // -------------------------------------------------------------------
127
128 /**
129  * Reads given configuration property. If not found, throws a
130  * {@link MissingParameterException}.
131  * @param name
132  *          Name of configuration property
133  * @param def
134  *          default value
135  */
136 public boolean getBoolean(String name, boolean def)
137 {
138         try {
139                 return getBool(name);
140         } catch (RuntimeException e) {
141                 manageDefault(name, def, e);
142                 return def;
143         }
144 }
145
146 // -------------------------------------------------------------------
147
148
149 /**
150  * Reads given property. If not found, or the value is empty string then
151  * throws a {@link MissingParameterException}. Empty string is not
152  * accepted as false due to the similar function of {@link #contains} which
153  * returns true in that case. True is returned if the lowercase value of
154  * the property is "true", otherwise false is returned.
155  * @param name
156  *          Name of configuration property
157  */
158 public boolean getBoolean(String name)
159 {
160         try {
161                 return getBool(name);
162         } catch (RuntimeException e) {
163                 manageException(name, e);
164                 return false;
165         }
166 }
167
168 //-------------------------------------------------------------------
169
170 /**
171  * The actual methods that implements getBoolean.
172  */
173 private boolean getBool(String name)
174 {
175         if (config.getProperty(name) == null) {
176                 throw new MissingParameterException(name);
177 //                              "\nPossibly incorrect property: " + getSimilarProperty(name));
178         }
179         if (config.getProperty(name).matches("\\p{Blank}*")) {
180                 throw new MissingParameterException(name,
181                                 "Blank value is not accepted when parsing Boolean.");
182         }
183         boolean ret = Boolean.valueOf(config.getProperty(name));
184         debug(name, "" + ret);
185         return ret;
186 }
187
188 // -------------------------------------------------------------------
189
190 /**
191  * Reads given configuration property. If not found, returns the default
192  * value.
193  * @param name
194  *          Name of configuration property
195  * @param def
196  *          default value
197  */
198 public int getInt(String name, int def)
199 {
200         try {
201                 Number ret = getVal(name, name, 0);
202                 debug(name, "" + ret);
203                 return ret.intValue();
204         } catch (RuntimeException e) {
205                 manageDefault(name, def, e);
206                 return def;
207         }
208 }
209
210 // -------------------------------------------------------------------
211
212 /**
213  * Reads given configuration property. If not found, throws a
214  * {@link MissingParameterException}.
215  * @param name
216  *          Name of configuration property
217  */
218 public int getInt(String name)
219 {
220         try {
221                 Number ret = getVal(name, name, 0);
222                 debug(name, "" + ret);
223                 return ret.intValue();
224         } catch (RuntimeException e) {
225                 manageException(name, e);
226                 return 0;
227         }
228 }
229
230 // -------------------------------------------------------------------
231
232 /**
233  * Reads given configuration property. If not found, returns the default
234  * value.
235  * @param name
236  *          Name of configuration property
237  * @param def
238  *          default value
239  */
240 public long getLong(String name, long def)
241 {
242         try {
243                 Number ret = getVal(name, name, 0);
244                 debug(name, "" + ret);
245                 return ret.longValue();
246         } catch (RuntimeException e) {
247                 manageDefault(name, def, e);
248                 return def;
249         }
250 }
251
252 // -------------------------------------------------------------------
253
254 /**
255  * Reads given configuration property. If not found, throws a
256  * {@link MissingParameterException}.
257  * @param name
258  *          Name of configuration property
259  */
260 public long getLong(String name)
261 {
262         try {
263                 Number ret = getVal(name, name, 0);
264                 debug(name, "" + ret);
265                 return ret.longValue();
266         } catch (RuntimeException e) {
267                 manageException(name, e);
268                         return 0;
269         }
270 }
271
272 // -------------------------------------------------------------------
273
274 /**
275  * Reads given configuration property. If not found, returns the default
276  * value.
277  * @param name
278  *          Name of configuration property
279  * @param def
280  *          default value
281  */
282 public double getDouble(String name, double def)
283 {
284         try {
285                 Number ret = getVal(name, name, 0);
286                 debug(name, "" + ret);
287                 return ret.doubleValue();
288         } catch (RuntimeException e) {
289                 manageDefault(name, def, e);
290                 return def;
291         }
292 }
293
294 // -------------------------------------------------------------------
295
296 /**
297  * Reads given configuration property. If not found, throws a
298  * MissingParameterException.
299  * @param name
300  *          Name of configuration property
301  */
302 public double getDouble(String name)
303 {
304         try {
305                 Number ret = getVal(name, name, 0);
306                 debug(name, "" + ret);
307                 return ret.doubleValue();
308         } catch (RuntimeException e) {
309                 manageException(name, e);
310                 return 0;
311         }
312 }
313
314 // -------------------------------------------------------------------
315
316 /**
317  * Read numeric property values, parsing expression if necessary.
318  * 
319  * @param initial
320  *          the property name that started this expression evaluation
321  * @param property
322  *          the current property name to be evaluated
323  * @param depth
324  *          the depth reached so far
325  * @return the evaluation of the expression associated to property
326  */
327 private Number getVal(String initial, String property, int depth)
328 {
329         if (depth > maxdepth) {
330                 throw new IllegalParameterException(initial,
331                                 "Probable recursive definition - exceeded maximum depth " + 
332                                 maxdepth);
333         }
334
335         String s = config.getProperty(property);
336         if (s == null || s.equals("")) {
337                 throw new MissingParameterException(property,
338                                 " when evaluating property " + initial);
339 //                                              + "\nPossibly incorrect property: " + getSimilarProperty(property));
340         }
341
342         GroupJep jep = new GroupJep(new Operators());
343         jep.setAllowUndeclared(true);
344
345         jep.parseExpression(s);
346         String[] symbols = getSymbols(jep);
347         for (int i = 0; i < symbols.length; i++) {
348                 Object d = getVal(initial, symbols[i], depth + 1);
349                 jep.addVariable(symbols[i], d);
350         }
351         Object ret = jep.getValueAsObject();
352         if (jep.hasError())
353                 System.err.println(jep.getErrorInfo());
354         return (Number) ret;
355 }
356
357 // -------------------------------------------------------------------
358
359 /**
360  * Returns an array of string, containing the symbols contained in the
361  * expression parsed by the specified JEP parser.
362  * @param jep
363  *          the java expression parser containing the list of variables
364  * @return an array of strings.
365  */
366 private String[] getSymbols(org.nfunk.jep.JEP jep)
367 {
368         Hashtable h = jep.getSymbolTable();
369         String[] ret = new String[h.size()];
370         Enumeration e = h.keys();
371         int i = 0;
372         while (e.hasMoreElements()) {
373                 ret[i++] = (String) e.nextElement();
374         }
375         return ret;
376 }
377
378 // -------------------------------------------------------------------
379
380 /**
381  * Reads given configuration property. If not found, returns the default
382  * value.
383  * @param name
384  *          Name of configuration property
385  * @param def
386  *          default value
387  */
388 public String getString(String name, String def)
389 {
390         try {
391                 return getStr(name);
392         } catch (RuntimeException e) {
393                 manageDefault(name, def, e);
394                 return def;
395         } 
396 }
397
398 // -------------------------------------------------------------------
399
400 /**
401  * Reads given configuration property. If not found, throws a
402  * MissingParameterException. Removes trailing whitespace characters.
403  * @param name
404  *          Name of configuration property
405  */
406 public String getString(String name)
407 {
408         try {
409                 return getStr(name);
410         } catch (RuntimeException e) {
411                 manageException(name, e);
412                 return "";
413         }
414 }
415
416 /**
417  * The actual method implementing getString().
418  */
419 private String getStr(String name)
420 {
421         String result = config.getProperty(name);
422         if (result == null) {
423                 throw new MissingParameterException(name);
424 //                              "\nPossibly incorrect property: " + getSimilarProperty(name));
425         }
426         debug(name, "" + result);
427
428         return result.trim();
429 }
430
431 // -------------------------------------------------------------------
432
433 /**
434  * Reads the given property from the configuration interpreting it as a
435  * protocol name. Returns the numeric protocol identifier of this protocol
436  * name. See the discussion of protocol name at {@link Configuration} for
437  * details on how this numeric id is calculated
438  * 
439  * @param name
440  *          Name of configuration property
441  * @return the numeric protocol identifier associated to the value of the
442  *         property
443  */
444 public int getPid(String name)
445 {
446         try {
447                 String protname = getStr(name);
448                 return lookupPid(protname);
449         } catch (RuntimeException e) {
450                 manageException(name, e);
451                 return 0;
452         }
453 }
454
455 // -------------------------------------------------------------------
456
457 /**
458  * Calls {@link #getPid(String)}, and returns the default if no property
459  * is defined with the given name.
460  * 
461  * @param name
462  *          Name of configuration property
463  * @param pid
464  *          the default protocol identifier
465  * @return the numeric protocol identifier associated to the value of the
466  *         property, or the default if not defined
467  */
468 public int getPid(String name, int pid)
469 {
470         try {
471                 String protname = getStr(name);
472                 return lookupPid(protname);
473         } catch (RuntimeException e) {
474                 manageDefault(name, pid, e);
475                 return pid;
476         }
477 }
478
479 // -------------------------------------------------------------------
480
481 /**
482  * Returns the numeric protocol identifier of the given protocol name.
483  * 
484  * @param protname
485  *          the protocol name.
486  * @return the numeric protocol identifier associated to the protocol name
487  */
488 public int lookupPid(String protname)
489 {
490         Integer ret = protocols.get(protname);
491         if (ret == null) {
492                 throw new MissingParameterException(Configuration.PAR_PROT + "." + protname);
493 //                              "\nPossibly incorrect property: "
494 //                              + getSimilarProperty(PAR_PROT + "." + protname));
495         }
496         return ret.intValue();
497 }
498
499 // -------------------------------------------------------------------
500
501 /**
502  * Returns the name of a protocol that has the given identifier.
503  * <p>
504  * Note that this is not a constant time operation in the number of
505  * protocols, although typically there are very few protocols defined.
506  * 
507  * @param pid
508  *          numeric protocol identifier.
509  * @return name of the protocol that has the given id. null if no protocols
510  *         have the given id.
511  */
512 public String lookupPid(int pid)
513 {
514
515         if (!protocols.containsValue(pid))
516                 return null;
517         for (Map.Entry<String, Integer> i : protocols.entrySet()) {
518                 if (i.getValue().intValue() == pid)
519                         return i.getKey();
520         }
521
522         // never reached but java needs it...
523         return null;
524 }
525
526 // -------------------------------------------------------------------
527
528 /**
529  * Reads given configuration property. If not found, throws a
530  * {@link MissingParameterException}. When creating the Class object, a
531  * few attempts are done to resolve the classname. See
532  * {@link Configuration} for details.
533  * @param name
534  *          Name of configuration property
535  */
536 public Class getClass(String name)
537 {
538         try {
539                 return getClazz(name);
540         } catch (RuntimeException e) {
541                 manageException(name, e);
542                 return null;
543         }
544 }
545
546 private Class getClazz(String name)
547 {
548         String classname = config.getProperty(name);
549         if (classname == null) {
550                 throw new MissingParameterException(name);
551 //                              "\nPossibly incorrect property: " + getSimilarProperty(name));
552         }
553         debug(name, classname);
554
555         Class c = null;
556
557         try {
558                 // Maybe classname is just a fully-qualified name
559                 c = Class.forName(classname);
560         } catch (ClassNotFoundException e) {
561         }
562         if (c == null) {
563                 // Maybe classname is a non-qualified name?
564                 String fullname = ClassFinder.getQualifiedName(classname);
565                 if (fullname != null) {
566                         try {
567                                 c = Class.forName(fullname);
568                         } catch (ClassNotFoundException e) {
569                         }
570                 }
571         }
572         if (c == null) {
573                 // Maybe there are multiple classes with the same
574                 // non-qualified name.
575                 String fullname = ClassFinder.getQualifiedName(classname);
576                 if (fullname != null) {
577                         String[] names = fullname.split(",");
578                         if (names.length > 1) {
579                                 for (int i = 0; i < names.length; i++) {
580                                         for (int j = i + 1; j < names.length; j++) {
581                                                 if (names[i].equals(names[j])) {
582                                                         throw new IllegalParameterException(name,
583                                                                         "The class " + names[i]
584                                                                 + " appears more than once in the classpath; please check"
585                                                                 + " your classpath to avoid duplications.");
586                                                 }
587                                         }
588                                 }
589                                 throw new IllegalParameterException(name,
590                                                 "The non-qualified class name " + classname
591                                                                 + "corresponds to multiple fully-qualified classes:" + fullname);
592                         }
593                 }
594         }
595         if (c == null) {
596                 // Last attempt: maybe the fully classified name is wrong,
597                 // but the classname is correct.
598                 String shortname = ClassFinder.getShortName(classname);
599                 String fullname = ClassFinder.getQualifiedName(shortname);
600                 if (fullname != null) {
601                         throw new IllegalParameterException(name, "Class "
602                                         + classname + " does not exist. Possible candidate(s): " + fullname);
603                 }
604         }
605         if (c == null) {
606                 throw new IllegalParameterException(name, "Class "
607                                 + classname + " not found");
608         }
609         return c;
610 }
611
612 // -------------------------------------------------------------------
613
614 /**
615  * Reads given configuration property. If not found, returns the default
616  * value.
617  * @param name
618  *          Name of configuration property
619  * @param def
620  *          default value
621  * @see #getClass(String)
622  */
623 public Class getClass(String name, Class def)
624 {
625
626         try {
627                 return Configuration.getClass(name);
628         } catch (RuntimeException e) {
629                 manageDefault(name, def, e);
630                 return def;
631         }
632 }
633
634 // -------------------------------------------------------------------
635
636 /**
637  * Reads given configuration property for a class name. It returns an
638  * instance of the class. The class must implement a constructor that takes
639  * a String as an argument. The value of this string will be <tt>name</tt>.
640  * The constructor of the class can see the configuration so it can make
641  * use of this name to read its own parameters from it.
642  * @param name
643  *          Name of configuration property
644  * @throws MissingParameterException
645  *           if the given property is not defined
646  * @throws IllegalParameterException
647  *           if there is any problem creating the instance
648  */
649 public Object getInstance(String name)
650 {
651   try {
652         return getInst(name);
653   } catch (RuntimeException e) {
654                 manageException(name, e);
655                 return null;
656   }
657 }
658
659 /**
660  * The actual method implementing getInstance().
661  */
662 private Object getInst(String name)
663 {
664         Class c = getClass(name);
665         if (c == null)
666                 return null;
667         final String classname = c.getSimpleName();
668
669         try {
670                 Class pars[] = {String.class};
671                 Constructor cons = c.getConstructor(pars);
672                 Object objpars[] = {name};
673                 return cons.newInstance(objpars);
674         } catch (NoSuchMethodException e) {
675                 throw new IllegalParameterException(name, "Class "
676                                 + classname + " has no " + classname + "(String) constructor");
677         } catch (InvocationTargetException e) {
678                 if (e.getTargetException() instanceof RuntimeException) {
679                         throw (RuntimeException) e.getTargetException();
680                 } else {
681                         e.getTargetException().printStackTrace();
682                         throw new RuntimeException("" + e.getTargetException());
683                 }
684         } catch (Exception e) {
685                 throw new IllegalParameterException(name, e + "");
686         }
687 }
688
689 // -------------------------------------------------------------------
690
691 /**
692  * Reads given configuration property for a class name. It returns an
693  * instance of the class. The class must implement a constructor that takes
694  * a String as an argument. The value of this string will be <tt>name</tt>.
695  * The constructor of the class can see the configuration so it can make
696  * use of this name to read its own parameters from it.
697  * @param name
698  *          Name of configuration property
699  * @param def
700  *          The default object that is returned if there is no property
701  *          defined with the given name
702  * @throws IllegalParameterException
703  *           if the given name is defined but there is a problem creating
704  *           the instance.
705  */
706 public Object getInstance(String name, Object def)
707 {
708   if (!contains(name)) 
709         return def;
710         try {
711                 return getInst(name);
712         } catch (RuntimeException e) {
713                 manageException(name, e);
714                 return def;
715         }
716 }
717
718 // -------------------------------------------------------------------
719
720 /**
721  * It returns an array of class instances. The instances are constructed by
722  * calling {@link #getInstance(String)} on the names returned by
723  * {@link #getNames(String)}.
724  * @param name
725  *          The component type (i.e. prefix of the list of configuration
726  *          properties) which will be passed to {@link #getNames(String)}.
727  */
728 public Object[] getInstanceArray(String name)
729 {
730
731         String names[] = getNames(name);
732         Object[] result = new Object[names.length];
733
734         for (int i = 0; i < names.length; ++i) {
735                 result[i] = getInstance(names[i]);
736         }
737
738         return result;
739 }
740
741 // -------------------------------------------------------------------
742
743 /**
744  * Returns an array of names prefixed by the specified name. The array is
745  * sorted as follows. If there is no config entry
746  * <code>{@value peersim.config.Configuration#PAR_INCLUDE}+"."+name</code> or
747  * <code>{@value peersim.config.Configuration#PAR_ORDER}+"."+name</code> then the order is
748  * alphabetical. Otherwise this entry defines the order. For more
749  * information see {@link Configuration}.
750  * @param name
751  *          the component type (i.e., the prefix)
752  * @return the full property names in the order specified by the
753  *         configuration
754  */
755 public String[] getNames(String name)
756 {
757         ArrayList<String> ll = new ArrayList<String>();
758         final String pref = name + ".";
759
760         Enumeration e = config.propertyNames();
761         while (e.hasMoreElements()) {
762                 String key = (String) e.nextElement();
763                 if (key.startsWith(pref) && key.indexOf(".", pref.length()) < 0)
764                         ll.add(key);
765         }
766         String[] ret = ll.toArray(new String[ll.size()]);
767         return order(ret, name);
768 }
769
770 // -------------------------------------------------------------------
771
772 /**
773  * The input of this method is a set of property <code>names</code> (e.g.
774  * initializers, controls and protocols) and a string specifying the type
775  * (prefix) of these. The output is in <code>names</code>, which will
776  * contain a permutation of the original array. Parameter
777  * PAR_INCLUDE+"."+type, or if not present, PAR_ORDER+"."+type is read from
778  * the configuration. If none of them are defined then the order is
779  * identical to that of <code>names</code>. Otherwise the configuration
780  * entry must contain entries from <code>names</code>. It is assumed
781  * that the entries in <code>names</code> contain only word characters
782  * (alphanumeric and underscore '_'. The order configuration entry thus
783  * contains a list of entries from <code>names</code> separated by any
784  * non-word characters.
785  * <p>
786  * It is not required that all entries are listed. If PAR_INCLUDE is used,
787  * then only those entries are returned that are listed. If PAR_ORDER is
788  * used, then all names are returned, but the array will start with those
789  * that are listed. The rest of the names follow in alphabetical order.
790  * 
791  * 
792  * @param names
793  *          the set of property names to be searched
794  * @param type
795  *          the string identifying the particular set of properties to be
796  *          inspected
797  */
798 private String[] order(String[] names, String type)
799 {
800         String order = getString(Configuration.PAR_INCLUDE + "." + type, null);
801         boolean include = order != null;
802         if (!include)
803                 order = getString(Configuration.PAR_ORDER + "." + type, null);
804
805         int i = 0;
806         if (order != null && !order.equals("")) {
807                 // split around non-word characters
808                 String[] sret = order.split("\\W+");
809                 for (; i < sret.length; i++) {
810                         int j = i;
811                         for (; j < names.length; ++j)
812                                 if (names[j].equals(type + "." + sret[i]))
813                                         break;
814                         if (j == names.length) {
815                                 throw new IllegalParameterException(
816                                                 (include ? Configuration.PAR_INCLUDE : Configuration.PAR_ORDER)
817                                                 + "." + type, type + "." + sret[i] + " is not defined.");
818                         } else // swap the element to current position
819                         {
820                                 String tmps = names[j];
821                                 names[j] = names[i];
822                                 names[i] = tmps;
823                         }
824                 }
825         }
826
827         Arrays.sort(names, i, names.length);
828         int retsize = (include ? i : names.length);
829         String[] ret = new String[retsize];
830         for (int j = 0; j < retsize; ++j)
831                 ret[j] = names[j];
832         return ret;
833 }
834
835 // -------------------------------------------------------------------
836
837 /**
838  * Print debug information for configuration. The amount of information
839  * depends on the debug level DEBUG. 0 = nothing 1 = just the config name 2 =
840  * config name plus method calling
841  * 
842  * @param name
843  */
844 private void debug(String name, String result)
845 {
846         if (debugLevel == DEBUG_NO)
847                 return;
848         StringBuffer buffer = new StringBuffer();
849         buffer.append("DEBUG ");
850         buffer.append(name);
851         buffer.append(" = ");
852         buffer.append(result);
853
854         // Additional info
855         if (debugLevel == DEBUG_CONTEXT) {
856
857                 buffer.append("\n  at ");
858                 // Obtain the stack trace
859                 StackTraceElement[] stack = null;
860                 try {
861                         throw new Exception();
862                 } catch (Exception e) {
863                         stack = e.getStackTrace();
864                 }
865
866                 // Search the element that invoked Configuration
867                 // It's the first whose class is different from Configuration
868                 int pos;
869                 for (pos = 0; pos < stack.length; pos++) {
870                         if (!stack[pos].getClassName().equals(Configuration.class.getName()))
871                                 break;
872                 }
873
874                 buffer.append(stack[pos].getClassName());
875                 buffer.append(":");
876                 buffer.append(stack[pos].getLineNumber());
877                 buffer.append(", method ");
878                 buffer.append(stack[pos - 1].getMethodName());
879                 buffer.append("()");
880         }
881
882         System.err.println(buffer);
883 }
884
885 // -------------------------------------------------------------------
886
887 /**
888  * @return an array of adjacent letter pairs contained in the input string
889  *         http://www.catalysoft.com/articles/StrikeAMatch.html
890  */
891 private String[] letterPairs(String str)
892 {
893         int numPairs = str.length() - 1;
894         String[] pairs = new String[numPairs];
895         for (int i = 0; i < numPairs; i++) {
896                 pairs[i] = str.substring(i, i + 2);
897         }
898         return pairs;
899 }
900
901 // -------------------------------------------------------------------
902
903 /**
904  * @return an ArrayList of 2-character Strings.
905  *         http://www.catalysoft.com/articles/StrikeAMatch.html
906  */
907 private ArrayList<String> wordLetterPairs(String str)
908 {
909         ArrayList<String> allPairs = new ArrayList<String>();
910         // Tokenize the string and put the tokens/words into an array
911         String[] words = str.split("\\s");
912         // For each word
913         for (int w = 0; w < words.length; w++) {
914                 // Find the pairs of characters
915                 String[] pairsInWord = letterPairs(words[w]);
916                 for (int p = 0; p < pairsInWord.length; p++) {
917                         allPairs.add(pairsInWord[p]);
918                 }
919         }
920         return allPairs;
921 }
922
923 // -------------------------------------------------------------------
924
925 /**
926  * @return lexical similarity value in the range [0,1]
927  *         http://www.catalysoft.com/articles/StrikeAMatch.html
928  */
929 private double compareStrings(String str1, String str2)
930 {
931         ArrayList pairs1 = wordLetterPairs(str1.toUpperCase());
932         ArrayList pairs2 = wordLetterPairs(str2.toUpperCase());
933         int intersection = 0;
934         int union_ = pairs1.size() + pairs2.size();
935         for (int i = 0; i < pairs1.size(); i++) {
936                 Object pair1 = pairs1.get(i);
937                 for (int j = 0; j < pairs2.size(); j++) {
938                         Object pair2 = pairs2.get(j);
939                         if (pair1.equals(pair2)) {
940                                 intersection++;
941                                 pairs2.remove(j);
942                                 break;
943                         }
944                 }
945         }
946         return (2.0 * intersection) / union_;
947 }
948
949 // -------------------------------------------------------------------
950
951 /**
952  * Among the defined properties, returns the one more similar to String
953  * property
954  */
955 private String getSimilarProperty(String property)
956 {
957         String bestProperty = null;
958         double bestValue = 0.0;
959         Enumeration e = config.keys();
960         while (e.hasMoreElements()) {
961                 String key = (String) e.nextElement();
962                 double compare = compareStrings(key, property);
963                 if (compare > bestValue) {
964                         bestValue = compare;
965                         bestProperty = key;
966                 }
967         }
968         return bestProperty;
969 }
970
971 //-------------------------------------------------------------------
972
973 private void manageDefault(String name, Object def, 
974                 RuntimeException e)
975 {
976         debug(name, "" + def + " (DEFAULT)");
977         if (check) {
978                 System.out.println("Warning: Property " + name + " = " + 
979                                 def + " (DEFAULT)");
980         }
981         if (e instanceof MissingParameterException) {
982                 // Do nothing
983         } else {
984                 manageException(name, e);
985         }
986 }
987
988 //-------------------------------------------------------------------
989
990 private void manageException(String name, RuntimeException e)
991 {
992         if (check) {
993                 if (e instanceof MissingParameterException) {
994                         // Print just the short message in this case
995                         System.out.println("Error: " + 
996                                         ((MissingParameterException) e).getShortMessage());
997                 } else if (e instanceof IllegalParameterException) {
998                         // Print just the short message in this case
999                         System.out.println("Error: " + 
1000                                         ((IllegalParameterException) e).getShortMessage());
1001                 } else {
1002                         System.out.println("Error: " + e.getMessage());
1003                 }
1004         } else {
1005                 throw e;
1006         }
1007 }
1008
1009 //-------------------------------------------------------------------
1010
1011 }