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 Optional<Machine> oldMachine = activeMachine;
126 activeMachine = Optional.ofNullable(machine);
128 notifyActiveMachineListeners(oldMachine, activeMachine);
131 public final Optional<String> getMachineId()
136 public final Optional<MachineDefinition> getMachineDefinition()
138 return machineDefinition;
141 public final Optional<Machine> getActiveMachine()
143 // activateMachine(); // TODO is this the best way to deal with this?
144 return activeMachine;
148 * Tries to activate the associated machine. This will not succeed if the project is not {@link MachineContextStatus#READY}. If the
149 * status is {@link MachineContextStatus#ACTIVE}, this method has no effect.
151 * @return true if the activation was successful
153 public final boolean activateMachine()
155 if (status == ACTIVE)
157 machineDefinition.ifPresent(md -> setActiveMachine(md.createNew()));
163 * This changes the internal status to a newly evaluated one and calls the {@link MachineContextStatusListener}s if this caused the
166 * @see #reevaluateStatus()
169 public final void updateStatus()
171 MachineContextStatus newStatus = reevaluateStatus();
172 forceUpdateStatus(newStatus);
175 final void forceUpdateStatus(MachineContextStatus newStatus)
177 MachineContextStatus oldStatus = status;
178 if (oldStatus == newStatus)
181 doPostStatusChangedAction();
182 notifyMachineContextStatusListeners(oldStatus);
186 * This method reevaluates the status <b>but does not change/update it</b>.<br>
187 * To update the status of the {@link MachineContext}, use {@link #updateStatus()}.
189 * @return the raw status of the project at the time of the call.
191 public final MachineContextStatus reevaluateStatus()
197 if (hasInvaildMograsimProject())
199 if (machineDefinition.isEmpty())
201 if (activeMachine.isEmpty())
203 if (!activeMachine.get().getDefinition().getId().equals(machineDefinition.get().getId()))
204 return ACTIVE_CHANGED;
208 private void doPostStatusChangedAction()
210 if ((status == DEAD || status == CLOSED) && activeMachine.isPresent())
212 Optional<Machine> oldMachine = activeMachine;
213 activeMachine = Optional.empty();
214 notifyActiveMachineListeners(oldMachine, activeMachine);
218 private boolean hasInvaildMograsimProject()
222 if (!owner.isNatureEnabled(MograsimNature.NATURE_ID))
224 return machineId.isEmpty();
226 catch (CoreException e)
228 // cannot happen, because this method is called after the exceptional states were checked.
234 final void updateDefinition(Optional<String> newMachineDefinitionId)
236 if (newMachineDefinitionId.equals(machineId))
238 machineId = newMachineDefinitionId;
239 machineDefinition = machineId.map(MachineRegistry::getMachine);
241 ProjectMachineContext.notifyListeners(new ProjectContextEvent(this, ProjectContextEventType.MACHINE_DEFINITION_CHANGE));
244 private void preferenceListener(PropertyChangeEvent changeEvent)
246 if (changeEvent.getProperty().equals(ProjectMachineContext.MACHINE_PROPERTY))
248 updateDefinition(Optional.ofNullable((String) changeEvent.getNewValue()));
252 private void notifyActiveMachineListeners(Optional<Machine> oldMachine, Optional<Machine> newMachine)
254 machineListeners.forEach(ob -> ob.setMachine(oldMachine, newMachine));
257 public void addActiveMachineListener(ActiveMachineListener ob)
259 machineListeners.add(ob);
260 ob.setMachine(Optional.empty(), activeMachine);
263 public void removeActiveMachineListener(ActiveMachineListener ob)
265 machineListeners.remove(ob);
268 private void notifyMachineContextStatusListeners(MachineContextStatus oldStatus)
270 MachineContextStatus newStatus = status;
271 stateListeners.forEach(ob -> ob.updateStatus(oldStatus, newStatus));
274 public void addMachineContextStatusListener(MachineContextStatusListener ob)
276 stateListeners.add(ob);
279 public void removeMachineContextStatusListener(MachineContextStatusListener ob)
281 stateListeners.remove(ob);
285 public static interface ActiveMachineListener
287 void setMachine(Optional<Machine> oldMachine, Optional<Machine> newMachine);
291 public static interface MachineContextStatusListener
293 void updateStatus(MachineContextStatus oldStatus, MachineContextStatus newStatus);