Skip to content

Appendix A — JSON Schema

Contents of DISL 0.4

The normative JSON Schema is published as disl.schema.json (JSON Schema draft 2020-12), $id https://etalii.net/adp/disl/schema/0.2/disl.schema.json. The 0.1 schema address stays published, and a document that names it is read against the 0.2 schema (2.9). While DISL 0.3 is a draft, its constructs are added to the schema at the 0.2 address, which keeps accepting every 0.2 specification and accepts "disl": "0.3"; the schema moves to a 0.3 address when 0.3 is published. Its root, #/$defs/Specification, validates specifications. Stored diagrams are validated by DID's schema, did.schema.json, which references this schema's QualifiedId, SemVer and Point. The schema is kept together with this document, and every specification example in section 17 validates against it.

A.1 Structure

All objects in the schema:

  • forbid unknown properties (additionalProperties: false), except extension properties matching ^x-;
  • accept an optional doc property where this specification allows documentation;
  • use shared $defs for recurring concepts (Expression, Dynamic, Paint, Stroke, SnapRule, Label, Action, …).

Rules that JSON Schema cannot express — name resolution, CEL type checking, inheritance narrowing, reserved names, compatibility of placement bindings with axis kinds, and the other checks in 14.1 step 9 — are the job of a DISL validator.

A.2 $defs by layer

Layer $defs
Common SimpleId, QualifiedId, TypeRef, TypeRefs, SemVer, LocalizedText, Doc, CelSource, Expression, CelValue, AttrBinding, TokenRef, ParamRef, Dynamic, BString, BNumber, BBool, GeomExpr, Length, Color, Size, Insets, Point, Box, MinMax, PluginUse, PluginCall, Modifier, Message, Reason, NamedReason, TimeUnit
Top level Specification, Language, Import, Function, Viewpoint, Plugin, Budget, BudgetWithhold
1 · Metamodel Metamodel, Attribute, DataType, Enum, EnumValue, NodeType, Containment, PortType, RelationType, RelationEnd, OutOfRange, DerivedNode, DerivedRelation, NeighbourBounds
2 · Coordinates Coordinates, Axis, AxisRef, Calendar, Category, Ruler, ZoomLevel, CoordinateSystem, GridDisplay, Guides, Placement, PlacementSource, Anchor, Snapping, SnappingRef, SnapRule, SnapRuleObject, AxisRules, AxisRange, RulerTicks
3 · Notation Notation, Theme, Paint, Gradient, Pattern, ImagePaint, Stroke, DashStyle, LineEffect, Font, Style, StyleRef, StyleRefs, States, Conditions, IconDef, IconRef, ShapeRef, ShapeInstance, ShapeDef, ShapeParam, PathDef, PathSegment, ShapePart, Handle, ScaleMode, Position, Label, Compartment, GroupBy, NodeIcon, Badge, PortNotation, AnchorSpec, ContainerSpec, SizeSpec, EmbeddedForm, LodRule, NodeNotation, NodeVariant, MarkerDef, MarkerInstance, MarkerRef, MidMarker, Routing, LineSpec, EndAnchor, EdgeNotation, EdgeVariant, Canvas, ContrastRequirement, TextMetric, BadgeLayout, GestureRefusals, ConnectGesture, BezierSpec, Stub, ChromeBand, Notice, Filter, EmptyMessage, LegendComputed, GesturePointer
4 · Toolbox and forms Toolbox, ToolGroup, Tool, ContextToolSet, ContextTool, Template, FragmentNode, FragmentRelation, Form, FormItem, FieldValidation, CreateEnd, DropSpec, DropTarget, FieldParse
5 · Constraints Constraints, Constraint, QuickFix, BuiltInSetting, SourceLocation, Finding, ValidatorOutput
6 · Behavior Behavior, Hook, Operation, Action, DeletionPolicy, Confirmation, Simulation, ParentPlacement
7 · Layout Layout, LayoutConfig, RowPacked, RowsLayout, TidyTree, ForceLayout
8 · Persistence Persistence, Migration, MigrationStep, IdStrategy, IdRule, ViewKind, TypeMapEntry, ViewBind
Documents Document, ElementRecord, RelationRecord, ViewRecord, NodeView, EdgeView

