001package org.cpsolver.ifs.util;
002
003import java.util.Enumeration;
004import java.util.HashMap;
005import java.util.Map;
006import java.util.Properties;
007import java.util.StringTokenizer;
008
009/**
010 * Data properties.
011 * 
012 * @version IFS 1.3 (Iterative Forward Search)<br>
013 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
014 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
015 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
016 * <br>
017 *          This library is free software; you can redistribute it and/or modify
018 *          it under the terms of the GNU Lesser General Public License as
019 *          published by the Free Software Foundation; either version 3 of the
020 *          License, or (at your option) any later version. <br>
021 * <br>
022 *          This library is distributed in the hope that it will be useful, but
023 *          WITHOUT ANY WARRANTY; without even the implied warranty of
024 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
025 *          Lesser General Public License for more details. <br>
026 * <br>
027 *          You should have received a copy of the GNU Lesser General Public
028 *          License along with this library; if not see
029 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
030 */
031public class DataProperties extends Properties {
032    private boolean iSaveDefaults = false;
033    private static final long serialVersionUID = 1L;
034
035    /** Constructor */
036    public DataProperties() {
037        super();
038    }
039
040    /**
041     * Constructor
042     * 
043     * @param defaults
044     *            default properties
045     */
046    public DataProperties(Properties defaults) {
047        super(defaults);
048        iSaveDefaults = getPropertyBoolean("General.SaveDefaultProperties", false);
049    }
050
051    /**
052     * Constructor
053     * 
054     * @param properties
055     *            default properties
056     */
057    public DataProperties(Map<String, String> properties) {
058        super();
059        for (Map.Entry<String, String> entry : properties.entrySet()) {
060            setProperty(entry.getKey(), entry.getValue());
061        }
062        iSaveDefaults = getPropertyBoolean("General.SaveDefaultProperties", false);
063    }
064
065    /**
066     * Returns string property
067     * 
068     * @param key
069     *            key
070     * @param defaultValue
071     *            default value to be returned when such property is not present
072     */
073    @Override
074    public String getProperty(String key, String defaultValue) {
075        if (!iSaveDefaults || containsPropery(key))
076            return super.getProperty(key, defaultValue);
077        if (defaultValue != null)
078            setProperty(key, defaultValue);
079        return defaultValue;
080    }
081
082    /**
083     * Sets string property
084     * 
085     * @param key
086     *            key
087     * @param value
088     *            value
089     */
090    @Override
091    public Object setProperty(String key, String value) {
092        if (value == null) {
093            Object ret = getProperty(key);
094            remove(key);
095            return ret;
096        }
097        Object ret = super.setProperty(key, value);
098        if ("General.SaveDefaultProperties".equals(key))
099            iSaveDefaults = getPropertyBoolean("General.SaveDefaultProperties", false);
100        return ret;
101    }
102
103    /**
104     * Returns int property
105     * 
106     * @param key
107     *            key
108     * @param defaultValue
109     *            default value to be returned when such property is not present
110     * @return value of the property
111     */
112    public int getPropertyInt(String key, int defaultValue) {
113        try {
114            if (containsPropery(key))
115                return Integer.parseInt(getProperty(key));
116            if (iSaveDefaults)
117                setProperty(key, String.valueOf(defaultValue));
118            return defaultValue;
119        } catch (NumberFormatException nfe) {
120            if (iSaveDefaults)
121                setProperty(key, String.valueOf(defaultValue));
122            return defaultValue;
123        }
124    }
125
126    /**
127     * Returns long property
128     * 
129     * @param key
130     *            key
131     * @param defaultValue
132     *            default value to be returned when such property is not present
133     * @return value of the property
134     */
135    public long getPropertyLong(String key, long defaultValue) {
136        try {
137            if (containsPropery(key))
138                return Long.parseLong(getProperty(key));
139            if (iSaveDefaults)
140                setProperty(key, String.valueOf(defaultValue));
141            return defaultValue;
142        } catch (NumberFormatException nfe) {
143            if (iSaveDefaults)
144                setProperty(key, String.valueOf(defaultValue));
145            return defaultValue;
146        }
147    }
148
149    /**
150     * Returns int property
151     * 
152     * @param key
153     *            key
154     * @param defaultValue
155     *            default value to be returned when such property is not present
156     * @return value of the property
157     */
158    public Integer getPropertyInteger(String key, Integer defaultValue) {
159        try {
160            if (containsPropery(key))
161                return Integer.valueOf(getProperty(key));
162            if (iSaveDefaults && defaultValue != null)
163                setProperty(key, String.valueOf(defaultValue));
164            return defaultValue;
165        } catch (NumberFormatException nfe) {
166            if (iSaveDefaults && defaultValue != null)
167                setProperty(key, String.valueOf(defaultValue));
168            return defaultValue;
169        }
170    }
171
172    /**
173     * Returns long property
174     * 
175     * @param key
176     *            key
177     * @param defaultValue
178     *            default value to be returned when such property is not present
179     * @return value of the property
180     */
181    public Long getPropertyLong(String key, Long defaultValue) {
182        try {
183            if (containsPropery(key))
184                return Long.valueOf(getProperty(key));
185            if (iSaveDefaults && defaultValue != null)
186                setProperty(key, String.valueOf(defaultValue));
187            return defaultValue;
188        } catch (NumberFormatException nfe) {
189            if (iSaveDefaults && defaultValue != null)
190                setProperty(key, String.valueOf(defaultValue));
191            return defaultValue;
192        }
193    }
194
195    /**
196     * Returns true if there is such property
197     * 
198     * @param key
199     *            key
200     * @return true if there is such property
201     */
202    public boolean containsPropery(String key) {
203        return getProperty(key) != null;
204    }
205
206    /**
207     * Returns boolean property
208     * 
209     * @param key
210     *            key
211     * @param defaultValue
212     *            default value to be returned when such property is not present
213     * @return value of the property
214     */
215    public boolean getPropertyBoolean(String key, boolean defaultValue) {
216        try {
217            if (containsPropery(key))
218                return (getProperty(key).equalsIgnoreCase("on") || getProperty(key).equalsIgnoreCase("true"));
219            if (iSaveDefaults)
220                setProperty(key, (defaultValue ? "true" : "false"));
221            return defaultValue;
222        } catch (Exception nfe) {
223            if (iSaveDefaults)
224                setProperty(key, (defaultValue ? "true" : "false"));
225            return defaultValue;
226        }
227    }
228
229    /**
230     * Returns double property
231     * 
232     * @param key
233     *            key
234     * @param defaultValue
235     *            default value to be returned when such property is not present
236     * @return value of the property
237     */
238    public double getPropertyDouble(String key, double defaultValue) {
239        try {
240            if (containsPropery(key))
241                return Double.parseDouble(getProperty(key));
242            if (iSaveDefaults)
243                setProperty(key, String.valueOf(defaultValue));
244            return defaultValue;
245        } catch (NumberFormatException nfe) {
246            if (iSaveDefaults)
247                setProperty(key, String.valueOf(defaultValue));
248            return defaultValue;
249        }
250    }
251
252    /**
253     * Returns float property
254     * 
255     * @param key
256     *            key
257     * @param defaultValue
258     *            default value to be returned when such property is not present
259     * @return value of the property
260     */
261    public float getPropertyFloat(String key, float defaultValue) {
262        try {
263            if (containsPropery(key))
264                return Float.parseFloat(getProperty(key));
265            if (iSaveDefaults)
266                setProperty(key, String.valueOf(defaultValue));
267            return defaultValue;
268        } catch (NumberFormatException nfe) {
269            if (iSaveDefaults)
270                setProperty(key, String.valueOf(defaultValue));
271            return defaultValue;
272        }
273    }
274
275    /**
276     * Returns boolean property
277     * 
278     * @param key
279     *            key
280     * @param defaultValue
281     *            default value to be returned when such property is not present
282     * @return value of the property
283     */
284    public Boolean getPropertyBoolean(String key, Boolean defaultValue) {
285        try {
286            if (containsPropery(key))
287                return new Boolean(getProperty(key).equalsIgnoreCase("on") || getProperty(key).equalsIgnoreCase("true"));
288            if (iSaveDefaults && defaultValue != null)
289                setProperty(key, (defaultValue.booleanValue() ? "true" : "false"));
290            return defaultValue;
291        } catch (Exception nfe) {
292            if (iSaveDefaults && defaultValue != null)
293                setProperty(key, (defaultValue.booleanValue() ? "true" : "false"));
294            return defaultValue;
295        }
296    }
297
298    /**
299     * Returns double property
300     * 
301     * @param key
302     *            key
303     * @param defaultValue
304     *            default value to be returned when such property is not present
305     * @return value of the property
306     */
307    public Double getPropertyDouble(String key, Double defaultValue) {
308        try {
309            if (containsPropery(key))
310                return Double.valueOf(getProperty(key));
311            if (iSaveDefaults && defaultValue != null)
312                setProperty(key, String.valueOf(defaultValue));
313            return defaultValue;
314        } catch (NumberFormatException nfe) {
315            if (iSaveDefaults && defaultValue != null)
316                setProperty(key, String.valueOf(defaultValue));
317            return defaultValue;
318        }
319    }
320
321    /**
322     * Returns float property
323     * 
324     * @param key
325     *            key
326     * @param defaultValue
327     *            default value to be returned when such property is not present
328     * @return value of the property
329     */
330    public Float getPropertyFloat(String key, Float defaultValue) {
331        try {
332            if (containsPropery(key))
333                return Float.valueOf(getProperty(key));
334            if (iSaveDefaults && defaultValue != null)
335                setProperty(key, String.valueOf(defaultValue));
336            return defaultValue;
337        } catch (NumberFormatException nfe) {
338            if (iSaveDefaults && defaultValue != null)
339                setProperty(key, String.valueOf(defaultValue));
340            return defaultValue;
341        }
342    }
343
344    public void setProperty(String key, Object[] value) {
345        StringBuffer sb = new StringBuffer();
346        for (int i = 0; i < value.length; i++) {
347            if (i > 0)
348                sb.append(",");
349            sb.append(value[i] == null ? "null" : value[i].toString());
350        }
351        setProperty(key, sb.toString());
352    }
353
354    public Long[] getPropertyLongArry(String key, Long[] defaultValue) {
355        try {
356            if (containsPropery(key)) {
357                StringTokenizer stk = new StringTokenizer(getProperty(key), ",");
358                Long ret[] = new Long[stk.countTokens()];
359                for (int i = 0; stk.hasMoreTokens(); i++) {
360                    String t = stk.nextToken();
361                    ret[i] = ("null".equals(t) ? null : Long.valueOf(t));
362                }
363                return ret;
364            }
365            if (iSaveDefaults && defaultValue != null)
366                setProperty(key, defaultValue);
367            return defaultValue;
368        } catch (NumberFormatException nfe) {
369            if (iSaveDefaults && defaultValue != null)
370                setProperty(key, defaultValue);
371            return defaultValue;
372        }
373    }
374
375    public Integer[] getPropertyIntegerArry(String key, Integer[] defaultValue) {
376        try {
377            if (containsPropery(key)) {
378                StringTokenizer stk = new StringTokenizer(getProperty(key), ",");
379                Integer ret[] = new Integer[stk.countTokens()];
380                for (int i = 0; stk.hasMoreTokens(); i++) {
381                    String t = stk.nextToken();
382                    ret[i] = ("null".equals(t) ? null : Integer.valueOf(t));
383                }
384                return ret;
385            }
386            if (iSaveDefaults && defaultValue != null)
387                setProperty(key, defaultValue);
388            return defaultValue;
389        } catch (NumberFormatException nfe) {
390            if (iSaveDefaults && defaultValue != null)
391                setProperty(key, defaultValue);
392            return defaultValue;
393        }
394    }
395    
396    public Double[] getPropertyDoubleArry(String key, Double[] defaultValue) {
397        try {
398            if (containsPropery(key)) {
399                StringTokenizer stk = new StringTokenizer(getProperty(key), ",");
400                Double ret[] = new Double[stk.countTokens()];
401                for (int i = 0; stk.hasMoreTokens(); i++) {
402                    String t = stk.nextToken();
403                    ret[i] = ("null".equals(t) ? null : Double.valueOf(t));
404                }
405                return ret;
406            }
407            if (iSaveDefaults && defaultValue != null)
408                setProperty(key, defaultValue);
409            return defaultValue;
410        } catch (NumberFormatException nfe) {
411            if (iSaveDefaults && defaultValue != null)
412                setProperty(key, defaultValue);
413            return defaultValue;
414        }
415    }
416
417    /**
418     * Returns properties as a map
419     * @return properties as a map
420     */
421    public Map<String, String> toMap() {
422        HashMap<String, String> ret = new HashMap<String, String>();
423        for (Enumeration<?> e = propertyNames(); e.hasMoreElements();) {
424            String key = (String) e.nextElement();
425            String prop = getProperty(key);
426            if (key != null && prop != null)
427                ret.put(key, prop);
428        }
429        return ret;
430    }
431
432    private void expand(String key) {
433        String value = getProperty(key);
434        if (value == null)
435            return;
436        int done = -1, idx = -1;
437        while ((idx = value.indexOf('%', done + 1)) >= 0) {
438            int idx2 = value.indexOf('%', idx + 1);
439            if (idx2 < 0)
440                return;
441            String subString = value.substring(idx + 1, idx2);
442            if (containsPropery(subString))
443                value = value.substring(0, idx) + getProperty(subString) + value.substring(idx2 + 1);
444            else
445                done = idx;
446        }
447        setProperty(key, value);
448    }
449
450    public void expand() {
451        for (Enumeration<?> e = keys(); e.hasMoreElements();) {
452            expand((String) e.nextElement());
453        }
454    }
455
456    /** Loads properties from an input stream */
457    @Override
458    public void load(java.io.InputStream inputStream) throws java.io.IOException {
459        super.load(inputStream);
460        expand();
461        iSaveDefaults = getPropertyBoolean("General.SaveDefaultProperties", false);
462    }
463}