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.draw.library;
7 
8 import de.neemann.digital.core.NodeException;
9 import de.neemann.digital.core.element.*;
10 import de.neemann.digital.draw.elements.Circuit;
11 import de.neemann.digital.draw.elements.PinException;
12 import de.neemann.digital.draw.elements.VisualElement;
13 import de.neemann.digital.draw.model.ModelCreator;
14 import de.neemann.digital.draw.model.NetList;
15 import de.neemann.digital.hdl.hgs.Parser;
16 import de.neemann.digital.hdl.hgs.ParserException;
17 import de.neemann.digital.hdl.hgs.refs.Reference;
18 import de.neemann.digital.hdl.hgs.refs.ReferenceToStruct;
19 import de.neemann.digital.hdl.hgs.refs.ReferenceToVar;
20 import de.neemann.digital.lang.Lang;
21 
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.TreeSet;
25 
26 /**
27  * The description of a nested element.
28  * This is a complete circuit which is used as a element.
29  */
30 public final class ElementTypeDescriptionCustom extends ElementTypeDescription {
31     private static final int MAX_DEPTH = 30;
32     private final File file;
33     private final Circuit circuit;
34     private final ResolveGenerics resolveGenerics;
35     private final LibraryInterface library;
36     private String description;
37     private NetList netList;
38     private String declarationDefault;
39 
40     /**
41      * Creates a new element
42      *
43      * @param file    the file which is loaded
44      * @param circuit the circuit
45      * @throws PinException PinException
46      */
ElementTypeDescriptionCustom(File file, Circuit circuit, ElementLibrary library)47     ElementTypeDescriptionCustom(File file, Circuit circuit, ElementLibrary library) throws PinException {
48         super(file.getName(), (ElementFactory) null, circuit.getInputNames());
49         this.file = file;
50         this.circuit = circuit;
51         this.library = library;
52         resolveGenerics = new ResolveGenerics(circuit, library);
53         setShortName(file.getName());
54         addAttribute(Keys.ROTATE);
55         addAttribute(Keys.LABEL);
56         addAttribute(Keys.SHAPE_TYPE);
57         if (isGeneric())
58             addAttribute(Keys.GENERIC);
59     }
60 
61     /**
62      * Returns the filename
63      * The returned file is opened if the user wants to modify the element
64      *
65      * @return the filename
66      */
getFile()67     public File getFile() {
68         return file;
69     }
70 
71     /**
72      * @return the elements attributes
73      */
getAttributes()74     public ElementAttributes getAttributes() {
75         return circuit.getAttributes();
76     }
77 
78     /**
79      * @return the circuit
80      */
getCircuit()81     public Circuit getCircuit() {
82         return circuit;
83     }
84 
85     /**
86      * Returns the resolved circuit if it is a generic circuit
87      *
88      * @param attributes the defining attributes
89      * @return the resolved circuit
90      * @throws NodeException            NodeException
91      * @throws ElementNotFoundException ElementNotFoundException
92      */
getResolvedCircuit(ElementAttributes attributes)93     public Circuit getResolvedCircuit(ElementAttributes attributes) throws NodeException, ElementNotFoundException {
94         if (isGeneric())
95             return resolveGenerics.resolveCircuit(attributes).getCircuit();
96         else
97             return circuit;
98     }
99 
100     /**
101      * Sets a custom description for this field
102      *
103      * @param description the description
104      */
setDescription(String description)105     public void setDescription(String description) {
106         this.description = description;
107     }
108 
109     @Override
getDescription(ElementAttributes elementAttributes)110     public String getDescription(ElementAttributes elementAttributes) {
111         if (description != null)
112             return description;
113         else
114             return super.getDescription(elementAttributes);
115     }
116 
117     /**
118      * Gets a {@link ModelCreator} of this circuit.
119      * Every time this method is called a new {@link ModelCreator} is created.
120      *
121      * @param subName                 name of the circuit, used to name unique elements
122      * @param depth                   recursion depth, used to detect a circuit which contains itself
123      * @param containingVisualElement the containing visual element
124      * @return the {@link ModelCreator}
125      * @throws PinException             PinException
126      * @throws NodeException            NodeException
127      * @throws ElementNotFoundException ElementNotFoundException
128      */
getModelCreator(String subName, int depth, VisualElement errorVisualElement, VisualElement containingVisualElement)129     ModelCreator getModelCreator(String subName, int depth, VisualElement errorVisualElement, VisualElement containingVisualElement) throws PinException, NodeException, ElementNotFoundException {
130         if (netList == null)
131             netList = new NetList(circuit);
132 
133         if (depth > MAX_DEPTH)
134             throw new NodeException(Lang.get("err_recursiveNestingAt_N0", circuit.getOrigin()));
135 
136         if (isGeneric()) {
137             Circuit c = resolveGenerics.resolveCircuit(containingVisualElement.getElementAttributes()).getCircuit();
138 
139             return new ModelCreator(c, library, true, new NetList(new NetList(c), errorVisualElement), subName, depth, errorVisualElement);
140         } else
141             return new ModelCreator(circuit, library, true, new NetList(netList, errorVisualElement), subName, depth, errorVisualElement);
142     }
143 
144     /**
145      * @return the generics field default value
146      * @throws NodeException NodeException
147      */
getDeclarationDefault()148     public String getDeclarationDefault() throws NodeException {
149         if (declarationDefault == null)
150             declarationDefault = createDeclarationDefault(circuit);
151         return declarationDefault;
152     }
153 
154     /**
155      * Creates the default for custom element declarations
156      *
157      * @param circuit the circuit
158      * @return the default code template
159      * @throws NodeException NodeException
160      */
createDeclarationDefault(Circuit circuit)161     public static String createDeclarationDefault(Circuit circuit) throws NodeException {
162         TreeSet<String> nameSet = new TreeSet<>();
163         for (VisualElement ve : circuit.getElements()) {
164             String gen = ve.getElementAttributes().get(Keys.GENERIC).trim();
165             if (!gen.isEmpty()) {
166                 try {
167                     Parser p = new Parser(gen);
168                     p.enableRefReadCollection();
169                     p.parse(false);
170                     for (Reference r : p.getRefsRead()) {
171                         if (r instanceof ReferenceToStruct) {
172                             ReferenceToStruct st = (ReferenceToStruct) r;
173                             if (st.getParent() instanceof ReferenceToVar) {
174                                 ReferenceToVar var = (ReferenceToVar) st.getParent();
175                                 if (var.getName().equals("args")) {
176                                     nameSet.add(st.getName());
177                                 }
178                             }
179                         }
180                     }
181                 } catch (ParserException | IOException e) {
182                     final NodeException ex = new NodeException(Lang.get("err_evaluatingGenericsCode_N_N", ve, gen), e);
183                     ex.setOrigin(circuit.getOrigin());
184                     throw ex;
185                 }
186             }
187         }
188         StringBuilder sb = new StringBuilder();
189         for (String name : nameSet)
190             sb.append(name).append(" := ;\n");
191         return sb.toString();
192     }
193 
194     /**
195      * @return true if the circuit is generic
196      */
isGeneric()197     public boolean isGeneric() {
198         return circuit.getAttributes().get(Keys.IS_GENERIC);
199     }
200 
201     @Override
getInputDescription(ElementAttributes elementAttributes)202     public PinDescriptions getInputDescription(ElementAttributes elementAttributes) throws NodeException {
203         if (isGeneric()) {
204             try {
205                 Circuit c = resolveGenerics.resolveCircuit(elementAttributes).getCircuit();
206                 return new PinDescriptions(c.getInputNames());
207             } catch (Exception e) {
208                 return super.getInputDescription(elementAttributes);
209             }
210         } else
211             return super.getInputDescription(elementAttributes);
212     }
213 
214     @Override
getOutputDescriptions(ElementAttributes elementAttributes)215     public PinDescriptions getOutputDescriptions(ElementAttributes elementAttributes) throws PinException {
216         if (isGeneric()) {
217             try {
218                 Circuit c = resolveGenerics.resolveCircuit(elementAttributes).getCircuit();
219                 return new PinDescriptions(c.getOutputNames());
220             } catch (Exception e) {
221                 return super.getOutputDescriptions(elementAttributes);
222             }
223         } else
224             return super.getOutputDescriptions(elementAttributes);
225     }
226 }
227