In total the schema has 181 $defs entries; the 35 in bold are new in 0.2, the 8 in italics in 0.3 and the 3 in bold italics in 0.4, and Finding and ValidatorOutput describe what a headless validator prints (8.6), not a specification. The stored-diagram structures Document, ElementRecord, RelationRecord, ViewRecord, NodeView and EdgeView of the earlier combined schema moved to DID's schema.

A.3 Excerpt: snapping

The following excerpt shows how per-axis snapping is encoded. A snap rule is either a string ("none", "inherit", "inherit-x", "inherit-y", or a profile name) or an object that carries exactly one rule key, enforced with oneOf over required.

{
  "Snapping": {
    "description": "Snapping restricts positions and sizes to discrete values, defined per axis in the axis's own units; it is declared in a snap profile, on a coordinate system, a viewpoint canvas, a node or edge notation, or a placement, and the most specific declaration wins per property and per axis. It holds a snap rule per axis (or both), rules for sizes, rotation, bendpoints and labels, the mode (always or magnetic), which targets participate, the tolerance, and the bypass key. See section 5.9.",
    "type": "object",
    "properties": {
      "enabled": {
        "type": "boolean"
      },
      "userToggle": {
        "type": "boolean"
      },
      "mode": {
        "enum": [
          "always",
          "magnetic"
        ]
      },
      "x": {
        "$ref": "#/$defs/SnapRule"
      },
      "y": {
        "$ref": "#/$defs/SnapRule"
      },
      "both": {
        "$ref": "#/$defs/SnapRule"
      },
      "angle": {
        "$ref": "#/$defs/SnapRule"
      },
      "radius": {
        "$ref": "#/$defs/SnapRule"
      },
      "size": {
        "$ref": "#/$defs/AxisRules"
      },
      "reference": {
        "enum": [
          "anchor",
          "bounds",
          "center",
          "edges"
        ]
      },
      "rotation": {
        "type": "object",
        "properties": {
          "step": {
            "type": "number"
          },
          "tolerance": {
            "type": "number"
          }
        },
        "additionalProperties": false,
        "patternProperties": {
          "^x-": true
        }
      },
      "bendpoints": {
        "anyOf": [
          {
            "enum": [
              "inherit",
              "none"
            ]
          },
          {
            "$ref": "#/$defs/AxisRules"
          }
        ]
      },
      "labels": {
        "anyOf": [
          {
            "const": "none"
          },
          {
            "type": "object",
            "properties": {
              "positions": {
                "type": "array",
                "items": true
              },
              "doc": {
                "$ref": "#/$defs/Doc"
              }
            },
            "additionalProperties": false,
            "patternProperties": {
              "^x-": true
            }
          }
        ]
      },
      "targets": {
        "type": "array",
        "items": {
          "enum": [
            "rule",
            "objects",
            "guides",
            "ports",
            "parent"
          ]
        }
      },
      "toleranceScreenPx": {
        "type": "number"
      },
      "bypassModifier": {
        "$ref": "#/$defs/Modifier"
      },
      "feedback": {
        "type": "object",
        "properties": {
          "showGhost": {
            "type": "boolean"
          },
          "showValue": {
            "type": "boolean"
          },
          "highlightBand": {
            "type": "boolean"
          },
          "style": {
            "$ref": "#/$defs/StyleRef"
          }
        },
        "additionalProperties": false,
        "patternProperties": {
          "^x-": true
        }
      },
      "applyToProgrammatic": {
        "type": "boolean"
      },
      "doc": {
        "$ref": "#/$defs/Doc"
      }
    },
    "additionalProperties": false,
    "patternProperties": {
      "^x-": true
    }
  },
  "SnapRuleObject": {
    "description": "A snap rule object is the object form of a snap rule and holds exactly one rule key: grid, values, calendar (time units), ticks (visible axis ticks), bands (ordinal bands), divisions (equal parts), ratio (aspect ratios), cel (a custom function), byZoom (zoom-dependent rules) or plugin. Common properties beside the key clamp the result with min and max, set the rounding direction (nearest by default) and override the mode. See section 5.10.",
    "type": "object",
    "properties": {
      "grid": {
        "type": "object",
        "properties": {
          "spacing": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              }
            ]
          },
          "offset": true,
          "subdivisions": {
            "type": "integer"
          },
          "subdivideAtZoom": {
            "type": "number"
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "required": [
          "spacing"
        ],
        "patternProperties": {
          "^x-": true
        }
      },
      "values": {
        "anyOf": [
          {
            "type": "array",
            "items": true
          },
          {
            "$ref": "#/$defs/CelValue"
          }
        ]
      },
      "calendar": {
        "type": "object",
        "properties": {
          "unit": {
            "enum": [
              "millisecond",
              "second",
              "minute",
              "hour",
              "day",
              "week",
              "month",
              "quarter",
              "year"
            ]
          },
          "step": {
            "type": "integer"
          },
          "align": {
            "enum": [
              "start",
              "end",
              "nearest"
            ]
          },
          "workingTime": {
            "type": "boolean"
          },
          "calendar": {
            "type": "string"
          },
          "at": {
            "type": "string"
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "required": [
          "unit"
        ],
        "patternProperties": {
          "^x-": true
        }
      },
      "ticks": {
        "type": "object",
        "properties": {
          "level": {
            "enum": [
              "major",
              "minor"
            ]
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "patternProperties": {
          "^x-": true
        }
      },
      "bands": {
        "type": "object",
        "properties": {
          "align": {
            "enum": [
              "start",
              "center",
              "end",
              "keep-offset"
            ]
          },
          "offsetGrid": {
            "type": "number"
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "patternProperties": {
          "^x-": true
        }
      },
      "divisions": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer"
          },
          "of": {
            "enum": [
              "parent",
              "canvas",
              "axis"
            ]
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "required": [
          "count"
        ],
        "patternProperties": {
          "^x-": true
        }
      },
      "ratio": {
        "type": "object",
        "properties": {
          "ratios": {
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "required": [
          "ratios"
        ],
        "patternProperties": {
          "^x-": true
        }
      },
      "cel": {
        "$ref": "#/$defs/CelSource"
      },
      "byZoom": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "maxZoom": {
              "type": "number"
            },
            "rule": {
              "$ref": "#/$defs/SnapRule"
            },
            "doc": {
              "$ref": "#/$defs/Doc"
            }
          },
          "additionalProperties": false,
          "required": [
            "rule"
          ],
          "patternProperties": {
            "^x-": true
          }
        },
        "minItems": 1
      },
      "plugin": {
        "$ref": "#/$defs/QualifiedId"
      },
      "args": {
        "type": "object"
      },
      "min": true,
      "max": true,
      "direction": {
        "enum": [
          "nearest",
          "floor",
          "ceil"
        ]
      },
      "mode": {
        "enum": [
          "always",
          "magnetic"
        ]
      },
      "doc": {
        "$ref": "#/$defs/Doc"
      }
    },
    "additionalProperties": false,
    "patternProperties": {
      "^x-": true
    },
    "oneOf": [
      {
        "required": [
          "grid"
        ]
      },
      {
        "required": [
          "values"
        ]
      },
      {
        "required": [
          "calendar"
        ]
      },
      {
        "required": [
          "ticks"
        ]
      },
      {
        "required": [
          "bands"
        ]
      },
      {
        "required": [
          "divisions"
        ]
      },
      {
        "required": [
          "ratio"
        ]
      },
      {
        "required": [
          "cel"
        ]
      },
      {
        "required": [
          "byZoom"
        ]
      },
      {
        "required": [
          "plugin"
        ]
      }
    ]
  }
}

A.4 Excerpt: placement

{
  "Placement": {
    "description": "Placement tells the runtime where each coordinate of a node comes from; it is declared in the node notation, so one model type can be placed differently per viewpoint. It names the coordinate system and gives a placement source for each axis's position and for its opposite edge or extent (x2 excludes width, y2 excludes height), plus the anchor. It also controls moving and resizing per axis, stacking within a band, and clamping to the parent. See section 5.8.",
    "type": "object",
    "properties": {
      "system": {
        "$ref": "#/$defs/SimpleId"
      },
      "x": {
        "$ref": "#/$defs/PlacementSource"
      },
      "y": {
        "$ref": "#/$defs/PlacementSource"
      },
      "x2": {
        "$ref": "#/$defs/PlacementSource"
      },
      "y2": {
        "$ref": "#/$defs/PlacementSource"
      },
      "width": {
        "$ref": "#/$defs/PlacementSource"
      },
      "height": {
        "$ref": "#/$defs/PlacementSource"
      },
      "angle": {
        "$ref": "#/$defs/PlacementSource"
      },
      "radius": {
        "$ref": "#/$defs/PlacementSource"
      },
      "anchor": {
        "$ref": "#/$defs/Anchor"
      },
      "movable": {
        "anyOf": [
          {
            "type": "boolean"
          },
          {
            "type": "object",
            "properties": {
              "x": {
                "type": "boolean"
              },
              "y": {
                "type": "boolean"
              }
            },
            "additionalProperties": false,
            "patternProperties": {
              "^x-": true
            }
          }
        ]
      },
      "resizable": {
        "anyOf": [
          {
            "type": "boolean"
          },
          {
            "type": "object",
            "properties": {
              "x": {
                "type": "boolean"
              },
              "y": {
                "type": "boolean"
              }
            },
            "additionalProperties": false,
            "patternProperties": {
              "^x-": true
            }
          }
        ]
      },
      "stack": {
        "type": "object",
        "properties": {
          "mode": {
            "enum": [
              "overlap",
              "stack",
              "pack"
            ]
          },
          "order": {
            "$ref": "#/$defs/Expression"
          },
          "rowSize": {
            "type": "number"
          },
          "growBand": {
            "type": "boolean"
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "patternProperties": {
          "^x-": true
        }
      },
      "clampToParent": {
        "type": "boolean"
      },
      "doc": {
        "$ref": "#/$defs/Doc"
      }
    },
    "additionalProperties": false,
    "patternProperties": {
      "^x-": true
    },
    "not": {
      "anyOf": [
        {
          "required": [
            "x2",
            "width"
          ]
        },
        {
          "required": [
            "y2",
            "height"
          ]
        }
      ]
    }
  },
  "PlacementSource": {
    "description": "A placement source says where one coordinate of a placement comes from. The string free, or omitting it, stores the value in view data; an attribute binding reads and writes a model attribute, optionally with a constant offset; a CEL expression computes a read-only value, and write actions let a move update the model from it; layout true leaves it to the layout algorithm. A bound attribute's type must suit the axis. See section 5.8.",
    "anyOf": [
      {
        "const": "free"
      },
      {
        "$ref": "#/$defs/AttrBinding"
      },
      {
        "type": "object",
        "properties": {
          "cel": {
            "$ref": "#/$defs/CelSource"
          },
          "write": {
            "type": "array",
            "items": {
              "$ref": "#/$defs/Action"
            }
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "required": [
          "cel"
        ],
        "patternProperties": {
          "^x-": true
        }
      },
      {
        "type": "object",
        "properties": {
          "layout": {
            "const": true
          },
          "doc": {
            "$ref": "#/$defs/Doc"
          }
        },
        "additionalProperties": false,
        "required": [
          "layout"
        ],
        "patternProperties": {
          "^x-": true
        }
      }
    ]
  }
}

Referenced from