All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cristalise.dsl.persistency.outcome.SchemaDelegate.groovy Maven / Gradle / Ivy

Go to download

CRISTAL-iSE Domain Specific Language module for bootstrapping and funtional testing

There is a newer version: 6.0.0
Show newest version
/**
 * This file is part of the CRISTAL-iSE kernel.
 * Copyright (c) 2001-2015 The CRISTAL Consortium. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * http://www.fsf.org/licensing/licenses/lgpl.html
 */
package org.cristalise.dsl.persistency.outcome

import org.cristalise.kernel.common.InvalidDataException
import org.cristalise.kernel.property.BuiltInItemProperties
import org.cristalise.kernel.property.PropertyDescriptionList
import org.cristalise.kernel.property.PropertyUtility

import groovy.util.logging.Slf4j
import groovy.xml.MarkupBuilder


/**
 *
 */
@Slf4j
class SchemaDelegate {

    String xsdString

    public void processClosure(Closure cl) {
        assert cl, "Schema only works with a valid Closure"

        def objBuilder = new ObjectGraphBuilder()
        objBuilder.setChildPropertySetter(new DSLPropertySetter())
        objBuilder.classLoader = this.class.classLoader
        objBuilder.classNameResolver = 'org.cristalise.dsl.persistency.outcome'

        cl.delegate = objBuilder

        xsdString = buildXSD( cl() )
    }

    public String buildXSD(Struct s) {
        if(!s) throw new InvalidDataException("Schema cannot be built from empty declaration")

        def writer = new StringWriter()
        def xsd = new MarkupBuilder(writer)

        xsd.setOmitEmptyAttributes(true)
        xsd.setOmitNullAttributes(true)

        xsd.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")

        xsd.'xs:schema'('xmlns:xs': 'http://www.w3.org/2001/XMLSchema') { 
            buildStruct(xsd,s) 
        }

        return writer.toString()
    }

    private void buildStruct(MarkupBuilder xsd, Struct s) {
        log.info "buildStruct() - Struct: $s.name"
        xsd.'xs:element'(name: s.name, minOccurs: s.minOccurs, maxOccurs: s.maxOccurs) {

            if(s.documentation || s.dynamicForms) {
                'xs:annotation' { 
                    if (s.documentation) {
                        'xs:documentation'(s.documentation)
                    }
                    if (s.dynamicForms) {
                        'xs:appinfo' {
                            dynamicForms {
                                if (s.dynamicForms.width)     width(     s.dynamicForms.width)
                                if (s.dynamicForms.label)     label(     s.dynamicForms.label)
                                if (s.dynamicForms.container) container( s.dynamicForms.container)
                            }
                        }
                    }
                }
            }

            'xs:complexType' {
                if (s.orderOfElements || s.anyField) {
                    if (s.useSequence) {
                        'xs:sequence' {
                            this.buildStructElements(xsd, s)
                        }
                    }
                    else {
                        'xs:all'(minOccurs: '0') {
                            this.buildStructElements(xsd, s)
                        }
                    }
                }

                if (s.attributes) {
                    s.attributes.each { Attribute a -> this.buildAtribute(xsd, a) }
                }
            }
        }
    }
    
    private void buildStructElements(MarkupBuilder xsd, Struct s) {
        s.orderOfElements.each { String name ->
            if (s.fields.containsKey(name))  this.buildField(xsd, s.fields[name])
            if (s.structs.containsKey(name)) this.buildStruct(xsd, s.structs[name])
        }
        if (s.anyField) this.buildAnyField(xsd, s.anyField)
    }


    private boolean hasRangeConstraints(Attribute a) {
        return a.minInclusive != null || a.maxInclusive != null || a.minExclusive != null || a.maxExclusive != null
    }

    private boolean hasLengthConstraints(Attribute a) {
        return a.length != null || a.maxLength != null || a.minLength != null
    }

    private boolean hasNumericConstraints(Attribute a) {
        return a.totalDigits != null || a.fractionDigits != null
    }

    private boolean hasRestrictions(Attribute a) {
        return a.values || a.pattern || hasRangeConstraints(a) || hasNumericConstraints(a) || hasLengthConstraints(a)
    }

    private boolean hasAppinfoNodes(Field f) {
        return f.dynamicForms || f.listOfValues || f.reference
    }

    /**
     * Checks whether the field has a restriction/attributes/unit or not, because the type of the element 
     * is either specified in the 'type' attribute or in the restriction as 'base'
     * 
     * @param f the actual field to check
     * @return the type if the field has no restriction, otherwise an empty string
     */
    private String fieldType(Field f) {
        if (hasRestrictions(f) || f.attributes || f.unit) return ''
        else                                              return f.type
    }

