/*******************************************************************************
 * Copyright (c) 2010 Neil Bartlett.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Neil Bartlett - initial API and implementation
 *******************************************************************************/
package bndtools.utils;

import java.util.concurrent.atomic.AtomicInteger;

import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.widgets.Text;

/**
 * <p>
 * This class is intended for use by UI event listeners that need to execute tasks <b>only</b> when fired as a result of
 * user interaction and not as a result of programmatic changes. For example a {@link ModifyListener} on a {@link Text}
 * field might wish to trigger a validation check when the user types in the field but not when
 * {@link Text#setText(String)} is invoked. Usually this is necessary when the programmatic modification is itself made
 * in response to an event, potentially leading to infinite event loops.
 * </p>
 * <p>
 * <b>NB:</b> This code is not intended for multi-threaded use, but merely for detecting modification operations
 * occurring earlier in the call-stack.
 * </p>
 * 
 * @author Neil Bartlett
 */
public class ModificationLock {

    private final AtomicInteger modifierCount = new AtomicInteger(0);

    /**
     * Perform a programmatic change. Changes are re-entrant, we can always excecute a new programmatic change inside an
     * existing one.
     * 
     * @param runnable
     */
    public void modifyOperation(Runnable runnable) {
        try {
            modifierCount.incrementAndGet();
            runnable.run();
        } finally {
            modifierCount.decrementAndGet();
        }
    }

    /**
     * @return Whether a modification operation is ongoing.
     */
    public boolean isUnderModification() {
        return modifierCount.get() > 0;
    }

    /**
     * Perform an action only if no programmatic changes are in progress.
     * 
     * @param runnable
     */
    public void ifNotModifying(Runnable runnable) {
        if (modifierCount.get() == 0) {
            runnable.run();
        }
    }
}
