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.Iterator;
027    import java.util.Map;
028    import java.util.TreeMap;
029    
030    /**
031     * Dependency meta-data.
032     * <p>A dependency consists of a name uniquely identifying the dependency in a
033     * set of dependencies and pairs a specification with corresponding
034     * implementations from a set of available implementations. Properties set with
035     * a dependency overwrite properties of the dependency's specification. The
036     * {@code bound} flag indicates if the instance of the dependency is bound to
037     * the declaring implementation.</p>
038     *
039     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
040     * @version $Id: Dependency.java 8044 2009-07-02 01:29:05Z schulte2005 $
041     */
042    public class Dependency extends ModelObject implements Cloneable, Serializable
043    {
044        //--Constants---------------------------------------------------------------
045    
046        /** Serial version UID for backwards compatibility with 1.0.x classes. */
047        private static final long serialVersionUID = -8556956703006143247L;
048    
049        //---------------------------------------------------------------Constants--
050        //--Dependency--------------------------------------------------------------
051    
052        /**
053         * The name of the dependency.
054         * @serial
055         */
056        private String name;
057    
058        /**
059         * The specification of the dependency.
060         * @serial
061         */
062        private Specification specification;
063    
064        /**
065         * The implementation of the dependency.
066         * @serial
067         */
068        private Implementation implementation;
069    
070        /**
071         * The properties of the dependency.
072         * @serial
073         */
074        private Properties properties;
075    
076        /**
077         * Flag indicating if the dependency is bound to the requesting
078         * implementation.
079         * @serial
080         */
081        private boolean bound;
082    
083        /**
084         * Gets the name of the dependency.
085         *
086         * @return the name of the dependency.
087         */
088        public String getName()
089        {
090            if ( this.name == null )
091            {
092                this.name = "";
093            }
094    
095            return this.name;
096        }
097    
098        /**
099         * Setter for property {@code name}.
100         *
101         * @param value the new name of the dependency.
102         */
103        public void setName( final String value )
104        {
105            this.name = value;
106        }
107    
108        /**
109         * Gets the specification of the dependency.
110         *
111         * @return the specification of the dependency.
112         */
113        public Specification getSpecification()
114        {
115            return this.specification;
116        }
117    
118        /**
119         * Setter for property {@code specification}.
120         *
121         * @param value the new specification of the dependency.
122         */
123        public void setSpecification( final Specification value )
124        {
125            this.specification = value;
126        }
127    
128        /**
129         * Gets the implementation of the dependency.
130         *
131         * @return the implementation of the dependency or {@code null}.
132         */
133        public Implementation getImplementation()
134        {
135            return this.implementation;
136        }
137    
138        /**
139         * Setter for property {@code implementation}.
140         *
141         * @param value the new implementation of the dependency.
142         */
143        public void setImplementation( final Implementation value )
144        {
145            this.implementation = value;
146        }
147    
148        /**
149         * Gets the properties of the dependency.
150         * <p>The properties of a dependency are a merged set of the properties
151         * declared for the dependency's implementation overwritten by any
152         * properties declared for the dependency.</p>
153         *
154         * @return the properties of the dependency.
155         *
156         * @throws IllegalPropertyTypeException for any property type collisions
157         * during merging.
158         *
159         * @see PropertyOverwriteConstraintException
160         */
161        public Properties getProperties()
162        {
163            final Map declaredProperties = new TreeMap();
164            final Map implementationProperties = new TreeMap();
165            final Map dependencyProperties = new TreeMap();
166    
167            for ( int i = this.getDeclaredProperties().size() - 1; i >= 0; i-- )
168            {
169                final Property declaredProperty =
170                    this.getDeclaredProperties().getProperty( i );
171    
172                declaredProperties.put( declaredProperty.getName(),
173                                        declaredProperty );
174    
175            }
176    
177            if ( this.getImplementation() != null )
178            {
179                for ( int i = this.getImplementation().getProperties().
180                    size() - 1; i >= 0; i-- )
181                {
182                    final Property implementationProperty =
183                        this.getImplementation().getProperties().
184                        getProperty( i );
185    
186                    implementationProperties.put(
187                        implementationProperty.getName(),
188                        implementationProperty );
189    
190                }
191            }
192    
193            dependencyProperties.putAll( implementationProperties );
194    
195            for ( Iterator it = declaredProperties.entrySet().iterator();
196                it.hasNext();)
197            {
198                final Map.Entry entry = (Map.Entry) it.next();
199                final String propertyName = (String) entry.getKey();
200                final Property property = (Property) entry.getValue();
201    
202                final Property dependencyProperty =
203                    (Property) dependencyProperties.get( propertyName );
204    
205                if ( dependencyProperty != null )
206                {
207                    if ( !dependencyProperty.getType().equals(
208                        property.getType() ) )
209                    {
210                        throw new IllegalPropertyTypeException(
211                            propertyName, property.getType(),
212                            dependencyProperty.getType() );
213    
214                    }
215                    if ( property.getDocumentation().getValue() == null &&
216                        dependencyProperty.getDocumentation().
217                        getValue() != null )
218                    {
219                        property.setDocumentation(
220                            dependencyProperty.getDocumentation() );
221    
222                    }
223    
224                    dependencyProperties.put( propertyName, property );
225                }
226            }
227    
228            final Properties p = new Properties();
229            p.setProperties( (Property[]) dependencyProperties.values().toArray(
230                             new Property[ dependencyProperties.size() ] ) );
231    
232            return p;
233        }
234    
235        /**
236         * Setter for property {@code properties}.
237         *
238         * @param value the new properties of the dependency.
239         *
240         * @see PropertyOverwriteConstraintException
241         */
242        public void setProperties( final Properties value )
243        {
244            this.properties = value;
245        }
246    
247        /**
248         * Gets the declared properties of the dependency.
249         *
250         * @return the declared properties of the dependency.
251         */
252        public Properties getDeclaredProperties()
253        {
254            if ( this.properties == null )
255            {
256                this.properties = new Properties();
257            }
258    
259            return this.properties;
260        }
261    
262        /**
263         * Gets the flag indicating if the dependency is bound to a requesting
264         * implementation.
265         *
266         * @return {@code true} if the dependency object is bound to the declaring
267         * implementation; {@code false} if not.
268         */
269        public boolean isBound()
270        {
271            return this.bound;
272        }
273    
274        /**
275         * Setter for property {@code bound}.
276         *
277         * @param value {@code true} if the dependency object should be bound to the
278         * declaring implementation; {@code false} if not.
279         */
280        public void setBound( boolean value )
281        {
282            this.bound = value;
283        }
284    
285        /**
286         * Creates a string representing the properties of the instance.
287         *
288         * @return a string representing the properties of the instance.
289         */
290        private String internalString()
291        {
292            return new StringBuffer( 500 ).append( '{' ).
293                append( this.internalString( this ) ).
294                append( ", name=" ).append( this.name ).
295                append( ", bound=" ).append( this.bound ).
296                append( ", implementation=" ).
297                append( this.implementation == null
298                        ? "null"
299                        : this.implementation.getIdentifier() + "@" +
300                        this.implementation.getVersion() ).
301                append( ", specification=" ).
302                append( this.specification == null
303                        ? "null"
304                        : this.specification.getIdentifier() + "@" +
305                        this.specification.getVersion() ).
306                append( ", properties=" ).append( this.properties ).
307                append( '}' ).toString();
308    
309        }
310    
311        //--------------------------------------------------------------Dependency--
312        //--Object------------------------------------------------------------------
313    
314        /**
315         * Returns a string representation of the object.
316         *
317         * @return a string representation of the object.
318         */
319        public String toString()
320        {
321            return super.toString() + this.internalString();
322        }
323    
324        /**
325         * Indicates whether some other object is equal to this one by comparing
326         * the values of all properties.
327         *
328         * @param o the reference object with which to compare.
329         *
330         * @return {@code true} if this object is the same as {@code o};
331         * {@code false} otherwise.
332         */
333        public boolean equals( final Object o )
334        {
335            boolean equal = this == o;
336    
337            if ( !equal && o instanceof Dependency )
338            {
339                final Dependency that = (Dependency) o;
340                equal = this.getName().equals( that.getName() ) &&
341                    ( this.implementation == null
342                    ? that.implementation == null
343                    : this.implementation.equals( that.implementation ) ) &&
344                    ( this.specification == null
345                    ? that.specification == null
346                    : this.specification.equals( that.specification ) );
347    
348            }
349    
350            return equal;
351        }
352    
353        /**
354         * Returns a hash code value for this object.
355         *
356         * @return a hash code value for this object.
357         */
358        public int hashCode()
359        {
360            return this.getName().hashCode() +
361                ( this.implementation == null
362                ? 0
363                : this.implementation.hashCode() ) +
364                ( this.specification == null
365                ? 0
366                : this.specification.hashCode() );
367    
368        }
369    
370        /**
371         * Creates and returns a copy of this object. This method  performs a
372         * "shallow copy" of this object, not a "deep copy" operation.
373         *
374         * @return a clone of this instance.
375         */
376        public Object clone()
377        {
378            try
379            {
380                return super.clone();
381            }
382            catch ( CloneNotSupportedException e )
383            {
384                throw new AssertionError( e );
385            }
386        }
387    
388        //------------------------------------------------------------------Object--
389    }