1 /* 2 * Copyright (c) 2019 Helmut Neemann. 3 * Use of this source code is governed by the GPL v3 license 4 * that can be found in the LICENSE file. 5 */ 6 package de.neemann.digital.gui.components; 7 8 import de.neemann.digital.core.element.Keys; 9 import de.neemann.digital.draw.elements.Circuit; 10 import de.neemann.digital.draw.elements.Movable; 11 import de.neemann.digital.draw.elements.VisualElement; 12 13 import java.util.ArrayList; 14 import java.util.HashMap; 15 import java.util.HashSet; 16 import java.util.Objects; 17 18 /** 19 * Used to renumber labels if copied elements are inserted into a circuit. 20 * Modifies the elements only if a element is present in the circuit which has 21 * the same label as the element to copy. 22 */ 23 public class CopiedElementLabelRenamer { 24 25 private final ArrayList<Movable> elements; 26 27 /** 28 * Creates a new instance 29 * 30 * @param circuit the circuit to copy to 31 * @param elements the element to insert 32 */ CopiedElementLabelRenamer(Circuit circuit, ArrayList<Movable> elements)33 CopiedElementLabelRenamer(Circuit circuit, ArrayList<Movable> elements) { 34 this.elements = elements; 35 36 37 HashMap<LabelClass, PresentIndex> circuitMap = new HashMap<>(); 38 for (VisualElement ve : circuit.getElements()) { 39 LabelInstance li = LabelInstance.create(ve); 40 if (li != null) { 41 PresentIndex pi = circuitMap.computeIfAbsent(li.getLabelClass(), labelClass -> new PresentIndex()); 42 pi.add(li.getNumber()); 43 } 44 } 45 46 HashMap<LabelClass, MinIndex> insertMap = new HashMap<>(); 47 for (Movable m : elements) 48 if (m instanceof VisualElement) { 49 LabelInstance li = LabelInstance.create((VisualElement) m); 50 if (li != null) { 51 MinIndex mi = insertMap.get(li.getLabelClass()); 52 if (mi == null) { 53 mi = new MinIndex(li.getNumber()); 54 insertMap.put(li.getLabelClass(), mi); 55 } else { 56 mi.checkMin(li.getNumber()); 57 } 58 } 59 } 60 61 for (Movable m : elements) 62 if (m instanceof VisualElement) { 63 final VisualElement ve = (VisualElement) m; 64 65 if (!ve.getElementAttributes().get(Keys.PINNUMBER).isEmpty()) 66 continue; 67 68 LabelInstance li = LabelInstance.create(ve); 69 if (li == null) 70 continue; 71 72 PresentIndex pi = circuitMap.get(li.getLabelClass()); 73 if (pi == null) 74 continue; 75 76 if (!pi.contains(li.getNumber())) 77 continue; 78 79 int maxAvail = pi.getMax(); 80 int minToInsert = insertMap.get(li.getLabelClass()).getMin(); 81 82 int delta = maxAvail - minToInsert + 1; 83 84 ve.setAttribute(Keys.LABEL, li.getLabel(delta)); 85 } 86 } 87 88 /** 89 * @return the elements with renamed labels. 90 */ rename()91 public ArrayList<Movable> rename() { 92 return elements; 93 } 94 95 static final class LabelInstance { 96 create(VisualElement ve)97 static LabelInstance create(VisualElement ve) { 98 return create(ve.getElementName(), ve.getElementAttributes().getLabel()); 99 } 100 create(String elementName, String fullLabel)101 static LabelInstance create(String elementName, String fullLabel) { 102 if (fullLabel == null) 103 return null; 104 105 int pos = fullLabel.length(); 106 if (pos == 0) 107 return null; 108 109 if (!Character.isDigit(fullLabel.charAt(pos - 1))) 110 return null; 111 112 int number = 0; 113 int base = 1; 114 while (pos > 0 && Character.isDigit(fullLabel.charAt(pos - 1))) { 115 pos--; 116 number += (fullLabel.charAt(pos) - '0') * base; 117 base *= 10; 118 } 119 120 String label = fullLabel.substring(0, pos); 121 LabelClass lc = new LabelClass(elementName, label); 122 123 return new LabelInstance(lc, number); 124 } 125 126 private final LabelClass lc; 127 private final int number; 128 LabelInstance(LabelClass lc, int number)129 private LabelInstance(LabelClass lc, int number) { 130 this.lc = lc; 131 this.number = number; 132 } 133 getLabelClass()134 LabelClass getLabelClass() { 135 return lc; 136 } 137 getNumber()138 public int getNumber() { 139 return number; 140 } 141 getLabel(int delta)142 public String getLabel(int delta) { 143 return lc.label + Integer.toString(number + delta); 144 } 145 } 146 147 148 static final class LabelClass { 149 private final String elementName; 150 private final String label; 151 LabelClass(String elementName, String label)152 private LabelClass(String elementName, String label) { 153 this.elementName = elementName; 154 this.label = label; 155 } 156 getElementName()157 public String getElementName() { 158 return elementName; 159 } 160 getLabel()161 public String getLabel() { 162 return label; 163 } 164 165 @Override equals(Object o)166 public boolean equals(Object o) { 167 if (this == o) return true; 168 if (o == null || getClass() != o.getClass()) return false; 169 LabelClass that = (LabelClass) o; 170 return Objects.equals(elementName, that.elementName) 171 && Objects.equals(label, that.label); 172 } 173 174 @Override hashCode()175 public int hashCode() { 176 return Objects.hash(elementName, label); 177 } 178 } 179 180 private static final class MinIndex { 181 private int min; 182 MinIndex(int number)183 private MinIndex(int number) { 184 this.min = number; 185 } 186 checkMin(int number)187 void checkMin(int number) { 188 if (this.min > number) 189 this.min = number; 190 } 191 getMin()192 public int getMin() { 193 return min; 194 } 195 } 196 197 private static final class PresentIndex { 198 private HashSet<Integer> numbers; 199 private int max = Integer.MIN_VALUE; 200 PresentIndex()201 private PresentIndex() { 202 numbers = new HashSet<>(); 203 } 204 add(int number)205 public void add(int number) { 206 numbers.add(number); 207 if (number > max) 208 max = number; 209 } 210 contains(int number)211 public boolean contains(int number) { 212 return numbers.contains(number); 213 } 214 getMax()215 public int getMax() { 216 return max; 217 } 218 } 219 } 220