# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # # adl-1.4.dtd # # Purpose: # Document Type Description for Application Description # Language. Normative for now; will be replaced by a schema. ` # # Author: Simon Brooke # Created: 24th January 2006 # Copyright: (c) 2007 Cygnet Solutions # # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # $Revision: 1.5 $ # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Before we start: import XHTML for use in documentation sections # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Before we start: some useful definitions # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # some basic character entities inherited from HTML. Actually we probably ought to # import all the HTML4 character entity files, and possibly the HTML4 Strict DTD (so # that we can allow HTML block level entities within content elements # boolean means true or false Boolean = "true" | "false" # Locale is a string comprising an ISO 639 language code followed by a space # followed by an ISO 3166 country code, or else the string 'default'. See: # # Locale = string # permissions a group may have on an entity, list, page, form or field # permissions are deemed to increase as you go right. A group cannot # have greater permission on a field than on the form it is in, or # greater permission on form than the entity it belongs to # # none: none # read: select # insert: insert # noedit: select, insert # edit: select, insert, update # all: select, insert, update, delete Permissions = "none" | "read" | "insert" | "noedit" | "edit" | "all" # actions which should be cascaded to dependent objects. All these values except # 'manual' are taken from Hibernate and should be passed through the adl2hibernate # mapping transparently. Relevent only for properties with type='entity', type='link' # and type='list' # # all : cascade delete, save and update # all-delete-orphan : see hibernate documentation; relates to transient objects only # delete : cascade delete actions, but not save and update # manual : cascading will be handled in manually managed code, code to # handle cascading should not be generated # save-update : cascade save and update actions, but not delete. CascadeActions = "all" | "all-delete-orphan" | "delete" | "manual" | "save-update" # data types which can be used in a typedef to provide validation - # e.g. a string can be used with a regexp or a scalar can be used with # min and max values # string: varchar java.sql.Types.VARCHAR # integer: int java.sql.Types.INTEGER # real: double java.sql.Types.DOUBLE # money: money java.sql.Types.INTEGER # date: date java.sql.Types.DATE # time: time java.sql.Types.TIME # timestamp: timestamp java.sql.Types.TIMESTAMP # uploadable: varchar java.sql.Types.VARCHAR # image: varchar java.sql.Types.VARCHAR # # uploadable is as string but points to an uploaded file; image is as # uploadable but points to an uploadable graphical image file DefinableDataTypes = "string" | "integer" | "real" | "money" | "date" | "time" | "timestamp" | "uploadable" # data types which are fairly straightforward translations of JDBC data types # boolean: boolean or java.sql.Types.BIT # char(1) java.sql.Types.CHAR # text: text or java.sql.Types.LONGVARCHAR # memo java.sql.Types.CLOB SimpleDataTypes = DefinableDataTypes | "boolean" | "text" # data types which are more complex than SimpleDataTypes... # entity : a foreign key link to another entity (i.e. the 'many' end of a # one-to-many link); # list : a list of some other entity that links to me (i.e. the 'one' end of # a one-to-many link); # link : a many to many link (via a link table); # defined : a type defined by a typedef. ComplexDataTypes = "entity" | "link" | "list" | "defined" # data types which require special handling - which don't simply map onto # common SQL data types # geopos : a latitude/longitude pair (experimental and not yet implemented) # image : a raster image file, in jpeg|gif|png format (experimental, not yet implemented) # message : an internationalised message, having different translations for different locales SpecialDataTypes = "geopos" | "image" | "message" # all data types AllDataTypes = ComplexDataTypes | SimpleDataTypes | SpecialDataTypes # content, for things like pages (i.e. forms, lists, pages) Content = head | top | foot FieldStuff = field | fieldgroup | auxlist | verb PageContent = Content | FieldStuff PageStuff = PageContent | permission | pragma # Properties for pages: # name: obviously, the name (URL stub) of the page # properties: the properties of the entity the page describes to be shown # as fields on the page # all: obviously, all properties (except the abstract primary key, if # present) # user-distinct: all properties which are user-distinct (NOTE: Not yet implemented) # listed: only those properties for which fields are explicitly listed PageAttrs = attribute name { text }, attribute properties { "all" | "user-distinct" | "listed" } # Actions for generators (mainly for keyfields - see entity 'generator', below # assigned: In manually-maintained code, you contract to assign a value # to this property before it is persisted. # guid: The system will supply a unique GUid value to this field # before it is persisted. # mannual: You contract to supply a generatos class in manually maintained # code. # native: The database will supply a unique value to this field when it # is persisted; the value will be an integer. RECOMMENDED! GeneratorActions = "assigned" | "guid" | "manual" | "native" # sequences for orderings of lists - see entity 'order' # canonical: Whatever the normal canonical ordering for this datatype is - # typically alpha-numeric, except for dates, etc. # reverse-canonical: The reverse of the above # # possibly there should be some further values but I have no idea what these are Sequences = "canonical" | "reverse-canonical" # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Elements # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # the application that the document describes: required top level element # # name: the name of this application # version: the version number of this application # revision: the revision of the ADL document # currency: the base monetary currency, in the form of an ISO 4217 three-letter code # xmlns: XML namespace, in case required application = element application { attlist.application, specification*, documentation?, content?, typedef*, group*, entity* } attlist.application &= attribute name { text }, attribute version { text }?, attribute revision { text }?, attribute currency { text }? # the definition of a defined type. At this stage a defined type is either # a string in which case it must have size and pattern, or # a scalar in which case it must have minimum and/or maximum # pattern must be a regular expression as interpreted by org.apache.regexp.RE # minimum and maximum must be of appropriate format for the datatype specified. # Validation may be done client-side and/or server-side at application layer # and/or server side at database layer. # # name: the name of this typedef # type: the simple type on which this defined type is based; must be # present unless in-implementation children are supplied # size: the data size of this defined type # pattern: a regular expression which values for this type must match # minimum: the minimum value for this type (if base type is scalar) # maximum: the maximum value for this type (if base type is scalar) typedef = element typedef { attlist.typedef, documentation?, in-implementation*, help* } attlist.typedef &= attribute name { text }, attribute type { DefinableDataTypes }?, attribute size { text }?, attribute pattern { text }?, attribute minimum { text }?, attribute maximum { text }? # information about how to translate a type into types known to different target # languages. TODO: Once again I'm not wholly comfortable with the name; I'm not # really comfortable that this belongs in ADL at all. # # target: the target language # value: the type to use in that target language # kind: OK, I confess I don't understand this, but Andrew needs it... in-implementation = element in-implementation { attlist.in-implementation, documentation? } attlist.in-implementation &= attribute target { text }, attribute value { text }, attribute kind { text }? # a group of people with similar permissions to one another # # name: the name of this group # parent: the name of a group of which this group is subset group = element group { attlist.group, documentation? } attlist.group &= attribute name { text }, attribute parent { text }? # an entity which has properties and relationships; maps onto a database # table or a Java serialisable class - or, of course, various other things # # name: obviously, the name of this entity # natural-key: if present, the name of a property of this entity which forms # a natural primary key [NOTE: Only partly implemented. NOTE: much of # the present implementation assumes all primary keys will be # integers. This needs to be fixed!] DEPRECATED: remove; replace with the # 'key' element, below. # table: the name of the table in which this entity is stored. Defaults to same # as name of entity. Strongly recommend this is not used unless it needs # to be different from the name of the entity # foreign: this entity is part of some other system; no code will be generated # for it, although code which links to it will be generated entity = element entity { attlist.entity, documentation?, prompt*, content?, key?, property*, permission*, (form | page | \list)* } attlist.entity &= attribute name { text }, attribute natural-key { text }?, attribute table { text }?, attribute foreign { Boolean }? # contains documentation on the element which immediately contains it. TODO: # should HTML markup within a documentation element be allowed? If so, are # there restrictions? documentation = element documentation { attlist.documentation, (text | reference)* } attlist.documentation &= empty # an explicit primary key, possibly compound key = element key { attlist.key, property* } attlist.key &= empty # a property (field) of an entity (table) # # name: the name of this property. # type: the type of this property. # default: the default value of this property. There will probably be # magic values of this! # typedef: name of the typedef to use, it type = 'defined'. # distinct: distinct='system' required that every value in the system # will be distinct (i.e. natural primary key); # distinct='user' implies that the value may be used by users # in distinguishing entities even if values are not formally # unique; # distinct='all' implies that the values are formally unique # /and/ are user friendly (NOTE: not implemented). # entity: if type='entity', the name of the entity this property is # a foreign key link to. # if type='list', the name of the entity that has a foreign # key link to this entity # farkey: if type='list', the name of farside key in the listed # entity; if type='entity' and the farside field to join to # is not the farside primary key, then the name of that # farside field # required: whether this propery is required (i.e. 'not null'). # immutable: if true, once a value has been set it cannot be changed. # size: fieldwidth of the property if specified. # concrete: if set to 'false', this property is not stored in the # database but must be computed (manually written code must # be provided to support this) # cascade: what action(s) on the parent entity should be cascaded to # entitie(s) linked on this property. Valid only if type='entity', # type='link' or type='list'. # column: name of the column in a SQL database table in which this property # is stored. TODO: Think about this. # unsaved-value: # of a property whose persistent value is set on first being # committed to persistent store, the value which it holds before # it has been committed property = element property { attlist.property, documentation?, generator?, (permission | option | prompt | help | ifmissing)* } attlist.property &= attribute name { text }, attribute type { AllDataTypes }, attribute default { text }?, attribute typedef { text }?, attribute distinct { "none" | "all" | "user" | "system" }?, attribute entity { text }?, attribute farkey { text }?, attribute required { Boolean }?, attribute immutable { Boolean }?, attribute size { text }?, attribute column { text }?, attribute concrete { Boolean }?, attribute cascade { CascadeActions }? # marks a property which is auto-generated by some part of the system. # This is based on the Hibernate construct, except that the Hibernate # implementation folds both its internal generators and custom generators # onto the same attribute. This separates them onto two attributes so we # can police values for Hibernate's 'builtin' generators. # # action: one of the supported Hibernate builtin generators, or # 'manual'. 'native' is strongly recommended in most instances # class: if action is 'manual', the name of a manually maintained # class conforming to the Hibernate IdentifierGenerator # interface, or its equivalent in other languages generator = element generator { attlist.generator, documentation?, param* } attlist.generator &= attribute action { GeneratorActions }, attribute class { text }? # A parameter passed to the generator. Again, based on the Hibernate # implementation. TODO: #PCDATA is wrong as the content model, as embedded # markup is definitely not allowed! # # name: the name of this parameter # # TODO: This needs to be renamed or removed because it conflicts with the # XHTML element of the same name. In fact it could be simply removed since # our usage is compatible with the XHTML usage, but it might be less # ambiguous to rename it. param = element param { attlist.param, text } attlist.param &= attribute name { text } # one of an explicit list of optional values a property may have # NOTE: whether options get encoded at application layer or at database layer # is UNDEFINED; either behaviour is correct. If at database layer it's also # UNDEFINED whether they're encoded as a single reference data table or as # separate reference data tables for each property. # # value: the value of this option # # TODO: This needs to be renamed or removed because it conflicts with the # XHTML element of the same name. In fact it could be simply removed since # our usage is compatible with the XHTML usage, but it might be less # ambiguous to rename it. option = element option { attlist.option, documentation?, prompt* } # if the value is different from the prompt the user sees, specify it attlist.option &= attribute value { text }? # permissions policy on an entity, a page, form, list or field # # group: the group to which permission is granted # permission: the permission which is granted to that group permission = element permission { attlist.permission, documentation? } attlist.permission &= attribute group { text }, attribute permission { Permissions } # pragmatic advice to generators of lists and forms, in the form of # name/value pairs which may contain anything. Over time some pragmas # will become 'well known', but the whole point of having a pragma # architecture is that it is extensible. pragma = element pragma { attlist.pragma, documentation? } attlist.pragma &= attribute name { text }, attribute value { text } # a prompt for a property or field; used as the prompt text for a widget # which edits it. Typically there will be only one of these per property # per locale; if there are more than one all those matching the locale may # be concatenated, or just one may be used. # # prompt: the prompt to use # locale: the locale in which to prefer this prompt prompt = element prompt { attlist.prompt, documentation? } attlist.prompt &= attribute prompt { text }, attribute locale { Locale } # helptext about a property of an entity, or a field of a page, form or # list, or a typedef. Typically there will be only one of these per property # per locale; if there are more than one all those matching the locale may # be concatenated, or just one may be used. # # locale: the locale in which to prefer this prompt help = element help { attlist.help, text } attlist.help &= attribute locale { Locale } # helpful text to be shown if a property value is missing, typically when # a form is submitted. Typically there will be only one of these per property # per locale; if there are more than one all those matching the locale may # be concatenated, or just one may be used. Later there may be more sophisticated # behaviour here. ifmissing = element ifmissing { attlist.ifmissing, text } attlist.ifmissing &= attribute locale { Locale } # a form through which an entity may be added or edited # # TODO: This needs to be renamed because it conflicts with the # XHTML element of the same name. form = element form { attlist.form, documentation?, PageStuff* } attlist.form &= PageAttrs # a page on which an entity may be displayed page = element page { attlist.page, documentation?, PageStuff* } attlist.page &= PageAttrs # an ordering or records in a list # property: the property on which to order # sequence: the sequence in which to order order = element order { attlist.order, documentation? } attlist.order &= attribute property { text }, attribute sequence { Sequences }? # a list on which entities of a given type are listed # # onselect: name of form/page/list to go to when # a selection is made from the list \list = element list { attlist.list, documentation?, (PageStuff | order)* } attlist.list &= PageAttrs, attribute onselect { text }? # a subsidiary list, on which entities related to primary # entities in the enclosing page or list are listed # # property: the property of the enclosing entity that this # list displays (obviously, must be of type='list') # onselect: the form or page of the listed entity to call # when an item from the list is selected # canadd: true if the user should be able to add records # to this list auxlist = element auxlist { attlist.auxlist, documentation?, (prompt | FieldStuff)* } attlist.auxlist &= PageAttrs, attribute property { text }, attribute onselect { text }?, attribute canadd { Boolean }? # a group of fields and other controls within a form or list, which the # renderer might render as a single pane in a tabbed display, for example. fieldgroup = element fieldgroup { attlist.fieldgroup, documentation?, (prompt | permission | FieldStuff)* } attlist.fieldgroup &= attribute name { text } # a field in a form or page # # property: the property which this field displays/edits field = element field { attlist.field, documentation?, (prompt | help | permission)* } attlist.field &= attribute property { text } # a verb is something that may be done through a form. Probably the verbs 'store' # and 'delete' are implied, but maybe they need to be explicitly declared. The 'verb' # attribute of the verb is what gets returned to the controller verb = element verb { attlist.verb, documentation?, (prompt | help | permission)* } attlist.verb &= attribute verb { text }, attribute dangerous { Boolean } # a container for global content content = element content { attlist.content, Content* } attlist.content &= empty # content to place in the head of the generated document; this is #PCDATA # because it will almost certainly belong to a different namespace # (usually HTML) # # TODO: This needs to be renamed or removed because it conflicts with the # XHTML element of the same name. In fact it could be simply removed since # our usage is compatible with the XHTML usage, but it might be less # ambiguous to rename it. head = element head { attlist.head, text } attlist.head &= empty # content to place in the top of the body of the generated document; # this is %Flow; which is any HTML block or inline level element. top = element top { attlist.top, text } attlist.top &= empty # content to place at the foot of the body of the generated document; # this is %Flow; which is any HTML block or inline level element. foot = element foot { attlist.foot, text } attlist.foot &= empty # The 'specification' and 'reference' elements are for documentation only, # and do not contribute to the engineering of the application described. # # A specification element is intended chiefly to declare the reference # documents which may be used in documentation elements later in the # document. # # url: The URL from which the document referenced can be retrieved # name: The full name (title) given to this document # abbr: A convenient abbreviated name specification = element specification { attlist.specification, documentation?, reference* } attlist.specification &= attribute url { text }?, attribute name { text }, attribute abbr { text } # The 'specification' and 'reference' elements are for documentation only, # and do not contribute to the engineering of the application described. # # A reference element is a reference to a specifying document. # # abbr: The abbreviated name of the specification to which this # reference refers # section: The 'anchor part' (part following a hash character) which, # when appended to the URL, will locate the exact section # referenced. # entity: A reference to another entity within this ADL document # property: A reference to another property within this ADL document; # if entity is also specified then of that entity, else of # the ancestor entity if any reference = element reference { attlist.reference, documentation? } attlist.reference &= attribute abbr { text }?, attribute section { text }?, attribute entity { text }?, attribute property { text }? start = application