diff --git a/src/main/dig/74xx/counter.dig b/src/main/dig/74xx/counter.dig index 95a8128cd..d23a55530 100644 --- a/src/main/dig/74xx/counter.dig +++ b/src/main/dig/74xx/counter.dig @@ -6,10 +6,6 @@ Description Three digit BCD counter with seven seg displays. - - lockedMode - true - diff --git a/src/main/java/de/neemann/digital/core/ExceptionWithOrigin.java b/src/main/java/de/neemann/digital/core/ExceptionWithOrigin.java index 7bb845b25..847afe30a 100644 --- a/src/main/java/de/neemann/digital/core/ExceptionWithOrigin.java +++ b/src/main/java/de/neemann/digital/core/ExceptionWithOrigin.java @@ -1,5 +1,7 @@ package de.neemann.digital.core; +import de.neemann.digital.draw.elements.VisualElement; + import java.io.File; import java.util.HashSet; import java.util.Set; @@ -10,6 +12,7 @@ import java.util.Set; */ public class ExceptionWithOrigin extends Exception { private File origin; + private VisualElement visualElement; /** * Returns the file or the files that caused the given exception. @@ -99,4 +102,21 @@ public class ExceptionWithOrigin extends Exception { } } + /** + * Sets the visual element which caused this error + * + * @param visualElement the visual element + * @return this for chained calls + */ + public ExceptionWithOrigin setVisualElement(VisualElement visualElement) { + this.visualElement = visualElement; + return this; + } + + /** + * @return the visual element which caused this error + */ + public VisualElement getVisualElement() { + return visualElement; + } } diff --git a/src/main/java/de/neemann/digital/draw/elements/PinException.java b/src/main/java/de/neemann/digital/draw/elements/PinException.java index eb932bd80..090e742b9 100644 --- a/src/main/java/de/neemann/digital/draw/elements/PinException.java +++ b/src/main/java/de/neemann/digital/draw/elements/PinException.java @@ -9,18 +9,17 @@ import de.neemann.digital.draw.model.Net; * @author hneemann */ public class PinException extends ExceptionWithOrigin { - private VisualElement element; private Net net; /** * Creates a new instance * - * @param message the message - * @param element the visual element affected + * @param message the message + * @param visualElement the visual element affected */ - public PinException(String message, VisualElement element) { + public PinException(String message, VisualElement visualElement) { super(message); - this.element = element; + setVisualElement(visualElement); } /** @@ -33,6 +32,7 @@ public class PinException extends ExceptionWithOrigin { super(message); this.net = net; setOrigin(net.getOrigin()); + setVisualElement(net.getVisualElement()); } /** @@ -44,13 +44,6 @@ public class PinException extends ExceptionWithOrigin { super(message); } - /** - * @return the effected element - */ - public VisualElement getVisualElement() { - return element; - } - /** * @return the effected net */ diff --git a/src/main/java/de/neemann/digital/draw/library/CustomElement.java b/src/main/java/de/neemann/digital/draw/library/CustomElement.java index 27e3f004f..6bc40b2bf 100644 --- a/src/main/java/de/neemann/digital/draw/library/CustomElement.java +++ b/src/main/java/de/neemann/digital/draw/library/CustomElement.java @@ -6,6 +6,7 @@ import de.neemann.digital.core.ObservableValues; import de.neemann.digital.core.element.Element; import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.elements.PinException; +import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.draw.model.ModelCreator; import de.neemann.digital.draw.model.NetList; import de.neemann.digital.lang.Lang; @@ -39,21 +40,22 @@ public class CustomElement implements Element { * Gets a {@link ModelCreator} of this circuit. * Every time this method is called a new {@link ModelCreator} is created. * - * @param subName name of the circuit, used to name unique elements - * @param depth recursion depth, used to detect a circuit which contains itself + * @param subName name of the circuit, used to name unique elements + * @param depth recursion depth, used to detect a circuit which contains itself + * @param containingVisualElement the containing visual element * @return the {@link ModelCreator} * @throws PinException PinException * @throws NodeException NodeException * @throws ElementNotFoundException ElementNotFoundException */ - public ModelCreator getModelDescription(String subName, int depth) throws PinException, NodeException, ElementNotFoundException { + public ModelCreator getModelCreator(String subName, int depth, VisualElement containingVisualElement) throws PinException, NodeException, ElementNotFoundException { if (netList == null) netList = new NetList(circuit); if (depth > MAX_DEPTH) throw new NodeException(Lang.get("err_recursiveNestingAt_N0", circuit.getOrigin())); - return new ModelCreator(circuit, library, true, new NetList(netList), subName, depth); + return new ModelCreator(circuit, library, true, new NetList(netList, containingVisualElement), subName, depth, containingVisualElement); } @Override diff --git a/src/main/java/de/neemann/digital/draw/model/ModelCreator.java b/src/main/java/de/neemann/digital/draw/model/ModelCreator.java index dac62bfb2..e62eeceef 100644 --- a/src/main/java/de/neemann/digital/draw/model/ModelCreator.java +++ b/src/main/java/de/neemann/digital/draw/model/ModelCreator.java @@ -59,23 +59,24 @@ public class ModelCreator implements Iterable { * @throws ElementNotFoundException ElementNotFoundException */ public ModelCreator(Circuit circuit, ElementLibrary library, boolean readAsCustom) throws PinException, NodeException, ElementNotFoundException { - this(circuit, library, readAsCustom, new NetList(circuit), "", 0); + this(circuit, library, readAsCustom, new NetList(circuit), "", 0, null); } /** * Creates a new instance * - * @param circuit the circuit to use - * @param library the library to use - * @param isNestedCircuit if true the model is created for use as nested element - * @param netList the NetList of the model. If known it is not necessary to create it. - * @param subName name of the circuit, used to name unique elements - * @param depth recursion depth, used to detect a circuit which contains itself + * @param circuit the circuit to use + * @param library the library to use + * @param isNestedCircuit if true the model is created for use as nested element + * @param netList the NetList of the model. If known it is not necessary to create it. + * @param subName name of the circuit, used to name unique elements + * @param depth recursion depth, used to detect a circuit which contains itself + * @param containingVisualElement th topmost containig visual element * @throws PinException PinException * @throws NodeException NodeException * @throws ElementNotFoundException ElementNotFoundException */ - public ModelCreator(Circuit circuit, ElementLibrary library, boolean isNestedCircuit, NetList netList, String subName, int depth) throws PinException, NodeException, ElementNotFoundException { + public ModelCreator(Circuit circuit, ElementLibrary library, boolean isNestedCircuit, NetList netList, String subName, int depth, VisualElement containingVisualElement) throws PinException, NodeException, ElementNotFoundException { this.circuit = circuit; this.netList = netList; entries = new ArrayList<>(); @@ -86,6 +87,10 @@ public class ModelCreator implements Iterable { try { for (VisualElement ve : circuit.getElements()) { + VisualElement cve = ve; + if (containingVisualElement != null) + cve = containingVisualElement; + Pins pins = ve.getPins(); ElementTypeDescription elementType = library.getElementType(ve.getElementName()); ElementAttributes attr = ve.getElementAttributes(); @@ -108,11 +113,11 @@ public class ModelCreator implements Iterable { if (elementType == In.DESCRIPTION || elementType == Out.DESCRIPTION || elementType == Clock.DESCRIPTION) { String label = ve.getElementAttributes().getLabel(); if (label == null || label.length() == 0) - throw new PinException(Lang.get("err_pinWithoutName", circuit.getOrigin())); + throw new PinException(Lang.get("err_pinWithoutName", circuit.getOrigin()), cve); if (pins.size() != 1) - throw new PinException(Lang.get("err_N_isNotInputOrOutput", label, circuit.getOrigin())); + throw new PinException(Lang.get("err_N_isNotInputOrOutput", label, circuit.getOrigin()), cve); if (ioMap.containsKey(label)) - throw new PinException(Lang.get("err_duplicatePinLabel", label, circuit.getOrigin())); + throw new PinException(Lang.get("err_duplicatePinLabel", label, circuit.getOrigin()), cve); ioMap.put(label, pins.get(0)); isNotAIO = false; @@ -120,7 +125,7 @@ public class ModelCreator implements Iterable { } if (isNotAIO) - entries.add(new ModelEntry(element, pins, ve, elementType.getInputDescription(ve.getElementAttributes()), isNestedCircuit, circuit.getOrigin())); + entries.add(new ModelEntry(element, pins, ve, elementType.getInputDescription(ve.getElementAttributes()), isNestedCircuit, circuit.getOrigin(), cve)); for (Pin p : pins) netList.add(p); @@ -133,7 +138,10 @@ public class ModelCreator implements Iterable { ModelEntry me = it.next(); if (me.getElement() instanceof CustomElement) { // at first look for custom elements CustomElement ce = (CustomElement) me.getElement(); - ModelCreator child = ce.getModelDescription(combineNames(subName, me.getVisualElement().getElementAttributes().getCleanLabel()), depth + 1); + ModelCreator child = ce.getModelCreator( + combineNames(subName, me.getVisualElement().getElementAttributes().getCleanLabel()), + depth + 1, + containingVisualElement != null ? containingVisualElement : me.getVisualElement()); modelCreators.add(child); HashMap netMatch = new HashMap<>(); @@ -189,6 +197,7 @@ public class ModelCreator implements Iterable { } } catch (PinException | NodeException e) { e.setOrigin(circuit.getOrigin()); + e.setVisualElement(containingVisualElement); throw e; } } @@ -281,7 +290,7 @@ public class ModelCreator implements Iterable { for (ModelEntry me : entries) { Element element = me.getElement(); if (element instanceof Node && nodeSet.contains(element)) - highLighted.add(me.getVisualElement()); + highLighted.add(me.getContainingVisualElement()); } } diff --git a/src/main/java/de/neemann/digital/draw/model/ModelEntry.java b/src/main/java/de/neemann/digital/draw/model/ModelEntry.java index 2fd6d5565..8a7ea0961 100644 --- a/src/main/java/de/neemann/digital/draw/model/ModelEntry.java +++ b/src/main/java/de/neemann/digital/draw/model/ModelEntry.java @@ -27,27 +27,30 @@ public class ModelEntry { private final Pins pins; private final PinDescriptions inputNames; private final boolean isNestedElement; - private final File origin; // Only used to create better error messages + private final File origin; // Only used to create better error messages + private final VisualElement containingVisualElement; // Only used to create better error messages private final VisualElement visualElement; private IOState ioState; /** * Creates a new instance * - * @param element the element which is created - * @param pins the pins transformed in the circuits coordinate system - * @param visualElement the visual element which has created the element - * @param inputNames the pin descriptions of the inputs. - * @param isNestedElement true if this visual element is a nested included element - * @param origin Used to create better error messages + * @param element the element which is created + * @param pins the pins transformed in the circuits coordinate system + * @param visualElement the visual element which has created the element + * @param inputNames the pin descriptions of the inputs. + * @param isNestedElement true if this visual element is a nested included element + * @param origin Used to create better error messages + * @param containingVisualElement only used to create better error messages */ - public ModelEntry(Element element, Pins pins, VisualElement visualElement, PinDescriptions inputNames, boolean isNestedElement, File origin) { + public ModelEntry(Element element, Pins pins, VisualElement visualElement, PinDescriptions inputNames, boolean isNestedElement, File origin, VisualElement containingVisualElement) { this.element = element; this.pins = pins; this.visualElement = visualElement; this.inputNames = inputNames; this.isNestedElement = isNestedElement; this.origin = origin; + this.containingVisualElement = containingVisualElement; } /** @@ -67,11 +70,11 @@ public class ModelEntry { for (PinDescription inputName : inputNames) { Pin pin = ins.get(inputName.getName()); if (pin == null) - throw new PinException(Lang.get("err_pin_N0_atElement_N1_notFound", inputName, visualElement), visualElement); + throw new PinException(Lang.get("err_pin_N0_atElement_N1_notFound", inputName, visualElement), containingVisualElement); ObservableValue value = pin.getValue(); if (value == null) - throw new PinException(Lang.get("err_noValueSetFor_N0_atElement_N1", inputName, visualElement), visualElement); + throw new PinException(Lang.get("err_noValueSetFor_N0_atElement_N1", inputName, visualElement), containingVisualElement); inputs.add(ic.invert(inputName.getName(), value)); } @@ -83,7 +86,7 @@ public class ModelEntry { bidirect = new ArrayList<>(); final ObservableValue readerValue = p.getReaderValue(); if (readerValue == null) - throw new PinException(Lang.get("err_noValueSetFor_N0_atElement_N1", p.getName(), visualElement), visualElement); + throw new PinException(Lang.get("err_noValueSetFor_N0_atElement_N1", p.getName(), visualElement), containingVisualElement); bidirect.add(readerValue); } } @@ -141,4 +144,11 @@ public class ModelEntry { public IOState getIoState() { return ioState; } + + /** + * @return the containing visual element + */ + public VisualElement getContainingVisualElement() { + return containingVisualElement; + } } diff --git a/src/main/java/de/neemann/digital/draw/model/Net.java b/src/main/java/de/neemann/digital/draw/model/Net.java index 9b6044d2f..b18a6d4b8 100644 --- a/src/main/java/de/neemann/digital/draw/model/Net.java +++ b/src/main/java/de/neemann/digital/draw/model/Net.java @@ -6,6 +6,7 @@ import de.neemann.digital.core.element.PinDescription; import de.neemann.digital.core.wiring.bus.DataBus; import de.neemann.digital.draw.elements.Pin; import de.neemann.digital.draw.elements.PinException; +import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.draw.elements.Wire; import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.lang.Lang; @@ -29,14 +30,17 @@ public class Net { private final ArrayList wires; private final HashSet labelSet; private File origin; + private VisualElement visualElement; // only used to create better error messages /** * Creates a copy of the given net * - * @param toCopy the net to copy + * @param toCopy the net to copy + * @param visualElement the containing visual element, only used to create better error messages */ - public Net(Net toCopy) { + public Net(Net toCopy, VisualElement visualElement) { points = toCopy.points; // no deep copy of points necessary + this.visualElement = visualElement; wires = null; // wires not needed pins = new ArrayList<>(toCopy.pins); // Pins are changed so create a deep copy labelSet = new HashSet<>(toCopy.labelSet); //ToDo copy necessary? @@ -255,4 +259,11 @@ public class Net { public File getOrigin() { return origin; } + + /** + * @return the containing visual element + */ + public VisualElement getVisualElement() { + return visualElement; + } } diff --git a/src/main/java/de/neemann/digital/draw/model/NetList.java b/src/main/java/de/neemann/digital/draw/model/NetList.java index 75cb51892..14834af2d 100644 --- a/src/main/java/de/neemann/digital/draw/model/NetList.java +++ b/src/main/java/de/neemann/digital/draw/model/NetList.java @@ -65,12 +65,13 @@ public class NetList implements Iterable { /** * Creates a copy of the given net list * - * @param toCopy the net list to copy + * @param toCopy the net list to copy + * @param visualElement the containing visual element, only used to create better error messages */ - public NetList(NetList toCopy) { + public NetList(NetList toCopy, VisualElement visualElement) { netList = new ArrayList<>(); for (Net net : toCopy) - netList.add(new Net(net)); + netList.add(new Net(net, visualElement)); } /** diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index d8c0f1568..9c7831f0b 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -1081,6 +1081,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS if (cause instanceof NodeException) { NodeException e = (NodeException) cause; circuitComponent.addHighLightedWires(e.getValues()); + circuitComponent.addHighLighted(e.getVisualElement()); if (modelCreator != null) modelCreator.addNodeElementsTo(e.getNodes(), circuitComponent.getHighLighted()); } else if (cause instanceof PinException) { diff --git a/src/test/java/de/neemann/digital/integration/TestErrorOrigin.java b/src/test/java/de/neemann/digital/integration/TestErrorOrigin.java new file mode 100644 index 000000000..a065e2045 --- /dev/null +++ b/src/test/java/de/neemann/digital/integration/TestErrorOrigin.java @@ -0,0 +1,63 @@ +package de.neemann.digital.integration; + +import de.neemann.digital.core.ExceptionWithOrigin; +import de.neemann.digital.core.NodeException; +import de.neemann.digital.draw.elements.PinException; +import de.neemann.digital.draw.library.ElementNotFoundException; +import junit.framework.TestCase; + +import java.io.IOException; + +public class TestErrorOrigin extends TestCase { + + public void testErrorMessage() throws PinException, NodeException, ElementNotFoundException, IOException { + try { + new ToBreakRunner("/dig/errorOrigin/main.dig"); + fail(); + } catch (PinException e) { + assertNotNull(e.getVisualElement()); + assertEquals("mid.dig", e.getVisualElement().getElementName()); + checkOrigin(e, "src/test/resources/dig/errorOrigin/inner.dig"); + } + } + + private void checkOrigin(ExceptionWithOrigin e, String origin) { + assertEquals(1, e.getOrigin().size()); + String file = e.getOrigin().iterator().next().getPath().replace('\\', '/'); + assertTrue(file, file.endsWith(origin)); + } + + public void testErrorMessage2() throws PinException, NodeException, ElementNotFoundException, IOException { + try { + new ToBreakRunner("/dig/errorOrigin/main2.dig"); + fail(); + } catch (PinException e) { + assertNotNull(e.getVisualElement()); + assertEquals("mid2.dig", e.getVisualElement().getElementName()); + checkOrigin(e, "src/test/resources/dig/errorOrigin/mid2.dig"); + } + } + + public void testErrorMessage3() throws PinException, NodeException, ElementNotFoundException, IOException { + try { + new ToBreakRunner("/dig/errorOrigin/main3.dig"); + fail(); + } catch (PinException e) { + assertNotNull(e.getVisualElement()); + assertEquals("midOk.dig", e.getVisualElement().getElementName()); + checkOrigin(e, "src/test/resources/dig/errorOrigin/midOk.dig"); + } + } + + public void testErrorMessage4() throws PinException, NodeException, ElementNotFoundException, IOException { + try { + new ToBreakRunner("/dig/errorOrigin/main4.dig"); + fail(); + } catch (PinException e) { + assertNotNull(e.getVisualElement()); + assertEquals("And", e.getVisualElement().getElementName()); + checkOrigin(e, "src/test/resources/dig/errorOrigin/main4.dig"); + } + } + +} diff --git a/src/test/java/de/neemann/digital/integration/TestExamples.java b/src/test/java/de/neemann/digital/integration/TestExamples.java index 9b5da5842..e61aad6c9 100644 --- a/src/test/java/de/neemann/digital/integration/TestExamples.java +++ b/src/test/java/de/neemann/digital/integration/TestExamples.java @@ -1,6 +1,7 @@ package de.neemann.digital.integration; import de.neemann.digital.core.Model; +import de.neemann.digital.core.element.Keys; import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.draw.model.ModelCreator; import de.neemann.digital.testing.TestCaseElement; @@ -61,6 +62,10 @@ public class TestExamples extends TestCase { throw e; } + boolean isLib = dig.getPath().replace('\\', '/').contains("/lib/"); + + assertTrue("wrong locked mode", isLib == br.getCircuit().getAttributes().get(Keys.LOCKED_MODE)); + try { for (VisualElement el : br.getCircuit().getElements()) if (el.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION)) { diff --git a/src/test/resources/dig/errorOrigin/inner.dig b/src/test/resources/dig/errorOrigin/inner.dig new file mode 100644 index 000000000..624df391c --- /dev/null +++ b/src/test/resources/dig/errorOrigin/inner.dig @@ -0,0 +1,61 @@ + + + 1 + + + + And + + + + + In + + + Label + A + + + + + + In + + + Label + B + + + + + + Out + + + Label + Y + + + + + + Not + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/inner2.dig b/src/test/resources/dig/errorOrigin/inner2.dig new file mode 100644 index 000000000..e02a61ba0 --- /dev/null +++ b/src/test/resources/dig/errorOrigin/inner2.dig @@ -0,0 +1,56 @@ + + + 1 + + + + And + + + + + In + + + Label + A + + + + + + In + + + Label + B + + + + + + Out + + + Label + Y + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/main.dig b/src/test/resources/dig/errorOrigin/main.dig new file mode 100644 index 000000000..ca4c792fe --- /dev/null +++ b/src/test/resources/dig/errorOrigin/main.dig @@ -0,0 +1,41 @@ + + + 1 + + + + In + + + + + In + + + + + Out + + + + + mid.dig + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/main2.dig b/src/test/resources/dig/errorOrigin/main2.dig new file mode 100644 index 000000000..56c14b9e1 --- /dev/null +++ b/src/test/resources/dig/errorOrigin/main2.dig @@ -0,0 +1,41 @@ + + + 1 + + + + In + + + + + In + + + + + Out + + + + + mid2.dig + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/main3.dig b/src/test/resources/dig/errorOrigin/main3.dig new file mode 100644 index 000000000..71650a7dd --- /dev/null +++ b/src/test/resources/dig/errorOrigin/main3.dig @@ -0,0 +1,32 @@ + + + 1 + + + + In + + + + + Out + + + + + midOk.dig + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/main4.dig b/src/test/resources/dig/errorOrigin/main4.dig new file mode 100644 index 000000000..e8079a290 --- /dev/null +++ b/src/test/resources/dig/errorOrigin/main4.dig @@ -0,0 +1,32 @@ + + + 1 + + + + In + + + + + Out + + + + + And + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/mid.dig b/src/test/resources/dig/errorOrigin/mid.dig new file mode 100644 index 000000000..a77fae3c6 --- /dev/null +++ b/src/test/resources/dig/errorOrigin/mid.dig @@ -0,0 +1,83 @@ + + + 1 + + + + inner.dig + + + + + In + + + Label + A + + + + + + In + + + Label + B + + + + + + Out + + + Label + Y + + + + + + Not + + + + + Not + + + + + Not + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/mid2.dig b/src/test/resources/dig/errorOrigin/mid2.dig new file mode 100644 index 000000000..195fd894c --- /dev/null +++ b/src/test/resources/dig/errorOrigin/mid2.dig @@ -0,0 +1,83 @@ + + + 1 + + + + In + + + Label + A + + + + + + In + + + Label + B + + + + + + Out + + + Label + Y + + + + + + Not + + + + + Not + + + + + Not + + + + + inner2.dig + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/errorOrigin/midOk.dig b/src/test/resources/dig/errorOrigin/midOk.dig new file mode 100644 index 000000000..62d8f30ca --- /dev/null +++ b/src/test/resources/dig/errorOrigin/midOk.dig @@ -0,0 +1,56 @@ + + + 1 + + + + In + + + Label + A + + + + + + Out + + + Label + Y + + + + + + In + + + Label + B + + + + + + And + + + + + + + + + + + + + + + + + + + \ No newline at end of file