/*******************************************************************************
* 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.model;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ListenerList;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel.ElementStateListener;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveSet;
import org.springsource.ide.eclipse.commons.livexp.core.LiveSetVariable;
import org.springsource.ide.eclipse.commons.livexp.core.ObservableSet;
import org.springsource.ide.eclipse.commons.livexp.core.ValueListener;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
import com.google.common.collect.ImmutableSet;
/**
* An instance of this class manages a LiveSet of BootDashModels, one model per
* RunTarget. New models are created or disposed to keep them in synch with the
* RunTargets.
*
* @author Nieraj Singh
* @author Kris De Volder
*/
public class BootDashModelManager implements Disposable {
private LiveSetVariable<BootDashModel> models;
private Map<String, BootDashModel> modelsPerTargetId;
private ObservableSet<RunTarget> targets;
private RunTargetChangeListener targetListener;
private ListenerList elementStateListeners = new ListenerList();
private ElementStateListener upstreamElementStateListener;
private BootDashModelContext context;
private BootDashViewModel viewModel;
public BootDashModelManager(BootDashModelContext context, BootDashViewModel viewModel, ObservableSet<RunTarget> targets) {
this.context = context;
this.viewModel = viewModel;
this.targets = targets;
}
public LiveExpression<ImmutableSet<BootDashModel>> getModels() {
if (models == null) {
models = new LiveSetVariable<>(new LinkedHashSet<BootDashModel>());
modelsPerTargetId = new LinkedHashMap<>();
targets.addListener(targetListener = new RunTargetChangeListener());
}
return models;
}
class RunTargetChangeListener implements ValueListener<ImmutableSet<RunTarget>> {
@Override
public void gotValue(LiveExpression<ImmutableSet<RunTarget>> exp, ImmutableSet<RunTarget> actualRunTargets) {
synchronized (modelsPerTargetId) {
Map<String, RunTarget> currentTargetsPerId = new LinkedHashMap<>();
if (actualRunTargets != null) {
for (RunTarget runTarget : actualRunTargets) {
String id = runTarget.getId();
Assert.isNotNull(id);
currentTargetsPerId.put(id, runTarget);
}
}
// To avoid firing unnecessary model change events, only modify
// list of models IF there is a change
boolean hasChanged = false;
// Add models for new targets
Set<BootDashModel> modelsToKeep = new LinkedHashSet<>();
for (Entry<String, RunTarget> entry : currentTargetsPerId.entrySet()) {
if (modelsPerTargetId.get(entry.getKey()) == null) {
BootDashModel model = entry.getValue().createElementsTabelModel(context, viewModel);
if (model != null) {
modelsPerTargetId.put(entry.getKey(), model);
hasChanged = true;
}
}
if (modelsPerTargetId.get(entry.getKey()) != null) {
modelsToKeep.add(modelsPerTargetId.get(entry.getKey()));
}
}
// Remove models for deleted targets
for (Iterator<Entry<String, BootDashModel>> it = modelsPerTargetId.entrySet().iterator(); it
.hasNext();) {
Entry<String, BootDashModel> entry = it.next();
if (currentTargetsPerId.get(entry.getKey()) == null) {
it.remove();
hasChanged = true;
}
}
if (hasChanged) {
models.replaceAll(modelsToKeep);
}
}
}
}
public void dispose() {
if (targetListener != null) {
targets.removeListener(targetListener);
targetListener = null;
}
if (models != null) {
for (BootDashModel m : models.getValues()) {
if (upstreamElementStateListener != null) {
m.removeElementStateListener(upstreamElementStateListener);
}
m.dispose();
}
upstreamElementStateListener = null;
models = null;
}
}
public void addElementStateListener(ElementStateListener l) {
elementStateListeners.add(l);
ensureUpstreamStateListener();
}
private synchronized void ensureUpstreamStateListener() {
if (upstreamElementStateListener == null) {
upstreamElementStateListener = new ElementStateListener() {
public void stateChanged(BootDashElement e) {
for (Object o : elementStateListeners.getListeners()) {
((ElementStateListener) o).stateChanged(e);
}
}
};
getModels().addListener(new ValueListener<ImmutableSet<BootDashModel>>() {
public void gotValue(LiveExpression<ImmutableSet<BootDashModel>> exp, ImmutableSet<BootDashModel> models) {
for (BootDashModel m : models) {
m.addElementStateListener(upstreamElementStateListener);
}
}
});
}
}
public void removeElementStateListener(ElementStateListener l) {
elementStateListeners.remove(l);
}
}