diff --git a/src/main/dig/processor/ALU/ALU.svg b/src/main/dig/processor/ALU/ALU.svg index 7b0282420..e644fb1d9 100644 --- a/src/main/dig/processor/ALU/ALU.svg +++ b/src/main/dig/processor/ALU/ALU.svg @@ -28,9 +28,9 @@ { private final String key; - private final VALUE def; + private final DefaultFactory defFactory; private final String langKey; private boolean groupEditAllowed = false; private Key dependsOn; private CheckEnabled checkEnabled; + private boolean isSecondary; + private boolean requiresRestart = false; // Both values are always null in digital. // Both are only used within a custom implemented component. private String name; private String description; - private boolean isSecondary; - private boolean requiresRestart = false; /** - * Creates a new Key + * Creates a new Key. + * Use this constructor only if the def value is not mutable! * * @param key the key * @param def the default value */ public Key(String key, VALUE def) { - this.key = key; - langKey = "key_" + key.replace(" ", ""); + this(key, () -> def); if (def == null) throw new NullPointerException(); - this.def = def; + } + + /** + * Creates a new Key. + * Use this constructor if the def value is mutable! + * + * @param key the key + * @param defFactory the factory to create a default value + */ + public Key(String key, DefaultFactory defFactory) { + this.key = key; + langKey = "key_" + key.replace(" ", ""); + if (defFactory == null) + throw new NullPointerException(); + this.defFactory = defFactory; } /** @@ -68,14 +82,14 @@ public class Key { * @return the default value of this key */ public VALUE getDefault() { - return def; + return defFactory.createDefault(); } /** * @return The values class */ public Class getValueClass() { - return def.getClass(); + return getDefault().getClass(); } @Override @@ -490,4 +504,18 @@ public class Key { */ boolean isEnabled(T t); } + + /** + * Used to provide a default value if the value is mutable. + * + * @param the type of the value + */ + public interface DefaultFactory { + /** + * Called to create a new default value. + * + * @return the default value + */ + VALUE createDefault(); + } } diff --git a/src/main/java/de/neemann/digital/core/element/Keys.java b/src/main/java/de/neemann/digital/core/element/Keys.java index 178438072..cfb5029f1 100644 --- a/src/main/java/de/neemann/digital/core/element/Keys.java +++ b/src/main/java/de/neemann/digital/core/element/Keys.java @@ -278,7 +278,7 @@ public final class Keys { * the data key for memory */ public static final Key DATA - = new Key<>("Data", DataField.DEFAULT); + = new Key<>("Data", DataField::new); /** * flag for flipping selector pos in muxers, decoders and drivers @@ -602,7 +602,7 @@ public final class Keys { * contains the input inverter config */ public static final Key INVERTER_CONFIG - = new Key<>("inverterConfig", new InverterConfig()); + = new Key<>("inverterConfig", new InverterConfig.Builder().build()); /** * Background Color of nested circuits @@ -661,7 +661,7 @@ public final class Keys { * The manager which contains all the roms data */ public static final Key ROMMANAGER - = new Key<>("romContent", ROMManger.EMPTY).setSecondary(); + = new Key<>("romContent", ROMManger::new).setSecondary(); /** @@ -707,7 +707,7 @@ public final class Keys { * Shape used to represent a visual element */ public static final Key CUSTOM_SHAPE - = new Key<>("customShape", CustomShapeDescription.EMPTY) + = new Key<>("customShape", new CustomShapeDescription.Builder().build()) .setSecondary() .setDependsOn(SHAPE_TYPE, st -> st.equals(CustomCircuitShapeType.CUSTOM)); diff --git a/src/main/java/de/neemann/digital/core/memory/DataField.java b/src/main/java/de/neemann/digital/core/memory/DataField.java index 52cded480..1b3a382d8 100644 --- a/src/main/java/de/neemann/digital/core/memory/DataField.java +++ b/src/main/java/de/neemann/digital/core/memory/DataField.java @@ -17,15 +17,17 @@ import java.util.Arrays; */ public class DataField implements HGSArray { - /*** - * Simple default data field - */ - public static final DataField DEFAULT = new DataField(0); - private long[] data; private final transient ArrayList listeners = new ArrayList<>(); + /** + * Creates a new DataField of size 0 + */ + public DataField() { + this(0); + } + /** * Creates a new DataField * @@ -174,6 +176,13 @@ public class DataField implements HGSArray { return data.length; } + /** + * @return true if the data field is empty + */ + public boolean isEmpty() { + return trim() == 0; + } + /** * Adds a listener to this DataField * @@ -247,4 +256,17 @@ public class DataField implements HGSArray { public long[] getData() { return data; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DataField dataField = (DataField) o; + return Arrays.equals(data, dataField.data); + } + + @Override + public int hashCode() { + return Arrays.hashCode(data); + } } diff --git a/src/main/java/de/neemann/digital/core/memory/EEPROM.java b/src/main/java/de/neemann/digital/core/memory/EEPROM.java index 9b02cd47a..b21034f44 100644 --- a/src/main/java/de/neemann/digital/core/memory/EEPROM.java +++ b/src/main/java/de/neemann/digital/core/memory/EEPROM.java @@ -5,10 +5,7 @@ */ package de.neemann.digital.core.memory; -import de.neemann.digital.core.Node; -import de.neemann.digital.core.NodeException; -import de.neemann.digital.core.ObservableValue; -import de.neemann.digital.core.ObservableValues; +import de.neemann.digital.core.*; import de.neemann.digital.core.element.Element; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.ElementTypeDescription; @@ -40,6 +37,7 @@ public class EEPROM extends Node implements Element, RAMInterface, ROMInterface private final int bits; private final int addrBits; + private final ElementAttributes attr; private final int size; private final String label; private final ObservableValue dataOut; @@ -65,6 +63,7 @@ public class EEPROM extends Node implements Element, RAMInterface, ROMInterface */ public EEPROM(ElementAttributes attr) { super(true); + this.attr = attr; bits = attr.get(Keys.BITS); addrBits = attr.get(Keys.ADDR_BITS); size = 1 << addrBits; @@ -77,6 +76,14 @@ public class EEPROM extends Node implements Element, RAMInterface, ROMInterface isProgramMemory = attr.get(Keys.IS_PROGRAM_MEMORY); } + @Override + public void registerNodes(Model model) { + super.registerNodes(model); + + if (memory.isEmpty()) + model.addObserver(event -> attr.set(Keys.DATA, memory), ModelEvent.STOPPED); + } + @Override public void setInputs(ObservableValues inputs) throws NodeException { addrIn = inputs.get(0).checkBits(addrBits, this).addObserverToValue(this); diff --git a/src/main/java/de/neemann/digital/core/memory/EEPROMDualPort.java b/src/main/java/de/neemann/digital/core/memory/EEPROMDualPort.java index d1f11e4dc..caa2c157c 100644 --- a/src/main/java/de/neemann/digital/core/memory/EEPROMDualPort.java +++ b/src/main/java/de/neemann/digital/core/memory/EEPROMDualPort.java @@ -5,6 +5,8 @@ */ package de.neemann.digital.core.memory; +import de.neemann.digital.core.Model; +import de.neemann.digital.core.ModelEvent; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.ElementTypeDescription; import de.neemann.digital.core.element.Keys; @@ -33,6 +35,9 @@ public class EEPROMDualPort extends RAMDualPort implements ROMInterface { .addAttribute(Keys.LABEL) .addAttribute(Keys.DATA); + private final ElementAttributes attr; + private DataField memory; + /** * Creates a new instance * @@ -40,11 +45,21 @@ public class EEPROMDualPort extends RAMDualPort implements ROMInterface { */ public EEPROMDualPort(ElementAttributes attr) { super(attr); + this.attr = attr; } @Override protected DataField createDataField(ElementAttributes attr, int size) { - return attr.get(Keys.DATA); + memory = attr.get(Keys.DATA); + return memory; + } + + @Override + public void registerNodes(Model model) { + super.registerNodes(model); + + if (memory.isEmpty()) + model.addObserver(event -> attr.set(Keys.DATA, memory), ModelEvent.STOPPED); } } diff --git a/src/main/java/de/neemann/digital/core/memory/rom/ROMManger.java b/src/main/java/de/neemann/digital/core/memory/rom/ROMManger.java index 875826f2b..413e1acbe 100644 --- a/src/main/java/de/neemann/digital/core/memory/rom/ROMManger.java +++ b/src/main/java/de/neemann/digital/core/memory/rom/ROMManger.java @@ -10,15 +10,12 @@ import de.neemann.digital.core.Node; import de.neemann.digital.core.memory.DataField; import java.util.HashMap; +import java.util.Objects; /** * The Manager to manage all necessary rom images */ public class ROMManger { - /** - * The empty instance - */ - public static final ROMManger EMPTY = new ROMManger(); private final HashMap roms; @@ -35,6 +32,8 @@ public class ROMManger { * @param model the mode to use */ public void applyTo(Model model) { + if (roms == null) + return; for (Node n : model.findNode(n -> n instanceof ROMInterface)) { ROMInterface rom = (ROMInterface) n; DataField data = roms.get(rom.getLabel()); @@ -65,19 +64,23 @@ public class ROMManger { roms.put(label, data); } - /** - * @return returns EMPTY it this ROMManager is empty, this otherwise - */ - public ROMManger getMinimized() { - if (roms.isEmpty()) - return EMPTY; - return this; - } - /** * @return true if no ROM's are stored */ public boolean isEmpty() { return roms.isEmpty(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ROMManger romManger = (ROMManger) o; + return Objects.equals(roms, romManger.roms); + } + + @Override + public int hashCode() { + return Objects.hash(roms); + } } diff --git a/src/main/java/de/neemann/digital/draw/model/InverterConfig.java b/src/main/java/de/neemann/digital/draw/model/InverterConfig.java index 3df26a194..46286b991 100644 --- a/src/main/java/de/neemann/digital/draw/model/InverterConfig.java +++ b/src/main/java/de/neemann/digital/draw/model/InverterConfig.java @@ -15,29 +15,12 @@ import java.util.Objects; /** * Manages the input inverting of a component */ -public class InverterConfig implements HGSMap { +public final class InverterConfig implements HGSMap { private HashSet inputs; - /** - * Creates a new instance. - * No input is inverted. - */ - public InverterConfig() { - inputs = null; - } - - /** - * Adds a signal to invert - * - * @param name the signale - * @return this for chained calls - */ - public InverterConfig add(String name) { - if (inputs == null) - inputs = new HashSet<>(); - inputs.add(name); - return this; + private InverterConfig(HashSet inputs) { + this.inputs = inputs; } /** @@ -129,4 +112,35 @@ public class InverterConfig implements HGSMap { return inputs.contains(key); } + + /** + * Builder to create InverterConfig instances + */ + public static class Builder { + + private HashSet inputs; + + /** + * Adds a signal to invert + * + * @param name the signale + * @return this for chained calls + */ + public Builder add(String name) { + if (inputs == null) + inputs = new HashSet<>(); + inputs.add(name); + return this; + } + + /** + * Creats the instance + * + * @return the created instance + */ + public InverterConfig build() { + return new InverterConfig(inputs); + } + + } } diff --git a/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java b/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java index 49564f1ee..be12a28fb 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java +++ b/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java @@ -212,7 +212,7 @@ public final class ShapeFactory { return new LayoutShape(customDescr, elementAttributes); case CUSTOM: final CustomShapeDescription customShapeDescription = customDescr.getAttributes().get(Keys.CUSTOM_SHAPE); - if (customShapeDescription != CustomShapeDescription.EMPTY) + if (!customShapeDescription.isEmpty()) return new CustomShape(customShapeDescription, elementAttributes.getLabel(), pt.getInputDescription(elementAttributes), pt.getOutputDescriptions(elementAttributes)); diff --git a/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java b/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java index 63bffe51a..f2ad8b9c9 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java +++ b/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java @@ -24,95 +24,19 @@ import java.util.Iterator; /** * Is intended to be stored in a file. */ -public class CustomShapeDescription implements Iterable { - /** - * The default empty shape instance - */ - public static final CustomShapeDescription EMPTY = new CustomShapeDescription(); +public final class CustomShapeDescription implements Iterable { - private HashMap pins; - private ArrayList drawables; - private TextHolder label; + private final HashMap pins; + private final ArrayList drawables; + private final TextHolder label; /** * Creates a new instance */ - public CustomShapeDescription() { - pins = new HashMap<>(); - drawables = new ArrayList<>(); - } - - /** - * Adds a pin to this shape description - * - * @param name the name of the pin - * @param pos the pins position - * @param showLabel if true the label of the pin is shown - * @return this for chained calls - */ - public CustomShapeDescription addPin(String name, Vector pos, boolean showLabel) { - pins.put(name, new Pin(pos, showLabel)); - return this; - } - - - /** - * Adds a polygon to the shape - * - * @param p1 starting point of the line - * @param p2 ending point of the line - * @param thickness the line thickness - * @param color the color to use - * @return this for chained calls - */ - public CustomShapeDescription addLine(Vector p1, Vector p2, int thickness, Color color) { - drawables.add(new LineHolder(p1, p2, thickness, color)); - return this; - } - - /** - * Adds a circle to the shape - * - * @param p1 upper left corner of the circles bounding box - * @param p2 lower right corner of the circles bounding box - * @param thickness the line thickness - * @param color the color to use - * @param filled true if filled - * @return this for chained calls - */ - public CustomShapeDescription addCircle(Vector p1, Vector p2, int thickness, Color color, boolean filled) { - drawables.add(new CircleHolder(p1, p2, thickness, color, filled)); - return this; - } - - /** - * Adds a polygon to the shape - * - * @param poly the polygon to add - * @param thickness the line thickness - * @param color the color to use - * @param filled true if filled - * @return this for chained calls - */ - public CustomShapeDescription addPolygon(Polygon poly, int thickness, Color color, boolean filled) { - drawables.add(new PolygonHolder(poly, thickness, filled, color)); - return this; - } - - /** - * Adds a text to the shape - * - * @param p1 position - * @param p2 second position to determin the base line orientation - * @param text the text to draw - * @param orientation the orientation of the text - * @param size the font size - * @param color the text color - * @return this for chained calls - */ - public CustomShapeDescription addText(Vector p1, Vector p2, String text, Orientation orientation, int size, Color color) { - drawables.add(new TextHolder(p1, p2, text, orientation, size, color)); - return this; + private CustomShapeDescription(HashMap pins, ArrayList drawables, TextHolder label) { + this.pins = pins; + this.drawables = drawables; + this.label = label; } /** @@ -155,19 +79,6 @@ public class CustomShapeDescription implements Iterable pins; + private final ArrayList drawables; + private TextHolder label; + + /** + * Creates a new builder + */ + public Builder() { + pins = new HashMap<>(); + drawables = new ArrayList<>(); + } + + /** + * Sets the label positioning info. + * + * @param pos0 pos0 + * @param pos1 pos1 + * @param textOrientation textOrientation + * @param fontSize fontSize + * @param filled filled + * @return this for chained calls + */ + public Builder setLabel(Vector pos0, Vector pos1, Orientation textOrientation, int fontSize, Color filled) { + label = new TextHolder(pos0, pos1, "", textOrientation, fontSize, filled); + return this; + } + + /** + * Adds a pin to this shape description + * + * @param name the name of the pin + * @param pos the pins position + * @param showLabel if true the label of the pin is shown + * @return this for chained calls + */ + public Builder addPin(String name, Vector pos, boolean showLabel) { + pins.put(name, new Pin(pos, showLabel)); + return this; + } + + + /** + * Adds a polygon to the shape + * + * @param p1 starting point of the line + * @param p2 ending point of the line + * @param thickness the line thickness + * @param color the color to use + * @return this for chained calls + */ + public Builder addLine(Vector p1, Vector p2, int thickness, Color color) { + drawables.add(new LineHolder(p1, p2, thickness, color)); + return this; + } + + /** + * Adds a circle to the shape + * + * @param p1 upper left corner of the circles bounding box + * @param p2 lower right corner of the circles bounding box + * @param thickness the line thickness + * @param color the color to use + * @param filled true if filled + * @return this for chained calls + */ + public Builder addCircle(Vector p1, Vector p2, int thickness, Color color, boolean filled) { + drawables.add(new CircleHolder(p1, p2, thickness, color, filled)); + return this; + } + + /** + * Adds a polygon to the shape + * + * @param poly the polygon to add + * @param thickness the line thickness + * @param color the color to use + * @param filled true if filled + * @return this for chained calls + */ + public Builder addPolygon(Polygon poly, int thickness, Color color, boolean filled) { + drawables.add(new PolygonHolder(poly, thickness, filled, color)); + return this; + } + + /** + * Adds a text to the shape + * + * @param p1 position + * @param p2 second position to determin the base line orientation + * @param text the text to draw + * @param orientation the orientation of the text + * @param size the font size + * @param color the text color + * @return this for chained calls + */ + public Builder addText(Vector p1, Vector p2, String text, Orientation orientation, int size, Color color) { + drawables.add(new TextHolder(p1, p2, text, orientation, size, color)); + return this; + } + + /** + * @return the {@link CustomShapeDescription} + */ + public CustomShapeDescription build() { + return new CustomShapeDescription(pins, drawables, label); + } + + } } diff --git a/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java b/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java index adb7d78c5..a07e3e82c 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java +++ b/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java @@ -67,9 +67,10 @@ public class SvgImporter { NodeList gList = svg.getElementsByTagName("svg").item(0).getChildNodes(); Context c = new Context(); try { - CustomShapeDescription csd = new CustomShapeDescription(); - create(csd, gList, c); + CustomShapeDescription.Builder builder = new CustomShapeDescription.Builder(); + create(builder, gList, c); + CustomShapeDescription csd = builder.build(); if (csd.getPinCount() > 0) { float xMin = Float.MAX_VALUE; float yMin = Float.MAX_VALUE; @@ -86,7 +87,7 @@ public class SvgImporter { } } - private void create(CustomShapeDescription csd, NodeList gList, Context c) throws SvgException { + private void create(CustomShapeDescription.Builder csd, NodeList gList, Context c) throws SvgException { for (int i = 0; i < gList.getLength(); i++) { final Node node = gList.item(i); if (node instanceof Element) { @@ -99,7 +100,7 @@ public class SvgImporter { } } - private void create(CustomShapeDescription csd, Element element, Context parent) throws SvgException { + private void create(CustomShapeDescription.Builder csd, Element element, Context parent) throws SvgException { Context c = new Context(parent, element); switch (element.getNodeName()) { case "a": @@ -150,19 +151,19 @@ public class SvgImporter { } } - private void drawTransformedPolygon(CustomShapeDescription csd, Context c, Polygon polygon) { + private void drawTransformedPolygon(CustomShapeDescription.Builder csd, Context c, Polygon polygon) { if (polygon != null) drawPolygon(csd, c, polygon.transform(c.getTransform())); } - private void drawPolygon(CustomShapeDescription csd, Context c, Polygon polygon) { + private void drawPolygon(CustomShapeDescription.Builder csd, Context c, Polygon polygon) { if (c.getFilled() != null && polygon.isClosed()) csd.addPolygon(polygon, c.getThickness(), c.getFilled(), true); if (c.getStroke() != null) csd.addPolygon(polygon, c.getThickness(), c.getStroke(), false); } - private void drawRect(CustomShapeDescription csd, Element element, Context c) { + private void drawRect(CustomShapeDescription.Builder csd, Element element, Context c) { VectorInterface size = new VectorFloat(c.getLength(element.getAttribute("width")), c.getLength(element.getAttribute("height"))); VectorInterface pos = new VectorFloat(c.getLength(element.getAttribute("x")), c.getLength(element.getAttribute("y"))); String rxStr = element.getAttribute("rx"); @@ -213,7 +214,7 @@ public class SvgImporter { drawPolygon(csd, c, polygon); } - private void drawCircle(CustomShapeDescription csd, Element element, Context c) { + private void drawCircle(CustomShapeDescription.Builder csd, Element element, Context c) { if (element.hasAttribute("id")) { VectorInterface pos = c.v(c.getLength(element.getAttribute("cx")), c.getLength(element.getAttribute("cy"))); String id = element.getAttribute("id"); @@ -279,7 +280,7 @@ public class SvgImporter { return new Vector(Math.round(pos.getXFloat() / SIZE) * SIZE, Math.round(pos.getYFloat() / SIZE) * SIZE); } - private void drawText(CustomShapeDescription csd, Context c, Element element) throws SvgException { + private void drawText(CustomShapeDescription.Builder csd, Context c, Element element) throws SvgException { VectorFloat p = new VectorFloat(c.getLength(element.getAttribute("x")), c.getLength(element.getAttribute("y"))); VectorInterface pos0 = p.transform(c.getTransform()); VectorInterface pos1 = p.add(new VectorFloat(1, 0)).transform(c.getTransform()); @@ -290,7 +291,7 @@ public class SvgImporter { drawTextElement(csd, c, element, pos0, pos1); } - private void drawTextElement(CustomShapeDescription csd, Context c, Element element, VectorInterface pos0, VectorInterface pos1) throws SvgException { + private void drawTextElement(CustomShapeDescription.Builder csd, Context c, Element element, VectorInterface pos0, VectorInterface pos1) throws SvgException { NodeList nodes = element.getElementsByTagName("*"); if (nodes.getLength() == 0) { String text = element.getTextContent(); diff --git a/src/main/java/de/neemann/digital/gui/components/CustomShapeEditor.java b/src/main/java/de/neemann/digital/gui/components/CustomShapeEditor.java index 68ffa1464..0acf90e1b 100644 --- a/src/main/java/de/neemann/digital/gui/components/CustomShapeEditor.java +++ b/src/main/java/de/neemann/digital/gui/components/CustomShapeEditor.java @@ -52,7 +52,7 @@ public class CustomShapeEditor extends EditorFactory.LabelEditor names; diff --git a/src/main/java/de/neemann/digital/testing/TestCaseElement.java b/src/main/java/de/neemann/digital/testing/TestCaseElement.java index 52da4df05..564d3e2c5 100644 --- a/src/main/java/de/neemann/digital/testing/TestCaseElement.java +++ b/src/main/java/de/neemann/digital/testing/TestCaseElement.java @@ -18,7 +18,7 @@ public class TestCaseElement implements Element { /** * the used {@link ElementAttributes} key */ - public static final Key TESTDATA = new Key<>("Testdata", TestCaseDescription.DEFAULT); + public static final Key TESTDATA = new Key<>("Testdata", () -> new TestCaseDescription("")); /** * The TestCaseElement description