/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.filesystems;
import java.io.*;
import java.util.Enumeration;
import org.openide.util.enums.QueueEnumeration;
/**
*
* @author rmatous
* @version
*/
class EventControl {
/** number of requests posted and not processed. to
* know what to do in sync.
*/
private int requests;
/** number of priority requests (requested from priority atomic block) posted
* and not processed. to
* know what to do in sync.
*/
private int priorityRequests;
/** Holds current propagation ID and link to previous*/
private AtomicActionLink currentAtomAction;
/** List of requests */
private QueueEnumeration requestsQueue;
/**
* Method that can fire events directly, postpone them, fire them in
* standalone thread or in RequestProcessor
*/
void dispatchEvent (FileSystem.EventDispatcher dispatcher) {
if (postponeFiring (dispatcher)) return;
dispatcher.run();
}
/**
* Begin of priority atomic actions. Atomic actions from inside of org.openide.FileSystems
* are considered as priority atomic actions. From last priority atomic actions
* are fired events regardless if nested in any normal atomic action.
*
* Begin of block, that should be performed without firing events.
* Firing of events is postponed after end of block .
* There is strong necessity to use always both methods: beginAtomicAction
* and finishAtomicAction. It is recomended use it in try - finally block.
* @see FileSystemt#beginAtomicAction
* @param run Events fired from this atomic action will be marked as events
* that were fired from this run.
*/
void beginAtomicAction (FileSystem.AtomicAction run) {
enterAtomicAction (run, true);
}
/**
* End of priority atomic actions. Atomic actions from inside of org.openide.FileSystems
* are considered as priority atomic actions. From last priority atomic actions
* are fired events regardless if nested in any normal atomic action.
*
* End of block, that should be performed without firing events.
* Firing of events is postponed after end of block .
* There is strong necessity to use always both methods: beginAtomicAction
* and finishAtomicAction. It is recomended use it in try - finally block.
* @see FileSystemt#finishAtomicAction
*/
void finishAtomicAction () {
exitAtomicAction (true);
}
/** Executes atomic action. The atomic action represents a set of
* operations constituting one logical unit. It is guaranteed that during
* execution of such an action no events about changes in the filesystem
* will be fired.*/
void runAtomicAction (final FileSystem.AtomicAction run) throws IOException {
try {
enterAtomicAction (run, false);
run.run ();
} finally {
exitAtomicAction (false);
}
}
/** Enters atomic action.
*/
private synchronized void enterAtomicAction (Object propID, boolean priority) {
AtomicActionLink nextPropID = new AtomicActionLink (propID);
nextPropID.setPreviousLink (currentAtomAction);
currentAtomAction = nextPropID;
if (priority) priorityRequests++;
if (requests++ == 0) {
requestsQueue = new QueueEnumeration ();
}
}
/** Exits atomic action.
*/
private void exitAtomicAction (boolean priority) {
boolean fireAll = false;
boolean firePriority = false;
Enumeration reqQueueCopy;
synchronized (this) {
currentAtomAction = currentAtomAction.getPreviousLink();
requests--;
if (priority)
priorityRequests--;
if (requests == 0)
fireAll = true;
if (!fireAll && priority && priorityRequests == 0)
firePriority = true;
if (fireAll || firePriority) {
reqQueueCopy = requestsQueue;
requestsQueue = null;
priorityRequests = 0;
} else
return;
}
/** firing events outside synchronized block*/
if (fireAll) {
invokeDispatchers (false, reqQueueCopy);
return;
}
if (firePriority) {
requestsQueue = new QueueEnumeration ();
QueueEnumeration newReqQueue = invokeDispatchers (true, reqQueueCopy);
synchronized (this) {
while (requestsQueue != null && requestsQueue.hasMoreElements()) {
FileSystem.EventDispatcher r = (FileSystem.EventDispatcher)requestsQueue.nextElement();
newReqQueue.put(r);
}
requestsQueue = newReqQueue;
}
}
}
private QueueEnumeration invokeDispatchers (boolean priority, Enumeration reqQueueCopy) {
QueueEnumeration newEnum = new QueueEnumeration ();
while (reqQueueCopy != null && reqQueueCopy.hasMoreElements()) {
FileSystem.EventDispatcher r = (FileSystem.EventDispatcher)reqQueueCopy.nextElement();
r.dispatch(priority);
if (priority) newEnum.put (r);
}
return newEnum;
}
/* Adds dispatcher to queue.*/
private synchronized boolean postponeFiring (FileSystem.EventDispatcher disp) {
if (priorityRequests == 0) {
disp.setAtomicActionLink (currentAtomAction);
disp.dispatch(true);
}
if (requestsQueue != null) {
// run later
disp.setAtomicActionLink (currentAtomAction);
requestsQueue.put (disp);
return true;
}
return false;
}
/** Container that holds hierarchy of propagation IDs related to atomic actions
* Implemented as linked list
*/
static final class AtomicActionLink {
private AtomicActionLink upper;
private Object propagationID;
AtomicActionLink (Object propagationID) {
this.propagationID = propagationID;
}
Object getAtomicAction () {
return propagationID;
}
void setPreviousLink (AtomicActionLink upper) {
this.upper = upper;
}
AtomicActionLink getPreviousLink () {
return upper;
}
}
}