Restructured high level state access
[Mograsim.git] / net.mograsim.logic.ui / src / net / mograsim / logic / ui / model / components / SubmodelComponent.java
index 08e7d1a..94dc693 100644 (file)
@@ -2,10 +2,12 @@ package net.mograsim.logic.ui.model.components;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 
 import net.haspamelodica.swt.helper.gcs.GCConfig;
 import net.haspamelodica.swt.helper.gcs.GeneralGC;
@@ -67,6 +69,15 @@ public abstract class SubmodelComponent extends GUIComponent
         */
        private final SubmodelInterface submodelInterface;
 
+       /**
+        * The list of all high level state IDs this component supports without delegating to subcomponents.
+        */
+       private final Set<String> highLevelAtomicStates;
+       /**
+        * A map of high level state subcomponent IDs to the {@link GUIComponent} high level state access requests are delegated to.
+        */
+       private final Map<String, GUIComponent> subcomponentsByHighLevelStateSubcomponentID;
+
        /**
         * The factor by which the submodel is scaled when rendering.
         */
@@ -101,6 +112,9 @@ public abstract class SubmodelComponent extends GUIComponent
                this.supermodelUnmovablePinsUnmodifiable = Collections.unmodifiableMap(supermodelPins);
                this.submodelInterface = new SubmodelInterface(submodelModifiable);
 
+               this.highLevelAtomicStates = new HashSet<>();
+               this.subcomponentsByHighLevelStateSubcomponentID = new HashMap<>();
+
                this.submodelScale = 1;
                this.maxVisibleRegionFillRatioForAlpha0 = 0.4;
                this.minVisibleRegionFillRatioForAlpha1 = 0.8;
@@ -247,6 +261,153 @@ public abstract class SubmodelComponent extends GUIComponent
                return supermodelPins.get(name);
        }
 
