/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/jdomobjects/XFactory.java,v 1.2 2004/09/15 14:58:26 pnever Exp $
 * $Revision: 1.2 $
 * $Date: 2004/09/15 14:58:26 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 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.store.tamino.jdomobjects;

import com.softwareag.common.instrumentation.logging.Level;
import com.softwareag.common.instrumentation.logging.Logger;
import com.softwareag.common.instrumentation.logging.LoggerFactory;
import com.softwareag.common.instrumentation.logging.LoggerUtil;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.store.tamino.common.IDesc;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.common.XUriCache;
import org.apache.slide.store.tamino.datastore.XUtilDbSession;
import org.apache.slide.store.tamino.datastore.schema.XSchemaFactory;
import org.apache.slide.store.tamino.store.XContentStore;
import org.apache.slide.store.tamino.tools.Env;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.Strings;
import org.apache.slide.util.XAssertionFailed;
import org.apache.slide.util.XException;
import org.apache.slide.util.XUri;
import org.jdom.DataConversionException;
import org.jdom.Element;
import org.jdom.Namespace;

/**
 * Main purpose is to instantiate XDescriptors. Instantiation comes in two flavors:
 * a) creating - which results in a descriptor with a new unique resource id
 * b) loading - which uses an existing resourceId with the specified JDOM document.
 *
 * @version $Revision: 1.2 $
 */
public class XFactory implements XGlobals, XJdom {
    private static final String LOGNAME = LoggerUtil.getThisClassName();
    private static final Logger logger = LoggerFactory.getLogger(LOGNAME);
    
    /** default mime-type */
    private final static String DEFAULT_MIME_TYPE = "application/octet-stream";
    
    /**
     * Create a XDescriptor for a member node.
     * This method is a factory method to create a new descriptors node.
     *
     * @pre        (uri != null)
     * @pre        (contentId != null)
     *
     * @param      taminoCollection       taminoCollection ( root directory )
     * @param      uri                    uri
     * @param      contentId              content id
     *
     * @return     XDescriptor
     * @exception  XException             xdav error
     */
    public static XDescriptors createMemberDescriptor(
        boolean useBinding, String taminoCollection,
        XUri uri,
        String contentId,
        long contentLength,
        String mimeType
    )
        throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( LOGNAME, "createMemberDescriptor", new Object[]{taminoCollection, uri, contentId});
        
        String uriStr = uri.toString();
        XDescriptors newDesc = createUriDescriptor( useBinding, taminoCollection, uriStr );
        NodeRevisionDescriptor rd = createMemberNRD( uriStr, mimeType );
        
        rd.setContentLength(contentLength);
        if (!useBinding) {
            rd.setName(uri.lastSegment());
        }
        
        XContentStore.setContentId(contentId, rd);
        
        newDesc.setReadOnly(false);
        newDesc.setObjectNode(new SubjectNode(uriStr));
        newDesc.setRevisionDescriptor(rd);
        newDesc.setRevisionDescriptors(createNRDs( uriStr ));
        
