/*******************************************************************************
* Copyright (c) 2015 Zend Technologies and others.
* 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:
* Zend Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.php.internal.debug.core.debugger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.php.internal.debug.core.preferences.IPHPExesListener;
import org.eclipse.php.internal.debug.core.preferences.PHPDebuggersRegistry;
import org.eclipse.php.internal.debug.core.preferences.PHPExesEvent;
import org.eclipse.php.internal.debug.core.preferences.PHPexes;
import org.eclipse.php.internal.server.core.manager.IServersManagerListener;
import org.eclipse.php.internal.server.core.manager.ServerManagerEvent;
import org.eclipse.php.internal.server.core.manager.ServersManager;
/**
* General manager for handling settings of different debuggers.
*
* @author Bartlomiej Laczkowski
*/
@SuppressWarnings("restriction")
public enum DebuggerSettingsManager {
/**
* Singleton Instance.
*/
INSTANCE;
private class EventNotifier {
private static final int ADDED = 0x01;
private static final int REMOVED = 0x02;
private static final int CHANGED = 0x04;
private PropertyChangeEvent[] events;
private int kind;
private IDebuggerSettings settings;
private void fireEvent() {
switch (kind) {
case ADDED: {
for (Object listener : listeners.getListeners())
((IDebuggerSettingsListener) listener).settingsAdded(settings);
break;
}
case REMOVED: {
for (Object listener : listeners.getListeners())
((IDebuggerSettingsListener) listener).settingsRemoved(settings);
break;
}
case CHANGED: {
for (Object listener : listeners.getListeners())
((IDebuggerSettingsListener) listener).settingsChanged(events);
break;
}
default:
break;
}
}
void fireChanged(List<PropertyChangeEvent> events) {
kind = CHANGED;
this.events = events.toArray(new PropertyChangeEvent[events.size()]);
fireEvent();
}
void fireAdded(IDebuggerSettings settings) {
kind = ADDED;
this.settings = settings;
fireEvent();
}
void fireRemoved(IDebuggerSettings settings) {
kind = REMOVED;
this.settings = settings;
fireEvent();
}
}
private class OwnersListener implements IServersManagerListener, IPHPExesListener {
@Override
public void phpExeAdded(PHPExesEvent event) {
for (String debuggerId : PHPDebuggersRegistry.getDebuggersIds()) {
IDebuggerSettings settings = findSettings(event.getPHPExeItem().getUniqueId(), debuggerId);
if (settings != null)
save(settings);
}
}
@Override
public void phpExeRemoved(PHPExesEvent event) {
for (String debuggerId : PHPDebuggersRegistry.getDebuggersIds()) {
IDebuggerSettings settings = findSettings(event.getPHPExeItem().getUniqueId(), debuggerId);
if (settings != null)
delete(settings);
}
}
@Override
public void serverAdded(ServerManagerEvent event) {
for (String debuggerId : PHPDebuggersRegistry.getDebuggersIds()) {
IDebuggerSettings settings = findSettings(event.getServer().getUniqueId(), debuggerId);
if (settings != null)
save(settings);
}
}
@Override
public void serverRemoved(ServerManagerEvent event) {
for (String debuggerId : PHPDebuggersRegistry.getDebuggersIds()) {
IDebuggerSettings settings = findSettings(event.getServer().getUniqueId(), debuggerId);
if (settings != null)
delete(settings);
}
}
@Override
public void serverModified(ServerManagerEvent event) {
// ignore
}
}
private final OwnersListener ownersListener = new OwnersListener();
private final ListenerList listeners = new ListenerList();
private final Map<String, IDebuggerSettingsProvider> settingsCache = new HashMap<String, IDebuggerSettingsProvider>();
private final Map<IDebuggerSettings, IDebuggerSettingsWorkingCopy> settingsCopies = new HashMap<IDebuggerSettings, IDebuggerSettingsWorkingCopy>();
/**
* Creates default manager instance.
*/
private DebuggerSettingsManager() {
for (String debuggerId : PHPDebuggersRegistry.getDebuggersIds()) {
settingsCache.put(debuggerId, DebuggerSettingsProviderRegistry.getProvider(debuggerId));
}
}
/**
* Starts up debugger settings manager.
*/
public synchronized void startup() {
ServersManager.addManagerListener(ownersListener);
PHPexes.getInstance().addPHPExesListener(ownersListener);
}
/**
* Shuts down debugger settings manager.
*/
public synchronized void shutdown() {
ServersManager.removeManagerListener(ownersListener);
PHPexes.getInstance().removePHPExesListener(ownersListener);
for (IDebuggerSettingsProvider provider : settingsCache.values()) {
if (provider instanceof AbstractDebuggerSettingsProvider) {
((AbstractDebuggerSettingsProvider) provider).cleanup();
}
}
}
/**
* Finds and returns debugger settings for given debugger type and owner.
* May return <code>null</code> if there is no settings provider for
* particular debugger type.
*
* @param ownerId
* @param debuggerId
* @return debugger settings
*/
public IDebuggerSettings findSettings(String ownerId, String debuggerId) {
IDebuggerSettingsProvider provider = settingsCache.get(debuggerId);
if (provider == null)
// There is no provider registered
return null;
IDebuggerSettings settings = provider.get(ownerId);
// Check if there is any pending working copy
IDebuggerSettingsWorkingCopy pendingCopy = findWorkingCopy(settings);
if (pendingCopy != null)
return pendingCopy;
return settings;
}
/**
* Finds and returns all debugger settings for given debugger type.
*
* @param debuggerId
* @return list of debugger settings
*/
public List<IDebuggerSettings> findSettings(String debuggerId) {
IDebuggerSettingsProvider provider = settingsCache.get(debuggerId);
if (provider == null)
// There is no provider registered
return new ArrayList<IDebuggerSettings>();
return provider.getAll();
}
/**
* Creates and returns editable working copy for provided settings. Callers
* of this method should always use
* {@link DebuggerSettingsManager#dropWorkingCopy(IDebuggerSettingsWorkingCopy)}
* every time when corresponding working copy is no longer used.
*
* @param settings
* @return settings working copy
*/
public IDebuggerSettingsWorkingCopy fetchWorkingCopy(IDebuggerSettings settings) {
IDebuggerSettingsWorkingCopy workingCopy = settingsCopies.get(settings);
if (workingCopy == null) {
IDebuggerSettingsProvider provider = settingsCache.get(settings.getDebuggerId());
workingCopy = provider.createWorkingCopy(settings);
settingsCopies.put(settings, workingCopy);
}
return workingCopy;
}
/**
* Drops working copy by removing it from copies cache.
*
* @param settingsWorkingCopy
*/
public void dropWorkingCopy(IDebuggerSettingsWorkingCopy settingsWorkingCopy) {
for (IDebuggerSettings key : settingsCopies.keySet()) {
if (settingsCopies.get(key) == settingsWorkingCopy) {
settingsCopies.remove(key);
break;
}
}
}
/**
* Adds debugger settings listener to this manager.
*
* @param listener
*/
public void addSettingsListener(IDebuggerSettingsListener listener) {
listeners.add(listener);
}
/**
* Removes debugger settings listener from this manager.
*
* @param listener
*/
public void removeSettingsListener(IDebuggerSettingsListener listener) {
listeners.remove(listener);
}
/**
* Saves the original settings with the use of provided working copy. This
* method should always be used by a clients whenever original settings
* should be saved to fire up the appropriate events.
*
* @param settingsWorkingCopy
*/
public void save(IDebuggerSettingsWorkingCopy settingsWorkingCopy) {
// Find the differences to send change notifications
IDebuggerSettings settings = settingsWorkingCopy.getOriginal();
Map<String, String> attributes = settings.getAttributes();
Map<String, String> attributesCopy = settingsWorkingCopy.getAttributes();
List<PropertyChangeEvent> events = new ArrayList<PropertyChangeEvent>();
// Check if there are some new ones that were not in original
for (String key : attributesCopy.keySet()) {
if (!attributes.keySet().contains(key)) {
PropertyChangeEvent event = new PropertyChangeEvent(settings, key, null, attributesCopy.get(key));
events.add(event);
}
}
// Check & compare original attributes with the ones from WC
for (String key : attributes.keySet()) {
if (!attributes.get(key).equals(attributesCopy.get(key))) {
PropertyChangeEvent event = new PropertyChangeEvent(settings, key, attributes.get(key),
attributesCopy.get(key));
events.add(event);
}
}
// Go out if there are no changes
if (events.isEmpty())
return;
// Update original settings
((AbstractDebuggerSettings) settings).update(settingsWorkingCopy);
// Delegate saving of persistent data to related provider
save(settings);
// Send change notifications
(new EventNotifier()).fireChanged(events);
}
/**
* Saves settings to cache & persistent storage.
*
* @param settings
*/
private void save(IDebuggerSettings settings) {
String debuggerId = settings.getDebuggerId();
// Delegate saving to appropriate settings provider
DebuggerSettingsProviderRegistry.getProvider(debuggerId).save(settings);
(new EventNotifier()).fireAdded(settings);
}
/**
* Deletes settings from cache & persistent storage.
*
* @param settings
*/
private void delete(IDebuggerSettings settings) {
String debuggerId = settings.getDebuggerId();
// Delegate deleting to appropriate settings provider
DebuggerSettingsProviderRegistry.getProvider(debuggerId).delete(settings);
(new EventNotifier()).fireRemoved(settings);
}
/**
* Finds and returns settings working copy if there is any
*
* @param settings
* @return settings working copy or <code>null</code>
*/
private IDebuggerSettingsWorkingCopy findWorkingCopy(IDebuggerSettings settings) {
for (IDebuggerSettings key : settingsCopies.keySet()) {
if (key == settings)
return settingsCopies.get(key);
}
return null;
}
}