1 package net.mograsim.plugin.nature;
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.HashSet;
7 import java.util.Objects;
8 import java.util.Optional;
11 import org.eclipse.core.resources.IProject;
12 import org.eclipse.core.resources.IResourceChangeEvent;
13 import org.eclipse.core.resources.ProjectScope;
14 import org.eclipse.core.resources.ResourcesPlugin;
15 import org.eclipse.core.runtime.Adapters;
16 import org.eclipse.core.runtime.CoreException;
17 import org.eclipse.core.runtime.IAdaptable;
18 import org.eclipse.ui.preferences.ScopedPreferenceStore;
20 import net.mograsim.machine.MachineRegistry;
21 import net.mograsim.plugin.nature.ProjectContextEvent.ProjectContextEventType;
24 * This class is a register for {@link MachineContext} mapped by their {@link IProject}
26 * It can be used to obtain (and thereby create if necessary) {@link MachineContext}s for projects and {@link IAdaptable}s that are somewhat
27 * associated to Mograsim nature. The register is unique and static context of this class. Since it also depends on the installed machines,
28 * it listens to changes of the {@link MachineRegistry}.
30 * @author Christian Femers
33 public class ProjectMachineContext
35 private static Map<IProject, MachineContext> projectMachineContexts = Collections.synchronizedMap(new HashMap<>());
36 private static final Set<ProjectContextListener> listeners = Collections.synchronizedSet(new HashSet<>());
38 public static final String MOGRASIM_PROJECT_PREFS_NODE = "net.mograsim";
39 public static final String MACHINE_PROPERTY = "net.mograsim.projectMachineId";
41 private ProjectMachineContext()
47 * This method returns the associated machine context or created a new one if none is associated yet.
49 * @param project the project to get the {@link MachineContext} for (or create one, if possible). It must have Mograsim nature.
51 * @throws IllegalArgumentException if the project is not accessible or has no mograsim nature
52 * @throws NullPointerException if the project is null
55 public static MachineContext getMachineContextOf(IProject project)
57 MachineContext mc = projectMachineContexts.get(project);
60 validateMograsimNatureProject(project);
61 mc = new MachineContext(project);
62 projectMachineContexts.put(project, mc);
63 notifyListeners(new ProjectContextEvent(mc, ProjectContextEventType.NEW));
68 * This method returns the associated machine context or created a new one if none is associated yet. The given resource must be
69 * adaptable to {@link IProject}.
71 * @param mograsimProjectAdapable the {@link IProject}-{@link IAdaptable} to get the {@link MachineContext} for (or create one, if
72 * possible). Must be contained in a Mograsim nature project.
74 * @throws IllegalArgumentException if the project is not accessible or has no mograsim nature
75 * @throws NullPointerException if the {@link IAdaptable} is null or it cannot be adapted to {@link IProject}
78 public static MachineContext getMachineContextOf(IAdaptable mograsimProjectAdapable)
80 IProject project = Adapters.adapt(mograsimProjectAdapable, IProject.class, true);
81 Objects.requireNonNull(project, "project was null / no project found for " + mograsimProjectAdapable);
82 return getMachineContextOf(project);
86 * Returns all {@link MachineContext} known, in the sense of all that got ever created during this runtime.
88 public static Map<IProject, MachineContext> getAllProjectMachineContexts()
90 return Collections.unmodifiableMap(projectMachineContexts);
93 static ScopedPreferenceStore getProjectPrefs(IProject mograsimProject)
95 return new ScopedPreferenceStore(new ProjectScope(mograsimProject), MOGRASIM_PROJECT_PREFS_NODE);
98 static IProject validateMograsimNatureProject(IAdaptable mograsimProjectAdapable)
101 if (mograsimProjectAdapable instanceof IProject)
103 project = (IProject) mograsimProjectAdapable;
104 Objects.requireNonNull(project, "Project was null");
107 project = Adapters.adapt(mograsimProjectAdapable, IProject.class, true);
108 Objects.requireNonNull(project, () -> mograsimProjectAdapable + " is not adaptable to IProject");
112 if (!project.isNatureEnabled(MograsimNature.NATURE_ID))
113 throw new IllegalArgumentException(mograsimProjectAdapable + "is not (in) a Mograsim project");
115 catch (CoreException e)
118 throw new IllegalArgumentException(mograsimProjectAdapable + " project nature could not be evaluated", e);
124 * Tests for Mograsim nature. This method is null safe and will not throw any exception.
126 static boolean hasMograsimNature(IProject project)
132 return project.isNatureEnabled(MograsimNature.NATURE_ID);
134 catch (CoreException e)
141 static Optional<String> getMachineIdFrom(ScopedPreferenceStore preferenceStore)
143 if (preferenceStore.contains(MACHINE_PROPERTY))
144 return Optional.of(preferenceStore.getString(MACHINE_PROPERTY));
145 return Optional.empty();
148 static void notifyListeners(ProjectContextEvent projectContextEvent)
150 listeners.forEach(l -> l.onProjectContextChange(projectContextEvent));
153 public static void addProjectContextListener(ProjectContextListener listener)
155 listeners.add(listener);
158 public static void removeProjectContextListener(ProjectContextListener listener)
160 listeners.remove(listener);
165 ResourcesPlugin.getWorkspace().addResourceChangeListener(ProjectMachineContext::resourceChanged);
166 MachineRegistry.addMachineRegistryListener(newMap -> updateAllStatus());
169 private static void updateAllStatus()
171 projectMachineContexts.forEach((p, mc) -> mc.updateStatus());
174 private static void resourceChanged(IResourceChangeEvent event)
176 // We try to do as many cheap tests first as possible, because this listener is not limited to plain project actions.
177 if (event.getResource() == null)
179 IProject project = event.getResource().getProject();
182 MachineContext mc = projectMachineContexts.get(project);
185 ProjectContextEventType eventType = ProjectContextEventType.ofResourceChangeEvent(event.getType());
186 // if (eventType == ProjectContextEventType.OTHER_CHANGE && project.isOpen())
187 // return; // we don't care about all small changes (TODO: research if this has any drawbacks)
188 eventType.getForcedStatus().ifPresent(mc::forceUpdateStatus);
189 notifyListeners(new ProjectContextEvent(mc, eventType));