    /**
     * Checks whether the attribute has a restriction or not, because the type of the attribute
     * is either specified in the 'type' attribute or in the restriction as 'base'
     * 
     * @param a the attribute to check
     * @return the type if the attribute has no restriction, otherwise an empty string
     */
    private String attributeType(Attribute a) {
        if (hasRestrictions(a)) return ''
        else                    return a.type
    }

    private void buildAtribute(MarkupBuilder xsd, Attribute a) {
        log.info "buildAtribute() - attribute: $a.name"

        if (a.documentation) throw new InvalidDataException('Attribute cannot define documentation')

        xsd.'xs:attribute'(name: a.name, type: attributeType(a), 'default': a.defaultVal, 'use': (a?.required ? "required": "")) {
            if(hasRestrictions(a)) {
                buildRestriction(xsd, a)
            }
        }
    }

    private void setAppinfoDynamicForms(xsd, Field f) {
        xsd.dynamicForms {
            if (f.dynamicForms.hidden   != null)             hidden(      f.dynamicForms.hidden)
            if (f.dynamicForms.required != null)             required(    f.dynamicForms.required)
            if (f.dynamicForms.disabled != null)             disabled(    f.dynamicForms.disabled)
            if (f.dynamicForms.multiple != null)             multiple(    f.dynamicForms.multiple)
            if (f.dynamicForms.label)                        label(       f.dynamicForms.label)
            if (f.dynamicForms.placeholder)                  placeholder( f.dynamicForms.placeholder)
            if (f.dynamicForms.type)                         type(        f.dynamicForms.type)
            if (f.dynamicForms.inputType)                    inputType(   f.dynamicForms.inputType)
            if (f.dynamicForms.min != null)                  min(         f.dynamicForms.min)
            if (f.dynamicForms.max != null)                  max(         f.dynamicForms.max)
            if (f.dynamicForms.value != null)                value(       f.dynamicForms.value)
            if (f.dynamicForms.mask != null)                 mask(        f.dynamicForms.mask)
            if (f.dynamicForms.autoComplete != null)         autoComplete(f.dynamicForms.autoComplete)
            if (f.dynamicForms.pattern != null)              pattern(     f.dynamicForms.pattern)
            if (f.dynamicForms.errmsg != null)               errmsg(      f.dynamicForms.errmsg)
            if (f.dynamicForms.showSeconds != null)          showSeconds( f.dynamicForms.showSeconds)
            if (f.dynamicForms.container != null)            container(   f.dynamicForms.container)
            if (f.dynamicForms.control != null)              control(     f.dynamicForms.control)
            if (f.dynamicForms.labelGrid != null)            labelGrid(   f.dynamicForms.labelGrid)
            if (f.dynamicForms.hideOnDateTimeSelect != null) hideOnDateTimeSelect( f.dynamicForms.hideOnDateTimeSelect)
            if (f.dynamicForms.precision)                    precision(   f.dynamicForms.precision)
            if (f.dynamicForms.scale)                        scale(       f.dynamicForms.scale)

            if (f.hasAdditional()) {
                additional {
                    if (f.dynamicForms.additional) {
                        f.dynamicForms.additional.fields.each { key, value -> "$key"(value) }
                    }
                    if (f.dynamicForms.updateScriptRef != null) updateScriptRef(f.dynamicForms.getUpdateScriptRefString())
                    if (f.dynamicForms.updateQuerytRef != null) updateQuerytRef(f.dynamicForms.getUpdateQueryRefString())
                    if (f.dynamicForms.warning != null) {
                        warning {
                            if (f.dynamicForms.warning.pattern != null)    pattern(f.dynamicForms.warning.pattern)
                            if (f.dynamicForms.warning.message != null)    message(f.dynamicForms.warning.message)
                            if (f.dynamicForms.warning.expression != null) expression {
                                mkp.yieldUnescaped("")
                            }
                        }
                    }
                    if (f.dynamicForms.updateFields !=null) updateFields(f.dynamicForms.updateFields.join(','))
                }
            }

            if (f.isFileUpload()) {
                if (f.dynamicForms.htmlAccept != null)            accept(   f.dynamicForms.htmlAccept)
            }
        }
    }

    private void setAppinfoListOfValues(xsd, Field f) {
        xsd.listOfValues {
            if (f.listOfValues.scriptRef)       scriptRef(      f.listOfValues.getScriptRefString())
            if (f.listOfValues.queryRef)        queryRef(       f.listOfValues.getQueryRefString())
            if (f.listOfValues.propertyNames)   propertyNames(  f.listOfValues.propertyNames)
            if (f.listOfValues.inputName)       inputName(      f.listOfValues.inputName)
            if (f.listOfValues.values)          values(         f.listOfValues.values.join(','))
        }
    }

