package javax.wvcm;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * A list of resource property names.
 * One of the most common applications of a PropertyNameList
 * is to identify a list of property values
 * to be retrieved from a persistent resource.
 * <p>
 * The property names can be nested, reflecting the fact that
 * property values can be objects that in turn have properties.
 * For example, the following code fragment creates
 * a property name list that identifies the
 * CREATOR_DISPLAYNAME, CHECKED_IN, and LAST_MODIFIED properties,
 * as well as the VERSION_NAME and CREATION_DATE of the value of
 * the CHECKED_IN property:
 * <pre>
 PropertyNameList pnl =
 new PropertyNameList(new PropertyName[] {
 PropertyName.CREATOR_DISPLAY_NAME,
 new NestedPropertyName(
 PropertyName.CHECKED_IN,
 new PropertyNameList(new PropertyName[] {
 PropertyName.VERSION_NAME,
 PropertyName.CREATION_DATE }),
 PropertyName.LAST_MODIFIED)});
 * </pre> </p>
 */
public final class PropertyNameList {
    
    public static PropertyNameList EMPTY_DO_NOT_DETERMINE_RESOURCETYPE = new PropertyNameList();
    
    /**
     * The name of a property of a persistent resource.
     */
    public static class PropertyName {
        
        private String _string;
        
        private PropertyName(String s) {
            _string = s;
        };
        
        /** Return the The string value of the property name */
        public String getString() {
            return _string;
        };
        
        /**
         * Returns a hash code value for the object.
         */
        public int hashCode() {
            return getString().hashCode();
        };
        
        /**
         * Indicates whether some other object is "equal to" this one.
         * @return true if and only if the specified object is a PropertyName whose name string
         * is equal to the name string of this.
         */
        public boolean equals(Object o) {
            if (o instanceof AttributeName) {
                return false;
            }
            if (o instanceof NestedPropertyName) {
                return false;
            }
            if (!(o instanceof PropertyName)) {
                return false;
            }
            PropertyName pn = (PropertyName) o;
            return _string.equals(pn._string);
        };
        
        /** This is a generic property that matches all attributes.
         * When ALL_ATTRIBUTES appear in a wanted property list,
         * of a {@link Resource#getAttribute getAttribute} request,
         * all attributes of that resource are available on the proxy. */
        public static final PropertyName ALL_ATTRIBUTES =
            new PropertyName("all-attributes");
        
        // RFC2518 Property Names
        
        /** @see Resource#getContentCharacterSet */
        public static final PropertyName CONTENT_CHARACTER_SET =
            new PropertyName("content-character-set");
        /** @see Resource#getContentIdentifier */
        public static final PropertyName CONTENT_IDENTIFIER =
            new PropertyName("content-identifier");
        /** @see Resource#getContentLanguage */
        public static final PropertyName CONTENT_LANGUAGE =
            new PropertyName("content-language");
        /** @see Resource#getContentLength */
        public static final PropertyName CONTENT_LENGTH =
            new PropertyName("content-length");
        /** @see Resource#getContentType */
        public static final PropertyName CONTENT_TYPE =
            new PropertyName("content-type");
        /** @see Resource#getCreationDate */
        public static final PropertyName CREATION_DATE =
            new PropertyName("creation-date");
        /** @see Resource#getDisplayName */
        public static final PropertyName DISPLAY_NAME =
            new PropertyName("display-name");
        /** @see Resource#getLastModified */
        public static final PropertyName LAST_MODIFIED =
            new PropertyName("last-modified");
        
        // RFC3253 Property Names
        
