1 /*
2  * Copyright (c) 2016 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.core.element;
7 
8 import de.neemann.digital.lang.Lang;
9 
10 import java.io.File;
11 
12 /**
13  * Class is used to define the keys used to access the models attributes
14  *
15  * @param <VALUE> the keys value type
16  */
17 public class Key<VALUE> {
18     private final String key;
19     private final DefaultFactory<VALUE> defFactory;
20     private final String langKey;
21     private boolean groupEditAllowed = false;
22     private Key dependsOn;
23     private CheckEnabled checkEnabled;
24     private boolean isSecondary;
25     private boolean requiresRestart = false;
26     private boolean requiresRepaint = false;
27     private String panelId;
28 
29     // Both values are always null in digital.
30     // Both are only used within a custom implemented component.
31     private String name;
32     private String description;
33     private boolean adaptiveIntFormat;
34 
35     /**
36      * Creates a new Key.
37      * Use this constructor only if the def value is not mutable!
38      *
39      * @param key the key
40      * @param def the default value
41      */
Key(String key, VALUE def)42     public Key(String key, VALUE def) {
43         this(key, () -> def);
44         if (def == null)
45             throw new NullPointerException();
46     }
47 
48     /**
49      * Creates a new Key.
50      * Use this constructor if the def value is mutable!
51      *
52      * @param key        the key
53      * @param defFactory the factory to create a default value
54      */
Key(String key, DefaultFactory<VALUE> defFactory)55     public Key(String key, DefaultFactory<VALUE> defFactory) {
56         this.key = key;
57         langKey = "key_" + key.replace(" ", "");
58         if (defFactory == null)
59             throw new NullPointerException();
60         this.defFactory = defFactory;
61     }
62 
63     /**
64      * Returns the attributes key
65      *
66      * @return the key
67      */
getKey()68     public String getKey() {
69         return key;
70     }
71 
72     /**
73      * Returns the attributes display name
74      *
75      * @return the name of the key
76      */
getName()77     public String getName() {
78         if (name != null)
79             return name;
80         else
81             return Lang.get(langKey);
82     }
83 
84     /**
85      * @return the default value of this key
86      */
getDefault()87     public VALUE getDefault() {
88         return defFactory.createDefault();
89     }
90 
91     /**
92      * @return The values class
93      */
getValueClass()94     public Class getValueClass() {
95         return getDefault().getClass();
96     }
97 
98     @Override
toString()99     public String toString() {
100         return getName();
101     }
102 
103     /**
104      * @return the keys description
105      */
getDescription()106     public String getDescription() {
107         if (description != null)
108             return description;
109         else {
110             String d = Lang.getNull(langKey + "_tt");
111             if (d != null)
112                 return d;
113             else
114                 return getName();
115         }
116     }
117 
118     /**
119      * @return the language key
120      */
getLangKey()121     public String getLangKey() {
122         return langKey;
123     }
124 
125     /**
126      * @return true if group edit is allowed
127      */
isGroupEditAllowed()128     public boolean isGroupEditAllowed() {
129         return groupEditAllowed;
130     }
131 
132     /**
133      * Allows this attribute in group edit.
134      *
135      * @return this for chained calls
136      */
allowGroupEdit()137     public Key<VALUE> allowGroupEdit() {
138         this.groupEditAllowed = true;
139         return this;
140     }
141 
142     /**
143      * Sets the name of this key.
144      * Is not used in Digital at all.
145      * This method can be used to define custom keys in custom java components.
146      *
147      * @param name the name of the key
148      * @return this for chained calls
149      */
setName(String name)150     public Key<VALUE> setName(String name) {
151         this.name = name;
152         return this;
153     }
154 
155     /**
156      * Sets the description of this key.
157      * Is not used in Digital at all.
158      * This method can be used to define custom keys in custom java components.
159      *
160      * @param description the name of the key
161      * @return this for chained calls
162      */
setDescription(String description)163     public Key<VALUE> setDescription(String description) {
164         this.description = description;
165         return this;
166     }
167 
168     /**
169      * @return returns the key this key depends on
170      */
getDependsOn()171     public Key getDependsOn() {
172         return dependsOn;
173     }
174 
175     /**
176      * @return true if dependency is inverted
177      */
getCheckEnabled()178     public CheckEnabled getCheckEnabled() {
179         return checkEnabled;
180     }
181 
182     /**
183      * Sets a bool dependency for this key
184      *
185      * @param key the key which this key depends on
186      * @return this for chained calls
187      */
setDependsOn(Key<Boolean> key)188     public Key<VALUE> setDependsOn(Key<Boolean> key) {
189         return setDependsOn(key, o -> o);
190     }
191 
192     /**
193      * Sets the key this key depends on.
194      *
195      * @param key          the key where this key depends on
196      * @param checkEnabled function which determines if the editor is enabled or not
197      * @param <KV>         type of key which this key depends on
198      * @return this for chained calls
199      */
setDependsOn(Key<KV> key, CheckEnabled<KV> checkEnabled)200     public <KV> Key<VALUE> setDependsOn(Key<KV> key, CheckEnabled<KV> checkEnabled) {
201         this.dependsOn = key;
202         this.checkEnabled = checkEnabled;
203         return this;
204     }
205 
206     /**
207      * @return true is this is a secondary attribute
208      */
isSecondary()209     public boolean isSecondary() {
210         return isSecondary;
211     }
212 
213     /**
214      * Makes this attribute to be a secondary attribute
215      *
216      * @return this for chained calls
217      */
setSecondary()218     public Key<VALUE> setSecondary() {
219         isSecondary = true;
220         return this;
221     }
222 
223     /**
224      * Called if the modification of this setting needs a restart.
225      *
226      * @return this for chained calls
227      */
setRequiresRestart()228     public Key<VALUE> setRequiresRestart() {
229         requiresRestart = true;
230         return this;
231     }
232 
233     /**
234      * @return true if changing this value needs a restart
235      */
getRequiresRestart()236     public boolean getRequiresRestart() {
237         return requiresRestart;
238     }
239 
240     /**
241      * Called if this setting needs a repaint.
242      * This means, that the circuit graphics became invalid
243      * if this setting has changed.
244      *
245      * @return this for chained calls
246      */
setRequiresRepaint()247     public Key<VALUE> setRequiresRepaint() {
248         requiresRepaint = true;
249         return this;
250     }
251 
252     /**
253      * @return true if changing this value needs a repaint
254      */
getRequiresRepaint()255     public boolean getRequiresRepaint() {
256         return requiresRepaint;
257     }
258 
259     /**
260      * Enables an adaptive int format in number editors.
261      * This means that the string representation of the number is controlled
262      * by the IntFormat stored in the elements attributes.
263      *
264      * @return this for chained calls
265      */
setAdaptiveIntFormat()266     public Key<VALUE> setAdaptiveIntFormat() {
267         adaptiveIntFormat = true;
268         return this;
269     }
270 
271     /**
272      * @return true if adaptive int format is required
273      */
isAdaptiveIntFormat()274     public boolean isAdaptiveIntFormat() {
275         return adaptiveIntFormat;
276     }
277 
278     /**
279      * Moves this key to the panel with the given id
280      *
281      * @param panelId the panel id
282      * @return this for chained calls
283      */
setPanelId(String panelId)284     public Key<VALUE> setPanelId(String panelId) {
285         this.panelId = panelId;
286         return this;
287     }
288 
289     /**
290      * @return the panel id, null if no panel is set
291      */
getPanelId()292     public String getPanelId() {
293         return panelId;
294     }
295 
296     /**
297      * A integer attribute.
298      * Stores additional combo box values
299      */
300     public static class KeyInteger extends Key<Integer> {
301         private int[] values;
302         private int min = Integer.MIN_VALUE;
303         private int max = Integer.MAX_VALUE;
304 
305         /**
306          * Creates a new instance
307          *
308          * @param key the key to use
309          * @param def the default value
310          */
KeyInteger(String key, Integer def)311         public KeyInteger(String key, Integer def) {
312             super(key, def);
313         }
314 
315         /**
316          * Sets the values to use in the combo box.
317          *
318          * @param values the values
319          * @return this for chained calls
320          */
setComboBoxValues(int... values)321         public KeyInteger setComboBoxValues(int... values) {
322             this.values = values;
323             return this;
324         }
325 
326         /**
327          * Sets the minimal value which is allowed.
328          *
329          * @param min the minimal value allowed
330          * @return this for chained calls
331          */
setMin(int min)332         public KeyInteger setMin(int min) {
333             this.min = min;
334             return this;
335         }
336 
337         /**
338          * Sets the maximal value which is allowed.
339          *
340          * @param max the  maximal value allowed
341          * @return this for chained calls
342          */
setMax(int max)343         public KeyInteger setMax(int max) {
344             this.max = max;
345             return this;
346         }
347 
348         /**
349          * @return the values to show in the combo box
350          */
getComboBoxValues()351         public int[] getComboBoxValues() {
352             return values;
353         }
354 
355         /**
356          * @return the min value
357          */
getMin()358         public int getMin() {
359             return min;
360         }
361 
362         /**
363          * @return the max value
364          */
getMax()365         public int getMax() {
366             return max;
367         }
368     }
369 
370     /**
371      * A bits attribute.
372      * Stores additional combo box values
373      */
374     public static final class KeyBits extends KeyInteger {
375 
376         /**
377          * Creates a new bits key
378          *
379          * @param key the key
380          * @param def the default value
381          */
KeyBits(String key, Integer def)382         public KeyBits(String key, Integer def) {
383             super(key, def);
384             setMin(1);
385             setMax(64);
386             setComboBoxValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32);
387             allowGroupEdit();
388         }
389     }
390 
391     /**
392      * Stores a file
393      */
394     public static final class KeyFile extends Key<File> {
395 
396         private boolean directoryOnly;
397 
398         /**
399          * Creates a new file key
400          *
401          * @param key the key
402          * @param def the default file
403          */
KeyFile(String key, File def)404         public KeyFile(String key, File def) {
405             super(key, def);
406             setDirectoryOnly(false);
407         }
408 
409         /**
410          * Set the directory only mode
411          *
412          * @param directoryOnly if true you can select only directories
413          * @return this for chained calls
414          */
setDirectoryOnly(boolean directoryOnly)415         public KeyFile setDirectoryOnly(boolean directoryOnly) {
416             this.directoryOnly = directoryOnly;
417             return this;
418         }
419 
420         /**
421          * @return true if you can select only directories
422          */
isDirectoryOnly()423         public boolean isDirectoryOnly() {
424             return directoryOnly;
425         }
426     }
427 
428     /**
429      * Used to store enum values
430      *
431      * @param <E> the enum type
432      */
433     public static final class KeyEnum<E extends Enum> extends Key<E> {
434         private final E[] values;
435         private final String[] names;
436         private final boolean toString;
437 
438         /**
439          * Creates a new emum key
440          *
441          * @param key    the key
442          * @param def    the default value
443          * @param values the possible values
444          */
KeyEnum(String key, E def, E[] values)445         public KeyEnum(String key, E def, E[] values) {
446             this(key, def, values, false);
447         }
448 
449         /**
450          * Creates a new emum key
451          *
452          * @param key      the key
453          * @param def      the default value
454          * @param values   the possible values
455          * @param toString if true, the names are not taken from the language file but created by calling toString()
456          */
KeyEnum(String key, E def, E[] values, boolean toString)457         public KeyEnum(String key, E def, E[] values, boolean toString) {
458             super(key, def);
459             this.values = values;
460             this.toString = toString;
461 
462             names = new String[values.length];
463             if (toString)
464                 for (int i = 0; i < values.length; i++)
465                     names[i] = values[i].toString();
466             else
467                 for (int i = 0; i < values.length; i++)
468                     names[i] = Lang.get(getLangKey(values[i]));
469             allowGroupEdit();
470         }
471 
472         /**
473          * creates the language key for the enum values
474          *
475          * @param value the value
476          * @return the language key
477          */
getLangKey(E value)478         public String getLangKey(E value) {
479             return getLangKey() + "_" + value.name();
480         }
481 
482         /**
483          * @return the enums values
484          */
getValues()485         public E[] getValues() {
486             return values;
487         }
488 
489         /**
490          * @return the enums translated names
491          */
getNames()492         public String[] getNames() {
493             return names;
494         }
495 
496         /**
497          * @return true if this enum key uses toString to create the display names
498          */
usesToString()499         public boolean usesToString() {
500             return toString;
501         }
502     }
503 
504     /**
505      * A special string key to flag long multi line strings.
506      */
507     public static final class LongString extends Key<String> {
508         private int rows = 6;
509         private int columns = 0;
510         private boolean lineNumbers = false;
511 
512         /**
513          * Creates a new Key
514          *
515          * @param key the key
516          */
LongString(String key)517         public LongString(String key) {
518             super(key, "");
519         }
520 
521         /**
522          * Creates a new Key
523          *
524          * @param key the key
525          * @param def the default value
526          */
LongString(String key, String def)527         public LongString(String key, String def) {
528             super(key, def);
529         }
530 
531         /**
532          * @return the rows of the editor field
533          */
getRows()534         public int getRows() {
535             return rows;
536         }
537 
538         /**
539          * Sets the rows in the editor
540          *
541          * @param rows the number ow rows
542          * @return this for chained calls
543          */
setRows(int rows)544         public LongString setRows(int rows) {
545             this.rows = rows;
546             return this;
547         }
548 
549         /**
550          * @return the coloums of the editor field
551          */
getColumns()552         public int getColumns() {
553             return columns;
554         }
555 
556 
557         /**
558          * Sets the columns in the editor
559          *
560          * @param columns the number ow rows
561          * @return this for chained calls
562          */
setColumns(int columns)563         public LongString setColumns(int columns) {
564             this.columns = columns;
565             return this;
566         }
567 
568         /**
569          * Sets the line numbers attribute
570          *
571          * @param lineNumbers true if line numbers should be visibla
572          * @return this for chained calls
573          */
setLineNumbers(boolean lineNumbers)574         public LongString setLineNumbers(boolean lineNumbers) {
575             this.lineNumbers = lineNumbers;
576             return this;
577         }
578 
579         /**
580          * @return true if line numbers are visible
581          */
getLineNumbers()582         public boolean getLineNumbers() {
583             return lineNumbers;
584         }
585     }
586 
587     /**
588      * Interface to define a dependancy of a key from an other key
589      *
590      * @param <T> the type of the key
591      */
592     public interface CheckEnabled<T> {
593         /**
594          * Returns true if editor is enabled
595          *
596          * @param t the value the editor depends on
597          * @return true if editor is enabled
598          */
isEnabled(T t)599         boolean isEnabled(T t);
600     }
601 
602     /**
603      * Used to provide a default value if the value is mutable.
604      *
605      * @param <VALUE> the type of the value
606      */
607     public interface DefaultFactory<VALUE> {
608         /**
609          * Called to create a new default value.
610          *
611          * @return the default value
612          */
createDefault()613         VALUE createDefault();
614     }
615 }
616