001 /*
002 * jDTAUS Core API
003 * Copyright (c) 2005 Christian Schulte
004 *
005 * Christian Schulte, Haldener Strasse 72, 58095 Hagen, Germany
006 * <schulte2005@users.sourceforge.net> (+49 2331 3543887)
007 *
008 * This library is free software; you can redistribute it and/or
009 * modify it under the terms of the GNU Lesser General Public
010 * License as published by the Free Software Foundation; either
011 * version 2.1 of the License, or any later version.
012 *
013 * This library is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016 * Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public
019 * License along with this library; if not, write to the Free Software
020 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
021 *
022 */
023 package org.jdtaus.core.container;
024
025 import java.io.Serializable;
026 import java.util.Arrays;
027 import java.util.Collection;
028 import java.util.HashMap;
029 import java.util.Map;
030
031 /**
032 * Collection of properties.
033 *
034 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
035 * @version $Id: Properties.java 8044 2009-07-02 01:29:05Z schulte2005 $
036 */
037 public class Properties extends ModelObject implements Cloneable, Serializable
038 {
039 //--Constants---------------------------------------------------------------
040
041 /** Serial version UID for backwards compatibility with 1.0.x classes. */
042 private static final long serialVersionUID = 3703581195509652826L;
043
044 //---------------------------------------------------------------Constants--
045 //--Properties--------------------------------------------------------------
046
047 /**
048 * The properties held by the instance.
049 * @serial
050 */
051 private Property[] properties;
052
053 /**
054 * Maps property names to properties.
055 * @serial
056 */
057 private final Map names = new HashMap();
058
059 /**
060 * Hash code.
061 * @serial
062 */
063 private int hashCode;
064
065 /**
066 * Gets the properties of the collection.
067 *
068 * @return the properties of the collection.
069 */
070 public Property[] getProperties()
071 {
072 if ( this.properties == null )
073 {
074 this.properties = new Property[ 0 ];
075 this.hashCode = 0;
076 }
077
078 return this.properties;
079 }
080
081 /**
082 * Setter for property {@code properties}.
083 *
084 * @param value the new collection of properties.
085 *
086 * @throws DuplicatePropertyException if {@code value} contains duplicate
087 * properties.
088 */
089 public void setProperties( final Property[] value )
090 {
091 this.names.clear();
092 this.hashCode = 0;
093 this.properties = null;
094
095 if ( value != null )
096 {
097 for ( int i = value.length - 1; i >= 0; i-- )
098 {
099 this.hashCode += value[i].hashCode();
100 if ( this.names.put( value[i].getName(), value[i] ) != null )
101 {
102 this.names.clear();
103 this.hashCode = 0;
104
105 throw new DuplicatePropertyException( value[i].getName() );
106 }
107 }
108
109 this.properties = value;
110 }
111 }
112
113 /**
114 * Gets a property for a name.
115 *
116 * @param name the name of the property to return.
117 *
118 * @return a reference to the property named {@code name}.
119 *
120 * @throws NullPointerException if {@code name} is {@code null}.
121 * @throws MissingPropertyException if no property matching {@code name}
122 * exists in the collection.
123 */
124 public Property getProperty( final String name )
125 {
126 if ( name == null )
127 {
128 throw new NullPointerException( "name" );
129 }
130
131 final Property ret = (Property) this.names.get( name );
132
133 if ( ret == null )
134 {
135 throw new MissingPropertyException( name );
136 }
137
138 return ret;
139 }
140
141 /**
142 * Gets a property for an index.
143 *
144 * @param index the index of the property to return.
145 *
146 * @return a reference to the property at {@code index}.
147 *
148 * @throws IndexOutOfBoundsException if {@code index} is negativ,
149 * greater than or equal to {@code size()}.
150 */
151 public final Property getProperty( final int index )
152 {
153 if ( index < 0 || index >= this.size() )
154 {
155 throw new ArrayIndexOutOfBoundsException( index );
156 }
157
158 return this.getProperties()[index];
159 }
160
161 /**
162 * Setter for a named property from property {@code properties}.
163 *
164 * @param property the property to update or to add to property
165 * {@code properties}.
166 *
167 * @return previous value of the property named {@code property.getName()}
168 * or {@code null} if no property with name {@code property.getName()}
169 * existed before.
170 *
171 * @throws NullPointerException if {@code property} is {@code null}.
172 * @throws IllegalArgumentException if a property with the same name but
173 * different type exists.
174 */
175 public Property setProperty( final Property property )
176 {
177 if ( property == null )
178 {
179 throw new NullPointerException( "property" );
180 }
181
182 Property ret = null;
183
184 try
185 {
186 final Property p = this.getProperty( property.getName() );
187 if ( !p.getType().equals( property.getType() ) )
188 {
189 throw new IllegalArgumentException( p.getType().getName() );
190 }
191 else
192 {
193 ret = (Property) p.clone();
194 this.hashCode -= p.hashCode();
195 p.setValue( property.getValue() );
196 this.hashCode += p.hashCode();
197 }
198
199 }
200 catch ( MissingPropertyException e )
201 {
202 final Collection props = Arrays.asList( this.getProperties() );
203 props.add( property );
204 this.setProperties( (Property[]) props.toArray(
205 new Property[ props.size() ] ) );
206
207 }
208
209 return ret;
210 }
211
212 /**
213 * Gets the number of properties held by the instance.
214 *
215 * @return the number of properties held by the instance.
216 */
217 public final int size()
218 {
219 return this.getProperties().length;
220 }
221
222 /**
223 * Creates a string representing the properties of the instance.
224 *
225 * @return a string representing the properties of the instance.
226 */
227 private String internalString()
228 {
229 final StringBuffer buf = new StringBuffer( 200 ).append( '{' );
230 buf.append( this.internalString( this ) );
231
232 final Property[] props = this.getProperties();
233 for ( int i = props.length - 1; i >= 0; i-- )
234 {
235 buf.append( ", [" ).append( i ).append( "]=" ).append( props[i] );
236 }
237
238 buf.append( '}' );
239 return buf.toString();
240 }
241
242 //--------------------------------------------------------------Properties--
243 //--Object------------------------------------------------------------------
244
245 /**
246 * Indicates whether some other object is equal to this one by comparing
247 * the values of all properties.
248 *
249 * @param o the reference object with which to compare.
250 *
251 * @return {@code true} if this object is the same as {@code o};
252 * {@code false} otherwise.
253 */
254 public boolean equals( final Object o )
255 {
256 boolean equal = this == o;
257
258 if ( !equal && o instanceof Properties )
259 {
260 final Properties that = (Properties) o;
261 final Collection these = Arrays.asList( this.getProperties() );
262 final Collection those = Arrays.asList( that.getProperties() );
263
264 equal = this.size() == that.size() && these.containsAll( those );
265 }
266
267 return equal;
268 }
269
270 /**
271 * Returns a hash code value for this object.
272 *
273 * @return a hash code value for this object.
274 */
275 public int hashCode()
276 {
277 return this.hashCode;
278 }
279
280 /**
281 * Returns a string representation of the object.
282 *
283 * @return a string representation of the object.
284 */
285 public String toString()
286 {
287 return super.toString() + this.internalString();
288 }
289
290 /**
291 * Creates and returns a deep copy of this object.
292 *
293 * @return a clone of this instance.
294 */
295 public Object clone()
296 {
297 try
298 {
299 final Properties ret = (Properties) super.clone();
300 final Property[] props = this.getProperties();
301 final Property[] cloned = new Property[ props.length ];
302
303 for ( int i = props.length - 1; i >= 0; i-- )
304 {
305 cloned[i] = (Property) props[i].clone();
306 }
307
308 ret.setProperties( cloned );
309 return ret;
310 }
311 catch ( CloneNotSupportedException e )
312 {
313 throw new AssertionError( e );
314 }
315 }
316
317 //------------------------------------------------------------------Object--
318 }