        /** @see Activity#getActivityCheckoutList */
        public static final PropertyName ACTIVITY_CHECKOUT_LIST =
            new PropertyName("activity-checkout-list");
        /** @see Workspace#getActivityFolderList */
        public static final PropertyName ACTIVITY_FOLDER_LIST =
            new PropertyName("activity-folder-list");
        /** @see Version#getActivityList */
        public static final PropertyName ACTIVITY_LIST =
            new PropertyName("activity-list");
        /** @see Activity#getActivityVersionList */
        public static final PropertyName ACTIVITY_VERSION_LIST =
            new PropertyName("activity-version-list");
        /** @see ControllableResource#getAutoMergeList */
        public static final PropertyName AUTO_MERGE_LIST =
            new PropertyName("auto-merge-list");
        /** @see Workspace#getBaselineControlledFolderList */
        public static final PropertyName BASELINE_CONTROLLED_FOLDER_LIST =
            new PropertyName("baseline-controlled-folder-list");
        /** @see Baseline#getBaselineFolder */
        public static final PropertyName BASELINE_FOLDER =
            new PropertyName("baseline-folder");
        /** @see Folder#getBindingList */
        public static final PropertyName BINDING_LIST =
            new PropertyName("binding-list");
        /** @see ControllableResource#getCheckedIn */
        public static final PropertyName CHECKED_IN =
            new PropertyName("checked-in");
        /** @see ControllableResource#getCheckedOut */
        public static final PropertyName CHECKED_OUT =
            new PropertyName("checked-out");
        /** @see Version#getCheckinFork */
        public static final PropertyName CHECKIN_FORK =
            new PropertyName("checkin-fork");
        /** @see Version#getCheckoutFork */
        public static final PropertyName CHECKOUT_FORK =
            new PropertyName("checkout-fork");
        /** @see Version#getCheckoutFork */
        public static final PropertyName CHECKOUT_LIST =
            new PropertyName("checkout-list");
        /** @see Resource#getComment */
        public static final PropertyName COMMENT =
            new PropertyName("comment");
        /** @see Resource#getCreatorDisplayName */
        public static final PropertyName CREATOR_DISPLAY_NAME =
            new PropertyName("creator-display-name");
        /** @see Workspace#getCurrentActivityList */
        public static final PropertyName CURRENT_ACTIVITY_LIST =
            new PropertyName("current-activity-list");
        /** @see Activity#getCurrentWorkspaceList */
        public static final PropertyName CURRENT_WORKSPACE_LIST =
            new PropertyName("current-workspace-list");
        /** @see ControllableFolder#getEclipsedList */
        public static final PropertyName ECLIPSED_LIST =
            new PropertyName("eclipsed-list");
        /** @see Version#getLabelNameList */
        public static final PropertyName LABEL_NAME_LIST =
            new PropertyName("label-name-list");
        /** @see ControllableResource#getMergeList */
        public static final PropertyName MERGE_LIST =
            new PropertyName("merge-list");
        /** @see Version#getPredecessorList */
        public static final PropertyName PREDECESSOR_LIST =
            new PropertyName("predecessor-list");
        /** @see Configuration#getRootFolder */
        public static final PropertyName ROOT_FOLDER =
            new PropertyName("root-folder");
        /** @see VersionHistory#getRootVersion */
        public static final PropertyName ROOT_VERSION =
            new PropertyName("root-version");
        /** @see Activity#getSubactivityList */
        public static final PropertyName SUBACTIVITY_LIST =
            new PropertyName("subactivity-list");
        /** @see Baseline#getSubbaselineList */
        public static final PropertyName SUBBASELINE_LIST =
            new PropertyName("subbaseline-list");
        /** @see Version#getSuccessorList */
        public static final PropertyName SUCCESSOR_LIST =
            new PropertyName("successor-list");
        /** @see ControllableResource#getUnreserved */
        public static final PropertyName UNRESERVED =
            new PropertyName("unreserved");
        /** @see VersionHistory#getVersionByLabelFolder */
        public static final PropertyName VERSION_BY_LABEL_FOLDER =
            new PropertyName("version-by-label-folder");
        /** @see ControllableFolder#getControlledConfiguration */
        public static final PropertyName VERSION_CONTROLLED_CONFIGURATION =
            new PropertyName("version-controlled-configuration");
        /** @see Version#getVersionHistory */
        public static final PropertyName VERSION_HISTORY =
            new PropertyName("version-history");
        /** @see Workspace#getVersionHistoryFolderList */
        public static final PropertyName VERSION_HISTORY_FOLDER_LIST =
            new PropertyName("version-history-folder-list");
        /** @see Version#getVersionName */
        public static final PropertyName VERSION_NAME =
            new PropertyName("version-name");
        /** @see ControllableResource#getWorkspace */
        public static final PropertyName WORKSPACE =
            new PropertyName("workspace");
        /** @see Workspace#getWorkspaceCheckoutList */
        public static final PropertyName WORKSPACE_CHECKOUT_LIST =
            new PropertyName("workspace-checkout-list");
        /** @see Resource#getWorkspaceFolderList */
        public static final PropertyName WORKSPACE_FOLDER_LIST =
            new PropertyName("workspace-folder-list");
        
