View Javadoc

1   /*
2    * Copyright 2008 COMMSEN International
3    *
4    * This file is part of APropOS.
5    * 
6    * APropOS is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser 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   * APropOS 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 Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public License
17   * along with APropOS.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package com.commsen.apropos.core;
20  
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.LinkedHashMap;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  
29  import org.apache.commons.lang.StringUtils;
30  
31  /***
32   * This class represents a package of properties. In APropOS a package is simply a container of
33   * {@link Property} objects. A package has name and may also have description, parent and child
34   * packages.
35   * 
36   * @author Milen Dyankov
37   * 
38   */
39  
40  public class PropertyPackage implements Cloneable {
41  
42  	/***
43  	 * the name of the package
44  	 */
45  	private String name;
46  
47  	/***
48  	 * the description of the package
49  	 */
50  	private String description;
51  
52  	/***
53  	 * reference to the parent
54  	 */
55  	private PropertyPackage parent = null;
56  
57  	/***
58  	 * properties
59  	 */
60  	private Map<String, Property> properties = new LinkedHashMap<String, Property>();
61  
62  	/***
63  	 * list of child packages
64  	 */
65  	private List<PropertyPackage> children = new LinkedList<PropertyPackage>();
66  
67  
68  	/***
69  	 * Constructs new package
70  	 * 
71  	 * @param name the name, this is required field
72  	 * @param description the description
73  	 * @param parent the parent package, if not provided it will become first level package
74  	 * @throws IllegalArgumentException if name is <code>null</code> or blank
75  	 */
76  	public PropertyPackage(String name, String description, PropertyPackage parent) {
77  		if (StringUtils.isBlank(name)) throw new IllegalArgumentException("name can not be null");
78  		this.name = name.trim();
79  		this.description = description;
80  		if (parent != null) {
81  			this.parent = parent;
82  			parent.children.add(this);
83  		}
84  	}
85  
86  
87  	/***
88  	 * Constructs new first level package (without parent)
89  	 * 
90  	 * @param name the name, this is required field
91  	 * @param description the description
92  	 */
93  	public PropertyPackage(String name, String description) {
94  		this(name, description, null);
95  	}
96  
97  
98  	/***
99  	 * Constructs new package
100 	 * 
101 	 * @param name the name, this is required field
102 	 * @param parent the parent package, if not provided it will become first level package
103 	 */
104 	public PropertyPackage(String name, PropertyPackage parent) {
105 		this(name, null, parent);
106 	}
107 
108 
109 	/***
110 	 * Constructs new package
111 	 * 
112 	 * @param name the name, this is required field
113 	 */
114 	public PropertyPackage(String name) {
115 		this(name, null, null);
116 	}
117 
118 
119 	/***
120 	 * {@inheritDoc}
121 	 */
122 	@Override
123 	public Object clone() throws CloneNotSupportedException {
124 		PropertyPackage result = (PropertyPackage) super.clone();
125 		result.properties = new LinkedHashMap<String, Property>();
126 		if (!properties.isEmpty()) {
127 			for (Property property : properties.values()) {
128 				result.properties.put(property.getName(), (Property) property.clone());
129 			}
130 		}
131 		return result;
132 	}
133 
134 
135 	/***
136 	 * Adds new property to this package.
137 	 * 
138 	 * @param property the {@link Property} to be added. This is a required parameter
139 	 * @return reference do the added property
140 	 * @throws PropertiesException if property with such name already exists
141 	 * @throws IllegalArgumentException if <code>property</code> is null
142 	 */
143 	public Property addProperty(Property property) throws PropertiesException {
144 		if (property == null) throw new IllegalArgumentException("property can not be null");
145 		if (properties.containsKey(property.getName())) throw new PropertiesException("Property called " + property.getName() + " already exists!");
146 		return properties.put(property.getName(), property);
147 	}
148 
149 
150 	/***
151 	 * Updates a property in this package.
152 	 * 
153 	 * @param property the {@link Property} to be updated. This is a required parameter
154 	 * @return reference do the added property
155 	 * @throws PropertiesException if no such property exists
156 	 * @throws IllegalArgumentException if <code>property</code> is null
157 	 */
158 	public Property updateProperty(Property property) throws PropertiesException {
159 		if (property == null) throw new IllegalArgumentException("property can not be null");
160 		if (!properties.containsKey(property.getName())) throw new PropertiesException("Property called " + property.getName() + " does not exists!");
161 		return properties.put(property.getName(), property);
162 	}
163 
164 
165 	/***
166 	 * Updates a property called <code>oldName</code> in this package with values from
167 	 * <code>property</code>. If <code>oldName</code> is null this method behaves exactly like
168 	 * {@link #updateProperty(Property)}
169 	 * 
170 	 * @param oldName the name of the property to be updated
171 	 * @param property the property to get values from
172 	 * @return reference do the added property
173 	 * @throws PropertiesException if no such property exists
174 	 * @throws IllegalArgumentException if <code>property</code> is null
175 	 */
176 	public Property updateProperty(String oldName, Property property) throws PropertiesException {
177 		if (StringUtils.isBlank(oldName)) return updateProperty(property);
178 		if (property == null) throw new IllegalArgumentException("property can not be null");
179 		if (!properties.containsKey(oldName)) throw new PropertiesException("Property called " + oldName + " does not exists!");
180 		if (!oldName.equals(property.getName())) {
181 			properties.remove(oldName);
182 		}
183 		return properties.put(property.getName(), property);
184 	}
185 
186 
187 	/***
188 	 * Removes property from this package
189 	 * 
190 	 * @param property the property to be removed
191 	 * @return reference to removed property
192 	 */
193 	public Property removeProperty(Property property) {
194 		if (property == null) throw new IllegalArgumentException("property can not be null");
195 		return properties.remove(property.getName());
196 	}
197 
198 
199 	/***
200 	 * Removes property called <code>propertyName</code> from this package
201 	 * 
202 	 * @param propertyName the name of the property to be removed
203 	 * @return reference to removed property
204 	 */
205 	public Property removeProperty(String propertyName) {
206 		if (propertyName == null) throw new IllegalArgumentException("property name can not be null or empty string");
207 		return properties.remove(propertyName);
208 	}
209 
210 
211 	/***
212 	 * Reads external {@link Properties} and adds them all to this package.
213 	 * 
214 	 * @param externalProperties the external properties to be added
215 	 * @param overwrite boolean flag indicating whether to overwrite existing properties
216 	 * @throws IllegalArgumentException if <code>externalProperties</code> is null
217 	 */
218 	public void importProperties(Properties externalProperties, boolean overwrite) {
219 		if (externalProperties == null) throw new IllegalArgumentException("properties can not be null");
220 		for (Object key : externalProperties.keySet()) {
221 			String propertyName = (String) key;
222 			if (!overwrite && this.properties.containsKey(propertyName)) continue;
223 			try {
224 				this.properties.put(propertyName, new Property(propertyName, externalProperties.getProperty(propertyName), null, null));
225 			} catch (PropertiesException e) {
226 				/*
227 				 * this should never happen since the key in Properties can not be null and that is
228 				 * the only case where new Property() throws PropertiesException
229 				 */
230 				throw new InternalError(e.getMessage());
231 			}
232 		}
233 	}
234 
235 
236 	/***
237 	 * Converts this package to {@link Properties} object. The result object contains all properties
238 	 * from all parents as returned by {@link #getAllProperties()}.
239 	 * 
240 	 * @return {@link Properties} object
241 	 */
242 	public Properties asProperties() {
243 		Properties result = new Properties();
244 		for (Property property : getAllProperties().values()) {
245 			result.setProperty(property.getName(), property.getValue());
246 		}
247 		return result;
248 	}
249 
250 
251 	/***
252 	 * Returns a map of all properties in this package which overwrite same properties from parent
253 	 * packages.
254 	 * 
255 	 * @return the properties a map of all properties in this package which overwrite same
256 	 *         properties from parent packages.
257 	 */
258 	public Map<String, Property> getOverwritenProperties() {
259 		Map<String, Property> parentProperties = new HashMap<String, Property>();
260 		if (parent != null) parentProperties.putAll(parent.getAllProperties());
261 
262 		Map<String, Property> result = new HashMap<String, Property>();
263 		for (String propertyName : properties.keySet()) {
264 			if (parentProperties.containsKey(propertyName)) {
265 				result.put(propertyName, parentProperties.get(propertyName));
266 			}
267 		}
268 		return result;
269 	}
270 
271 
272 	/***
273 	 * Returns a map of all properties from this package and all parent packages.
274 	 * 
275 	 * @return a map of all properties from this package and all parent packages.
276 	 */
277 	public Map<String, Property> getAllProperties() {
278 		Map<String, Property> result = new HashMap<String, Property>();
279 		if (parent != null) result.putAll(parent.getAllProperties());
280 		result.putAll(properties);
281 		return result;
282 	}
283 
284 
285 	/***
286 	 * Checks if <code>propertyPackage</code> is a child package of this package.
287 	 * 
288 	 * @param propertyPackage 0 the package to be checked
289 	 * @return <code>true</code> if <code>propertyPackage</code> is a child package of this
290 	 *         package, <code>false</code> otherwise.
291 	 */
292 	public boolean containsChild(PropertyPackage propertyPackage) {
293 		if (propertyPackage == null) return false;
294 		if (children.contains(propertyPackage)) return true;
295 		for (PropertyPackage child : children) {
296 			if (child.containsChild(propertyPackage)) return true;
297 		}
298 		return false;
299 	}
300 
301 
302 	/***
303 	 * @return the properties
304 	 */
305 	public Map<String, Property> getProperties() {
306 		return properties;
307 	}
308 
309 
310 	/***
311 	 * @return the parent
312 	 */
313 	public PropertyPackage getParent() {
314 		return this.parent;
315 	}
316 
317 
318 	/***
319 	 * @param parent the parent to set
320 	 * @throws PropertiesException
321 	 */
322 	public void setParent(PropertyPackage parent) throws PropertiesException {
323 		if (this.parent == parent) return;
324 		if (containsChild(parent)) {
325 			throw new PropertiesException("Package " + parent.getName() + " is child of " + name + " and can not be set as it's parent!");
326 		}
327 		if (this.parent != null) {
328 			this.parent.children.remove(this);
329 		}
330 		this.parent = parent;
331 		if (this.parent != null) {
332 			this.parent.children.add(this);
333 		}
334 	}
335 
336 
337 	/***
338 	 * @return the description
339 	 */
340 	public String getDescription() {
341 		return this.description;
342 	}
343 
344 
345 	/***
346 	 * @param description the description to set
347 	 */
348 	public void setDescription(String description) {
349 		this.description = description;
350 	}
351 
352 
353 	/***
354 	 * @return the name
355 	 */
356 	public String getName() {
357 		return this.name;
358 	}
359 
360 
361 	/***
362 	 * @return the children
363 	 */
364 	public List<PropertyPackage> getChildren() {
365 		return Collections.unmodifiableList(this.children);
366 	}
367 
368 }