1 package net.mograsim.plugin.nature;
3 import static net.mograsim.plugin.nature.MachineContextStatus.ACTIVE;
4 import static net.mograsim.plugin.nature.MachineContextStatus.ACTIVE_CHANGED;
5 import static net.mograsim.plugin.nature.MachineContextStatus.BROKEN;
6 import static net.mograsim.plugin.nature.MachineContextStatus.CLOSED;
7 import static net.mograsim.plugin.nature.MachineContextStatus.DEAD;
8 import static net.mograsim.plugin.nature.MachineContextStatus.INTACT;
9 import static net.mograsim.plugin.nature.MachineContextStatus.READY;
10 import static net.mograsim.plugin.nature.MachineContextStatus.UNKOWN;
12 import java.io.IOException;
13 import java.util.HashSet;
14 import java.util.Objects;
15 import java.util.Optional;
18 import org.eclipse.core.resources.IProject;
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.jface.util.PropertyChangeEvent;
21 import org.eclipse.ui.preferences.ScopedPreferenceStore;
23 import net.mograsim.machine.Machine;
24 import net.mograsim.machine.MachineDefinition;
25 import net.mograsim.machine.MachineRegistry;
26 import net.mograsim.plugin.nature.ProjectContextEvent.ProjectContextEventType;
29 * A MachineContext is a project specific context for the Mograsim machine associated to them.
31 * It stores the {@link MachineDefinition#getId() machine id}, the {@link MachineDefinition} if applicable and an active machine if present.
32 * {@link ActiveMachineListener}s and {@link MachineContextStatusListener}s can be used to track the state of the MachineContext.
34 * @author Christian Femers
37 public class MachineContext
40 final ScopedPreferenceStore prefs;
41 Optional<String> machineId = Optional.empty();
42 Optional<MachineDefinition> machineDefinition = Optional.empty();
43 Optional<Machine> activeMachine = Optional.empty();
45 private MachineContextStatus status = UNKOWN;
47 private final Set<ActiveMachineListener> machineListeners = new HashSet<>();
48 private final Set<MachineContextStatusListener> stateListeners = new HashSet<>();
50 public MachineContext(IProject owner)
52 this.owner = Objects.requireNonNull(owner);
53 prefs = ProjectMachineContext.getProjectPrefs(owner);
54 prefs.addPropertyChangeListener(this::preferenceListener);
55 updateDefinition(ProjectMachineContext.getMachineIdFrom(prefs));
58 public final IProject getProject()
63 public final ScopedPreferenceStore getPreferences()
69 * Returns true if the project configuration is valid in the current environment
71 public final boolean isCurrentyValid()
73 return status == READY || isActive();
77 * Returns true if the persisted project configuration itself is intact
79 * @see MachineContextStatus#INTACT
81 public final boolean isIntact()
83 return isCurrentyValid() || status == INTACT;
87 * Returns true if a machine is instantiated and (possibly) running
89 public final boolean isActive()
91 return status == ACTIVE || status == ACTIVE_CHANGED;
95 * Returns the current status of this machine context
97 public final MachineContextStatus getStatus()
103 * Sets the projects machineId. Will likely break things, if the {@link MachineContext} {@link #isActive()}.
105 public final boolean setMachineId(String machineId)
107 prefs.setValue(ProjectMachineContext.MACHINE_PROPERTY, machineId);
112 catch (IOException e)
121 * Sets the active machine in the {@link MachineContext}'s project scope.
123 public final void setActiveMachine(Machine machine)
125 activeMachine = Optional.ofNullable(machine);
127 notifyActiveMachineListeners();
130 public final Optional<String> getMachineId()
135 public final Optional<MachineDefinition> getMachineDefinition()
137 return machineDefinition;
140 public final Optional<Machine> getActiveMachine()
142 // activateMachine(); // TODO is this the best way to deal with this?
143 return activeMachine;
147 * Tries to activate the associated machine. This will not succeed if the project is not {@link MachineContextStatus#READY}. If the
148 * status is {@link MachineContextStatus#ACTIVE}, this method has no effect.
150 * @return true if the activation was successful
152 public final boolean activateMachine()
154 if (status == ACTIVE)
156 machineDefinition.ifPresent(md -> setActiveMachine(md.createNew()));
162 * This changes the internal status to a newly evaluated one and calls the {@link MachineContextStatusListener}s if this caused the
165 * @see #reevaluateStatus()
168 public final void updateStatus()
170 MachineContextStatus newStatus = reevaluateStatus();
171 forceUpdateStatus(newStatus);
174 final void forceUpdateStatus(MachineContextStatus newStatus)
176 MachineContextStatus oldStatus = status;
177 if (oldStatus == newStatus)
180 doPostStatusChangedAction();
181 notifyMachineContextStatusListeners(oldStatus);
185 * This method reevaluates the status <b>but does not change/update it</b>.<br>
186 * To update the status of the {@link MachineContext}, use {@link #updateStatus()}.
188 * @return the raw status of the project at the time of the call.
190 public final MachineContextStatus reevaluateStatus()
196 if (hasInvaildMograsimProject())
198 if (machineDefinition.isEmpty())
200 if (activeMachine.isEmpty())
202 if (!activeMachine.get().getDefinition().getId().equals(machineDefinition.get().getId()))
203 return ACTIVE_CHANGED;
207 private void doPostStatusChangedAction()
209 if ((status == DEAD || status == CLOSED) && activeMachine.isPresent())
211 activeMachine = Optional.empty();
212 notifyActiveMachineListeners();
216 private boolean hasInvaildMograsimProject()
220 if (!owner.isNatureEnabled(MograsimNature.NATURE_ID))
222 return machineId.isEmpty();
224 catch (CoreException e)
226 // cannot happen, because this method is called after the exceptional states were checked.
232 final void updateDefinition(Optional<String> newMachineDefinitionId)
234 if (newMachineDefinitionId.equals(machineId))
236 machineId = newMachineDefinitionId;
237 machineDefinition = machineId.map(MachineRegistry::getMachine);
239 ProjectMachineContext.notifyListeners(new ProjectContextEvent(this, ProjectContextEventType.MACHINE_DEFINITION_CHANGE));
242 private void preferenceListener(PropertyChangeEvent changeEvent)
244 if (changeEvent.getProperty().equals(ProjectMachineContext.MACHINE_PROPERTY))
246 updateDefinition(Optional.ofNullable((String) changeEvent.getNewValue()));
250 private void notifyActiveMachineListeners()
252 machineListeners.forEach(ob -> ob.setMachine(activeMachine));
255 public void addActiveMachineListener(ActiveMachineListener ob)
257 machineListeners.add(ob);
258 ob.setMachine(activeMachine);
261 public void removeActiveMachineListener(ActiveMachineListener ob)
263 machineListeners.remove(ob);
266 private void notifyMachineContextStatusListeners(MachineContextStatus oldStatus)
268 MachineContextStatus newStatus = status;
269 stateListeners.forEach(ob -> ob.updateStatus(oldStatus, newStatus));
272 public void addMachineContextStatusListener(MachineContextStatusListener ob)
274 stateListeners.add(ob);
277 public void removeMachineContextStatusListener(MachineContextStatusListener ob)
279 stateListeners.remove(ob);
283 public static interface ActiveMachineListener
285 void setMachine(Optional<Machine> machine);
289 public static interface MachineContextStatusListener
291 void updateStatus(MachineContextStatus oldStatus, MachineContextStatus newStatus);