/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/search/IndexTrigger.java,v 1.5 2005/01/12 19:53:19 masonjm Exp $
 * $Revision: 1.5 $
 * $Date: 2005/01/12 19:53:19 $
 *
 * ====================================================================
 *
 * Copyright 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.search;

import org.apache.slide.common.Domain;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.event.*;
import org.apache.slide.util.conf.Configurable;
import org.apache.slide.util.conf.Configuration;
import org.apache.slide.util.conf.ConfigurationException;
import org.apache.slide.util.logger.Logger;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

/**
 * @version $Revision: 1.5 $
 */

public class IndexTrigger implements EventCollectionListener, Configurable {
    protected static final String LOG_CHANNEL = IndexTrigger.class.getName();

    protected List indexers = new ArrayList();

    public void configure(Configuration configuration) throws ConfigurationException {
        Enumeration contentIndexers = configuration.getConfigurations("indexer");
        while (contentIndexers.hasMoreElements()) {
            Configuration listenerConfig = (Configuration)contentIndexers.nextElement();
            String classname = listenerConfig.getAttribute("classname");
            String uri = listenerConfig.getAttribute("uri", null);
            String contentType = listenerConfig.getAttribute("content-type", null);
            boolean synchronous = listenerConfig.getAttributeAsBoolean("synchronous", false);
             try {
                Class listenerClass = Class.forName(classname);
                Indexer indexer = null;
                try {
                    Method getInstanceMethod = listenerClass.getMethod("getInstance", new Class[0]);
                    indexer = (Indexer)getInstanceMethod.invoke(null, null);
                } catch ( NoSuchMethodException e) {
                    indexer = (Indexer)listenerClass.newInstance();
                }
                if ( indexer instanceof Configurable ) {
                    ((Configurable)indexer).configure(listenerConfig.getConfiguration("configuration"));
                }
                addIndexer(indexer, contentType, uri, synchronous);
            } catch (ClassCastException e) {
                throw new ConfigurationException("Indexer '"+classname+"' is not of type Indexer", configuration);
            } catch (Exception e) {
                throw new ConfigurationException("Indexer '"+classname+"' could not be loaded", configuration);
            }
        }
    }

    public void vetoableCollected(EventCollection collection) throws VetoException {
        try {
            triggerIndexers(collection, true);
        } catch ( IndexException e ) {
            throw new VetoException(e.getMessage());
        }
    }

    public void collected(EventCollection collection) {
        try {
            triggerIndexers(collection, false);
        } catch ( IndexException e ) {
            Domain.log("Index might be out of sync! Reason: "+e.getMessage(), LOG_CHANNEL, Logger.CRITICAL);
        }
    }

    private synchronized void triggerIndexers(EventCollection collection, boolean synchronous) throws IndexException {
        ContentEvent[] update = EventCollectionFilter.getChangedContents(collection);
        for ( int i = 0; i < update.length; i++ ) {
            Indexer[] indexers = getIndexers(update[i].getRevisionDescriptors(), 
                    update[i].getRevisionDescriptor(), synchronous);
            for ( int j = 0; j < indexers.length; j++ ) {
                indexers[j].updateIndex(new Uri(update[i].getToken(),
                        update[i].getNamespace(), 
                        update[i].getUri()), update[i].getRevisionDescriptor(), 
                        update[i].getRevisionContent());
            }
        }
        ContentEvent[] insert = EventCollectionFilter.getCreatedContents(collection);
        for ( int i = 0; i < insert.length; i++ ) {
            Indexer[] indexers = getIndexers(insert[i].getRevisionDescriptors(), 
                    insert[i].getRevisionDescriptor(), synchronous);
            for ( int j = 0; j < indexers.length; j++ ) {
                indexers[j].createIndex(new Uri(insert[i].getToken(),
                        insert[i].getNamespace(), 
                        insert[i].getUri()), insert[i].getRevisionDescriptor(), 
                        insert[i].getRevisionContent());
            }
        }
        ContentEvent[] delete = EventCollectionFilter.getRemovedContents(collection);
        for ( int i = 0; i < delete.length; i++ ) {
            Indexer[] indexers = getIndexers(delete[i].getRevisionDescriptors(), delete[i].getRevisionDescriptor(), synchronous);
            for ( int j = 0; j < indexers.length; j++ ) {
                indexers[j].dropIndex(new Uri(delete[i].getToken(),
                        delete[i].getNamespace(), 
                        delete[i].getUri()), 
                        delete[i].getRevisionDescriptor() == null ? null : 
                            delete[i].getRevisionDescriptor().getRevisionNumber());
            }
        }
    }

    private void addIndexer(Indexer indexer, String contentType, String uri, boolean synchronous) {
        indexers.add(new IndexerMapping(indexer, contentType, uri, synchronous));
    }

    private Indexer []getIndexers(NodeRevisionDescriptors descriptors, NodeRevisionDescriptor descriptor, boolean synchronous) {
        List matchingIndexers = new ArrayList();
        for ( Iterator i = indexers.iterator(); i.hasNext(); ) {
            IndexerMapping mapping = (IndexerMapping)i.next();
            if ( mapping.isSynchronous() == synchronous && mapping.matches(descriptors, descriptor)) {
                matchingIndexers.add(mapping.getIndexer());
            }
        }
        Indexer[] indexers = new Indexer[matchingIndexers.size()];
        return (Indexer [])matchingIndexers.toArray(indexers);
    };

    class IndexerMapping {
        Indexer indexer;
        String uri, contentType;
        boolean synchronous;

        public IndexerMapping(Indexer indexer, String contentType, String uri, boolean synchronous) {
            this.indexer = indexer;
            this.uri = uri;
            this.contentType = contentType;
            this.synchronous = synchronous;
        }

        public Indexer getIndexer() {
            return indexer;
        }

        public boolean isSynchronous() {
            return synchronous;
        }

        public boolean matches(NodeRevisionDescriptors descriptors, NodeRevisionDescriptor descriptor) {
            boolean matching = true;
            if ( descriptor != null && contentType != null && !descriptor.getContentType().equals(contentType) ) {
                matching = false;
            }
            if ( descriptors != null && uri != null && !descriptors.getUri().startsWith(uri) ) {
                matching = false;
            }
            return matching;
        }
    }
}