001    /*
002     * Copyright 2004 Stephen J. McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.metro.info;
020    
021    import java.util.Properties;
022    
023    import net.dpml.lang.Version;
024    
025    /**
026     * This class is used to provide explicit information to assembler
027     * and administrator about the Component. It includes information
028     * such as;
029     *
030     * <ul>
031     *   <li>a symbolic name</li>
032     *   <li>classname</li>
033     *   <li>version</li>
034     * </ul>
035     *
036     * <p>The InfoDescriptor also includes an arbitrary set
037     * of attributes about component. Usually these are container
038     * specific attributes that can store arbitrary information.
039     * The attributes should be stored with keys based on package
040     * name of container.
041     *
042     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
043     * @version 1.0.4
044     */
045    public final class InfoDescriptor extends Descriptor
046    {
047        //-------------------------------------------------------------------
048        // static
049        //-------------------------------------------------------------------
050    
051       /**
052        * Serial version identifier.
053        */
054        static final long serialVersionUID = 1L;
055    
056        //-------------------------------------------------------------------
057        // immutable state
058        //-------------------------------------------------------------------
059    
060        /**
061         * The short name of the Component Type. Useful for displaying
062         * human readable strings describing the type in
063         * assembly tools or generators.
064         */
065        private final String m_name;
066    
067        /**
068         * The implementation classname.
069         */
070        private final String m_classname;
071    
072        /**
073         * The version of component that descriptor describes.
074         */
075        private final Version m_version;
076    
077        /**
078         * The component lifestyle.
079         */
080        private final LifestylePolicy m_lifestyle;
081    
082        /**
083         * The component garbage collection policy. The value returned is either
084         * WEAK, SOFT, HARD or HARD.  A component implementing a WEAK policy
085         * will be decommissioned if no references exist.  A component declaring a
086         * SOFT policy will exist without reference so long as memory contention
087         * does not occur.  A component implementing HARD policies will be
088         * maintained irrespective of usage and memory constraints.  The default
089         * policy is SYSTEM which implies delegation of policy selection to the 
090         * component's container.
091         */
092        private final CollectionPolicy m_collection;
093    
094        /**
095         * Threadsafe policy.
096         */
097        private final ThreadSafePolicy m_threadsafe;
098    
099    
100        //-------------------------------------------------------------------
101        // constructor
102        //-------------------------------------------------------------------
103    
104        /**
105         * Creation of a new info descriptor using a supplied name, key, version
106         * and attribute set.
107         *
108         * @param name the default component name
109         * @param classname the implemetation classname
110         * @exception IllegalArgumentException if the classname is invalid
111         * @exception NullPointerException if the classname is null
112         */
113        public InfoDescriptor( final String name, final String classname )
114                throws IllegalArgumentException, NullPointerException
115        {
116            this( name, classname, null, null, CollectionPolicy.SYSTEM, ThreadSafePolicy.UNKNOWN, null );
117        }
118    
119        /**
120         * Creation of a new info descriptor using a supplied name, key, version
121         * and attribute set.
122         *
123         * @param name the default component name
124         * @param classname the implemetation classname
125         * @param version the implementation version
126         * @param lifestyle the component lifestyle (singleton, thread, etc.)
127         * @param collection the garbage collection policy for the component
128         * @param threadsafe if TRUE the type is declaring itself as threadsafe
129         * @param attributes a set of attributes associated with the component type
130         * @exception IllegalArgumentException if the implementation classname is invalid
131         * @exception NullPointerException if the classname argument is null.
132         */
133        public InfoDescriptor( final String name,
134                               final String classname,
135                               final Version version,
136                               final LifestylePolicy lifestyle,
137                               final CollectionPolicy collection,
138                               final ThreadSafePolicy threadsafe,
139                               final Properties attributes )
140                throws IllegalArgumentException, NullPointerException
141        {
142            super( attributes );
143            
144            m_threadsafe = threadsafe;
145            
146            if( null == classname )
147            {
148                throw new NullPointerException( "classname" );
149            }
150            
151            if( classname.indexOf( "/" ) > -1 )
152            {
153                throw new IllegalArgumentException( "classname: " + classname );
154            }
155            
156            m_classname = classname;
157            
158            if( null == version )
159            {
160                m_version = Version.parse( "0" );
161            }
162            else
163            {
164                m_version = version;
165            }
166            
167            if( lifestyle == null )
168            {
169                m_lifestyle = LifestylePolicy.SYSTEM;
170            }
171            else
172            {
173                m_lifestyle = lifestyle;
174            }
175            
176            if( null == collection )
177            {
178                m_collection = CollectionPolicy.SYSTEM;
179            }
180            else
181            {
182                m_collection = collection;
183            }
184            
185            if( name != null )
186            {
187                m_name = name;
188            }
189            else
190            {
191                m_name = getClassName( classname );
192            }
193        }
194    
195       /**
196        * Internal utility to get the name of the class without the package name. Used
197        * when constructing a default component name.
198        * @param classname the fully qualified classname
199        * @return the short class name without the package name
200        */
201        private String getClassName( String classname )
202        {
203            int i = classname.lastIndexOf( "." );
204            if( i == -1 )
205            {
206                return classname.toLowerCase();
207            }
208            else
209            {
210                return classname.substring( i + 1, classname.length() ).toLowerCase();
211            }
212        }
213    
214        /**
215         * Return the symbolic name of component.
216         *
217         * @return the symbolic name of component.
218         */
219        public String getName()
220        {
221            return m_name;
222        }
223    
224        /**
225         * Return the component collection policy.
226         *
227         * @return the policy
228         */
229        public CollectionPolicy getCollectionPolicy()
230        {
231            return m_collection;
232        }
233    
234       /**
235        * Test is the component type implements a weak collection policy.
236        *
237        * @return TRUE if the policy is weak
238        */
239        public boolean isWeak()
240        {
241            return m_collection.equals( CollectionPolicy.WEAK );
242        }
243    
244        /**
245         * Test is the component type implements a soft collection policy.
246         *
247         * @return TRUE if the policy is soft
248         */
249        public boolean isSoft()
250        {
251            return m_collection.equals( CollectionPolicy.SOFT );
252        }
253    
254        /**
255         * Test is the component type implements a hard collection policy.
256         *
257         * @return TRUE if the policy is hard
258         */
259        public boolean isHard()
260        {
261            return m_collection.equals( CollectionPolicy.HARD );
262        }
263    
264        /**
265         * Return the implementation class name for the component type.
266         *
267         * @return the implementation class name
268         */
269        public String getClassname()
270        {
271            return m_classname;
272        }
273    
274        /**
275         * Return the version of component.
276         *
277         * @return the version of component.
278         */
279        public Version getVersion()
280        {
281            return m_version;
282        }
283    
284        /**
285         * Return the component lifestyle policy.
286         *
287         * @return the lifestyle policy
288         */
289        public LifestylePolicy getLifestylePolicy()
290        {
291            return m_lifestyle;
292        }
293    
294        /**
295         * Ruturn the thread-safe status.
296         *
297         * @return the thread-safe status
298         */
299        public boolean isThreadSafe()
300        {
301            return m_threadsafe.equals( ThreadSafePolicy.TRUE );
302        }
303    
304        /**
305         * Ruturn the thread-safe policy value.
306         *
307         * @return the thread-safe policy value
308         */
309        public ThreadSafePolicy getThreadSafePolicy()
310        {
311            return m_threadsafe;
312        }
313    
314        /**
315         * Return a string representation of the info descriptor.
316         * @return the stringified type
317         */
318        public String toString()
319        {
320            return "[" + getName() + "] " + getClassname() + ":" + getVersion();
321        }
322    
323       /**
324        * Test is the supplied object is equal to this object.
325        * @param other the other object
326        * @return true if the object are equivalent
327        */
328        public boolean equals( Object other )
329        {
330            boolean isEqual = super.equals( other ) && other instanceof InfoDescriptor;
331            if( isEqual )
332            {
333                InfoDescriptor info = (InfoDescriptor) other;
334                isEqual = isEqual && m_threadsafe.equals( info.m_threadsafe );
335                isEqual = isEqual && m_classname.equals( info.m_classname );
336                isEqual = isEqual && m_collection.equals( info.m_collection );
337                isEqual = isEqual && m_name.equals( info.m_name );
338                isEqual = isEqual && m_lifestyle.equals( info.m_lifestyle );
339                if( null == m_version )
340                {
341                    isEqual = isEqual && null == info.m_version;
342                }
343                else
344                {
345                    isEqual = isEqual && m_version.equals( info.m_version );
346                }
347            }
348            return isEqual;
349        }
350    
351       /**
352        * Return the hashcode for the object.
353        * @return the hashcode value
354        */
355        public int hashCode()
356        {
357            int hash = super.hashCode();
358            hash ^= m_collection.hashCode();
359            hash ^= m_threadsafe.hashCode();
360            hash ^= m_classname.hashCode();
361            if ( null != m_name )
362            {
363                hash ^= m_name.hashCode();
364            }
365    
366            if ( null != m_lifestyle )
367            {
368                hash = hash + 9873234;
369                hash ^= m_lifestyle.hashCode();
370            }
371    
372            if ( null != m_version )
373            {
374                hash ^= m_version.hashCode();
375            }
376            return hash;
377        }
378    }