1/*
2 * Copyright 2003-2009 OFFIS, Henri Tremblay
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 */
16package org.easymock.internal;
17
18import java.io.BufferedInputStream;
19import java.io.IOException;
20import java.io.InputStream;
21import java.util.Map;
22import java.util.Properties;
23
24/**
25 * Contains properties used by EasyMock to change its default behavior. The
26 * loading order is (any step being able to overload the properties of the
27 * previous step):
28 * <ul>
29 * <li>easymock.properties in classpath default package</li>
30 * <li>System properties</li>
31 * <li>explicit call to setProperty</li>
32 * </ul>
33 */
34public final class EasyMockProperties {
35
36    private static final String PREFIX = "easymock.";
37
38    // volatile for double-checked locking
39    private static volatile EasyMockProperties instance;
40
41    private final Properties properties = new Properties();
42
43    public static EasyMockProperties getInstance() {
44        if (instance == null) {
45            synchronized (EasyMockProperties.class) {
46                // ///CLOVER:OFF
47                if (instance == null) {
48                    // ///CLOVER:ON
49                    instance = new EasyMockProperties();
50                }
51            }
52        }
53        return instance;
54    }
55
56    private EasyMockProperties() {
57        // Load the easymock.properties file
58        InputStream in = getClassLoader().getResourceAsStream(
59                "easymock.properties");
60        if (in != null) {
61            in = new BufferedInputStream(in);
62            try {
63                properties.load(in);
64            } catch (IOException e) {
65                throw new RuntimeException(
66                        "Failed to read easymock.properties file");
67            } finally {
68                try {
69                    in.close();
70                } catch (IOException e) {
71                    // Doesn't matter
72                }
73            }
74        }
75        // Then overload it with system properties
76        for (Map.Entry<Object, Object> entry : ((Properties)System.getProperties()
77                 .clone()).entrySet()) {
78            if (entry.getKey() instanceof String
79                    && entry.getKey().toString().startsWith(PREFIX)) {
80                properties.put(entry.getKey(), entry.getValue());
81            }
82        }
83    }
84
85    /**
86     * Searches for the property with the specified key. If the key is not
87     * found, return the default value.
88     *
89     * @param key
90     *            key leading to the property
91     * @param defaultValue
92     *            the value to be returned if the key isn't found
93     * @return the value found for the key or the default value
94     */
95    public String getProperty(String key, String defaultValue) {
96        return properties.getProperty(key, defaultValue);
97    }
98
99    /**
100     * Searches for the property with the specified key. Return null if the key
101     * is not found.
102     *
103     * @param key
104     *            key leading to the property
105     * @return the value found for the key or null
106     */
107    public String getProperty(String key) {
108        return properties.getProperty(key);
109    }
110
111    /**
112     * Add a value referenced by the provided key. A null value will remove the
113     * key
114     *
115     * @param key
116     *            the key of the new property
117     * @param value
118     *            the value corresponding to <tt>key</tt>.
119     * @return the property previous value
120     */
121    public String setProperty(String key, String value) {
122        if (!key.startsWith(PREFIX)) {
123            throw new IllegalArgumentException("Invalid key (" + key
124                    + "), an easymock property starts with \"" + PREFIX + "\"");
125        }
126        if (value == null) {
127            return (String) properties.remove(key);
128        }
129        return (String) properties.setProperty(key, value);
130    }
131
132    private ClassLoader getClassLoader() {
133        ClassLoader cl = null;
134        try {
135            cl = Thread.currentThread().getContextClassLoader();
136        } catch (Throwable ex) {
137            // Cannot access thread context ClassLoader - falling back to system
138            // class loader
139        }
140        if (cl == null) {
141            // No thread context class loader -> use class loader of this class.
142            cl = getClass().getClassLoader();
143        }
144        return cl;
145    }
146}
147