/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/content/NodeRevisionDescriptor.java,v 1.44 2005/02/28 17:25:44 luetzkendorf Exp $
 * $Revision: 1.44 $
 * $Date: 2005/02/28 17:25:44 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.slide.content;

import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Vector;

import org.apache.slide.common.ObjectValidationFailedException;
import org.apache.slide.common.PropertyName;
import org.apache.slide.util.EmptyEnumeration;
import org.apache.slide.util.Messages;

/**
 * Node Revision Descriptor class.
 *
 * @version $Revision: 1.44 $
 */
public final class NodeRevisionDescriptor implements Serializable, Cloneable {
    
    
    // -------------------------------------------------------------- Constants
    
    
    /**
     * Creation date.
     */
    public static final String CREATION_DATE = "creationdate";
    public static final String CREATION_USER = "creationuser";
    public static final String MODIFICATION_DATE = "modificationdate";
    public static final String MODIFICATION_USER = "modificationuser";
    
    /**
     * Last modification date.
     */
    public static final String LAST_MODIFIED = "getlastmodified";
    
    /**
     * Name.
     */
    public static final String NAME = "displayname";
    
    /**
     * Type.
     */
    public static final String RESOURCE_TYPE = "resourcetype";
    public static final String TYPE = RESOURCE_TYPE;

    
    /**
     * Source.
     */
    public static final String SOURCE = "source";
    
    
    /**
     * Owner.
     */
    public static final String OWNER = "owner";
    
    
    /**
     * MIME type of the content.
     */
    public static final String CONTENT_TYPE = "getcontenttype";
    
    
    /**
     * Content language.
     */
    public static final String CONTENT_LANGUAGE = "getcontentlanguage";
    
    
    /**
     * Content length.
     */
    public static final String CONTENT_LENGTH = "getcontentlength";
    
    
    /**
     * ETag.
     */
    public static final String ETAG = "getetag";
    
    
    /**
     * Redirect reference target.
     */
    public static final String REFTARGET = "reftarget";
    
    
    /**
     * Redirect reference lifetime.
     */
    public static final String REDIRECT_LIFETIME = "redirect-lifetime";
    
    
    /**
     * Temporary redirect reference.
     */
    public static final String TEMPORARY_LIFETIME = "<temporary/>";
    
    
    /**
     * Permanent redirect reference.
     */
    public static final String PERMANENT_LIFETIME = "<permanent/>";
    
    
    /**
     * Collection type.
     */
    public static final String COLLECTION_TYPE = "<collection/>";
    
    
    /**
     * Redirect Reference type.
     */
    public static final String REDIRECTREF_TYPE = "<redirectref/>";
    
    
    /**
     * HTTP date format.
     */
    protected static final SimpleDateFormat format =
        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
    
    
    /**
     * Date formats using for Date parsing.
     */
    protected static final SimpleDateFormat formats[] = {
        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
            new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US),
            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
    };
    
    
    /**
     * Simple date format for the creation date ISO representation (partial).
     */
    protected static final SimpleDateFormat creationDateFormat =
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    
    
    static {
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        for (int i = 0; i < formats.length; i++) {
            formats[i].setTimeZone(TimeZone.getTimeZone("GMT"));
        }
    }
    
    
    // ----------------------------------------------------------- Constructors
    
    
    /**
     * Constructor.
     */
    public NodeRevisionDescriptor() {
        this.properties = new Hashtable();
        this.labels = new Vector();
        this.branchName = NodeRevisionDescriptors.MAIN_BRANCH;
        this.updatedProperties = new Hashtable();
        this.removedProperties = new Hashtable();
        initDefaultProperties();
    }
    
    
    /**
     * Constructor for client applications.
     */
    public NodeRevisionDescriptor(long contentLength) {
        this();
        setContentLength(contentLength);
    }
    
    
    /**
     * Constructor used for retrieval. Can be used by client apps, but it is
     * mostly useless as number and creationDate fields are ignored during
     * revision creation.
     * 
     * @deprecated because of weird structure of the <code>properties</code> parameter.
     *   please use {@link #NodeRevisionDescriptor(NodeRevisionNumber, String, Vector, List)}.
     */
    public NodeRevisionDescriptor(NodeRevisionNumber number, String branchName,
                                  Vector labels, Hashtable properties) {
        this();
        this.number = number;
        if (branchName != null) {
            this.branchName = branchName;
        }
        this.labels = labels;
        for(Enumeration e = properties.elements(); e.hasMoreElements();) {
            setProperty((NodeProperty)e.nextElement());
        }
    }
    
    /**
     * Creates a new NodeRevisionDescriptor.
     * @param number The revision number of the descriptor.
     * @param branchName The branch name.
     * @param labels A Vector of labels (Strings)
     * @param properties A list of {@link NodeProperty} objects used as the
     *          inititial property set.
     */
    public NodeRevisionDescriptor(NodeRevisionNumber number, String branchName,
            Vector labels, List properties)
    {
        this();
        this.number = number;
        if (branchName != null) {
            this.branchName = branchName;
        }
        this.labels = labels;
        for (Iterator i = properties.iterator(); i.hasNext();) {
            setProperty((NodeProperty) i.next());
        }
    }
    
    public NodeRevisionDescriptor(NodeRevisionNumber number, String branchName) {
        this();
        this.number = number;
        if (branchName != null) {
            this.branchName = branchName;
        }
        this.labels = new Vector();
    }
    
    // ----------------------------------------------------- Instance Variables
    
    
    /**
     * Branch name.
     */
    private String branchName;
    
    
    /**
     * Revision number.
     */
    private NodeRevisionNumber number;
    
    
    /**
     * Revision labels.
     */
    private Vector labels;
    
    
    /**
     * Properties field.
     * Maps {@link PropertyName}s to {@link NodeProperty}s.
     */
    private Hashtable properties;

    /**
     * Track updated properties
     */
    private Hashtable updatedProperties = null;
    private Hashtable removedProperties = null;

    // ------------------------------------------------------------- Properties
    
    
    /**
     * Branch name accessor.
     */
    public String getBranchName() {
        return this.branchName;
    }
    
    
    /**
     * Branch name mutator.
     */
    void setBranchName(String branchName) {
        this.branchName = branchName;
    }
    
    
    /**
     * Revision number accessor.
     */
    public NodeRevisionNumber getRevisionNumber() {
        return this.number;
    }
    
    
    /**
     * Revision number mutator.
     */
    void setRevisionNumber(NodeRevisionNumber number) {
        this.number = number;
    }
    
    
    /**
     * Add new label.
     */
    public void addLabel(String label) {
        this.labels.addElement(label);
    }
    
    
    /**
     * Remove a label.
     */
    public void removeLabel(String label) {
        this.labels.removeElement(label);
    }
    
    
    /**
     * Remove all labels.
     */
    public void removeLabels() {
        this.labels.removeAllElements();
    }
    
    
    /**
     * Remove specified labels.
     */
    /*    public void removeLabels(Collection c) {
     this.labels.removeAll(c);
     }*/
    
    
    /**
     * Enumerate labels.
     */
    public Enumeration enumerateLabels() {
        return this.labels.elements();
    }
    
    /**
     * Tests if a property with the namespace <code>DAV:</code> has been set.
     *
     * @param name Property name
     * @return true if the property has been set
     * @see use of {@link #exists(PropertyName)} is preferred.
     */
    public boolean exists(String name) {
        return exists(PropertyName.getPropertyName(name, NodeProperty.DEFAULT_NAMESPACE));
    }
    
    
    /**
     * Tests if a property has been set.
     *
     * @param name properties name
     * @param namespace properties namesapce
     * @return true if the property has been set
     * @see use of {@link #exists(PropertyName)} is preferred.
     */
    public boolean exists(String name, String namespace) {
        return exists(PropertyName.getPropertyName(name, namespace));
    }
    
    /**
     * Tests if a property has been set.
     *
     * @param name Property name
     * @return true if the property has been set
     */
    public boolean exists(PropertyName name) {
        return properties.containsKey(name);
    }
    
    /**
     * Property accessor for properties in <code>DAV:</code> namespace.
     *
     * @param name Property name
     * @return NodeProperty object or <code>null</code> if the property 
     * is not defined on this node.
     */
    public NodeProperty getProperty(String name) {
        return getProperty(PropertyName.getPropertyName(name, NodeProperty.DEFAULT_NAMESPACE));
    }
    
    
    /**
     * Property accessor.
     *
     * @param name Property name
     * @param namespace Property namespace
     * @return NodeProperty object or <code>null</code> if the property 
     * is not defined on this node.
     */
    public NodeProperty getProperty(String name, String namespace) {
        return getProperty(PropertyName.getPropertyName(name, namespace));
    }
    
    /**
     * Property accessor.
     * @param name properties name
     * @return NodeProperty object or <code>null</code> if the property 
     * is not defined on this node.
     */
    public NodeProperty getProperty(PropertyName name) {
        return (NodeProperty)properties.get(name);
    }
    
    /**
     * Property mutator.
     *
     * @param name Property name
     * @param value Property value
     */
    public void setProperty(String name, Object value) {
        setProperty(new NodeProperty(name, value, NodeProperty.DEFAULT_NAMESPACE));
    }
    
    
    /**
     * Property mutator.
     *
     * @param name Property name
     * @param value Property value
     * @param namespace Property value
     */
    public void setProperty(String name, String namespace, Object value) {
        setProperty(new NodeProperty(name, value, namespace));
    }
    
    public void setProperty(PropertyName name, Object value) {
        setProperty(new NodeProperty(name, value));
    }
    
    /**
     * Property mutatory.
     *
     * @param property Property
     */
    public void setProperty(NodeProperty property) {
        PropertyName name = property.getPropertyName();
        properties.put(name, property);
        
        if (this.updatedProperties == null) this.updatedProperties = new Hashtable();
        if (this.removedProperties == null) this.removedProperties = new Hashtable();
        updatedProperties.put(name, property);
        removedProperties.remove(name);
    }
    
    
    /**
     * Remove a property.
     * 
     * @param property
     *            Property
     */
    public void removeProperty(NodeProperty property) {
        removeProperty(property.getPropertyName());
    }
    
    
    /**
     * Remove a property.
     *
     * @param property Property
     */
    public void removeProperty(String property) {
        removeProperty(property, NodeProperty.DEFAULT_NAMESPACE);
    }
    
    
    /**
     * Remove a property.
     *
     * @param property property name
     * @param nameSpace properties namespace
     */
    public void removeProperty(String property, String nameSpace) {
        removeProperty(PropertyName.getPropertyName(property, nameSpace));
    }
    
    public void removeProperty(PropertyName name) {
        NodeProperty nodeProperty = (NodeProperty)properties.remove(name);
        // check if the property existed before
        if (nodeProperty != null) {
            if (this.updatedProperties == null) this.updatedProperties = new Hashtable();
            if (this.removedProperties == null) this.removedProperties = new Hashtable();

            removedProperties.put(name, nodeProperty);
            updatedProperties.remove(name);
        }
    }
    
    
    /**
     * Properties names enumerator.
     *
     * @return Enumeration of the properties names
     * @deprecated Replaced by enumeratePropertiesName
     */
    public Enumeration getPropertiesNames() {
        return enumeratePropertiesName();
    }
    
    
    /**
     * Properties names enumerator.
     *
     * @return Enumeration of the properties names
     */
    public Enumeration enumeratePropertiesName() {
        Vector result = new Vector();
        Enumeration propertyList = enumerateProperties();
        while (propertyList.hasMoreElements()) {
            NodeProperty currentProperty =
                (NodeProperty) propertyList.nextElement();
            result.addElement(currentProperty.getName());
        }
        return result.elements();
    }
    
    
    /**
     * Properties values enumerator.
     *
     * @return Enumeration of the properties values
     * @deprecated Replaced by enumerate properties
     */
    public Enumeration getPropertiesValues() {
        return enumerateProperties();
    }
    
    
    /**
     * Properties values enumerator.
     *
     * @return Enumeration of the properties values
     */
    public Enumeration enumerateProperties() {
        return properties.elements();
    }

    public Enumeration enumerateRemovedProperties() {
        if (this.removedProperties == null) {
            return EmptyEnumeration.INSTANCE;
        } else {
            return removedProperties.elements();
        }
    }

    public Enumeration enumerateUpdatedProperties() {
        if (this.updatedProperties == null) {
            return EmptyEnumeration.INSTANCE;
        } else {
            return updatedProperties.elements();
        }
    }

    public void resetUpdatedProperties() {
        updatedProperties = null;
    }
    
    public void resetRemovedProperties() {
        removedProperties = null;
    }
                                     
    /**
     * Checks whether the value of the given property contains the specified
     * substring.
     *
     * @param name the property name
     * @param substr the substring to check
     * @return true, if the value of the given property contains the specified
     *         substring
     */
    public boolean propertyValueContains( String name, String substr ) {
        return propertyValueContains(PropertyName.getPropertyName(name), substr);
    }
    
    /**
     * Checks whether the value of the given property contains the specified
     * substring.
     *
     * @param name the property name
     * @param namespace the namespace
     * @param substr the substring to check
     * @return true, if the value of the given property contains the specified
     *         substring
     */
    public boolean propertyValueContains( String name, String namespace, String substr ) {
        return propertyValueContains(PropertyName.getPropertyName(name, namespace), substr);
    }
    
    public boolean propertyValueContains(PropertyName name, String substr ) {
        NodeProperty p = getProperty(name);
        
        if( p != null ) {
            Object v = p.getValue();
            if( v instanceof String && ((String)v).indexOf(substr) >= 0 ) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Name accessor.
     *
     * @return String name
     */
    public String getName() {
        NodeProperty name = getProperty(PropertyName.DISPLAY_NAME);
        if (name == null) {
            return "";
        } else {
            return (String) name.getValue();
        }
    }
    
    
    /**
     * Name mutator.
     *
     * @param name New name
     */
    public void setName(String name) {
        setProperty(PropertyName.DISPLAY_NAME, name);  // live property, but can be modified
    }
    
    
    /**
     * Get the ETAG property (if any).
     *
     * @return String
     */
    public String getETag() {
        NodeProperty contentType = getProperty(PropertyName.ETAG);
        if (contentType == null) {
            return "";
        } else {
            return (String) contentType.getValue();
        }
    }
    
    
    /**
     * Set ETAG property.
     *
     * @param eTag  New etag
     */
    public void setETag(String eTag) {
        setProperty(PropertyName.ETAG, eTag);  // live property, can not be modified
    }
    
    
    
    
    
    /**
     * Get the owner property (if any).
     *
     * @return String
     */
    public String getOwner() {
        NodeProperty owner = getProperty(PropertyName.OWNER);
        if (owner == null) {
            return "";
        } else {
            return (String) owner.getValue();
        }
    }
    
    
    /**
     * Set owner property.
     *
     * @param owner  New owner
     */
    public void setOwner(String owner) {
        setProperty(PropertyName.OWNER, owner);  // live property, can not be modified
    }
    
    /**
     * Set owner property.
     *
     * @param owner  New owner
     */
    public void setOwner(String owner, String userpath) {
        setProperty(PropertyName.OWNER, userpath + "/" + owner);  // live property, can not be modified
    }
    
    /**
     * Get the source property (if any).
     *
     * @return String
     */
    public String getSource() {
        NodeProperty source = getProperty(PropertyName.SOURCE);
        if (source == null) {
            return "";
        } else {
            return (String) source.getValue();
        }
    }
    
    
    /**
     * Set source property.
     *
     * @param source  New source
     */
    public void setSource(String source) {
        setProperty(PropertyName.SOURCE, source);  // live property, can not be modified
    }
    
    
    
    
    
    
    /**
     * Get the ResourceType property (if any).
     *
     * @return String
     */
    public String getResourceType() {
        NodeProperty resourceType = getProperty(PropertyName.RESOURCE_TYPE);
        if (resourceType == null) {
            return "";
        } else {
//            return (String) resourceType.getValue();
            return String.valueOf(resourceType.getValue());
            
        }
    }
    
    
    /**
     * Set ResourceType property.
     *
     * @param resourceType  New ResourceType
     */
    public void setResourceType(String resourceType) {
        setProperty(PropertyName.RESOURCE_TYPE, resourceType);  // live property, can not be modified
    }
    
    
    
    
    /**
     * Get the MIME content type of the data (if any).
     *
     * @return String
     */
    public String getContentType() {
        NodeProperty contentType = getProperty(PropertyName.CONTENT_TYPE);
        if (contentType == null) {
            return "";
        } else {
            return (String) contentType.getValue();
        }
    }
    
    
    /**
     * Content type mutator.
     *
     * @param contentType New content type
     */
    public void setContentType(String contentType) {
        setProperty(PropertyName.CONTENT_TYPE, contentType);  // live property, can not be modified
    }
    
    
    /**
     * Content language accessor.
     *
     * @return String content language
     */
    public String getContentLanguage() {
        NodeProperty contentLanguage = getProperty(PropertyName.CONTENT_LANGUAGE);
        if (contentLanguage == null) {
            return "";
        } else {
            return (String) contentLanguage.getValue();
        }
    }
    
    
    /**
     * Content language mutator.
     *
     * @param contentLanguage New content language
     */
    public void setContentLanguage(String contentLanguage) {
        setProperty(PropertyName.CONTENT_LANGUAGE, contentLanguage);  // live property, can not be modified
    }
    
    
    /**
     * Creation date accessor.
     *
     * @return String creation date
     */
    public String getCreationDate() {
        NodeProperty creationDate = getProperty(PropertyName.CREATION_DATE);
        if (creationDate == null) {
            return null;
        } else {
            if (creationDate.getValue() instanceof Date) {
                return creationDateFormat.format
                    ((Date) creationDate.getValue());
            }
            return creationDate.getValue().toString();
        }
    }
    
    
    /**
     * Modification date accessor.
     *
     * @return String modification date
     */
    public String getModificationDate() {
        NodeProperty modificationDate = getProperty(PropertyName.MODIFICATION_DATE);
        if (modificationDate == null) {
            return null;
        } else {
            if (modificationDate.getValue() instanceof Date) {
                return creationDateFormat.format
                    ((Date) modificationDate.getValue());
            }
            return modificationDate.getValue().toString();
        }
    }
    
    
    /**
     * Get the creation user
     *
     * @return String
     */
    public String getCreationUser() {
        NodeProperty creationUser = getProperty(PropertyName.CREATION_USER);
        if (creationUser == null) {
            return "";
        } else {
            return (String) creationUser.getValue();
        }
    }
    
    
    /**
     * Get the mofications user
     *
     * @return String
     */
    public String getModificationUser() {
        NodeProperty modificationUser = getProperty(PropertyName.MODIFICATION_USER);
        if (modificationUser == null) {
            return "";
        } else {
            return (String) modificationUser.getValue();
        }
    }
    
    
    /**
     * Creation date accessor.
     *
     * @return String creation date
     */
    public Date getCreationDateAsDate() {
        NodeProperty creationDate = getProperty(PropertyName.CREATION_DATE);
        if (creationDate == null)
            return null;
        if (creationDate.getValue() instanceof Date) {
            return (Date) creationDate.getValue();
        } else {
            String creationDateValue = creationDate.getValue().toString();
            Date result = null;
            // Parsing the HTTP Date
            for (int i = 0; (result == null) && (i < formats.length); i++) {
                try {
                    synchronized (formats[i]) {
                        result = formats[i].parse(creationDateValue);
                    }
                } catch (ParseException e) {
                    ;
                }
            }
            return result;
        }
    }
    
    
    /**
     * Creation date mutator.
     *
     * @param creationDate New creation date
     */
    public void setCreationDate(Date creationDate) {
        // live property, can not be modified
        setProperty(PropertyName.CREATION_DATE, creationDateFormat.format(creationDate));
    }
     
    
    /**
     * Modification date mutator.
     *
     * @param modificationDate New modification date
     */
    public void setModificationDate(Date modificationDate) {
        // live property, can not be modified
        setProperty(PropertyName.MODIFICATION_DATE, 
                creationDateFormat.format(modificationDate));
    }
     
    
    /**
     * Modification date mutator.
     *
     * @param modificationDate New modification date
     */
    public void setModificationDate(String modificationDate) {
        // live property, can not be modified
        setProperty(PropertyName.MODIFICATION_DATE, modificationDate);
    }
     
    
    /**
     * Creation user mutator.
     *
     * @param creationUser New creation user
     */
    public void setCreationUser(String creationUser) {
        // live property, can not be modified
        setProperty(PropertyName.CREATION_USER, creationUser);
    }
     
    
    /**
     * Modification user mutator.
     *
     * @param modificationUser New modification user
     */
    public void setModificationUser(String modificationUser) {
        // live property, can not be modified
        setProperty(PropertyName.MODIFICATION_USER, modificationUser);
    }
    
    
    /**
     * Creation date mutator.
     *
     * @param creationDate New creation date
     */
    public void setCreationDate(String creationDate) {
        setProperty(PropertyName.CREATION_DATE, creationDate);  // live property, can not be modified
    }
    
    
    /**
     * Last modification date accessor.
     *
     * @return String last modification date
     */
    public String getLastModified() {
        NodeProperty lastModified = getProperty(PropertyName.LAST_MODIFIED);
        if (lastModified == null) {
            return null;
        } else {
            if (lastModified.getValue() instanceof Date) {
                return format.format((Date) lastModified.getValue());
            } else {
                return lastModified.getValue().toString();
            }
        }
    }
    
    
    /**
     * Creation date accessor.
     *
     * @return String creation date
     */
    public Date getLastModifiedAsDate() {
        NodeProperty lastModified = getProperty(PropertyName.LAST_MODIFIED);
        if (lastModified == null)
            return null;
        if (lastModified.getValue() instanceof Date) {
            return (Date) lastModified.getValue();
        } else {
            String lastModifiedValue = lastModified.getValue().toString();
            Date result = null;
            // Parsing the HTTP Date
            for (int i = 0; (result == null) && (i < formats.length); i++) {
                try {
                    synchronized (formats[i]) {
                        result = formats[i].parse(lastModifiedValue);
                    }
                } catch (ParseException e) {
                    ;
                }
            }
            return result;
        }
    }
    
    
    /**
     * Last modified mutator.
     *
     * @param lastModified New last modified date
     */
    public void setLastModified(Date lastModified) {
        // live property, can not be modified
        setProperty(PropertyName.LAST_MODIFIED, format.format(lastModified));
    }
    
    
    /**
     * Last modified mutator.
     *
     * @param lastModified New last modified
     */
    public void setLastModified(String lastModified) {
        setProperty(PropertyName.LAST_MODIFIED, lastModified);   // live property, can not be modified
    }
    
    
    /**
     * Creation length mutator.
     *
     * @param contentLength New content length
     */
    public void setContentLength(long contentLength) {
        setProperty(PropertyName.CONTENT_LENGTH, new Long(contentLength));  // live property, can not be modified
    }
    
    
    /**
     * Creation length mutator.
     *
     * @param contentLength New content length
     */
    public void setContentLength(String contentLength) {
        Long contentLengthValue = null;
        try {
            contentLengthValue = new Long(contentLength);
        } catch (NumberFormatException e) {
            // Ignore
        }
        if (contentLengthValue == null) {
            contentLengthValue = new Long(0);
        }
        setProperty(PropertyName.CONTENT_LENGTH, contentLengthValue);  // live property, can not be modified
    }
    
    
    /**
     * Content length accessor.
     *
     * @return String
     */
    public long getContentLength() {
        NodeProperty contentLength = getProperty(PropertyName.CONTENT_LENGTH);
        if (contentLength == null) {
            return -1L;
        } else {
            if (contentLength.getValue() instanceof Long) {
                return ((Long) contentLength.getValue()).longValue();
            }
            
            if (contentLength.getValue() instanceof String) {
                return (new Long((String) contentLength.getValue()))
                    .longValue();
            }
            return -1L;
        }
    }


    /**
     * Get the RefTarget property (if any).
     *
     * @return String
     */
    public String getRefTarget() {
        NodeProperty refTarget = getProperty(PropertyName.REFTARGET);
        if (refTarget == null) {
            return "";
        } else {
            return (String) refTarget.getValue();
        }
    }
    
         
    /**
     * Set RefTarget property.
     *
     * @param refTarget  New RefTarget
     */
    public void setRefTarget(String refTarget) {
        setProperty(PropertyName.REFTARGET, refTarget);  // live property, can not be modified
    }
    
    
    /**
     * Get the RedirectLifetime property (if any).
     *
     * @return redirect lifetime or <code>null</code>.
     */
    public String getRedirectLifetime() {
        NodeProperty redirectLifetime = getProperty(PropertyName.REDIRECT_LIFETIME);
        if (redirectLifetime == null) {
            return "";
        } else {
            return (String) redirectLifetime.getValue();
        }
    }
    
    
    /**
     * Set RedirectLifetime property.
     *
     * @param redirectLifetime  New RedirectLifetime
     */
    public void setRedirectLifetime(String redirectLifetime) {
        setProperty(PropertyName.REDIRECT_LIFETIME, redirectLifetime);  // live property, can not be modified
    }

    
    /**
     * Set default properties.
     */
    void setDefaultProperties(Enumeration defaultProperties) {
        while (defaultProperties.hasMoreElements()) {
            NodeProperty currentProperty =
                (NodeProperty) defaultProperties.nextElement();
            NodeProperty lookup = getProperty(currentProperty.getPropertyName());
            if (lookup == null) {
                // Adding property
                setProperty(currentProperty.getPropertyName(), 
                        currentProperty.getValue());
            }
        }
    }
    
    
    // -------------------------------------------------------- Private methods
    
    
    private void initDefaultProperties() {
        
        setCreationDate(new Date());
//        setName("");
        // By default, a resource is a collection
        setResourceType(COLLECTION_TYPE);
        setProperty(PropertyName.SOURCE, "");  // live property, can not be modified
        // Collection content lengths are usually 0, and should be non-negative
        // to support WebFolders, but we use -1 internally to indicate an unset
        // content length.
        setContentLength(-1);
        setLastModified(new Date());
        
    }
    
    /**
     * Calculate the property name concatenated with the namespace, if available
     *
     * @deprecated
     * @param  namespace the namespace name, possibly null
     * @return String the property name, including the namespace
     */
    public String getNamespacedPropertyName(String namespace, String propertyName) {
        String result;
        if (namespace == null) result = propertyName;
        else                   result = namespace + propertyName;
        return result;
    }
    
    
    // --------------------------------------------------------- Object Methods
    
    
    /**
     * Clone.
     *
     * @return Object clone
     */
    public NodeRevisionDescriptor cloneObject() {
        NodeRevisionDescriptor result = null;
        try {
            result = (NodeRevisionDescriptor) super.clone();
            // Cloning properties
            Hashtable propertiesClone = new Hashtable();
            Enumeration propertiesList = this.properties.keys();
            while (propertiesList.hasMoreElements()) {
                Object key = propertiesList.nextElement();
                Object value = this.properties.get(key);
                propertiesClone.put(key, value);
            }
            result.properties = propertiesClone;
            // Cloning labels
            result.labels = (Vector) this.labels.clone();
        } catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    
    /**
     * Equals.
     *
     * @param obj Object to test
     * @return boolean True if the two object are equal :
     * <li>obj is of type NodeRevisionDescriptor and is not null</li>
     * <li>The two revision numbers are equal</li>
     */
    public boolean equals(Object obj) {
        boolean result = false;
        if ((obj != null) && (obj instanceof NodeRevisionDescriptor)) {
            NodeRevisionDescriptor revisionDescriptor =
                (NodeRevisionDescriptor) obj;
            result = this.getRevisionNumber()
                .equals(revisionDescriptor.getRevisionNumber());
        }
        return result;
    }
    
    
    /**
     * Validate.
     */
    public void validate() {
        
        if (branchName == null)
            throw new ObjectValidationFailedException
                (Messages.message
                     (NodeRevisionDescriptor.class.getName() + ".nullBranchName"));
        
        if (number == null)
            throw new ObjectValidationFailedException
                (Messages.message
                     (NodeRevisionDescriptor.class.getName() + ".nullNumber"));
        number.validate();
        
        if (labels == null)
            throw new ObjectValidationFailedException
                (Messages.message
                     (NodeRevisionDescriptor.class.getName() + ".nullLabels"));
        
        if (properties == null)
            throw new ObjectValidationFailedException
                (Messages.message
                     (NodeRevisionDescriptor.class.getName() + ".nullProperties"));
        Enumeration propertyList = properties.elements();
        while (propertyList.hasMoreElements()) {
            Object obj = propertyList.nextElement();
            if (!(obj instanceof NodeProperty))
                throw new ObjectValidationFailedException
                    (Messages.message
                         (NodeRevisionDescriptor.class.getName()
                              + ".invalidPropertyType"));
            ((NodeProperty) obj).validate();
            
            if (REDIRECTREF_TYPE.equals(getResourceType())) {
                if (getRefTarget() == null)
                    throw new ObjectValidationFailedException (Messages.message
                            (NodeRevisionDescriptor.class.getName()
                            + ".missingRefTarget"));
    
                if (getRedirectLifetime() == null)
                    throw new ObjectValidationFailedException (Messages.message
                            (NodeRevisionDescriptor.class.getName()
                            + ".missingRedirectLifetime"));
            }
            
        }
        
    }
    
    
}
