001    /*
002     * Copyright 2004-2005 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.io.Serializable;
022    import java.beans.Introspector;
023    import java.lang.reflect.Method;
024    
025    
026    /**
027     * A descriptor that describes a value that must be placed
028     * in components Context. It contains information about;
029     * <ul>
030     *   <li>key: the key that component uses to look up entry</li>
031     *   <li>classname: the class/interface of the entry</li>
032     *   <li>isOptional: true if entry is optional rather than required</li>
033     * </ul>
034     *
035     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
036     * @version 1.0.4
037     */
038    public final class EntryDescriptor implements Serializable, Comparable
039    {
040       /**
041        * Serial version identifier.
042        */
043        static final long serialVersionUID = 1L;
044    
045       /**
046        * Constant prefix for get operations.
047        */
048        public static final String GET = "get";
049    
050       /**
051        * Utility function that covers a supplied method to a 
052        * entry name.
053        * @see Introspector#decapitalize(String)
054        * @param method the method
055        * @return the key
056        */
057        public static String getEntryKey( Method method )
058        {
059            String name = method.getName();
060            if( name.startsWith( GET ) )
061            {
062                String remainder = name.substring( GET.length() );
063                return Introspector.decapitalize( remainder );
064            }
065            else
066            {
067                final String error = 
068                  "Unrecognized context accessor method signature ["
069                  + name 
070                  + "]";
071                throw new IllegalArgumentException( error );
072            }
073        }
074    
075       /**
076        * Optional constant.
077        */
078        public static final boolean OPTIONAL = true;
079    
080       /**
081        * Required constant.
082        */
083        public static final boolean REQUIRED = false;
084    
085        /**
086         * The name the component uses to lookup entry.
087         */
088        private final String m_key;
089    
090        /**
091         * The class/interface of the Entry.
092         */
093        private final String m_classname;
094    
095        /**
096         * True if entry is optional, false otherwise.
097         */
098        private final boolean m_optional;
099    
100        /**
101         * Immutable state of the entry.
102         */
103        private final boolean m_volatile;
104    
105        /**
106         * Construct an non-volotile required EntryDescriptor.
107         * @param key the context entry key
108         * @param classname the classname of the context entry
109         * @exception NullPointerException if the key or type value are null
110         */
111        public EntryDescriptor( final String key, final String classname ) 
112          throws NullPointerException
113        {
114            this( key, classname, false );
115        }
116    
117        /**
118         * Construct an non-volotile EntryDescriptor.
119         * @param key the context entry key
120         * @param classname the classname of the context entry
121         * @param optional TRUE if this is an optional entry
122         * @exception NullPointerException if the key or type value are null
123         */
124        public EntryDescriptor( final String key,
125                                final String classname,
126                                final boolean optional ) throws NullPointerException
127        {
128            this( key, classname, optional, false );
129        }
130    
131        /**
132         * Construct an EntryDescriptor.
133         * @param key the context entry key
134         * @param classname the classname of the context entry
135         * @param optional TRUE if this is an optional entry
136         * @param isVolatile TRUE if the entry is consider to be immutable
137         * @exception NullPointerException if the key or type value are null
138         */
139        public EntryDescriptor( final String key,
140                                final String classname,
141                                final boolean optional,
142                                final boolean isVolatile ) throws NullPointerException
143        {
144            if ( null == key )
145            {
146                throw new NullPointerException( "key" );
147            }
148            if ( null == classname )
149            {
150                throw new NullPointerException( "classname" );
151            }
152    
153            m_key = key;
154            m_classname = classname;
155            m_optional = optional;
156            m_volatile = isVolatile;
157        }
158    
159        /**
160         * Return the key that Component uses to lookup entry.
161         *
162         * @return the key that Component uses to lookup entry.
163         */
164        public String getKey()
165        {
166            return m_key;
167        }
168    
169        /**
170         * Return the type of value that is stored in the context entry.
171         *
172         * @return the context entry classname
173         */
174        public String getClassname()
175        {
176            return m_classname;
177        }
178    
179        /**
180         * Return true if entry is optional, false otherwise.
181         *
182         * @return true if entry is optional, false otherwise.
183         */
184        public boolean getOptional()
185        {
186            return m_optional;
187        }
188    
189        /**
190         * Return true if entry is optional, false otherwise.
191         *
192         * @return true if entry is optional, false otherwise.
193         */
194        public boolean isOptional()
195        {
196            return getOptional();
197        }
198    
199        /**
200         * Return true if entry is required, false otherwise.
201         *
202         * @return true if entry is required, false otherwise.
203         */
204        public boolean isRequired()
205        {
206            return !isOptional();
207        }
208    
209        /**
210         * Return true if entry is volotile.
211         *
212         * @return the volatile state of the entry
213         */
214        public boolean isVolatile()
215        {
216            return getVolatile();
217        }
218    
219        /**
220         * Return true if entry is volotile.
221         *
222         * @return the volatile state of the entry
223         */
224        public boolean getVolatile()
225        {
226            return m_volatile;
227        }
228        
229       /**
230        * Test is the supplied object is equal to this object.
231        * @param other the object to compare with this instance
232        * @return true if the object are equivalent
233        */
234        public boolean equals( Object other )
235        {
236            if( other == null )
237            {
238                return false;
239            }
240    
241            if( !( other instanceof EntryDescriptor ) )
242            {
243                return false;
244            }
245    
246            EntryDescriptor entry = (EntryDescriptor) other;
247    
248            if( m_optional != entry.m_optional )
249            {
250                return false;
251            }
252    
253            if( m_volatile != entry.m_volatile )
254            {
255                return false;
256            }
257    
258            if( !m_key.equals( entry.m_key ) )
259            {
260                return false;
261            }
262    
263            if( !m_classname.equals( entry.m_classname ) )
264            {
265                return false;
266            }
267    
268            return true;
269        }
270    
271       /**
272        * Return the hashcode for the object.
273        * @return the hashcode value
274        */
275        public int hashCode()
276        {
277            int hash = m_key.hashCode();
278            hash ^= m_classname.hashCode();
279            if( m_volatile )
280            {
281                hash =  hash + 1806621635;
282            }
283            else
284            {
285                hash =  hash - 1236275651;
286            }
287            if( m_optional )
288            {
289                hash =  hash + 1232368545;
290            }
291            else
292            {
293                hash =  hash - 923798133;
294            }
295            return hash;
296        }
297        
298       /**
299        * Compare this entry with another entry.
300        * @param other the other object
301        * @return the comparative index
302        */
303        public int compareTo( Object other )
304        {
305            if( null == other )
306            {
307                throw new NullPointerException( "other" );
308            }
309            EntryDescriptor entry = (EntryDescriptor) other;
310            if( getOptional() == entry.getOptional() )
311            {
312                return m_key.compareTo( entry.getKey() );
313            }
314            else if( isRequired() )
315            {
316                return -1;
317            }
318            else
319            {
320                return 1;
321            }
322        }
323    }