/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/wvcm/src/org/apache/wvcm/model/PropertyDescriptor.java,v 1.15 2004/12/03 14:27:40 pnever Exp $
 * $Revision: 1.15 $
 * $Date: 2004/12/03 14:27:40 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Slide", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */

package org.apache.wvcm.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.wvcm.Baseline;
import javax.wvcm.Configuration;
import javax.wvcm.Location;
import javax.wvcm.PropertyNameList.AttributeName;
import javax.wvcm.PropertyNameList.PropertyName;
import javax.wvcm.WvcmException;
import javax.wvcm.WvcmException.ReasonCode;
import org.apache.wvcm.ActivityImpl;
import org.apache.wvcm.ControllableFolderImpl;
import org.apache.wvcm.ControllableResourceImpl;
import org.apache.wvcm.FolderImpl;
import org.apache.wvcm.LocationImpl;
import org.apache.wvcm.PrincipalImpl;
import org.apache.wvcm.PrivilegeImpl;
import org.apache.wvcm.ResourceImpl;
import org.apache.wvcm.VersionHistoryImpl;
import org.apache.wvcm.VersionImpl;
import org.apache.wvcm.WorkspaceImpl;
import org.jdom.Element;
import org.jdom.Namespace;

/**
 * Property Descriptor
 *
 * @author <a href="mailto:peter.nevermann@softwareag.com">Peter Nevermann</a>
 * @version $Revision: 1.15 $
 */
public class PropertyDescriptor {
    
    /**
     * Get an instance of this class.
     *
     * @param    propertyName        a  PropertyName
     * @return   a PropertyDescriptor
     */
    public static PropertyDescriptor getInstance( PropertyName propertyName ) {
        return (PropertyDescriptor)propertyDescriptors.get( propertyName );
    }
    
    /**
     * Create an instance of this class for the specified attribute value.
     *
     * @param    attributeName       an AttributeName
     * @param    propertyElm         a property element
     * @return   a PropertyDescriptor
     */
    public static PropertyDescriptor getInstance( AttributeName attributeName, Element propertyElm ) {
        PropertyDescriptor result = null;
        Namespace nsp = Namespace.getNamespace( "http://jakarta.apache.org/slide/" );
        Element propdescElm = propertyElm.getChild( "propdescriptor", nsp );
        if( propdescElm != null ) {
            try {
                String basetype = propdescElm.getAttributeValue("basetype");
                String collectiontype = propdescElm.getAttributeValue("collectiontype");
                result = new PropertyDescriptor(
                    Class.forName(basetype),
                    collectiontype != null ? Class.forName(collectiontype) : null
                );
            } catch (ClassNotFoundException e) {}
        }
        return result;
    }
    
