package ws.prova.reference;

/*
 * Copyright (C) 2002-2007 <a href="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</a>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

import org.mandarax.kernel.ComplexTerm;
import ws.prova.reagent;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * Exception handler encapsulating the mapping from execption classes to predicates
 * <p>Title: Prova</p>
 * <p>Description: Exception handler encapsulating the mapping from execption classes to predicates</p>
 * <p>Copyright: Copyright (C) 2002-2007</p>
 * <p>Company: City University, London, United Kingdom </p>
 *
 * @author Alex Kozlenkov
 * @version 2.0 <25 March 2007>
 */
public class ExceptionHandler {
	private List handlers;
	private Throwable exception;
	private reagent reagent;
	private boolean clean = true;

	public ExceptionHandler(final reagent reagent, final Throwable exception) {
		handlers = new ArrayList();
		this.reagent = reagent;
		this.exception = exception;
	}

	/**
	 * Copy constructor used to pass the handler recursively
	 *
	 * @param exhandler
	 */
	public ExceptionHandler(final ExceptionHandler exhandler) {
		// Just lazily copying the reference to the previous handlers until
		//    the actual update happens when we set clean=true
		this.handlers = exhandler.handlers;
		this.exception = exhandler.exception;
		this.reagent = exhandler.reagent;
	}

	public reagent prova() {
		return reagent;
	}

	/**
	 * Retrieve the last occurred exception
	 *
	 * @return
	 */
	public Throwable getException() {
		return exception;
	}

	/**
	 * Store the last occurred exception
	 *
	 * @param exception
	 */
	public void setException(final Throwable exception) {
		this.exception = exception;
	}

	/**
	 * Return <code>true</code> if no handlers are registered
	 *
	 * @return
	 */
	public boolean isEmpty() {
		return handlers.isEmpty();
	}

	/**
	 * Return the last predicate handler for an exception class assignable from the class
	 * of the supplied exception.
	 *
	 * @param ex exception to be tested.
	 * @return the predicate handler as a <code>ComplexTerm</code> or <code>null</code> if there is no matching handler.
	 */
	public ComplexTerm handler(final Throwable ex) {
		for (ListIterator iterator = handlers.listIterator(handlers.size()); iterator.hasPrevious();) {
			Object[] pair = (Object[]) iterator.previous();
			if (((Class) pair[0]).isAssignableFrom(ex.getClass())) {
				return (ComplexTerm) pair[1];
			}
		}
		return null;
	}

	/**
	 * Register an exception handler predicate call
	 *
	 * @param exclass a class of an exception
	 * @param ct      the predicate call to execute if the exception of the specified class occurs
	 */
	public void register(final Class exclass, final ComplexTerm ct) {
		if (clean)
			reInit();
		handlers.add(new Object[]{exclass, ct});
	}

	/**
	 * Lazy initialization occurring if the list of handlers is updated.
	 */
	private void reInit() {
		List handlers2 = new ArrayList();
		handlers2.addAll(handlers);
		handlers = handlers2;
		clean = false;
	}

	/**
	 * Unregister all classes from which the supplied class <code>exclass</class> is assignable
	 *
	 * @param exclass
	 */
	public void unregister(Class exclass) {
		if (clean)
			reInit();
		List toRemove = new ArrayList();
		for (Iterator iterator = handlers.iterator(); iterator.hasNext();) {
			Object[] pair = (Object[]) iterator.next();
			if (exclass.isAssignableFrom((Class) pair[0])) {
				toRemove.add(pair);
			}
		}
		for (int i = 0; i < toRemove.size(); i++) {
			Object[] pair = (Object[]) toRemove.get(i);
			handlers.remove(pair);
		}
	}
}