        if( logger.isLoggable(Level.FINE) )
            logger.exiting( LOGNAME, "createMemberDescriptor" );
        return newDesc;
    }
    
    /**
     ** Create NodeRevisionDescriptor object for a non-collection,
     ** non-versioned resource.
     **
     ** @param      uriStr the URI string
     ** @return     a new NodeRevisionDescriptor object
     **/
    public static NodeRevisionDescriptor createMemberNRD( String uriStr ) {
        String mimeType = DEFAULT_MIME_TYPE;
        
        //get the extension to derive the mime type
        String[] uriTokens = Strings.tokenizeUri( uriStr );
        int l = uriTokens.length;
        String e = Strings.getFileExtension( uriTokens[l-1] ); //the extension
        
        Iterator i = Env.get().getMimeMapping().getMimeTypes(e).iterator();
        if( i.hasNext() )
            mimeType = (String)i.next();
        
        return createMemberNRD( uriStr, mimeType );
    }
    
    /**
     ** Create NodeRevisionDescriptor object for a non-collection,
     ** non-versioned resource using the given mimeType.
     **
     ** @param      uriStr the URI string
     ** @param      mimeType the mime type
     ** @return     a new NodeRevisionDescriptor object
     **/
    public static NodeRevisionDescriptor createMemberNRD( String uriStr, String mimeType ) {
        String mainBranch = NodeRevisionDescriptors.MAIN_BRANCH;
        NodeRevisionNumber rn = new NodeRevisionNumber();
        NodeRevisionDescriptor rd = new NodeRevisionDescriptor(
            rn, mainBranch, new Vector(), new Hashtable() );
        
        rd.setName(new XUri(uriStr).lastSegment());
        rd.setCreationDate(new Date());
        rd.setLastModified(new Date());
        rd.setContentType( mimeType );
        rd.setResourceType("");
        rd.setETag(uriStr.hashCode() + "_"
                       + rd.getRevisionNumber().hashCode() + "_"
                       + rd.getContentLength());
        rd.setSource("");
        
        return rd;
    }
    
    /**
     ** Create NodeRevisionDescriptors object for a non-versioned resource.
     **
     ** @param      uriStr the URI string
     ** @return     a new NodeRevisionDescriptors object
     **/
    public static NodeRevisionDescriptors createNRDs( String uriStr ) {
        String mainBranch = NodeRevisionDescriptors.MAIN_BRANCH;
        NodeRevisionNumber rn = new NodeRevisionNumber();
        Hashtable bs = new Hashtable();
        Hashtable lrns = new Hashtable();
        
        bs.put( rn, new Vector() );
        lrns.put( mainBranch, rn );
        NodeRevisionDescriptors rds =
            new NodeRevisionDescriptors(
            uriStr, rn, new Hashtable(), lrns, bs, false ); // isVersioned=false
        
        return rds;
    }
    
    /**
     ** Create NodeRevisionDescriptor object for a collection resource.
     **
     ** @param      uriStr the URI string
     ** @return     a new NodeRevisionDescriptor object
     **/
    public static NodeRevisionDescriptor createCollectionNRD( String uriStr ) {
        String mainBranch = NodeRevisionDescriptors.MAIN_BRANCH;
        NodeRevisionNumber rn = new NodeRevisionNumber();
        NodeRevisionDescriptor rd = new NodeRevisionDescriptor(
            rn, mainBranch, new Vector(), new Hashtable() );
        
        rd.setName(new XUri(uriStr).lastSegment());
        rd.setCreationDate(new Date());
        rd.setLastModified(new Date());
        rd.setContentType("");
        rd.setContentLength( 0 );
        rd.setResourceType(NodeRevisionDescriptor.COLLECTION_TYPE);
        rd.setSource("");
        return rd;
    }
    
    /**
     ** Create NodeRevisionDescriptor object for a passed resource.
     **
     ** @param      uriStr the URI string
     ** @return     a new NodeRevisionDescriptor object
     **/
    public static NodeRevisionDescriptor createNRD(String uriStr, String resourceType) {
        String mainBranch = NodeRevisionDescriptors.MAIN_BRANCH;
        NodeRevisionNumber rn = new NodeRevisionNumber();
        NodeRevisionDescriptor rd = new NodeRevisionDescriptor(
            rn, mainBranch, new Vector(), new Hashtable() );
        
        rd.setName(new XUri(uriStr).lastSegment());
        rd.setCreationDate(new Date());
        rd.setLastModified(new Date());
        rd.setContentType("");
        rd.setContentLength( 0 );
        rd.setResourceType(resourceType);
        rd.setSource("");
        return rd;
    }
    
    /**
     * Create an XDescriptor for a collection.
     * This method is a factory method to create a new descriptors for a collection.
     *
     * @pre        (uri != null)
     * @pre        (taminoCollection != null)
     *
     * @param      uriStr                 uri
     * @param      taminoCollection       taminoCollection ( root directory )
     *
     * @return     XDescriptor
     * @exception  XException             xdav error
     */
    public static XDescriptors createCollectionDescriptor(
        boolean useBinding,
        String taminoCollection,
        String uriStr
    )
        throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( LOGNAME, "createCollectionDescriptor", new Object[]{taminoCollection, uriStr});
        
        XDescriptors        newDesc = createUriDescriptor( useBinding, taminoCollection, uriStr );
        SubjectNode         newNode = new SubjectNode(uriStr);
        NodeRevisionDescriptor   rd = createCollectionNRD( uriStr );
        NodeRevisionDescriptors rds = createNRDs( uriStr );
        
        // set CDATA
        if (!useBinding) {
            rd.setName(new XUri(uriStr).lastSegment());
        }
        
        newDesc.setReadOnly(false);
        newDesc.setObjectNode(newNode);
        newDesc.setRevisionDescriptor(rd);
        newDesc.setRevisionDescriptors(rds);
        
        if( logger.isLoggable(Level.FINE) )
            logger.exiting( LOGNAME, "createCollectionDescriptor" );
        return newDesc;
    }
    
    /**
     * Create a Descriptors object using given collection and uri.
     * Call this within XDescriptorsHandler.
     *
     * @pre        collection != null
     * @pre        collection.length() gt 0
     * @post       true
     *
     * @param      collection  to which corresponding Content documents
     *             are written
     * @param      uri uri
     */
    public static XDescriptors createUriDescriptor (boolean useBinding, String collection, String uri) {
        return createUuriDescriptor(collection, XUuri.create(useBinding, uri));
    }
    
    /**
     * Create a Descriptors object using given collection and uri.
     * Call this within XDescriptorsHandler.
     *
     * @pre        collection != null
     * @pre        collection.length() gt 0
     * @post       true
     *
     * @param      collection  to which corresponding Content documents
     *             are written
     * @param      uri uri
     */
    private static XDescriptors createUuriDescriptor (String collection, String uuri) {
        Namespace contentNamespace;
        String contentCollection;
        String prefix;
        int pos;
        
        pos = collection.indexOf(":");
        if (pos != -1) {
            prefix = collection.substring (0, pos);
            contentCollection = collection.substring (pos + 1);
            contentNamespace = Namespace.getNamespace(prefix, XSchemaFactory.getDefaultNamespace (collection));
        } else {
            contentNamespace = null;
            contentCollection = collection;
            prefix = null;
        }
        return new XDescriptors(contentCollection, contentNamespace, uuri);
    }
    
    /**
     * Creates a descriptors object from a JDOM document.
     * When called after object is read from datastore, state is NO_STATE, when
     * called from getClone (), take whatever state is.
     * <p>
     * Is protected to give test tools the chance to overwrite and manipulate the state.
     *
     * @pre        (root != null)
     * @post
     *
     * @param      root a jdom element representing the Descriptors object
     */
    public static XDescriptors loadDescriptor(Element root) {
        String uuri;
        
        uuri = root.getAttributeValue(A_URI);
        return loadDescriptor(uuri, root, XObjectNode.toSlideExtract(root, uuri));
    }
    
    public static XDescriptors load_0_x_Descriptor (boolean useBinding, Element root, XUriCache uriCache) {
        String uri;
        String uuri;
        
        uri = root.getAttributeValue(A_URI);
        uuri = uriCache.getOrCreate(useBinding, uri);
        return loadDescriptor(uuri, root, XObjectNode.toSlideExtractFrom_0_x(useBinding, root, uri, uuri, uriCache));
    }
    
    private static XDescriptors loadDescriptor (String uuri, Element root, ObjectNode objectNode) {
        XStateMachine stateMachine;
        XDescriptors d;
        int aspects;
        String inoId;
        
        int state;
        
        try {
            aspects = root.getAttribute (A_ASPECTS).getIntValue();
        } catch (DataConversionException e) {
            throw new XAssertionFailed(e);
        }
        inoId = XUtilDbSession.getInoId(root);
        state = (inoId == null)? IDesc.CREATED : IDesc.UNCHANGED;
        stateMachine = new XStateMachine (aspects, state);
        d = new XDescriptors(inoId, root.getName(), root.getNamespace(),
                             objectNode, XNodeRevisionDescriptors.toSlideExtract(root),
                             XNodeRevisionDescriptor.toSlideExtract(root), XNodeLock.toSlideListExtract(root),
                             uuri, stateMachine);
        return d;
    }
}


