This is our typed map class (Dict) with typed map keys (Dict.Prop) with some brain surgery level code to get type information out of Java's generics. It's used throughout Field (the type information is important because it enables the Conversions class which in turn allows us to massage dynamic lang
| 28 | * dynamic languages well while style exploiting Java's type system) |
| 29 | */ |
| 30 | public class Dict implements Serializable, fieldlinker.AsMap { |
| 31 | private static final long serialVersionUID = 4506062700963421662L; |
| 32 | |
| 33 | static public class Canonical { |
| 34 | static protected Map<String, Prop> canon = Collections.synchronizedMap(new HashMap<>()); |
| 35 | |
| 36 | static public <T> Prop<T> canonicalize(Prop<T> p) { |
| 37 | Prop<T> prop = canon.computeIfAbsent(p.name, x -> p); |
| 38 | if (p.isCanon() && !prop.isCanon()) { |
| 39 | canon.put(p.name, p); |
| 40 | prop = p; |
| 41 | } else if (p.isCanon() && prop.isCanon() && p != prop) { |
| 42 | // should be an Error? |
| 43 | System.err.println(" WARNING: two competing canonical definitions of a Prop <" + p + ">"); |
| 44 | if (p.typeInformation != null && prop.typeInformation == null) { |
| 45 | canon.put(p.name, p); |
| 46 | prop = p; |
| 47 | } else if (p.typeInformation == null && prop.typeInformation != null) { |
| 48 | |
| 49 | } else if (p.typeInformation != null && prop.typeInformation != null) { |
| 50 | if (!Conversions.typeInformationEquals(p.typeInformation, prop.typeInformation)) { |
| 51 | System.err.println(" ERROR: the two competing canonical definitions of " + p + " have different type information"); |
| 52 | throw new IllegalArgumentException(p.typeInformation + " " + prop.typeInformation + " " + p + " " + prop); |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | prop.setCanon(); |
| 57 | return prop; |
| 58 | } |
| 59 | |
| 60 | static public <T> Prop<T> findCanon(Prop<T> p) { |
| 61 | return canon.get(p.name); |
| 62 | } |
| 63 | |
| 64 | static public <T> Prop<T> findCanon(String p) { |
| 65 | return canon.get(p); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * this property is used to tag other properties as pertaining to a particular "domain" of use, for example FLine attributes |
| 71 | */ |
| 72 | public static Dict.Prop<String> domain = new Dict.Prop<>("domain").toCanon(); |
| 73 | /** |
| 74 | * this property tags a property as readOnly (from the point of view of the scripting world, from Java there's nothing in place to prevent writing properties) |
| 75 | */ |
| 76 | public static Prop<Boolean> readOnly = new Prop<>("readOnly").toCanon().set(domain, "*/attributes"); |
| 77 | |
| 78 | /** |
| 79 | * this lets you add to a property a function that massages values as they are set |
| 80 | */ |
| 81 | public static Prop<Function<Object, Object>> customCaster = new Prop<>("customCaster").toCanon().set(domain, "*/attributes"); |
| 82 | |
| 83 | /** |
| 84 | * Range of for UI hint |
| 85 | */ |
| 86 | public static Prop<Collection<Number>> range = new Prop<>("range").toCanon().set(domain, "*/attributes").set(customCaster, new Function<Object, Object>() { |
| 87 | @Override |