1 package net.mograsim.plugin.nature;
3 import static net.mograsim.plugin.nature.MachineContextStatus.*;
5 import java.io.IOException;
6 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.runtime.CoreException;
13 import org.eclipse.jface.util.PropertyChangeEvent;
14 import org.eclipse.ui.preferences.ScopedPreferenceStore;
16 import net.mograsim.machine.Machine;
17 import net.mograsim.machine.MachineDefinition;
18 import net.mograsim.machine.MachineRegistry;
19 import net.mograsim.plugin.nature.ProjectContextEvent.ProjectContextEventType;
22 * A MachineContext is a project specific context for the Mograsim machine associated to them.
24 * It stores the {@link MachineDefinition#getId() machine id}, the {@link MachineDefinition} if applicable and an active machine if present.
25 * {@link ActiveMachineListener}s and {@link MachineContextStatusListener}s can be used to track the state of the MachineContext.
27 * @author Christian Femers
30 public class MachineContext
33 final ScopedPreferenceStore prefs;
34 Optional<String> machineId = Optional.empty();
35 Optional<MachineDefinition> machineDefinition = Optional.empty();
36 Optional<Machine> activeMachine = Optional.empty();
38 private MachineContextStatus status = UNKOWN;
40 private final Set<ActiveMachineListener> machineListeners = new HashSet<>();
41 private final Set<MachineContextStatusListener> stateListeners = new HashSet<>();
43 public MachineContext(IProject owner)
45 this.owner = Objects.requireNonNull(owner);
46 prefs = ProjectMachineContext.getProjectPrefs(owner);
47 prefs.addPropertyChangeListener(this::preferenceListener);
48 updateDefinition(ProjectMachineContext.getMachineIdFrom(prefs));
51 public final IProject getProject()
56 public final ScopedPreferenceStore getPreferences()
62 * Returns true if the project configuration is valid in the current environment
64 public final boolean isCurrentyValid()
66 return status == READY || status == ACTIVE;
70 * Returns true if the persisted project configuration itself is intact
72 * @see MachineContextStatus#INTACT
74 public final boolean isIntact()
76 return isCurrentyValid() || status == INTACT;
80 * Returns true if a machine is instantiated and (possibly) running
82 public final boolean isActive()
84 return status == ACTIVE || status == ACTIVE_CHANGED;
88 * Returns the current status of this machine context
90 public final MachineContextStatus getStatus()
96 * Sets the projects machineId. Will likely break things, if the {@link MachineContext} {@link #isActive()}.
98 public final boolean setMachineId(String machineId)
100 prefs.setValue(ProjectMachineContext.MACHINE_PROPERTY, machineId);
105 catch (IOException e)
114 * Sets the active machine in the {@link MachineContext}'s project scope.
116 public final void setActiveMachine(Machine machine)
118 activeMachine = Optional.ofNullable(machine);
120 notifyActiveMachineListeners();
123 public final Optional<String> getMachineId()
128 public final Optional<MachineDefinition> getMachineDefinition()
130 return machineDefinition;
133 public final Optional<Machine> getActiveMachine()
135 // activateMachine(); // TODO is this the best way to deal with this?
136 return activeMachine;
140 * Tries to activate the associated machine. This will not succeed if the project is not {@link MachineContextStatus#READY}. If the
141 * status is {@link MachineContextStatus#ACTIVE}, this method has no effect.
143 * @return true if the activation was successful
145 public final boolean activateMachine()
147 if (status == ACTIVE)
149 machineDefinition.ifPresent(md -> setActiveMachine(md.createNew()));
150 if (activeMachine.isPresent())
151 System.out.format("Created new machine %s for project %s%n", activeMachine.get().getDefinition().getId(), owner.getName());
157 * This changes the internal status to a newly evaluated one and calls the {@link MachineContextStatusListener}s if this caused the
160 * @see #reevaluateStatus()
163 public final void updateStatus()
165 MachineContextStatus newStatus = reevaluateStatus();
166 forceUpdateStatus(newStatus);
169 final void forceUpdateStatus(MachineContextStatus newStatus)
171 MachineContextStatus oldStatus = status;
172 if (oldStatus == newStatus)
175 System.out.format("Project %s context status: %s -> %s%n", owner.getName(), oldStatus, newStatus);
176 doPostStatusChangedAction();
177 notifyMachineContextStatusListeners(oldStatus);
181 * This method reevaluates the status <b>but does not change/update it</b>.<br>
182 * To update the status of the {@link MachineContext}, use {@link #updateStatus()}.
184 * @return the raw status of the project at the time of the call.
186 public final MachineContextStatus reevaluateStatus()
192 if (hasInvaildMograsimProject())
194 if (machineDefinition.isEmpty())
196 if (activeMachine.isEmpty())
198 if (!activeMachine.get().getDefinition().getId().equals(machineDefinition.get().getId()))
199 return ACTIVE_CHANGED;
203 private void doPostStatusChangedAction()
205 if ((status == DEAD || status == CLOSED) && activeMachine.isPresent())
207 System.out.format("Removed machine %s for project %s%n", activeMachine.get().getDefinition().getId(), owner.getName());
208 activeMachine = Optional.empty();
209 notifyActiveMachineListeners();
213 private boolean hasInvaildMograsimProject()
217 if (!owner.isNatureEnabled(MograsimNature.NATURE_ID))
219 return machineId.isEmpty();
221 catch (CoreException e)
223 // cannot happen, because this method is called after the exceptional states were checked.
229 final void updateDefinition(Optional<String> newMachineDefinitionId)
231 if (newMachineDefinitionId.equals(machineId))
233 machineId = newMachineDefinitionId;
234 machineDefinition = machineId.map(MachineRegistry::getMachine);
236 ProjectMachineContext.notifyListeners(new ProjectContextEvent(this, ProjectContextEventType.MACHINE_DEFINITION_CHANGE));
239 private void preferenceListener(PropertyChangeEvent changeEvent)
241 if (changeEvent.getProperty().equals(ProjectMachineContext.MACHINE_PROPERTY))
243 updateDefinition(Optional.ofNullable((String) changeEvent.getNewValue()));
247 private void notifyActiveMachineListeners()
249 machineListeners.forEach(ob -> ob.setMachine(activeMachine));
252 public void addActiveMachineListener(ActiveMachineListener ob)
254 machineListeners.add(ob);
255 ob.setMachine(activeMachine);
258 public void removeActiveMachineListener(ActiveMachineListener ob)
260 machineListeners.remove(ob);
263 private void notifyMachineContextStatusListeners(MachineContextStatus oldStatus)
265 MachineContextStatus newStatus = status;
266 stateListeners.forEach(ob -> ob.updateStatus(oldStatus, newStatus));
269 public void addMachineContextStatusListener(MachineContextStatusListener ob)
271 stateListeners.add(ob);
274 public void removeMachineContextStatusListener(MachineContextStatusListener ob)
276 stateListeners.remove(ob);
280 public static interface ActiveMachineListener
282 void setMachine(Optional<Machine> machine);
286 public static interface MachineContextStatusListener
288 void updateStatus(MachineContextStatus oldStatus, MachineContextStatus newStatus);