        // Non-RFC3253 Property Names
        
        /** @see ControllableFolder#getBaselineControllable */
        public static final PropertyName BASELINE_CONTROLLABLE =
            new PropertyName("baseline-controllable");
        /** @see ControllableResource#getDirtyPropertyList */
        public static final PropertyName DIRTY_PROPERTY_LIST =
            new PropertyName("dirty-property-list");
        /** @see ControllableResource#getIsCheckedOut */
        public static final PropertyName IS_CHECKED_OUT =
            new PropertyName("is-checked-out");
        /** @see ControllableResource#getIsDirtyContent */
        public static final PropertyName IS_DIRTY_CONTENT =
            new PropertyName("is-dirty-content");
        /** @see ControllableResource#getIsStaleContent */
        public static final PropertyName IS_STALE_CONTENT =
            new PropertyName("is-stale-content");
        /** @see Resource#getParentBindingList */
        public static final PropertyName PARENT_BINDING_LIST =
            new PropertyName("parent-binding-list");
        /** @see Resource#getProviderList */
        public static final PropertyName PROVIDER_LIST =
            new PropertyName("provider-list");
        /** @see Resource#getResourceIdentifier */
        public static final PropertyName RESOURCE_IDENTIFIER =
            new PropertyName("resource-id");
        /** @see ControllableResource#getServerState
         *  @see Workspace#setServerState */
        public static final PropertyName SERVER_STATE =
            new PropertyName("server-state");
        /** @see ControllableResource#getStalePropertyList */
        public static final PropertyName STALE_PROPERTY_LIST =
            new PropertyName("stale-property-list");
        /** @see ControllableResource#getVersionControllable */
        public static final PropertyName VERSION_CONTROLLABLE =
            new PropertyName("version-controllable");
        
        /**
         * NOT YET STANDARD: ACL-related properties
         */
        /** @see Principal.Group#getGroupMemberList */
        public static final PropertyName GROUP_MEMBER_LIST =
            new PropertyName("group-member-list");
        /** @see Principal#getGroupMembership */
        public static final PropertyName GROUP_MEMBERSHIP =
            new PropertyName("group-membership");
        /** @see Resource#getOwner */
        public static final PropertyName OWNER =
            new PropertyName("owner");
        /** @see Resource#getSupportedPrivilegeList */
        public static final PropertyName SUPPORTED_PRIVILEGE_LIST =
            new PropertyName("supported-privilege-list");
        /** @see Resource#getPrincipalFolderList */
        public static final PropertyName PRINCIPAL_FOLDER_LIST =
            new PropertyName("principal-folder-list");
        /** @see Resource#getPrivilegeFolderList */
        public static final PropertyName PRIVILEGE_FOLDER_LIST =
            new PropertyName("privilege-folder-list");
        /** @see Resource#getModificationDate */
        public static final PropertyName MODIFICATION_DATE =
            new PropertyName("modification-date");
        /** @see Resource#getCreationUser */
        public static final PropertyName CREATION_USER =
            new PropertyName("creation-user");
        /** @see Resource#getModificationUser */
        public static final PropertyName MODIFICATION_USER =
            new PropertyName("modification-user");
    };
    
    
    /**
     * A property name that has a nested property name list.
     */
    public static final class NestedPropertyName extends PropertyName {
        
