001/* 002 * jDTAUS Core API 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.core.container; 022 023import java.io.Serializable; 024import java.util.Arrays; 025import java.util.Collection; 026import java.util.HashMap; 027import java.util.Map; 028 029/** 030 * Collection of properties. 031 * 032 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 033 * @version $JDTAUS: Properties.java 8743 2012-10-07 03:06:20Z schulte $ 034 */ 035public class Properties extends ModelObject implements Cloneable, Serializable 036{ 037 //--Constants--------------------------------------------------------------- 038 039 /** Serial version UID for backwards compatibility with 1.0.x classes. */ 040 private static final long serialVersionUID = 3703581195509652826L; 041 042 //---------------------------------------------------------------Constants-- 043 //--Properties-------------------------------------------------------------- 044 045 /** 046 * The properties held by the instance. 047 * @serial 048 */ 049 private Property[] properties; 050 051 /** 052 * Maps property names to properties. 053 * @serial 054 */ 055 private final Map names = new HashMap(); 056 057 /** 058 * Hash code. 059 * @serial 060 */ 061 private int hashCode; 062 063 /** Creates a new {@code Properties} instance. */ 064 public Properties() 065 { 066 super(); 067 } 068 069 /** 070 * Gets the properties of the collection. 071 * 072 * @return the properties of the collection. 073 */ 074 public Property[] getProperties() 075 { 076 if ( this.properties == null ) 077 { 078 this.properties = new Property[ 0 ]; 079 this.hashCode = 0; 080 } 081 082 return this.properties; 083 } 084 085 /** 086 * Setter for property {@code properties}. 087 * 088 * @param value the new collection of properties. 089 * 090 * @throws DuplicatePropertyException if {@code value} contains duplicate 091 * properties. 092 */ 093 public void setProperties( final Property[] value ) 094 { 095 this.names.clear(); 096 this.hashCode = 0; 097 this.properties = null; 098 099 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}