+       // high-level access
+
+       /**
+        * Adds the given subcomponent ID to the set of allowed subcomponent IDs and links the given {@link GUIComponent} as the delegate target
+        * for this subcomponent ID. <br>
+        * Note that this method does not affect whether {@link #setSubcomponentHighLevelState(String, String, Object)
+        * set}/{@link #getSubcomponentHighLevelState(String, String)} will be called. <br>
+        * See {@link GUIComponent#setHighLevelState(String, Object)} for details about subcomponent IDs.
+        * 
+        * @author Daniel Kirschten
+        */
+       protected void addHighLevelStateSubcomponentID(String subcomponentID, GUIComponent subcomponent)
+       {
+               checkHighLevelStateIDPart(subcomponentID);
+               subcomponentsByHighLevelStateSubcomponentID.put(subcomponentID, subcomponent);
+       }
+
+       /**
+        * Removes the given subcomponent ID from the set of allowed subcomponent IDs. <br>
+        * Note that this method does not affect whether {@link #setSubcomponentHighLevelState(String, String, Object)
+        * set}/{@link #getSubcomponentHighLevelState(String, String)} will be called.<br>
+        * See {@link GUIComponent#setHighLevelState(String, Object)} for details about subcomponent IDs.
+        * 
+        * @author Daniel Kirschten
+        */
+       protected void removeHighLevelStateSubcomponentID(String subcomponentID)
+       {
+               subcomponentsByHighLevelStateSubcomponentID.remove(subcomponentID);
+       }
+
+       /**
+        * Adds the given atomic state ID to the set of allowed atomic state IDs. <br>
+        * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs.
+        * 
+        * @author Daniel Kirschten
+        */
+       protected void addAtomicHighLevelStateID(String stateID)
+       {
+               checkHighLevelStateIDPart(stateID);
+               highLevelAtomicStates.add(stateID);
+       }
+
+       /**
+        * Removes the given atomic state ID from the set of allowed atomic state IDs. <br>
+        * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs.
+        * 
+        * @author Daniel Kirschten
+        */
+       protected void removeAtomicHighLevelStateID(String stateID)
+       {
+               highLevelAtomicStates.remove(stateID);
+       }
+
+       @Override
+       public final void setHighLevelState(String stateID, Object newState)
+       {
+               int indexOfDot = stateID.indexOf('.');
+               if (indexOfDot == -1)
+                       if (highLevelAtomicStates.contains(stateID))
+                               setAtomicHighLevelState(stateID, newState);
+                       else
+                               super.setHighLevelState(stateID, newState);
+               else
+                       setSubcomponentHighLevelState(stateID.substring(0, indexOfDot), stateID.substring(indexOfDot + 1), newState);
+       }
+
+       /**
+        * This method is called in {@link #setHighLevelState(String, Object)} when the state ID is not atomic. The default implementation uses
+        * the information given to {@link #addHighLevelStateSubcomponentID(String, GUIComponent)
+        * add}/{@link #removeHighLevelStateSubcomponentID(String)} to decide which subcomponent to delegate to.<br>
+        * Note that {@link #addHighLevelStateSubcomponentID(String, GUIComponent) add}/{@link #removeHighLevelStateSubcomponentID(String)}
+        * don't affect whether this method will be called.
+        * 
+        * @author Daniel Kirschten
+        */
+       protected void setSubcomponentHighLevelState(String subcomponentID, String subcomponentHighLevelStateID, Object newState)
+       {
+               GUIComponent subcomponent = subcomponentsByHighLevelStateSubcomponentID.get(subcomponentID);
+               if (subcomponent != null)
+                       subcomponent.setHighLevelState(subcomponentHighLevelStateID, newState);
+               else
+                       super.setHighLevelState(subcomponentID + "." + subcomponentHighLevelStateID, newState);
+       }
+
+       /**
+        * This method is called in {@link #setHighLevelState(String, Object)} when the state ID is atomic and in the set of allowed atomic
+        * state IDs. <br>
+        * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs.
+        * 
+        * @author Daniel Kirschten
+        */
+       @SuppressWarnings({ "static-method", "unused" }) // this method is intended to be overridden
+       protected void setAtomicHighLevelState(String stateID, Object newState)
+       {
+               throw new IllegalStateException("Unknown high level state ID: " + stateID);
+       }
+
+       @Override
+       public final Object getHighLevelState(String stateID)
+       {
+               int indexOfDot = stateID.indexOf('.');
+               if (indexOfDot == -1)
+               {
+                       if (highLevelAtomicStates.contains(stateID))
+                               return getAtomicHighLevelState(stateID);
+                       return super.getHighLevelState(stateID);
+               }
+               return getSubcomponentHighLevelState(stateID.substring(0, indexOfDot), stateID.substring(indexOfDot + 1));
+       }
+
+       /**
+        * This method is called in {@link #getHighLevelState(String, Object)} when the state ID is not atomic. The default implementation uses
+        * the information given to {@link #addHighLevelStateSubcomponentID(String, GUIComponent)
+        * add}/{@link #removeHighLevelStateSubcomponentID(String)} to decide which subcomponent to delegate to. <br>
+        * Note that {@link #addHighLevelStateSubcomponentID(String, GUIComponent) add}/{@link #removeHighLevelStateSubcomponentID(String)}
+        * don't affect whether this method will be called.
+        * 
+        * @author Daniel Kirschten
+        */
+       protected Object getSubcomponentHighLevelState(String subcomponentID, String subcomponentHighLevelStateID)
+       {
+               GUIComponent subcomponent = subcomponentsByHighLevelStateSubcomponentID.get(subcomponentID);
+               if (subcomponent != null)
+                       return subcomponent.getHighLevelState(subcomponentHighLevelStateID);
+               return super.getHighLevelState(subcomponentID + "." + subcomponentHighLevelStateID);
+       }
+
+       /**
+        * This method is called in {@link SubmodelComponent#getHighLevelState(String)} when the state ID is in the set of allowed atomic state
+        * IDs. <br>
+        * See {@link GUIComponent#setHighLevelState(String, Object)} for details about atomic state IDs.
+        * 
+        * @author Daniel Kirschten
+        */
+       @SuppressWarnings("static-method") // this method is intended to be overridden
+       protected Object getAtomicHighLevelState(String stateID)
+       {
+               throw new IllegalStateException("Unknown high level state ID: " + stateID);
+       }
+
+       private static void checkHighLevelStateIDPart(String stateIDPart)
+       {
+               if (stateIDPart.indexOf('.') != -1)
+                       throw new IllegalArgumentException("Illegal high level state ID part (contains dot): " + stateIDPart);
+
+       }
+
        // "graphical" operations
 
        /**
@@ -303,8 +464,18 @@ public abstract class SubmodelComponent extends GUIComponent
                renderOutline(gc, visibleRegion);
        }
 
+       /**
+        * Render the outline of this {@link SubmodelComponent}, e.g. the graphical elements that should stay visible if the submodel is drawn.
+        * 
+        * @author Daniel Kirschten
+        */
        protected abstract void renderOutline(GeneralGC gc, Rectangle visibleRegion);
 
+       /**
+        * Render the symbol of this {@link SubmodelComponent}, e.g. the things that should be hidden if the submodel is drawn.
+        * 
+        * @author Daniel Kirschten
+        */
        protected abstract void renderSymbol(GeneralGC gc, Rectangle visibleRegion);
 
        private static double map(double val, double valMin, double valMax, double mapMin, double mapMax)
@@ -331,6 +502,7 @@ public abstract class SubmodelComponent extends GUIComponent
        public SubmodelComponentParams calculateParams()
        {
                SubmodelComponentParams params = new SubmodelComponentParams();
+               params.name = getIdentifier();
                params.type = SubmodelComponent.class.getSimpleName();
                params.composition = calculateCompositionParams();
 
@@ -368,8 +540,8 @@ public abstract class SubmodelComponent extends GUIComponent
                        InnerComponentParams inner = new InnerComponentParams();
                        comps[i] = inner;
                        inner.params = component.getInstantiationParameters();
-                       inner.pos = new Point(getPosX(), getPosY());
-                       inner.type = component.getIdentifier();
+                       inner.pos = new Point(component.getPosX(), component.getPosY());
+                       inner.name = component.getIdentifier();
                        i++;
                }
                params.subComps = comps;