/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/tools/repairer/ContentCheck.java,v 1.4 2004/12/15 10:38:27 pnever Exp $
 * $Revision: 1.4 $
 * $Date: 2004/12/15 10:38:27 $
 *
 * ====================================================================
 *
 * 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.tools.repairer;

import java.util.*;

import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.store.tamino.common.IContent;
import org.apache.slide.store.tamino.common.XContent;
import org.apache.slide.store.tamino.common.XContentNotFoundException;
import org.apache.slide.store.tamino.common.XDatastoreException;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.datastore.XPathFactory;
import org.apache.slide.store.tamino.jdomobjects.XDescriptors;
import org.apache.slide.store.tamino.jdomobjects.XDescriptorsUtil;
import org.apache.slide.store.tamino.tools.stores.XStore;
import org.apache.slide.store.tamino.tools.stores.XStoreGroup;
import org.apache.slide.util.XException;
import org.apache.slide.util.XUri;
import org.apache.slide.webdav.util.DeltavConstants;


/**
 ** Test the content length in meta data.
 ** This class compares the real content length of document with the
 ** content length entered in meta data. A wrong content will be
 ** repaired by entering the real content length.
 **
 ** @author    Hardy.Kiefer@softwareag.com
 ** @author    Peter.Nevermann@softwareag.com
 ** @version   $Revision: 1.4 $
 **/


///////////// TODO: has to run after uuri unique check and after history fixes //////////////////////////


public class ContentCheck extends AbstractCheck implements RepairConstants {
    
    XStoreGroup group;
    XTaminoClient taminoClient4History;
    
    /**
     ** Creates a test to match the content length of the document.
     **
     ** @pre        (taminoClient != null)
     **
     ** @param      taminoClient       db session
     **/
    protected ContentCheck(XStoreGroup storeGroup, XTaminoClient taminoClient, XTaminoClient taminoClient4History) throws XException {
        super(taminoClient, MSG_START_CONTENT_INCONSISTENCIES, MSG_DESCR_CONTENT_INCONSISTENCIES);
        this.group = storeGroup;
        this.taminoClient4History = taminoClient4History;
    }
    
    /**
     * This method checks the content length in metadata.
     * The method reads the document from database, get the length of it and compare it
     * with the length entered in meta data.
     */
    public void check() throws XException {
        Iterator iter;
        Map allUuris;
        String uuri;
        XDescriptors desc;
        IContent content;
        Enumeration revisions;
        NodeRevisionDescriptor revision;
        NodeRevisionNumber nrn;
        String contentId;
        String inoId;
        Map.Entry e;
        
        List patchesToApply = new ArrayList();
        allUuris = taminoClient.getDescriptorsAccessor().getUurisMap(XPathFactory.uuris( taminoCollection )); // inoId -> uuri
        iter = allUuris.entrySet().iterator();
        while ( iter.hasNext() ) {
            e = (Map.Entry) iter.next();
            inoId = (String) e.getKey();
            uuri = (String) e.getValue();
            desc = taminoClient.getDescriptorsAccessor().readOneRwDescriptorsByUuri( uuri );
            revisions = desc.getRevisionDescriptorList();
            while (revisions.hasMoreElements()) {
                revision = (NodeRevisionDescriptor) revisions.nextElement();
                nrn = revision.getRevisionNumber();
                boolean isCollection = revision.propertyValueContains(NodeRevisionDescriptor.RESOURCE_TYPE, "collection");
                if (!isCollection) {
                    contentId = XDescriptorsUtil.getContentId(desc, nrn);
                    if (contentId != null) {
                        try {
                            content = taminoClient.getContentAccessor().readContent(contentId);
                        }
                        catch (XContentNotFoundException notFound) {
                            content = null;
                        }
                        
                        if (content != null) {
                            long correctContentLength = content.getLength();
                            long metadataContentLength = XDescriptorsUtil.getContentLength(desc, revision.getRevisionNumber());
                            if ( correctContentLength != metadataContentLength) {
                                logAndApplyPatch(new SetContentLength(desc, revision.getRevisionNumber(), correctContentLength, metadataContentLength));
                            }
                        }
                        else {
                            String msg = XRepairLogWriter.format(MSG_FOUND_METADATA_WITHOUT_CONTENT,
                                                                 new Object[] { (String) allUuris.get(inoId), contentId });
                            NodeProperty p = revision.getProperty(DeltavConstants.P_CHECKED_OUT);
                            if (p == null) {
                                p = revision.getProperty(DeltavConstants.P_CHECKED_IN);
                            }
                            IContent versionContent = null;
                            XDescriptors versionDesc = null;
                            String version = null;
                            NodeRevisionNumber versionNumber = null;
                            if (p != null) {
                                String versionUri = ((String)p.getValue());
                                versionUri = versionUri.substring(versionUri.indexOf('>') + 1, versionUri.indexOf("</"));
                                version = new XUri(versionUri).lastSegment();
                                versionNumber = new NodeRevisionNumber(version);
                                versionDesc = taminoClient4History.getDescriptorsAccessor()
                                    .readDescriptorsByPath(versionUri, group.getMainStore().useBinding());
                                String versionContentId = XDescriptorsUtil.getContentId(versionDesc, versionNumber);
                                if (versionContentId != null) {
                                    try {
                                        versionContent = taminoClient4History.getContentAccessor().readContent(versionContentId);
                                        versionContent = new XContent(versionContent.getContent(), versionDesc.getRevisionDescriptor(versionNumber));
                                        versionContent.setContentId(versionContentId);
                                    }
                                    catch (XContentNotFoundException notFound) {}
                                }
                            }
                            
                            if (versionContent == null) {
                                msg += "; metadata document will be removed";
                                patchesToApply.add(new RemoveDescriptor(inoId, msg));
                                // logAndApplyPatch(new RemoveDescriptor(inoId, msg));
                            }
                            else {
                                // restore content from history
                                msg += "; content will be restored from history";
                                patchesToApply.add(new RestoreContentFromHistory(group.getMainStore(), desc, nrn, versionContent, msg));
                                //logAndApplyPatch(new RestoreContentFromHistory(group.getMainStore(), desc, nrn, versionContent, msg));
                            }
                        }
                    }
                    else {
                        // desc is a collection
                        //
                        // TODO: check there are no children
                    }
                }
            }
        }
        // apply patches
        Iterator pit = patchesToApply.iterator();
        while (pit.hasNext()) {
            Patch p = (Patch)pit.next();
            logAndApplyPatch(p);
        }
    }
    
