1 /*
2 * Zemucan: A Syntax Assistant for DB2
3 * Copyright (C) 2009, 2010 Andres Gomez Casanova
4 *
5 * This file is part of Zemucan.
6 *
7 * Zemucan is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Zemucan is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Contact:
21 * a n g o c a at y a h o o dot c o m
22 * Cra. 45 No 61 - 31, Bogota, Colombia.
23 *
24 * Author: $LastChangedBy: angoca $:
25 * Date: $LastChangedDate: 2011-03-07 00:34:33 -0500 (lun, 07 mar 2011) $:
26 * Revision: $LastChangedRevision: 1924 $:
27 * URL: $HeadURL: https://zemucan.svn.sourceforge.net/svnroot/zemucan/branches/zemucan_v1/source-code/tools/src/main/java/name/angoca/zemucan/tools/configurator/Configurator.java $:
28 */
29 package name.angoca.zemucan.tools.configurator;
30
31 import java.util.Enumeration;
32 import java.util.Properties;
33
34 import name.angoca.zemucan.tools.Constants;
35 import name.angoca.zemucan.tools.messages.Messages;
36
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41 * This class loads the configuration of the application. The implementation is
42 * with a singleton. The name of the configuration file is in a Constant
43 * (SA_CONF_XML_FILENAME = sa_conf.xml) This class is not thread safe because the
44 * singleton does not have a synchronized part.
45 * <p>
46 * This class defines all the default values in a set of constants; their names
47 * are in the Constants interface.
48 * <p>
49 * <b>Control Version</b>
50 * <p>
51 * <ul>
52 * <li>0.0.1 Class creation.</li>
53 * <li>0.1.0 Singleton and get method.</li>
54 * <li>0.1.1 Basic configuration and new values.</li>
55 * <li>0.2.0 Destroy instance and set property.</li>
56 * <li>0.2.1 Warn level when file problem.</li>
57 * <li>0.2.2 Use of constants.</li>
58 * <li>0.2.3 getInstance synchronized.</li>
59 * <li>0.3.0 Remove of circular dependencies.</li>
60 * <li>0.3.1 Include file name in log.</li>
61 * <li>0.3.2 Log the filename.</li>
62 * <li>1.0.0 Moved to version 1.</li>
63 * <li>1.0.1 zemucan conf file and property.</li>
64 * <li>1.1.0 Delimiters deleted.</li>
65 * <li>1.2.0 Exception hierarchy changed.</li>
66 * <li>1.3.0 Exit and prompt token, and publics.</li>
67 * <li>1.3.1 Log modified.</li>
68 * <li>1.3.2 Not synchronized, not thread safe.</li>
69 * <li>1.3.3 Strings externalized.</li>
70 * <li>1.3.4 Assert.</li>
71 * <li>1.3.5 Grammar reader.</li>
72 * <li>1.3.6 Extra tokens problem.</li>
73 * <li>1.3.7 Synchronization.</li>
74 * <li>1.4.0 Delete property, for tests.</li>
75 * <li>1.4.1 Override some parameters.</li>
76 * </ul>
77 *
78 * @author Andres Gomez Casanova <a
79 * href="mailto:a n g o c a at y a h o o dot c o m">(AngocA)</a>
80 * @version 1.4.1 2010-08-08
81 */
82 public final class Configurator {
83
84 /**
85 * Property value: Grammar file name.
86 */
87 static final String GRAMMAR_FILE_DESCRIPTORS_VALUE = "grammar"; //$NON-NLS-1$
88 /**
89 * Property value: Grammar reader name.
90 */
91 public static final String GRAMMAR_READER_NAME_VALUE = "name.angoca.zemucan.grammarReader.impl.xml.ImplementationXMLGrammarReader"; //$NON-NLS-1$
92 /**
93 * Singleton instance.
94 */
95 private static Configurator instance;
96 /**
97 * Logger.
98 */
99 private static final Logger LOGGER = LoggerFactory
100 .getLogger(Configurator.class);
101
102 /**
103 * Destroys the sole instance. Useful for testing purposes.
104 */
105 public static void destroyInstance() {
106 Configurator.LOGGER.debug("Destroying Configurator instance."); //$NON-NLS-1$
107
108 if (Configurator.instance != null) {
109 Configurator.instance.properties.clear();
110 Configurator.instance.properties = null;
111 Configurator.instance = null;
112 }
113 assert Configurator.instance == null;
114 }
115
116 /**
117 * This is the singleton implementation. This method returns the sole
118 * instance of this class. This is not thread safe, because this method does
119 * not have a synchronized part.
120 * <p>
121 * This method has a part where it is synchronized, however it is not thread
122 * safe because of the problem with the Single Pattern in Java
123 * (http://www.ibm.com/developerworks/java/library/j-dcl.html)
124 *
125 * @return The sole instance of the Configurator class.
126 */
127 public static Configurator/* ! */getInstance() {
128 if (Configurator.instance == null) {
129 Configurator.LOGGER.debug("Creating Configurator instance."); //$NON-NLS-1$
130 synchronized (Configurator.class) {
131 Configurator.instance = new Configurator();
132 }
133 }
134
135 assert Configurator.instance != null;
136 return Configurator.instance;
137 }
138
139 /**
140 * Set of properties.
141 */
142 private Properties properties;
143
144 /**
145 * Default constructor.
146 */
147 private Configurator() {
148 // Loads the XML properties file in a Properties structure.
149 // Tries to retrieve a filename in the system properties. It's for
150 // unit test.
151 final String fileName = System
152 .getProperty(Constants.ZEMUCAN_CONF_XML_PROPERTY);
153 try {
154 if ((fileName == null) || fileName.equals("")) { //$NON-NLS-1$
155 if (Configurator.LOGGER.isDebugEnabled()) {
156 Configurator.LOGGER.debug("Properties from: " //$NON-NLS-1$
157 + Constants.ZEMUCAN_CONF_XML_FILENAME);
158 }
159
160 this.loadXMLFile(Constants.ZEMUCAN_CONF_XML_FILENAME);
161
162 } else {
163 if (Configurator.LOGGER.isDebugEnabled()) {
164 Configurator.LOGGER
165 .debug("Properties from file: " + fileName); //$NON-NLS-1$
166 }
167 this.loadXMLFile(fileName);
168 }
169 } catch (final AbstractConfiguratorException e) {
170 Configurator.LOGGER.warn(Messages.getString("Configurator."//$NON-NLS-1$
171 + "CONF3-LoadingDefaultProperties"), //$NON-NLS-1$
172 fileName);
173 Configurator.LOGGER.debug("File's problem.", e); //$NON-NLS-1$
174
175 this.loadDefaults();
176 }
177
178 // Override some properties defined in the System
179 this.modifyLoadedProperties();
180
181 // If Debug level then show the properties.
182 if (Configurator.LOGGER.isDebugEnabled()) {
183 final Enumeration<Object> enumen = this.properties.keys();
184 while (enumen.hasMoreElements()) {
185 final String prop = (String) enumen.nextElement();
186 Configurator.LOGGER.debug("{} : {} ", prop.toString(), //$NON-NLS-1$
187 this.properties.getProperty(prop));
188 }
189 }
190 }
191
192 /**
193 * Removes a property from the list.
194 *
195 * @param key
196 * Property to delete.
197 */
198 public void deleteProperty(final String/* ! */key) {
199 assert key != null;
200
201 this.properties.remove(key);
202 }
203
204 /**
205 * Retrieves a property from the load properties.
206 *
207 * @param key
208 * Name of the property to load. A property cannot be empty nor
209 * null.
210 * @return Value of the property. Null if there is no value with that name.
211 */
212 public String /* ? */getProperty(final String/* ! */key) {
213 assert key != null;
214 assert !key.equals("");
215
216 Configurator.LOGGER.debug(
217 "Getting: {} - {}", key, this.properties.getProperty(key)); //$NON-NLS-1$
218 return this.properties.getProperty(key);
219 }
220
221 /**
222 * Defines a set of defaults properties.
223 */
224 private void loadDefaults() {
225 this.properties = new Properties();
226 this.properties.setProperty(Constants.GRAMMAR_READER_NAME_PROPERTY,
227 Configurator.GRAMMAR_READER_NAME_VALUE);
228 this.properties.setProperty(
229 Constants.GRAMMAR_FILE_DESCRIPTORS_PROPERTY,
230 Configurator.GRAMMAR_FILE_DESCRIPTORS_VALUE);
231 this.properties.setProperty(Constants.ABOUT_TOKEN_PROPERTY,
232 Constants.ABOUT_TOKEN_VALUE);
233 this.properties.setProperty(Constants.HELP_TOKEN_PROPERTY,
234 Constants.HELP_TOKEN_VALUE);
235 }
236
237 /**
238 * Loads the properties that are in a XML file.
239 *
240 * @param fileName
241 * Name of configuration file.
242 * @throws AbstractReaderException
243 * If there is a problem with the file (when finding or
244 * reading.)
245 */
246 private void loadXMLFile(final String/* ! */fileName)
247 throws AbstractConfiguratorException {
248 assert fileName != null;
249
250 this.properties = ConfigurationReader.readFile(fileName);
251 }
252
253 /**
254 * Modifies some already loaded values with specific values received from
255 * the System properties.
256 */
257 private void modifyLoadedProperties() {
258 final String fileDescriptors = System
259 .getProperty(Constants.GRAMMAR_FILE_DESCRIPTORS_PROPERTY);
260 if (fileDescriptors != null) {
261 this.properties.setProperty(
262 Constants.GRAMMAR_FILE_DESCRIPTORS_PROPERTY,
263 fileDescriptors);
264 }
265
266 }
267
268 /**
269 * Set a property after the properties file has been read. This is for
270 * testing purposes, because the configuration is not the same for all
271 * cases.
272 *
273 * @param key
274 * Name of the property. A key cannot be null nor empty.
275 * @param value
276 * Value of the property. A value cannot be null.
277 */
278 public void setProperty(final String /* ! */key, final String /* ! */value) {
279 assert key != null;
280 assert !key.equals("");
281 assert value != null;
282
283 this.properties.setProperty(key, value);
284 }
285 }