    /**
     * Mapping: PropertyName -> PropertyDescriptor
     */
    private static Map propertyDescriptors = new HashMap();
    static {
        propertyDescriptors.put( PropertyName.ACTIVITY_CHECKOUT_LIST, new PropertyDescriptor(ControllableResourceImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.ACTIVITY_FOLDER_LIST, new PropertyDescriptor(FolderImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.ACTIVITY_LIST, new PropertyDescriptor(ActivityImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.ACTIVITY_VERSION_LIST, new PropertyDescriptor(VersionImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.AUTO_MERGE_LIST, new PropertyDescriptor(VersionImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.BASELINE_CONTROLLABLE, new PropertyDescriptor(Boolean.class, null) );
        propertyDescriptors.put( PropertyName.BASELINE_CONTROLLED_FOLDER_LIST, new PropertyDescriptor(ControllableFolderImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.BASELINE_FOLDER, new PropertyDescriptor(ControllableFolderImpl.class, null) );
        propertyDescriptors.put( PropertyName.BINDING_LIST, new PropertyDescriptor(FolderImpl.BindingImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.CHECKED_IN, new PropertyDescriptor(VersionImpl.class, null) );
        propertyDescriptors.put( PropertyName.CHECKED_OUT, new PropertyDescriptor(VersionImpl.class, null) );
        propertyDescriptors.put( PropertyName.CHECKIN_FORK, new PropertyDescriptor(Integer.class, null) );
        propertyDescriptors.put( PropertyName.CHECKOUT_FORK, new PropertyDescriptor(Integer.class, null) );
        propertyDescriptors.put( PropertyName.CHECKOUT_LIST, new PropertyDescriptor(ControllableResourceImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.COMMENT, new PropertyDescriptor(String.class, null) );
        propertyDescriptors.put( PropertyName.CONTENT_CHARACTER_SET, new PropertyDescriptor(String.class, null) );
        propertyDescriptors.put( PropertyName.CONTENT_IDENTIFIER, new PropertyDescriptor(String.class, null) );
        propertyDescriptors.put( PropertyName.CONTENT_LANGUAGE, new PropertyDescriptor(Locale.class, null) );
        propertyDescriptors.put( PropertyName.CONTENT_LENGTH, new PropertyDescriptor(Long.class, null) );
        propertyDescriptors.put( PropertyName.CONTENT_TYPE, new PropertyDescriptor(String.class, null) );
        propertyDescriptors.put( PropertyName.CREATION_DATE, new PropertyDescriptor(Date.class, null) );
        propertyDescriptors.put( PropertyName.CREATOR_DISPLAY_NAME, new PropertyDescriptor(String.class, null) );
        propertyDescriptors.put( PropertyName.CURRENT_ACTIVITY_LIST, new PropertyDescriptor(ActivityImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.CURRENT_WORKSPACE_LIST, new PropertyDescriptor(WorkspaceImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.DIRTY_PROPERTY_LIST, new PropertyDescriptor(PropertyName.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.DISPLAY_NAME, new PropertyDescriptor(String.class, null) );
        propertyDescriptors.put( PropertyName.ECLIPSED_LIST, new PropertyDescriptor(String.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.IS_CHECKED_OUT, new PropertyDescriptor(Boolean.class, null) );
        propertyDescriptors.put( PropertyName.IS_DIRTY_CONTENT, new PropertyDescriptor(Boolean.class, null) );
        propertyDescriptors.put( PropertyName.IS_STALE_CONTENT, new PropertyDescriptor(Boolean.class, null) );
        propertyDescriptors.put( PropertyName.LABEL_NAME_LIST, new PropertyDescriptor(String.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.LAST_MODIFIED, new PropertyDescriptor(Date.class, null) );
        propertyDescriptors.put( PropertyName.MERGE_LIST, new PropertyDescriptor(VersionImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.PARENT_BINDING_LIST, new PropertyDescriptor(FolderImpl.BindingImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.PREDECESSOR_LIST, new PropertyDescriptor(VersionImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.PROVIDER_LIST, new PropertyDescriptor(String.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.RESOURCE_IDENTIFIER, new PropertyDescriptor(ResourceImpl.XmlPropertyValue.class, null) );
        propertyDescriptors.put( PropertyName.ROOT_FOLDER, new PropertyDescriptor(ControllableFolderImpl.class, null) );
        propertyDescriptors.put( PropertyName.ROOT_VERSION, new PropertyDescriptor(VersionImpl.class, null) );
        propertyDescriptors.put( PropertyName.SERVER_STATE, new PropertyDescriptor(ControllableResourceImpl.class, null) );
        propertyDescriptors.put( PropertyName.STALE_PROPERTY_LIST, new PropertyDescriptor(PropertyName.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.SUBACTIVITY_LIST, new PropertyDescriptor(ActivityImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.SUBBASELINE_LIST, new PropertyDescriptor(Baseline.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.SUCCESSOR_LIST, new PropertyDescriptor(VersionImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.UNRESERVED, new PropertyDescriptor(Boolean.class, null) );
        propertyDescriptors.put( PropertyName.VERSION_BY_LABEL_FOLDER, new PropertyDescriptor(FolderImpl.class, null) );
        propertyDescriptors.put( PropertyName.VERSION_CONTROLLABLE, new PropertyDescriptor(Boolean.class, null) );
        propertyDescriptors.put( PropertyName.VERSION_CONTROLLED_CONFIGURATION, new PropertyDescriptor(Configuration.class, null) );
        propertyDescriptors.put( PropertyName.VERSION_HISTORY, new PropertyDescriptor(VersionHistoryImpl.class, null) );
        propertyDescriptors.put( PropertyName.VERSION_HISTORY_FOLDER_LIST, new PropertyDescriptor(FolderImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.VERSION_NAME, new PropertyDescriptor(String.class, null) );
        propertyDescriptors.put( PropertyName.WORKSPACE, new PropertyDescriptor(WorkspaceImpl.class, null) );
        propertyDescriptors.put( PropertyName.WORKSPACE_CHECKOUT_LIST, new PropertyDescriptor(ControllableResourceImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.WORKSPACE_FOLDER_LIST, new PropertyDescriptor(FolderImpl.class, ArrayList.class) );
        
        // NOT YET STANDARD: ACL-related properties
        propertyDescriptors.put( PropertyName.GROUP_MEMBER_LIST, new PropertyDescriptor(PrincipalImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.GROUP_MEMBERSHIP, new PropertyDescriptor(PrincipalImpl.GroupImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.OWNER, new PropertyDescriptor(PrincipalImpl.class, null) );
        propertyDescriptors.put( PropertyName.PRINCIPAL_FOLDER_LIST, new PropertyDescriptor(FolderImpl.class, ArrayList.class) );
        propertyDescriptors.put( PropertyName.PRIVILEGE_FOLDER_LIST, new PropertyDescriptor(FolderImpl.class, ArrayList.class) );
//        propertyDescriptors.put( PropertyName.SUPPORTED_PRIVILEGE_LIST, new PropertyDescriptor(ResourceImpl.XmlPropertyValue.class, ArrayList.class));
        propertyDescriptors.put( PropertyName.SUPPORTED_PRIVILEGE_LIST, new PropertyDescriptor(PrivilegeImpl.class, ArrayList.class){public boolean isHref(){return false;}});
        propertyDescriptors.put( PropertyName.MODIFICATION_DATE, new PropertyDescriptor(Date.class, null) );
        propertyDescriptors.put( PropertyName.CREATION_USER, new PropertyDescriptor(PrincipalImpl.class, null) );
        propertyDescriptors.put( PropertyName.MODIFICATION_USER, new PropertyDescriptor(PrincipalImpl.class, null) );
    }
    
    private Class basetype;
    private Class collectiontype;
    private boolean isHref;
    
    /**
     * Private constructor
     */
    private PropertyDescriptor( Class basetype, Class collectiontype ) {
        this.basetype = basetype;
        this.collectiontype = collectiontype;
        this.isHref = false;
        Class cls = basetype;
        while( cls != null ) {
            if( cls == ResourceImpl.class ) {
                this.isHref = true;
                break;
            }
            cls = cls.getSuperclass();
        }
    }
    
    /**
     * Get the basetype
     *
     * @return   a Class
     */
    public Class basetype() {
        return basetype;
    }
    
    /**
     * Get the collectiontype
     *
     * @return   a Class
     */
    public Class collectiontype() {
        return collectiontype;
    }
    
    /**
     * Return true, if the property is a href property.
     *
     * @return   true, if the property is a href property.
     */
    public boolean isHref() {
        return isHref;
    }
    
    /**
     * Create instance for the base type.
     *
     * @param    location the location of the resource
     * @return   a resource proxy
     * @throws   WvcmException
     *
     */
    public Object basetypeInstance( Location location ) throws WvcmException {
        try {
            Class[] parmtypes = {LocationImpl.class};
            Object[] initargs = {location};
            return basetype.getConstructor(parmtypes).newInstance(initargs);
        }
        catch( Exception e ) {
            Exception[] nested = {e};
            throw new WvcmException( "Cannot create basetype instance for "+basetype,
                                    location.string(), ReasonCode.CANNOT_CREATE_NEW_RESOURCE, nested );
        }
    }
    
    /**
     * Create collection instance for multi-valued properties.
     *
     * @return   a Collection
     * @throws   WvcmException
     *
     */
    public Collection collectiontypeInstance() throws WvcmException {
        if( collectiontype == null )
            return null;
        
        try {
            Class[] parmtypes = {};
            Object[] initargs = {};
            return (Collection)collectiontype.getConstructor(parmtypes).newInstance(initargs);
        }
        catch( Exception e ) {
            Exception[] nested = {e};
            throw new WvcmException( "Cannot create collectiontype instance for "+collectiontype,
                                    null, ReasonCode.CANNOT_CREATE_NEW_RESOURCE, nested );
        }
    }
}

