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