package org.exist.collections.triggers;

import org.exist.storage.txn.Txn;

/** Finite State Machine, managing the state of a Running trigger, 
 * allows to avoid infinite recursions by forbidding another trigger to run
 * where there is allready one; feature trigger_update
 * TODO: encapsulate the ThreadLocal ; apply "state" design pattern */
public class TriggerStatePerThread {

	public static final int NO_TRIGGER_RUNNING = (0);
	public static final int TRIGGER_RUNNING_PREPARE = (1);
	public static final int TRIGGER_RUNNING_FINISH = (2);
	
	private static ThreadLocal triggerRunningState = new ThreadLocal() {
	    protected synchronized Object initialValue() {
	        return new TriggerState(NO_TRIGGER_RUNNING);
	    };
	};

	public static boolean verifyUniqueTriggerPerThreadBeforePrepare() {
		if( getTriggerRunningState() == NO_TRIGGER_RUNNING ) {
			setTriggerRunningState( TRIGGER_RUNNING_PREPARE );
			return true;
		} else {
			return false;
		}
	}
	
	public static boolean verifyUniqueTriggerPerThreadBeforeFinish() {
		if ( getTriggerRunningState() == TRIGGER_RUNNING_PREPARE) {
			setTriggerRunningState( TRIGGER_RUNNING_FINISH );
			return true;
		} else {
			return false;
		}
	}
	
	public static class TriggerState {
		private int state;
		private Txn transaction;
		public TriggerState(int state) {
			super();
			this.setState(state);
		}
		
		private void setState(int state) {
			this.state = state;
		}

		private int getState() {
			return state;
		}

		void setTransaction(Txn transaction) {
			this.transaction = transaction;
		}
		Txn getTransaction() {
			return transaction;
		}	
	}
	
	public static int getTriggerRunningState() {
		return ((TriggerState)triggerRunningState.get()).getState();
	}
	
	public static void setTriggerRunningState(int state) {
		((TriggerState)triggerRunningState.get()).setState(state);
	}

	public static Txn getTransaction() {
		return ((TriggerState)triggerRunningState.get()).getTransaction();
	}
	
	public static void setTransaction(Txn transaction) {
		((TriggerState)triggerRunningState.get()).setTransaction(transaction);
	}

}

@@ -280,38 +280,8 @@
 ODO:
- encapsulate the ThreadLocal ;
  app
@@ -765,16 +765,39 @@
 Prepare(
+DocumentTrigger trigger
 ) %7B%0A%09%09if
@@ -899,16 +899,25 @@
 _PREPARE
+, trigger
  );%0A%09%09%09r
@@ -1029,19 +1029,143 @@
 eFinish(
-) %7B
+DocumentTrigger trigger) %7B%0A%0A%09%09// another trigger is allready running%0A%09%09if( trigger != getRunningTrigger() )%0A%09%09%09return false;%0A%09%09
 %0A%09%09if ( 
@@ -1268,16 +1268,25 @@
 G_FINISH
+, trigger
  );%0A%09%09%09r
@@ -1391,16 +1391,58 @@
  state;%0A
+%09%09private DocumentTrigger currentTrigger;%0A
 %09%09privat
@@ -1525,24 +1525,30 @@
 tState(state
+, null
 );%0A%09%09%7D%0A%09%09%0A%09%09
@@ -1565,24 +1565,25 @@
 id setState(
+ 
 int state) %7B
@@ -1583,33 +1583,170 @@
 tate
-) %7B%0A%09%09%09this.state = state
+, DocumentTrigger trigger) %7B%0A%09%09%09this.state = state;%0A%09%09%09if( state == NO_TRIGGER_RUNNING )%0A%09%09%09%09this.currentTrigger = null;%0A%09%09%09else%0A%09%09%09%09this.currentTrigger = trigger
 ;%0A%09%09
@@ -1928,16 +1928,87 @@
 ion;%0A%09%09%7D
+%0A%0A%09%09public DocumentTrigger getTrigger() %7B%0A%09%09%09return currentTrigger;%0A%09%09%7D
 %09%0A%09%7D%0A%09%0A%09
@@ -2113,24 +2113,146 @@
 tState();%0A%09%7D
+%0A%0A%09public static DocumentTrigger getRunningTrigger() %7B%0A%09%09return ((TriggerState)triggerRunningState.get()).getTrigger();%0A%09%7D
 %0A%09%0A%09public s
@@ -2290,24 +2290,49 @@
 te(int state
+, DocumentTrigger trigger
 ) %7B%0A%09%09((Trig
@@ -2381,16 +2381,25 @@
 te(state
+, trigger
 );%0A%09%7D%0A%0A%09
@@ -2503,26 +2503,24 @@
 ction();%0A%09%7D%0A
-%09%0A
 %09public stat

Store the currently running trigger. Not yet in use - will be used by XQueryTrigger and Modification .