/******************************************************************************* * Copyright (c) 2015 Pivotal, Inc. * 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: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.livexp; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.springsource.ide.eclipse.commons.livexp.core.DisposeListener; import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; import org.springsource.ide.eclipse.commons.livexp.core.ValueListener; import org.springsource.ide.eclipse.commons.livexp.ui.Disposable; import org.springsource.ide.eclipse.commons.livexp.core.ObservableSet; import com.google.common.collect.ImmutableSet; /** * A disposing factory creates objects of some type V based on some * parameter of type K. It guarantees that the same element is returned * when the same key is passed in as argument, provided that the * key is one of the currently 'validKeys'. * <p> * The factory is also responsible for monitoring the set of 'validKeys' and calling * the dispose method on the values associated with keys that are no longer * valid. * * @author Kris De Volder */ public abstract class DisposingFactory<K,V extends Disposable> implements Disposable { private ObservableSet<K> validKeys; private Map<K,V> cachedInstances = new HashMap<>(); private ValueListener<ImmutableSet<K>> validKeyListener = null; public DisposingFactory(ObservableSet<K> validKeys) { this.validKeys = validKeys; validKeys.onDispose(new DisposeListener() { public void disposed(Disposable disposed) { DisposingFactory.this.dispose(); } }); } protected abstract V create(K key); public synchronized V createOrGet(K key) { ImmutableSet<K> valid = getValidKeys(); if (valid.contains(key)) { enableValidKeyTracking(); V instance = cachedInstances.get(key); if (instance==null) { instance=create(key); if (instance!=null) { cachedInstances.put(key, instance); } } return instance; } return null; } private void enableValidKeyTracking() { if (validKeyListener==null) { validKeys.addListener(validKeyListener = new ValueListener<ImmutableSet<K>>() { public void gotValue(LiveExpression<ImmutableSet<K>> exp, ImmutableSet<K> value) { retainOnlyValidKeys(); } }); } } private ImmutableSet<K> getValidKeys() { if (validKeys!=null) { return validKeys.getValues(); } return ImmutableSet.of(); } @Override public synchronized void dispose() { if (validKeys!=null) { if (validKeyListener!=null) { validKeys.removeListener(validKeyListener); } validKeys = null; retainOnlyValidKeys(); cachedInstances = null; } } private synchronized void retainOnlyValidKeys() { if (cachedInstances!=null) { ImmutableSet<K> valid = getValidKeys(); Iterator<Entry<K, V>> iter = cachedInstances.entrySet().iterator(); while (iter.hasNext()) { Entry<K, V> e = iter.next(); K k = e.getKey(); if (valid.contains(k)) { //keep } else { e.getValue().dispose(); iter.remove(); } } } } }