/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.diagram.handler.impl; import java.util.Collection; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.LifeCycle; import org.simantics.g2d.diagram.handler.TransactionContext; import org.simantics.g2d.element.IElement; import org.simantics.utils.datastructures.ListenerList; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; /** * @author Tuukka Lehtonen */ public class LockingTransactionContext implements TransactionContext, LifeCycle { public static final LockingTransactionContext INSTANCE = new LockingTransactionContext(); private static final Key TRANSACTION_LISTENERS = new KeyOf(ListenerList.class, "TRANSACTION LISTENERS"); private static final Key TRANSACTION_LOCK = new KeyOf(ReadWriteLock.class, "TRANSACTION LOCK"); @Override public void onDiagramCreated(IDiagram diagram) { diagram.setHint(TRANSACTION_LISTENERS, new ListenerList(TransactionListener.class)); diagram.setHint(TRANSACTION_LOCK, new ReentrantReadWriteLock(true)); } @Override public void onDiagramLoaded(IDiagram diagram, Collection initialElements) { } @Override public void onDiagramDestroyed(IDiagram diagram) { } @Override public void onDiagramDisposed(IDiagram diagram) { diagram.removeHint(TRANSACTION_LISTENERS); diagram.removeHint(TRANSACTION_LOCK); } @Override public void addTransactionListener(IDiagram d, TransactionListener l) { ListenerList ll; synchronized (d) { ll = d.getHint(TRANSACTION_LISTENERS); if (ll == null) { ll = new ListenerList(TransactionListener.class); d.setHint(TRANSACTION_LISTENERS, ll); } } ll.add(l); } @Override public void removeTransactionListener(IDiagram d, TransactionListener l) { synchronized (d) { ListenerList ll = d.getHint(TRANSACTION_LISTENERS); if (ll == null) return; ll.remove(l); } } void lock(ReadWriteLock lock, TransactionType type) { switch (type) { case READ: lock.readLock().lock(); return; case WRITE: lock.writeLock().lock(); return; } } void unlock(ReadWriteLock lock, TransactionType type) { switch (type) { case READ: lock.readLock().unlock(); return; case WRITE: lock.writeLock().unlock(); return; } } @Override public Transaction startTransaction(IDiagram d, TransactionType type) { ReadWriteLock lock = d.getHint(TRANSACTION_LOCK); assert lock != null; lock(lock, type); try { Transaction t = new Transaction.Impl(type); ListenerList ll = d.getHint(TRANSACTION_LISTENERS); if (ll != null) for (TransactionListener l : ll.getListeners()) l.transactionStarted(d, t); return t; } catch (RuntimeException e) { unlock(lock, type); throw e; } } @Override public void finishTransaction(IDiagram d, Transaction t) { ReadWriteLock lock = d.getHint(TRANSACTION_LOCK); assert lock != null; try { ListenerList ll = d.getHint(TRANSACTION_LISTENERS); if (ll != null) for (TransactionListener l : ll.getListeners()) l.transactionFinished(d, t); } finally { unlock(lock, t.getType()); } } }