View Javadoc

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 }