package org.netbeans.gradle.project.util; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; import org.jtrim.cancel.Cancellation; import org.jtrim.cancel.CancellationToken; import org.jtrim.collections.RefCollection; import org.jtrim.collections.RefLinkedList; import org.jtrim.collections.RefList; import org.jtrim.concurrent.CancelableTask; import org.jtrim.concurrent.MonitorableTaskExecutor; import org.jtrim.concurrent.SyncTaskExecutor; import org.jtrim.concurrent.TaskExecutor; import org.jtrim.concurrent.TaskExecutors; import org.jtrim.event.ListenerRef; import org.jtrim.event.SimpleListenerRegistry; import org.jtrim.property.PropertyFactory; import org.jtrim.property.PropertySource; import org.jtrim.utils.ExceptionHelper; import org.netbeans.gradle.project.properties.NbProperties; public final class CloseableActionContainer { private static final Logger LOGGER = Logger.getLogger(CloseableActionContainer.class.getName()); private final Lock actionsLock; private final RefList<ActionDef> actions; private final MonitorableTaskExecutor openSynchronizer; private boolean opened; public CloseableActionContainer() { this.actionsLock = new ReentrantLock(); this.actions = new RefLinkedList<>(); this.openSynchronizer = TaskExecutors.inOrderSyncExecutor(); this.opened = false; } public static CloseableAction mergeActions(CloseableAction... actions) { final CloseableAction[] actionsCopy = actions.clone(); ExceptionHelper.checkNotNullElements(actionsCopy, "actions"); return new CloseableAction() { @Override public Ref open() { Ref[] result = new Ref[actionsCopy.length]; for (int i = 0; i < result.length; i++) { result[i] = actionsCopy[i].open(); } return mergeActionRefs(result); } }; } private static CloseableAction.Ref mergeActionRefs(final CloseableAction.Ref[] refs) { return new CloseableAction.Ref() { @Override public void close() { for (CloseableAction.Ref ref: refs) { try { ref.close(); } catch (Throwable ex) { LOGGER.log(Level.SEVERE, "Unexpected close exception.", ex); } } } }; } private void executeSync(CancelableTask task) { openSynchronizer.execute(Cancellation.UNCANCELABLE_TOKEN, task, null); } public <Listener> CloseableAction.Ref definePropertyChangeAction( final PropertySource<?> property, final Runnable changeListener) { return definePropertyChangeAction(property, changeListener, SyncTaskExecutor.getSimpleExecutor()); } public <Listener> CloseableAction.Ref definePropertyChangeAction( final PropertySource<?> property, final Runnable changeListener, TaskExecutor executor) { SimpleListenerRegistry<Runnable> asRegistry = NbProperties.asChangeListenerRegistry(property); return defineEventAction(asRegistry, changeListener, executor); } public <Listener> CloseableAction.Ref defineEventAction( final SimpleListenerRegistry<Listener> listenerRegistry, final Listener listener) { return defineEventAction(listenerRegistry, listener, SyncTaskExecutor.getSimpleExecutor()); } public <Listener> CloseableAction.Ref defineEventAction( final SimpleListenerRegistry<Listener> listenerRegistry, final Listener listener, TaskExecutor executor) { ExceptionHelper.checkNotNullArgument(listenerRegistry, "listenerRegistry"); ExceptionHelper.checkNotNullArgument(listener, "listener"); CloseableAction action = new CloseableAction() { @Override public CloseableAction.Ref open() { return toActionRef(listenerRegistry.registerListener(listener)); } }; return defineAction(PropertyFactory.constSource(action), executor); } private static CloseableAction.Ref toActionRef(final ListenerRef ref) { ExceptionHelper.checkNotNullArgument(ref, "ref"); return new CloseableAction.Ref() { @Override public void close() { ref.unregister(); } }; } public CloseableAction.Ref defineAction(CloseableAction action) { return defineAction(action, SyncTaskExecutor.getSimpleExecutor()); } public CloseableAction.Ref defineAction(CloseableAction action, TaskExecutor executor) { ExceptionHelper.checkNotNullArgument(action, "action"); return defineAction(PropertyFactory.constSource(action), executor); } public CloseableAction.Ref defineAction(PropertySource<? extends CloseableAction> actionProperty) { return defineAction(actionProperty, SyncTaskExecutor.getSimpleExecutor()); } public CloseableAction.Ref defineAction(PropertySource<? extends CloseableAction> actionProperty, TaskExecutor executor) { final ActionDef actionDef = new ActionDef(actionProperty, executor); final RefCollection.ElementRef<ActionDef> elementRef; actionsLock.lock(); try { elementRef = actions.addGetReference(actionDef); } finally { actionsLock.unlock(); } executeSync(new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { if (opened) { actionDef.open(); } } }); return new CloseableAction.Ref() { @Override public void close() { actionsLock.lock(); try { elementRef.remove(); } finally { actionsLock.unlock(); } actionDef.closeForGood(); } }; } private List<ActionDef> getActionDefs() { actionsLock.lock(); try { return new ArrayList<>(actions); } finally { actionsLock.unlock(); } } private void openNow() { assert openSynchronizer.isExecutingInThis(); if (opened) { return; } opened = true; for (ActionDef action: getActionDefs()) { action.open(); } } private void closeNow() { assert openSynchronizer.isExecutingInThis(); if (!opened) { return; } opened = false; for (ActionDef action: getActionDefs()) { action.close(); } } public void open() { executeSync(new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { openNow(); } }); } public void close() { executeSync(new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { if (opened) { closeNow(); } } }); } public static interface AddedActionRef extends AutoCloseable { public void replaceAction(CloseableAction action); @Override public void close(); } private static final class ActionDef { private final MonitorableTaskExecutor syncExecutor; private final PropertySource<? extends CloseableAction> actionProperty; private CloseableAction.Ref openedActionRef; private ListenerRef actionChangeRef; private boolean closedForGood; public ActionDef(PropertySource<? extends CloseableAction> actionProperty, TaskExecutor executor) { ExceptionHelper.checkNotNullArgument(actionProperty, "actionProperty"); ExceptionHelper.checkNotNullArgument(executor, "executor"); this.actionProperty = actionProperty; this.actionChangeRef = null; this.syncExecutor = TaskExecutors.inOrderExecutor(executor); this.openedActionRef = null; this.closedForGood = false; } private void executeSync(CancelableTask task) { syncExecutor.execute(Cancellation.UNCANCELABLE_TOKEN, task, null); } public void replaceAction() { executeSync(new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { closeNow(); openNow(); } }); } private void openNow() { assert openedActionRef == null; if (closedForGood) { return; } ListenerRef prevActionChangeRef = actionChangeRef; if (prevActionChangeRef != null) { prevActionChangeRef.unregister(); } actionChangeRef = actionProperty.addChangeListener(new Runnable() { @Override public void run() { replaceAction(); } }); CloseableAction action = actionProperty.getValue(); openedActionRef = action != null ? action.open() : null; } public void open() { executeSync(new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { if (openedActionRef == null) { openNow(); } } }); } private void closeNow() { assert syncExecutor.isExecutingInThis(); ListenerRef prevActionChangeRef = actionChangeRef; if (prevActionChangeRef != null) { actionChangeRef = null; prevActionChangeRef.unregister(); } CloseableAction.Ref toClose = openedActionRef; if (toClose != null) { openedActionRef = null; toClose.close(); } } public void close() { executeSync(new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { closeNow(); } }); } public void closeForGood() { executeSync(new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { closedForGood = true; closeNow(); } }); } } }