    private void setAppinfoReference(xsd, Field f) {
        xsd.reference {
            if (f.reference.itemType) {
                def itemRef = ""

                if (f.reference.itemType instanceof String)  {
                    itemRef = f.reference.itemType
                }
                else if (f.reference.itemType instanceof PropertyDescriptionList) {
                    def propDesc = (PropertyDescriptionList) f.reference.itemType
                    itemRef = PropertyUtility.getDefaultValue(propDesc.list, BuiltInItemProperties.TYPE.getName())

                    if (!itemRef) throw new InvalidDataException("Property called '${BuiltInItemProperties.TYPE}' is missing")
                }
                else
                    throw new InvalidDataException("itemType must be a String or PropertyDescriptionList")

                itemType(itemRef)
            }
        }
    }
        
    private void buildField(MarkupBuilder xsd, Field f) {
        log.info "buildField() - Field: $f.name"

        //TODO: implement support for this combination - see issue 129
        if (((f.attributes || f.unit) && hasRestrictions(f)) || (f.attributes && f.unit))
            throw new InvalidDataException('Field cannot have attributes, unit and restrictions at the same time')

        xsd.'xs:element'(name: f.name, type: fieldType(f), 'default': f.defaultVal, minOccurs: f.minOccurs, maxOccurs: f.maxOccurs) {
            if(f.documentation || this.hasAppinfoNodes(f)) {
                'xs:annotation' {
                    if (f.documentation) 'xs:documentation'(f.documentation)
                    if (this.hasAppinfoNodes(f)) {
                        'xs:appinfo' {
                            if (f.dynamicForms) this.setAppinfoDynamicForms(xsd, f)
                            if (f.listOfValues) this.setAppinfoListOfValues(xsd, f)
                            if (f.reference)    this.setAppinfoReference(xsd, f)
                        }
                    }
                }
            }

            if(f.attributes) {
                'xs:complexType' {
                    'xs:simpleContent' {
                        'xs:extension'(base: f.type) {
                            f.attributes.each { Attribute a -> this.buildAtribute(xsd, a) }
                        }
                    }
                }
            }
            else if(f.unit) {
                'xs:complexType' {
                    'xs:simpleContent' {
                        'xs:extension'(base: f.type) {
                            'xs:attribute'(name:"unit", type: (!f.unit.values ? 'xs:string' : ''), 'default': f.unit.defaultVal, 'use': (f.unit.defaultVal ? "optional": "required")) {
                                if(f.unit.values) {
                                    'xs:simpleType' {
                                        'xs:restriction'(base: 'xs:string') {
                                            f.unit.values.each { 'xs:enumeration'(value: it) }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else if(hasRestrictions(f)) {
                buildRestriction(xsd, f)
            }
        }
    }

    private void buildAnyField(MarkupBuilder xsd, AnyField any) {
        log.info "buildAnyField()"

        xsd.'xs:any'(minOccurs: any.minOccurs, maxOccurs: any.maxOccurs, processContents: any.processContents)
    }

    private void buildRestriction(MarkupBuilder xsd, Attribute fieldOrAttr) {
        log.info "buildRestriction() - type:$fieldOrAttr.type"

        xsd.'xs:simpleType' {
            'xs:restriction'(base: fieldOrAttr.type) {
                if (fieldOrAttr.values) fieldOrAttr.values.each { 'xs:enumeration'(value: it) }

                if (fieldOrAttr.pattern != null) 'xs:pattern'(value: fieldOrAttr.pattern)

                if (fieldOrAttr.minInclusive != null) 'xs:minInclusive'(value: fieldOrAttr.minInclusive)
                if (fieldOrAttr.minExclusive != null) 'xs:minExclusive'(value: fieldOrAttr.minExclusive)
                if (fieldOrAttr.maxInclusive != null) 'xs:maxInclusive'(value: fieldOrAttr.maxInclusive)
                if (fieldOrAttr.maxExclusive != null) 'xs:maxExclusive'(value: fieldOrAttr.maxExclusive)

                if (fieldOrAttr.totalDigits    != null) 'xs:totalDigits'(value: fieldOrAttr.totalDigits)
                if (fieldOrAttr.fractionDigits != null) 'xs:fractionDigits'(value: fieldOrAttr.fractionDigits)

                if (fieldOrAttr.length    != null) 'xs:length'(value: fieldOrAttr.length)
                if (fieldOrAttr.minLength != null) 'xs:minLength'(value: fieldOrAttr.minLength)
                if (fieldOrAttr.maxLength != null) 'xs:maxLength'(value: fieldOrAttr.maxLength)
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy