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