001/*
002 * file PropertyNameList.java
003 *
004 * Licensed Materials - Property of IBM
005 * Restricted Materials of IBM
006 *
007 * (c) Copyright IBM Corporation 2004, 2008.  All Rights Reserved. 
008 * Note to U.S. Government Users Restricted Rights:  Use, duplication or  
009 * disclosure restricted by GSA ADP  Schedule Contract with IBM Corp. 
010 */
011package javax.wvcm;
012
013import java.util.Arrays;
014import java.util.Collections;
015import java.util.HashSet;
016import java.util.Set;
017
018/**
019 * A list of resource property names.
020 * 
021 * @since 1.0
022 */
023public final class PropertyNameList implements PropertyRequestItem {
024
025    /**
026     * The name of a property of a persistent resource. The PropertyName
027     * type parameter specifies the type of the value of the PropertyName
028     */
029    public static class PropertyName<T> implements PropertyRequestItem {
030
031        private final String _namespace;
032        private final String _name;
033
034        /**
035         * Create a PropertyName with a null namespace and the given name.
036         * 
037         * @param name the name for this property.  May not be null.
038         */
039        PropertyName(String name) {
040            this(null, name);
041        }
042
043        /**
044         * Create a PropertyName with the given namespace and name.
045         * 
046         * @param namespace the namespace for this PropertyName.
047         * May be null to indicate a system property, or non-null
048         * to indicate a property in the specified name space.
049         * @param name the name for this property.  May not be null.
050         */
051        public PropertyName(String namespace, String name) {
052            _namespace = namespace;
053            _name = name;
054        }
055
056        /**
057         * Get the namespace of the property name.
058         * 
059         * @return the namespace.  May be null to indicate a system property,
060         * or non-null to indicate a property in the specified name space.
061         */
062        public String getNamespace() {
063            return _namespace;
064        }
065
066        /**
067         * Get the property name.
068         * 
069         * @return the name part of the property name */
070        public String getName() {
071            return _name;
072        }
073
074        /**
075         * Calculate a hash code value for the object.
076         * 
077         * @return the hash code for the object.
078         */
079        @Override
080        public int hashCode() {
081            int result = 17;
082            result = 37*result + ((_namespace == null) ? 0 : _namespace.hashCode());
083            result = 37*result + _name.hashCode();
084            return result;
085        }
086
087        /**
088         * Indicates whether some other object is "equal to" this one.
089         * 
090         * @param o the object to compare with.
091         * @return true if and only if the specified object is a PropertyName whose
092         * type, namespace value, and name value equals those of this object.
093         */
094        @Override
095        public boolean equals(Object o) {
096            if (o == null || this.getClass() != o.getClass()) {
097                return false;
098            }
099            PropertyName<?> pn = (PropertyName<?>) o;
100            if (_namespace == null) {
101                return pn._namespace == null && _name.equals(pn._name);
102            }
103            return _namespace.equals(pn._namespace) && _name.equals(pn._name);
104        }
105
106        /**
107         * Returns a string representation of the PropertyName for diagnostic 
108         * purposes.  
109         * @see java.lang.Object#toString
110         * 
111         * @return The string representation of this PropertyName in the format
112         * [namespace:]name.
113         */
114        @Override
115        public String toString() {
116            if (_namespace != null)
117                return _namespace + ":" + _name; //$NON-NLS-1$
118            else
119                return _name;
120        }
121        
122        /**
123         * Constructs a NestedPropertyName whose root property is this
124         * PropertyName with the property request items supplied as arguments as
125         * its nested property request. This method allows a more succinct
126         * syntax for the declaration of PropertyRequest objects at compile
127         * time. For example,
128         * 
129         * <pre>
130         * final static PropertyRequest WORKSPACE_PROPERTIES = 
131         *     new PropertyRequest(
132         *         Workspace.DISPLAY_NAME,
133         *         Workspace.COMMENT,
134         *         Workspace.ACTIVITY_LIST.nest( 
135         *             Activity.DISPLAY_NAME,
136         *             Activity.ACTIVITY_VERSION_LIST.nest( 
137         *                 Version.DISPLAY_NAME, 
138         *                 Version.COMMENT, 
139         *                 Version.CREATION_DATE)));
140         * </pre>
141         * 
142         * @param pnl An array of PropertyRequestItem objects that specify the
143         *            properties to be requested from the value of the property
144         *            identified by this PropertyName.
145         * @return A NestedPropertyName whose root is this PropertyName and
146         *         whose nested properties are the property request items given
147         *         as arguments.
148         */
149        public NestedPropertyName<T> nest(PropertyRequestItem... pnl)
150        {
151            return new NestedPropertyName<T>(this, pnl);
152        }
153
154        /**
155         * A convenience method for constructing nested property names from a
156         * NestedPropertyName[] without requiring an explicit cast to
157         * PropertyRequestItem[]
158         * 
159         * @param pnl A NestedPropertyName[] that specifies the nested
160         *            properties of the NestedPropertyName to be constructed
161         * @return A NestedPropertyName whose root is this PropertyName and
162         *         whose nested properties are the elements of the
163         *         NestedPropertyNames[] argument.
164         */
165        public NestedPropertyName<T> nest(NestedPropertyName<?>[] pnl)
166        {
167            return new NestedPropertyName<T>(this, (PropertyRequestItem[])pnl);
168        }
169
170        /**
171         * A convenience method for constructing nested property names from a
172         * PropertyName[] without requiring an explicit cast to
173         * PropertyRequestItem[]
174         * 
175         * @param pnl A PropertyName[] that specifies the nested properties of
176         *            the NestedPropertyName to be constructed
177         * @return A NestedPropertyName whose root is this PropertyName and
178         *         whose nested properties are the elements of the
179         *         PropertyNames[] argument.
180         */
181        public NestedPropertyName<T> nest(PropertyName<?>[] pnl)
182        {
183            return new NestedPropertyName<T>(this, (PropertyRequestItem[])pnl);
184        }
185    }
186
187    // An empty immutable PropertyName[]
188    private static final PropertyName<?>[] EMPTY_PNA = new PropertyName[0];
189
190    // The immutable array of property names for this PropertyNameList.
191    private final PropertyName<?>[] _propertyNames;
192
193    // The lazy initialized Set for this immutable object.
194    private volatile Set<PropertyName<?>> _asSet = null;
195
196    /**
197     * Construct a PropertyNameList from an array of property names.
198     * 
199     * @param propertyNames an array of property names, or null.
200     * Passing null is equivalent to passing an empty array.
201     */
202    public PropertyNameList(PropertyName<?>... propertyNames) {
203        _propertyNames = (propertyNames == null || propertyNames.length == 0)
204        ? EMPTY_PNA : propertyNames;
205    }
206
207    /**
208     * Get an array of property names from a property name list.
209     * 
210     * @return the array of property names maintained by this 
211     * PropertyNameList. Will never be <code>null</code>.
212     */
213    public PropertyName<?>[] getPropertyNames() {
214        return _propertyNames;
215    }
216
217    /**
218     * Indicates whether some other object is "equal to" this one.
219     * 
220     * @param o the object to compare with.
221     * @return true if and only if the specified object is a PropertyNameList
222     *         whose property names array is equal to the property names array
223     *         of this ignoring order.
224     */
225    @Override
226    public boolean equals(Object o) {
227        if (o == null || this.getClass() != o.getClass()) {
228            return false;
229        }
230        PropertyNameList pnl = (PropertyNameList) o;
231        return getPropertyNamesAsSet().equals(pnl.getPropertyNamesAsSet());
232    }
233
234
235    /**
236     * Calculate a hash code for a PropertyNameList.
237     * 
238     * @return a hash code for the list.
239     */
240    @Override
241    public int hashCode() {
242        return getPropertyNamesAsSet().hashCode();
243    }
244
245    /**
246     * Returns a string representation of this PropertyNameList suitable for
247     * diagnostics.
248     * 
249     * @return The string representation of this PropertyNameList formatted as
250     * "[&LT;first property name&GT;, &LT;second property name&GT;]"
251     */
252    @Override
253    public String toString() {
254
255        StringBuffer builder = new StringBuffer();
256        builder.append('[');
257
258        for(PropertyName<?> name: getPropertyNames()) {
259            if (builder.length() > 1)
260                builder.append(", "); //$NON-NLS-1$
261            
262            builder.append(name.toString());
263        }
264            
265        builder.append(']');
266
267        return builder.toString();
268    }
269
270    // the property names maintained by this PropertyNameList as a Set.
271    private Set<PropertyName<?>> getPropertyNamesAsSet() {
272        if (_asSet == null) {
273            if (_propertyNames == EMPTY_PNA)
274                _asSet = Collections.emptySet();
275            else
276                _asSet = new HashSet<PropertyName<?>>(Arrays.asList(_propertyNames));
277        }
278        return _asSet;
279    }
280}