package org.codemap;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.codemap.Configuration.Builder;
import org.codemap.callhierarchy.CallOverlay;
import org.codemap.commands.MapCommands;
import org.codemap.layers.CodemapVisualization;
import org.codemap.layers.CurrentSelectionOverlay;
import org.codemap.layers.Layer;
import org.codemap.layers.OpenFileIconsLayer;
import org.codemap.layers.YouAreHereOverlay;
import org.codemap.mapview.MapController;
import org.codemap.mapview.MapView;
import org.codemap.resources.MapValueBuilder;
import org.codemap.resources.MapValues;
import org.codemap.util.Log;
import org.codemap.util.Resources;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.jdt.core.IJavaProject;
import org.osgi.service.prefs.BackingStoreException;
import ch.akuhn.util.Arrays;
import ch.akuhn.values.ActionValue;
import ch.akuhn.values.Arguments;
import ch.akuhn.values.Value;
/**
* Holds corpus, map and visualization of a project. Use this class to store project specific information.
*
*/
public class MapPerProject {
private Map<String,String> properties = new HashMap<String,String>();
private static final String POINT_NODE_ID = CodemapCore.PLUGIN_ID + ".points";
private static final String UI_STATE_NODE_ID = CodemapCore.PLUGIN_ID + ".ui.mapview";
private static final int MINIMAL_SIZE = 256;
private final IJavaProject project;
private MapValues mapValues;
private CodemapVisualization mapVisualization;
private MapPerProjectCache cache;
private ActionValue<Void> redrawAction;
private MapCommands commands;
/*default*/ MapPerProject(IJavaProject project, MapPerProjectCache mapPerProjectCache) {
this.project = project;
this.cache = mapPerProjectCache;
readPreviousProperties();
commands = new MapCommands(this);
}
public void initialize() {
MapValueBuilder builder = new MapValueBuilder();
builder.setName("default.map");
builder.setProjects(Arrays.asList(Resources.asPath(project)));
builder.setFileExtensions(Arrays.asList("*.java"));
builder.setInitialConfiguration(readPreviousMapState());
mapValues = new MapValues(builder);
mapVisualization = new CodemapVisualization(mapValues);
redrawAction = new ActionValue<Void>(
mapValues.mapInstance,
mapValues.background,
mapValues.labeling,
mapValues.extensions,
mapValues.selections) {
@Override
protected Void performAction(Arguments args) {
// well ... during shutdown everything can be null. yay.
CodemapCore plugin = CodemapCore.getPlugin();
if (plugin == null) return null;
MapController controller = plugin.getController();
if (controller == null) return null;
controller.onRedraw();
return null;
}
};
commands.applyOn(getValues());
}
public CodemapVisualization getVisualization() {
return mapVisualization;
}
public IProject getProject() {
return getJavaProject().getProject();
}
/*default*/ IJavaProject getJavaProject() {
return project;
}
private void readPreviousProperties() {
IEclipsePreferences node = getProjectPreferences(UI_STATE_NODE_ID);
try {
for(String key: node.keys()) {
String value = node.get(key, null);
if (value==null) continue;
properties.put(key, value);
}
} catch (BackingStoreException e) {
Log.error(e);
}
}
private Configuration readPreviousMapState() {
IEclipsePreferences node = getProjectPreferences(POINT_NODE_ID);
Builder builder = Configuration.builder();
try {
for (String key: node.keys()) {
String pointString = node.get(key, null);
if (pointString == null) continue;
String[] split = pointString.split("@");
// cover legacy format
if (split.length != 2 ) split = pointString.split("#");
if (split.length != 2 ) {
Log.error(new RuntimeException("Invalid format of point storage for " + getProject().getName() + ": " + pointString));
continue;
}
double x = Double.parseDouble(split[0]);
double y = Double.parseDouble(split[1]);
builder.add(key, x, y);
}
return builder.build();
} catch (BackingStoreException e) {
Log.error(e);
}
return null;
}
public MapPerProject updateSize(int size) {
int actualsize = Math.max(size, MINIMAL_SIZE);
mapValues.mapSize.setValue(actualsize);
// FIXME deif set dependency in mapValueBuilder and trigger this automatically
mapValues.mapInstance.getValue(); // trigger computation
return this;
}
private void writePointPreferences() {
if (mapValues.configuration.isError()) return;
Configuration config = mapValues.configuration.getValueOrFail();
IEclipsePreferences node = getProjectPreferences(POINT_NODE_ID);
for(Point each: config.points()) {
node.put(each.getDocument(), each.x + "@" + each.y);
}
try {
node.flush();
} catch (BackingStoreException e) {
Log.error(e);
}
}
private void writeProperties() {
IEclipsePreferences node = getProjectPreferences(UI_STATE_NODE_ID);
try {
// remove any old entries
node.clear();
for (Entry<String, String> entry : properties.entrySet()) {
node.put(entry.getKey(), entry.getValue());
}
node.flush();
} catch (BackingStoreException e) {
Log.error(e);
}
}
private IEclipsePreferences getProjectPreferences(String nodeId) {
IScopeContext context = new ProjectScope(getProject());
IEclipsePreferences node = context.getNode(nodeId);
return node;
}
public String getPropertyOrDefault(String key, String defaultValue) {
String value = properties.get(key);
return value == null ? defaultValue : value;
}
public void setProperty(String key, String value) {
properties.put(key, value);
}
public boolean getPropertyOrDefault(String key, boolean defaultValue) {
String value = properties.get(key);
return value == null ? defaultValue : Boolean.parseBoolean(value);
}
public void setProperty(String key, boolean checked) {
properties.put(key, Boolean.toString(checked));
}
public void addLayer(Layer layer) {
mapVisualization.getSharedLayer().add(layer);
}
public void addSelectionLayer(Layer layer, MapSelection selection) {
addLayer(layer);
mapValues.selections.add(selection);
}
public boolean containsLayer(Layer layer) {
return mapVisualization.getSharedLayer().contains(layer);
}
public boolean containsLayer(Class<?> layerClass) {
for (Layer each: mapVisualization.getSharedLayer()) {
if (each.getClass().equals(layerClass)) return true;
}
return false;
}
public Configuration getConfiguration() {
return mapValues.configuration.getValue(); // what if it's null?
}
public MapValues getValues() {
return mapValues;
}
public void redrawWhenChanges(Value<?> selection) {
selection.addDependent(redrawAction);
}
public void saveState() {
writePointPreferences();
writeProperties();
}
public void removeLayer(Class<?> layerClass) {
for (Layer each: mapVisualization.getSharedLayer()) {
if (each.getClass().equals(layerClass)) {
mapVisualization.getSharedLayer().remove(each);
return;
}
}
}
public Layer getLayer(Class<CallOverlay> layerClass) {
for (Layer each: mapVisualization.getSharedLayer()) {
if (each.getClass().equals(layerClass)) {
return each;
}
}
return null;
}
public void reloadFromScratch() {
cache.reload(this);
}
public void configureOn(MapView view) {
commands.configure(view);
view.updateProjectName(getName());
}
public String getName() {
return getProject().getName();
}
public YouAreHereOverlay getYouAreHereOverlay() {
return mapVisualization.getYouAreHere();
}
public OpenFileIconsLayer getOpenFilesOverlay() {
return mapVisualization.getOpenFiles();
}
public CurrentSelectionOverlay getCurrentSelectionOverlay() {
return mapVisualization.getSelection();
}
}