1 /*
2 * Copyright (c) 2003-2008 by Cosylab d. d.
3 *
4 * This file is part of Java-Common.
5 *
6 * Java-Common is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Java-Common is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Java-Common. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package com.cosylab.util;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.util.Enumeration;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Properties;
31
32
33 /**
34 * DOCUMENT ME!
35 *
36 * @version $id$
37 */
38 public class ExtendedProperties extends Properties
39 {
40 /**
41 * Suffix used to speficy a linked properties file. The key
42 * up to the string is used as the root for all the properties found in
43 * the referenced file.
44 * <p>e.g.: <code>icon.@link = gui/icons.properties</code> will look
45 * for a file "gui/icons.properties" and a property named <code>text.edit</code>
46 * from this file will be referenced in the parent properties as "icon.text.edit".
47 * <p>
48 * If multiple links are assigned to the same prefix, they should be numbered
49 * so that their property keys remain unique, e.g.:<br/>
50 * <code>icon.@link1 = gui/myIcons.properties</code>
51 * <code>icon.@link2 = gui/otherIcons.properties</code>
52 * </p>
53 *
54 */
55 public static final String LINK_SUFFIX="@link";
56
57 /**
58 * The delimiter that is used to separate each part in the property name
59 * that makes the hierarchy. For example the property name 'windowX' under
60 * the property 'Panel1' becomes 'Panel1.windowX' and the dot between
61 * those names is the ExtendedProperties.DELIMITER
62 */
63 public static final String DELIMITER = ".";
64
65 /**
66 * Creates a new ExtendedProperties object.
67 */
68 public ExtendedProperties()
69 {
70 super();
71 }
72
73 /**
74 * Creates a new ExtendedProperties object.
75 *
76 * @param defaults DOCUMENT ME!
77 */
78 public ExtendedProperties(Properties defaults)
79 {
80 super(defaults);
81 }
82
83 /**
84 * Convenience method that tries to return int value of property.
85 *
86 * @param key Name of key to query value for
87 *
88 * @return int type value of property
89 *
90 * @throws NumberFormatException
91 *
92 * @see #getProperty(String)
93 */
94 public int getIntProperty(String key) throws NumberFormatException
95 {
96 String value = getProperty(key);
97
98 if (value == null) {
99 throw new NumberFormatException("Cannot create int from null.");
100 }
101
102 return Integer.parseInt(value);
103 }
104
105 /**
106 * Convenience method that tries to return double value of property.
107 *
108 * @param key Name of key to query value for
109 *
110 * @return double type value of property
111 *
112 * @throws NumberFormatException
113 *
114 * @see #getProperty(String)
115 */
116 public double getDoubleProperty(String key) throws NumberFormatException
117 {
118 String value = getProperty(key);
119
120 if (value == null) {
121 throw new NumberFormatException("Cannot create double from null.");
122 }
123
124 return Double.parseDouble(value);
125 }
126
127 /**
128 * Convenience method that tries to return <code>Class</code> instace
129 * specified by fully qualified string.
130 *
131 * @param key Name of key associated with value.
132 *
133 * @return Instance of <code>Class</code> or null if not found.
134 *
135 * @see #getProperty(String)
136 */
137 public Class getClassProperty(String key)
138 {
139 String value = getProperty(key);
140
141 if (value == null) {
142 return null;
143 }
144
145 Class c;
146
147 try {
148 c = Class.forName(value);
149 } catch (ClassNotFoundException e) {
150 return null;
151 }
152
153 return c;
154 }
155
156 /**
157 * Returns an instance of the ExtendedProperties object which have only
158 * properties under the hierarchy given by <code>mementoName</code>. For
159 * example if this object has 'window.x', 'window.y' and 'something else'
160 * properties then invoking getProperties("window") will return
161 * ExtendedProperties with anly 'x' and 'y' properties.
162 *
163 * @param mementoName name to extract from this properties
164 *
165 * @return an ExtendedProperties object with properties matching
166 * mementoName
167 */
168 public ExtendedProperties getProperties(String mementoName)
169 {
170 assert (mementoName != null);
171
172 // calculate susbstring length that we will skip on ech key
173 int stripLen = mementoName.length() + DELIMITER.length();
174 ExtendedProperties ep = new ExtendedProperties();
175
176 for (Enumeration e = keys(); e.hasMoreElements();) {
177 String key = (String)e.nextElement();
178
179 if (!key.startsWith(mementoName)) {
180 continue;
181 }
182
183 ep.put(key.substring(stripLen), get(key));
184 }
185
186 return ep;
187 }
188
189 /**
190 * Inserts the content of the given ExtendedProperties to this properties
191 * by prefixing each key with given <code>mementoName</code>.
192 *
193 * @param mementoName
194 * @param ep
195 */
196 public void setProperties(String mementoName, ExtendedProperties ep)
197 {
198 assert (ep != null);
199
200 String prefix=mementoName+DELIMITER;
201 if (mementoName==null || mementoName.length()<1) {
202 prefix="";
203 }
204
205 for (Enumeration e = ep.keys(); e.hasMoreElements();) {
206 String key = (String)e.nextElement();
207 String newKey = prefix + key;
208 put(newKey, ep.get(key));
209 }
210 }
211
212 /* (non-Javadoc)
213 * @see java.util.Properties#load(java.io.InputStream)
214 */
215 public synchronized void load(URL resource) throws IOException, MalformedURLException {
216 InputStream stream =resource.openStream();
217 super.load(stream);
218 checkForLinks(resource);
219 stream.close();
220 }
221
222 /**
223 * @deprecated Use @link #load(URL) to process links properly
224 */
225 public synchronized void load(InputStream inStream) throws IOException {
226 super.load(inStream);
227 }
228
229 public synchronized void checkForLinks(URL resource) throws IOException, MalformedURLException{
230 Map linksToAdd=null;
231 Iterator iter=entrySet().iterator();
232 while (iter.hasNext()) {
233 Map.Entry entry = (Map.Entry) iter.next();
234 String key=(String)entry.getKey();
235 int lastDelIdx=key.lastIndexOf(ExtendedProperties.DELIMITER);
236 String lastPart=key;
237 if (lastDelIdx>=0) {
238 lastPart=key.substring(lastDelIdx+1);
239 }
240 if (lastPart.startsWith(LINK_SUFFIX)) {
241 String prefix="";
242 if (lastDelIdx>0) {
243 prefix=key.substring(0, lastDelIdx);
244 }
245
246 String linkPath=(String)entry.getValue();
247 URL childURL=new URL(resource,linkPath);
248 ExtendedProperties childProps=new ExtendedProperties();
249 childProps.load(childURL);
250 if (linksToAdd==null) {
251 linksToAdd=new HashMap();
252 }
253 linksToAdd.put(prefix,childProps);
254 }
255 }
256 if (linksToAdd!=null) {
257 iter=linksToAdd.keySet().iterator();
258 while(iter.hasNext()) {
259 String prefix=(String)iter.next();
260 ExtendedProperties childProps=(ExtendedProperties)linksToAdd.get(prefix);
261 setProperties(prefix, childProps);
262 }
263 }
264 }
265 }
266
267 /* __oOo__ */