1 /*
2 * jDTAUS Core API
3 * Copyright (C) 2005 Christian Schulte
4 * <cs@schulte.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or any later version.
10 *
11 * This library 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21 package org.jdtaus.core.container;
22
23 import java.io.Serializable;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.Map;
28
29 /**
30 * Collection of properties.
31 *
32 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
33 * @version $JDTAUS: Properties.java 8743 2012-10-07 03:06:20Z schulte $
34 */
35 public class Properties extends ModelObject implements Cloneable, Serializable
36 {
37 //--Constants---------------------------------------------------------------
38
39 /** Serial version UID for backwards compatibility with 1.0.x classes. */
40 private static final long serialVersionUID = 3703581195509652826L;
41
42 //---------------------------------------------------------------Constants--
43 //--Properties--------------------------------------------------------------
44
45 /**
46 * The properties held by the instance.
47 * @serial
48 */
49 private Property[] properties;
50
51 /**
52 * Maps property names to properties.
53 * @serial
54 */
55 private final Map names = new HashMap();
56
57 /**
58 * Hash code.
59 * @serial
60 */
61 private int hashCode;
62
63 /** Creates a new {@code Properties} instance. */
64 public Properties()
65 {
66 super();
67 }
68
69 /**
70 * Gets the properties of the collection.
71 *
72 * @return the properties of the collection.
73 */
74 public Property[] getProperties()
75 {
76 if ( this.properties == null )
77 {
78 this.properties = new Property[ 0 ];
79 this.hashCode = 0;
80 }
81
82 return this.properties;
83 }
84
85 /**
86 * Setter for property {@code properties}.
87 *
88 * @param value the new collection of properties.
89 *
90 * @throws DuplicatePropertyException if {@code value} contains duplicate
91 * properties.
92 */
93 public void setProperties( final Property[] value )
94 {
95 this.names.clear();
96 this.hashCode = 0;
97 this.properties = null;
98
99 if ( value != null )
100 {
101 for ( int i = value.length - 1; i >= 0; i-- )
102 {
103 this.hashCode += value[i].hashCode();
104 if ( this.names.put( value[i].getName(), value[i] ) != null )
105 {
106 this.names.clear();
107 this.hashCode = 0;
108
109 throw new DuplicatePropertyException( value[i].getName() );
110 }
111 }
112
113 this.properties = value;
114 }
115 }
116
117 /**
118 * Gets a property for a name.
119 *
120 * @param name the name of the property to return.
121 *
122 * @return a reference to the property named {@code name}.
123 *
124 * @throws NullPointerException if {@code name} is {@code null}.
125 * @throws MissingPropertyException if no property matching {@code name}
126 * exists in the collection.
127 */
128 public Property getProperty( final String name )
129 {
130 if ( name == null )
131 {
132 throw new NullPointerException( "name" );
133 }
134
135 final Property ret = (Property) this.names.get( name );
136
137 if ( ret == null )
138 {
139 throw new MissingPropertyException( name );
140 }
141
142 return ret;
143 }
144
145 /**
146 * Gets a property for an index.
147 *
148 * @param index the index of the property to return.
149 *
150 * @return a reference to the property at {@code index}.
151 *
152 * @throws IndexOutOfBoundsException if {@code index} is negativ,
153 * greater than or equal to {@code size()}.
154 */
155 public final Property getProperty( final int index )
156 {
157 if ( index < 0 || index >= this.size() )
158 {
159 throw new ArrayIndexOutOfBoundsException( index );
160 }
161
162 return this.getProperties()[index];
163 }
164
165 /**
166 * Setter for a named property from property {@code properties}.
167 *
168 * @param property the property to update or to add to property
169 * {@code properties}.
170 *
171 * @return previous value of the property named {@code property.getName()}
172 * or {@code null} if no property with name {@code property.getName()}
173 * existed before.
174 *
175 * @throws NullPointerException if {@code property} is {@code null}.
176 * @throws IllegalArgumentException if a property with the same name but
177 * different type exists.
178 */
179 public Property setProperty( final Property property )
180 {
181 if ( property == null )
182 {
183 throw new NullPointerException( "property" );
184 }
185
186 Property ret = null;
187
188 try
189 {
190 final Property p = this.getProperty( property.getName() );
191 if ( !p.getType().equals( property.getType() ) )
192 {
193 throw new IllegalArgumentException( p.getType().getName() );
194 }
195 else
196 {
197 ret = (Property) p.clone();
198 this.hashCode -= p.hashCode();
199 p.setValue( property.getValue() );
200 this.hashCode += p.hashCode();
201 }
202
203 }
204 catch ( final MissingPropertyException e )
205 {
206 final Collection props = Arrays.asList( this.getProperties() );
207 props.add( property );
208 this.setProperties( (Property[]) props.toArray(
209 new Property[ props.size() ] ) );
210
211 }
212
213 return ret;
214 }
215
216 /**
217 * Gets the number of properties held by the instance.
218 *
219 * @return the number of properties held by the instance.
220 */
221 public final int size()
222 {
223 return this.getProperties().length;
224 }
225
226 /**
227 * Creates a string representing the properties of the instance.
228 *
229 * @return a string representing the properties of the instance.
230 */
231 private String internalString()
232 {
233 final StringBuffer buf = new StringBuffer( 200 ).append( '{' );
234 buf.append( this.internalString( this ) );
235
236 final Property[] props = this.getProperties();
237 for ( int i = props.length - 1; i >= 0; i-- )
238 {
239 buf.append( ", [" ).append( i ).append( "]=" ).append( props[i] );
240 }
241
242 buf.append( '}' );
243 return buf.toString();
244 }
245
246 //--------------------------------------------------------------Properties--
247 //--Object------------------------------------------------------------------
248
249 /**
250 * Indicates whether some other object is equal to this one by comparing
251 * the values of all properties.
252 *
253 * @param o the reference object with which to compare.
254 *
255 * @return {@code true} if this object is the same as {@code o};
256 * {@code false} otherwise.
257 */
258 public boolean equals( final Object o )
259 {
260 boolean equal = this == o;
261
262 if ( !equal && o instanceof Properties )
263 {
264 final Properties that = (Properties) o;
265 final Collection these = Arrays.asList( this.getProperties() );
266 final Collection those = Arrays.asList( that.getProperties() );
267
268 equal = this.size() == that.size() && these.containsAll( those );
269 }
270
271 return equal;
272 }
273
274 /**
275 * Returns a hash code value for this object.
276 *
277 * @return a hash code value for this object.
278 */
279 public int hashCode()
280 {
281 return this.hashCode;
282 }
283
284 /**
285 * Returns a string representation of the object.
286 *
287 * @return a string representation of the object.
288 */
289 public String toString()
290 {
291 return super.toString() + this.internalString();
292 }
293
294 /**
295 * Creates and returns a deep copy of this object.
296 *
297 * @return a clone of this instance.
298 */
299 public Object clone()
300 {
301 try
302 {
303 final Properties ret = (Properties) super.clone();
304 final Property[] props = this.getProperties();
305 final Property[] cloned = new Property[ props.length ];
306
307 for ( int i = props.length - 1; i >= 0; i-- )
308 {
309 cloned[i] = (Property) props[i].clone();
310 }
311
312 ret.setProperties( cloned );
313 return ret;
314 }
315 catch ( final CloneNotSupportedException e )
316 {
317 throw new AssertionError( e );
318 }
319 }
320
321 //------------------------------------------------------------------Object--
322 }