/*
 * 
 * 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.outrigger;

import java.util.Set;

/**
 * Holds a collection of <code>TemplateHandle</code>s who's templates
 * are all of exactly the same class. Unless otherwise noted all
 * methods are thread safe. This method provides the linkage between
 * <code>TemplateHandle</code>s and <code>TransitionWatchers</code>
 * and for the most part is not visible to the clients of either.
 */
class WatchersForTemplateClass {
    /** All the templates we know about */
    private final FastList contents = new FastList();	

    /** The object we are inside of */
    private final TransitionWatchers owner;

    /**
     * Create a new <code>WatchersForTemplateClass</code> object
     * associated with the specified <code>TransitionWatchers</code> object.
     * @param owner The <code>TransitionWatchers</code> that
     *              this object will be a part of.
     * @throws NullPointerException if <code>owner</code> is
     *         <code>null</code>.
     */
    WatchersForTemplateClass(TransitionWatchers owner) {
	if (owner == null)
	    throw new NullPointerException("owner must be non-null");
	this.owner = owner;
    }

    /**
     * Add a <code>TransitionWatcher</code> to the list
     * of watchers looking for visibility transitions in
     * entries that match the specified template. Associates
     * a <code>TemplateHandle</code> using 
     * <code>TransitionWatcher.setTemplateHandle</code> method.
     *
     * @param watcher The <code>TransitionWatcher</code> being added.
     * @param template The <code>EntryRep</code> that represents
     *                 the template of interest.
     * @throws NullPointerException if either argument is
     *         <code>null</code>.
     */
    void add(TransitionWatcher watcher, EntryRep template) {
	/* We try to find an existing handle, but it is ok
	 * if we have more than one with the same template. It
	 * is bad if we add the watcher to a removed handle.
	 */	
	TemplateHandle handle = (TemplateHandle)contents.head();
	for (; handle != null; handle = (TemplateHandle)handle.next()) {
	    if (template.equals(handle.rep())) {
		synchronized (handle) {
		    if (!handle.removed()) {
			/* Found one, add and break. Call
			 * setTemplateHandle() before adding to handle
			 * so if the handle calls the watcher it will
			 * be in a complete state. Add inside the
			 * lock so handle can't be removed.
			 */
			watcher.setTemplateHandle(handle);
			handle.addTransitionWatcher(watcher);
			return; // found a handle and added the watcher
		    }
		}
	    }
	}

	/* If we are here we could not find a handle with the right
	 * template, create one, add the watcher to it, and it
	 * to contents.
	 */
	handle = new TemplateHandle(template, this);

	/* We need the sync both to prevent concurrent modification
	 * of handle (since we add it to contents first), and to
	 * make sure other threads see the changes we are about to make.
	 */
	synchronized (handle) {
	    /* First add handle to contents so handle is fully initialized 
	     * before we start to pass it around use
	     */
	    contents.add(handle);
	    watcher.setTemplateHandle(handle);
	    handle.addTransitionWatcher(watcher);
	}
    }
    
    /**
     * Iterate over the watchers associated with 
     * this object calling <code>isInterested</code> on each
     * and if it returns <code>true</code> adding the watcher to the
     * passed set.
     *
     * @param set The set to accumulate interested watchers
     *            into.
     * @param transition The transition being processed.
     * @param ordinal The ordinal associated with <code>transition</code>.
     * @throws NullPointerException if either argument is <code>null</code>.
     */
    void collectInterested(Set set, EntryTransition transition, 
			   long ordinal) 
    {
	final EntryHandle entryHandle = transition.getHandle();
	final EntryRep rep = entryHandle.rep();
	final long entryHash = entryHandle.hash();
	final int repNumFields = rep.numFields();

	/* Look at each of handles, check to see if they match
	 * the changed entry and if they do ask them to 
	 * put the appropriate watchers in the set.
	 */
	for (TemplateHandle handle = (TemplateHandle)contents.head();
	     handle != null;
	     handle = (TemplateHandle)handle.next())
	{
	    // See the if handle mask is incompatible
	    EntryHandleTmplDesc desc = handle.descFor(repNumFields);

	    if ((entryHash & desc.mask) != desc.hash)
		continue;

	    if (handle.matches(rep)) {
		if (handle.removed()) //no sync, ok if we check a removed one
		    continue;
		handle.collectInterested(set, transition, ordinal);
	    }
	}
    }

    /**
     * Return the <code>OutriggerServerImpl</code> this 
     * handle is part of.
     * @return The <code>OutriggerServerImpl</code> this 
     * handle is part of.
     */
    OutriggerServerImpl getServer() {
	return owner.getServer();
    }

    /**
     * Visit each <code>TransitionWatcher</code> and check to see if
     * it has expired, removing it if it has. Also reaps the
     * <code>FastList</code> associated with this object.
     * @param now an estimate of the current time expressed as
     *            milliseconds since the beginning of the epoch.
     */
    void reap(long now) {
	// First remove empty handles
	for (TemplateHandle handle = (TemplateHandle)contents.head();
	     handle != null;
	     handle = (TemplateHandle)handle.next())
	{
	    // Dump any expired watchers.
	    handle.reap(now);

	    /* Need to lock on the handle so no one will
	     * add a watcher between the check for empty and
	     * when it gets marked removed.
	     */
	    synchronized (handle) {
		if (handle.isEmpty()) {
		    contents.remove(handle);
		    continue;
		}
	    }
	}

	/* This provides the FastList with an opportunity to actually
	 * excise the items identified as "removed" from the list.
	 */
	contents.reap();
    }
}

