/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.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://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */
package com.sun.jini.mercury;

import net.jini.id.Uuid;


import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.LinkedList;
import java.util.List;

import net.jini.core.event.RemoteEvent;
 
/**
 * This class serves as a factory for generating <tt>EventLogIterator</tt>
 * objects. The iterator objects are cached so that subsequent calls 
 * for the same iterator return the same object.
 *
 * @author Sun Microsystems, Inc.
 *
 * @since 1.1
 */
class EventLogFactory {

    /** <tt>Map</tt> that contains references to generated iterators */
    private HashMap iterators = new HashMap();

    /** 
     * Method to return the iterator object for the designated 
     * <tt>Uuid</tt>. The <tt>File</tt> argument determines where the
     * persistence store will be maintained and is only used for the
     * first instance of the iterator. (Note that upon recovery
     * from a restart/crash the <tt>Uuid</tt> will already exist so the
     * <tt>logPath</tt> arg will not be used.)
     * Subsequent calls for the same <tt>Uuid</tt> will return the 
     * original object.
     */
    public EventLogIterator iterator(Uuid uuid, File logPath) {

        // Try to get reference from cache
        EventLogIteratorImpl eli = (EventLogIteratorImpl)iterators.get(uuid);
        if (eli == null) { // doesn't exist, so create one
            eli = new EventLogIteratorImpl(uuid, logPath);
	    try { 
		eli.init(); // initialize the iterator
	    } catch (IOException ioe) {
		// ignore ... the next usage of this
		// object will throw IOException
	    }
            iterators.put(uuid, eli); // add to cache
	}
            
	return eli;
    }
    
    /** 
     * Method to return the iterator object for the designated 
     * <tt>Uuid</tt>. 
     * Subsequent calls for the same <tt>Uuid</tt> will return the 
     * original object.
     */
    public EventLogIterator iterator(Uuid uuid) {

        // Try to get reference from cache
        EventLogIteratorImpl eli = (EventLogIteratorImpl)iterators.get(uuid);
        if (eli == null) { // doesn't exist, so create one
            eli = new EventLogIteratorImpl(uuid);
            iterators.put(uuid, eli); // add to cache
	}
            
	return eli;
    }

    /**
     * Remove the <TT>EventLogIterator</TT> associated with the given
     * <TT>Uuid</TT>. This is (presumably) called to flush an existing
     * <TT>EventLogIterator</TT> whose storage location has changed.
     * This way, the next call to <TT>iterator</TT> will produce a new
     * object instead of returning the cached version.
     */
    public void remove(Uuid uuid) {
        iterators.remove(uuid); // remove from cache, if it's there
    }

    /**
     * Private class which implements the <tt>EventLogIterator</tt>
     * interface. This class delegates to an <tt>EventLog</tt> for most
     * of its functionality.
     */
    private static class EventLogIteratorImpl 
	implements EventLogIterator
    {
      
        /** The associated <tt>Uuid</tt> for this iterator */
        private final Uuid uuid;

        /** The associated <tt>EventLog</tt> for this iterator */
        private EventLog log = null; 

        /** 
         * Simple constructor that assigns the <tt>Uuid</tt>
         * field to the appropriate internal field and creates
         * a  persistent <tt>EventLog</tt> object using the provided
         * <tt>File</tt> argument.
         */
        EventLogIteratorImpl(Uuid id, File logPath) {
            uuid = id;
	    log = new PersistentEventLog(id, logPath);
        }

        /** 
         * Simple constructor that assigns the <tt>Uuid</tt>
         * field to the appropriate internal field and creates
	 * a transient event log.
	 */
        EventLogIteratorImpl(Uuid id) {
            uuid = id;
	    log = new TransientEventLog(id);
        }

	// Inherit documentation from supertype
        public void init() throws IOException {
	    log.init();
	}

        // Inherit documentation from supertype
        public void add(RemoteEvent evt) throws IOException, 
		IllegalArgumentException 
	{
            log.add(evt);
	}
            
        // Inherit documentation from supertype
        public boolean hasNext()  throws IOException {
            return !log.isEmpty();
        }

        // Inherit documentation from supertype
        public RemoteEvent next() throws IOException, 
		ClassNotFoundException, NoSuchElementException 
	{
            return log.next();
        }

        // Inherit documentation from supertype
        public void remove() throws IOException, IllegalStateException {
            log.remove();
        }

        // Inherit documentation from supertype
        public void destroy() throws IOException {
            log.close();
	    log.delete();
        }
    }
}