        private PropertyName _name;
        private PropertyNameList _nested;
        
        /**
         * @param name A property name.
         * @param nested A list of properties of the value of <code>name</code>.
         */
        public NestedPropertyName(PropertyName name, PropertyNameList nested) {
            super(name._string);
            _name = name;
            _nested = nested;
        };
        
        /** Return the property name. */
        public PropertyName getName() {
            return _name;
        };
        
        /** Return the nested property names. */
        public PropertyNameList getNested() {
            return _nested;
        };
        
        /**
         * Indicates whether some other object is "equal to" this one.
         * @return true if and only if the specified object is a NestedPropertyName whose name string
         * is equal to the name string of this and whose nested property name list is equal to the
         * nested property name list of this.
         */
        public boolean equals(Object o) {
            if (!(o instanceof NestedPropertyName)) {
                return false; };
            NestedPropertyName npn = (NestedPropertyName) o;
            return _name.equals(npn._name) && _nested.equals(npn._nested);
        };
    };
    
    /**
     * A name of an attribute.
     */
    public static /*final*/ class AttributeName extends PropertyName {
        // NOT YET STANDARD: not final to allow xpath pseudo-attributes (XPathAttributeName)
        
        private String _namespace;
        
        /**
         * @param namespace An attribute namespace.
         * @param name An attribute name.
         */
        public AttributeName(String namespace, String name) {
            super(name);
            _namespace = namespace;
        };
        
        /** Return the namespace of the attribute */
        public String getNamespace() {
            return _namespace;
        };
        
        public int hashCode() {
            return super.hashCode()+getNamespace().hashCode();
        }
        
        /**
         * Indicates whether some other object is "equal to" this one.
         * @return true if and only if the specified object is an AttributeName whose name string
         * is equal to the name string of this and whose namespace string is equal to the namespace
         * string of this.
         */
        public boolean equals(Object o) {
            if (!(o instanceof AttributeName)) {
                return false; };
            AttributeName an = (AttributeName) o;
            return getString().equals(an.getString()) && _namespace.equals(an._namespace);
        };
    };
    
    // The array of property names for this PropertyNameList.
    private PropertyName[] _propertyNames;
    
    /**
     * Private dummy constructor
     */
    private PropertyNameList() {
    };
    
    /**
     * @param propertyNames An array of property names.
     */
    public PropertyNameList(PropertyName[] propertyNames) {
        super();
        _propertyNames = propertyNames;
    };
    
    /**
     * Return the array of property names maintained by this PropertyNameList.
     */
    public PropertyName[] getPropertyNames() {
        return _propertyNames;
    };
    
    /**
     * Indicates whether some other object is "equal to" this one.
     * @return true if and only if the specified object is a PropertyNameList whose property
     * names array is equal to the property names array of this ignoring order.
     */
    public boolean equals(Object o) {
        if (!(o instanceof PropertyNameList)) {
            return false; };
        PropertyNameList pnl = (PropertyNameList) o;
        return getPropertyNamesAsSet().equals(pnl.getPropertyNamesAsSet());
    };
    
    /**
     * Returns a hash code value for the object.
     */
    public int hashCode() {
        return (_propertyNames == null ? 0 : _propertyNames.length);
    };
    
    /**
     * Return the property names maintained by this PropertyNameList as Set.
     */
    private Set getPropertyNamesAsSet() {
        if (_propertyNames == null) {
            return Collections.EMPTY_SET; };
        return new HashSet(Arrays.asList(_propertyNames));
    };
}