    public static class RestoreContentFromHistory extends Patch {
        XStore store;
        XDescriptors desc;
        NodeRevisionNumber nrn;
        IContent versionContent;
        String message;
        
        /**
         * Constructor
         *
         * @param    store               a  XStore
         * @param    desc                a  XDescriptors
         * @param    nrn                 a  NodeRevisionNumber
         * @param    versionContent      an IContent
         * @param    message             a  String
         */
        public RestoreContentFromHistory(XStore store, XDescriptors desc, NodeRevisionNumber nrn, IContent versionContent, String message) {
            this.store = store;
            this.desc = desc;
            this.nrn = nrn;
            this.versionContent = versionContent;
            this.message = message;
        }
                
        /**
         * Method apply
         *
         * @param    taminoClient        a  XTaminoClient
         * @throws   XException
         */
        public void apply(XTaminoClient taminoClient) throws XException {
            IContent result = taminoClient.writeContent(store, versionContent);
            String contentId = result.getContentId();
            NodeRevisionDescriptor nrd = desc.getRevisionDescriptor(nrn);
            
            taminoClient.setContentId(contentId, nrd);
            nrd.setContentLength (result.getLength());
            nrd.setLastModified (new Date());
            nrd.removeProperty (XGlobals.IS_XML, XGlobals.TAMINO_NAMESPACE_URI);
            NodeProperty isXmlProp =
                new NodeProperty (XGlobals.IS_XML,
                                  new Boolean (result.getIsXml()),
                                  XGlobals.TAMINO_NAMESPACE_URI);
            isXmlProp.setKind (NodeProperty.Kind.PROTECTED);
            nrd.setProperty (isXmlProp);
            
            desc.setReadOnly(false);
            desc.removeRevisionDescriptor(nrd);
            desc.setRevisionDescriptor(nrd);
            taminoClient.getDescriptorsAccessor().update(desc);
        }
        
        /**
         * Method log
         *
         * @param    logWriter           a  XRepairLogWriter
         */
        public void log(XRepairLogWriter logWriter) {
            logWriter.println(TC_CHECK_DETAILS, message);
        }
        
    }
    
    // TODO: add empty content, don't remove descriptor. This allows to get rid of taminoCollection
    // and to turn this into a DescriptorsPatch
    public static class RemoveDescriptor extends Patch {
        private final String inoId;
        private final String message;
        
        public RemoveDescriptor(String inoId, String message) {
            this.inoId = inoId;
            this.message = message;
        }
        
        public void apply(XTaminoClient taminoClient) throws XException {
            taminoClient.getDescriptorsAccessor().deleteById(inoId);
        }
        
        public void log(XRepairLogWriter logWriter) {
            logWriter.println(TC_CHECK_DETAILS, message);
        }
    }
    
    public static class SetContentLength extends Patch {
        private final XDescriptors desc;
        private final NodeRevisionNumber nrn;
        private final long correctContentLength;
        private final long wrongContentLength;
        
        public SetContentLength(XDescriptors desc, NodeRevisionNumber nrn, long correctContentLength, long wrongContentLength) {
            this.desc = desc;
            this.nrn = nrn;
            this.correctContentLength = correctContentLength;
            this.wrongContentLength = wrongContentLength;
        }
        
        public void apply(XTaminoClient taminoClient) throws XDatastoreException {
            NodeRevisionDescriptor revision;
            
            revision =  desc.getRevisionDescriptor(nrn); //default and change only 1.0
            revision.setContentLength(correctContentLength); //value is povided by the method
            desc.setReadOnly(false);
            desc.removeRevisionDescriptor(revision);
            desc.setRevisionDescriptor(revision);
            taminoClient.getDescriptorsAccessor().update(desc);
        }
        
        public void log(XRepairLogWriter logWriter) {
            logWriter.println(TC_CHECK_DETAILS, MSG_FOUND_CONTENTLENGTH_MISMATCH,
                              new Object[] { desc.getUuri(), new Long(correctContentLength), new Long(wrongContentLength)});
            
        }
    }
}


