/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 com.sun.jini.outrigger;

import java.util.Map;
import java.util.Collection;
import java.util.SortedSet;

/**
 * Given an <code>EntryHandle</code> who's entry is making a
 * visibility transition this class will find all the 
 * <code>TransitionWatcher</code>s who are interested in that
 * transition. The <code>TransitionWatcher</code>s are organized
 * into groups using <code>TemplateHandle</code>. Each
 * <code>TemplateHandle</code> aggregates a number of watchers
 * all interested in the same template.
 * 
 * @see TransitionWatcher
 * 
 */
class TransitionWatchers {
    /** 
     * A map from class names to <code>WatchersForTemplateClass</code>
     * objects 
     */
    final private Map holders = new java.util.HashMap();

    /** The server we are working for */
    final private OutriggerServerImpl server;

    /**
     * Create a new <code>TransitionWatchers</code> object
     * for the specified server.
     * @param server The server the new <code>TransitionWatchers</code> 
     *               object is working for.
     * @throws NullPointerException if <code>server</code> is 
     *        <code>null</code>
     */
    TransitionWatchers(OutriggerServerImpl server) {
	if (server == null)
	    throw new NullPointerException("server must be non-null");
	this.server = server;
    }

    /**
     * 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.
     * <p>
     * This method is thread safe. The watcher added in this call is
     * guaranteed to be consulted by the next call to
     * <code>allMatches</code> that starts after this call completes even
     * if that call is made from another thread. Also, all of
     * of the assigned values in the calling thread's working
     * memory will be copied out to main memory as part of the 
     * process of making the passed watcher visible to future
     * <code>allMatches</code> and <code>findTransitionWatcher</code> calls.
     *
     * @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) {
	// Get/create the appropriate WatchersForTemplateClass
	final String className = template.classFor();
	WatchersForTemplateClass holder;
	synchronized (holders) {
	    holder = (WatchersForTemplateClass) holders.get(className);	    
	    if (holder == null) {
		holder = new WatchersForTemplateClass(this);
		holders.put(className, holder);
	    }
	}

	// Add the watcher to the WatchersForTemplateClass
	holder.add(watcher, template);
    }

    /**
     * Return a <code>SortedSet</code> of all the
     * <code>TransitionWatcher</code> who's <code>isInterested</code>
     * methods return <code>true</code> when asked about the specified
     * visibility transition.
     * <p>
     * This method is thread safe. This call is guaranteed to check unremoved
     * watchers that were added by <code>add</code> calls that completed
     * before this call started, even if the calls were made from
     * different threads. Before the <code>isInterested</code> method
     * of the first watcher is called the working memory of this thread
     * will be flushed so any changes made to main memory before
     * this call started will be visible.
     * 
     * @param transition A <code>EntryTransition</code> that
     *              describes the transition and what
     *              entry is transitioning. This method
     *              will assume that <code>transition.getHandle</code>
     *              returns a non-null value.
     * @param ordinal The ordinal associated with <code>transition</code>.
     * @return A new <code>SortedSet</code> of all the 
     *         <code>TransitionWatcher</code>s interested in the specified
     *         visibility transition. If none are interested an empty
     *         map will be returned.
     * @throws NullPointerException if <code>transition</code> is 
     *         <code>null</code>.
     */
    SortedSet allMatches(EntryTransition transition, long ordinal) {
	final EntryRep rep = transition.getHandle().rep();
	final SortedSet rslt = new java.util.TreeSet();
	final String className = rep.classFor();
	WatchersForTemplateClass holder;
	
	/* Collect all the watchers looking for the exact class of the
	 * transitioned entry. Sync both to protect holder, and
	 * to make sure we do at least one sync so we see any
	 * watchers added before this call.
	 */
	synchronized (holders) {
	    holder = (WatchersForTemplateClass)holders.get(className);	    
	}

	if (holder != null)
	    holder.collectInterested(rslt, transition, ordinal);

	// Get all the templates that are super classes of className
	final String[] superclasses = rep.superclasses();
	for (int i=0; i<superclasses.length; i++) {	    
	    synchronized (holders) {
		holder = 
		    (WatchersForTemplateClass)holders.get(superclasses[i]);
	    }
	    if (holder != null)
		holder.collectInterested(rslt, transition, ordinal);
	}

	// Including those registered for the null template
	final String nullClass = EntryRep.matchAnyEntryRep().classFor();
	synchronized(holders) {
	    holder =
		(WatchersForTemplateClass)holders.get(nullClass);
	}
        
	if (holder!=null){
	    holder.collectInterested(rslt, transition, ordinal);
	}
	
	return rslt;
    }

    /**
     * Visit each <code>TransitionWatcher</code> and check to see if
     * it has expired, removing it if it has.  Also reap the
     * <code>FastList</code>s.
     */
    void reap() {
 	/* This could take a while, instead of blocking all other
	 * access to handles, clone the contents of handles and
	 * iterate down the clone (we don't do this too often and
	 * handles should never be that big so a shallow copy should
	 * not be that bad, if it did get to be bad caching the
	 * clone would probably work well since we don't add (and
	 * never remove) elements that often.)
	 */
	final WatchersForTemplateClass content[];
	synchronized (holders) {
	    final Collection values = holders.values();
	    content = new WatchersForTemplateClass[values.size()];
	    values.toArray(content);
	}

	final long now = System.currentTimeMillis();
	for (int i=0; i<content.length; i++) {
	    content[i].reap(now);
	}	    
    }

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

}
