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 }