![JAR search and dependency download from the Maven repository](/logo.png)
org.kuali.rice.krad.schema.SchemaGenerator.groovy Maven / Gradle / Ivy
/**
* Copyright 2005-2015 The Kuali Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ecl2.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kuali.rice.krad.schema
import org.apache.commons.lang.StringUtils
import org.apache.log4j.Logger
import org.kuali.rice.core.api.util.type.TypeUtils
import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute
import org.kuali.rice.krad.datadictionary.parse.BeanTagAttributeInfo
import org.kuali.rice.krad.datadictionary.parse.BeanTagInfo
import org.kuali.rice.krad.datadictionary.parse.CustomTagAnnotations
import org.kuali.rice.krad.uif.component.Component
import org.kuali.rice.krad.uif.component.ComponentBase
import org.kuali.rice.krad.uif.element.Content
import org.w3c.dom.CDATASection
import org.w3c.dom.Document
import org.w3c.dom.Element
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.Transformer
import javax.xml.transform.TransformerException
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
/**
* Generates the a custom schema file for the given schema.
*
* @author Kuali Rice Team ([email protected])
*/
class SchemaGenerator {
static final Logger LOG = Logger.getRootLogger()
static final String KRAD_SCHEMA = "krad"
Map> nameTagMap
List otherSchemaTagsList
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
DocumentBuilder builder = factory.newDocumentBuilder()
List types = new ArrayList()
List elements = new ArrayList()
Set> processedClassChoiceTypes = new HashSet>()
Map elementObjects = new HashMap()
Set mixedTypes = new HashSet()
Map componentAttributes = CustomTagAnnotations.getAttributes(ComponentBase.class)
Element componentBaseElement
Set schemaFileNames
ResourceBundle doc
String[] scanPackages
String outputPath
ResourceBundle additionalSchemaTagsProperties
String schemaName
Map> otherSchemaPackages
def generateSchema(ResourceBundle doc, String[] scanPackages, String outputPath,
ResourceBundle additionalSchemaTagsProperties, String schemaName,
Map> otherSchemaPackages) {
LOG.info("Generating Custom Schema ...")
this.doc = doc
this.scanPackages = scanPackages
this.outputPath = outputPath
this.additionalSchemaTagsProperties = additionalSchemaTagsProperties
this.schemaName = schemaName
this.otherSchemaPackages = otherSchemaPackages
nameTagMap = buildNameTagMap()
schemaFileNames = new HashSet()
buildSchemaElements()
fillAndWriteSchema()
}
def buildSchemaElements() {
Document document = builder.newDocument()
Set classKeys = nameTagMap.keySet()
componentBaseElement = buildClassSchemaType(document, "componentAttributes-type",
ComponentBase.class)
// create types for each element
for (String className : classKeys) {
Map tagMap = nameTagMap.get(className)
Class> clazz = Class.forName(className)
// content component is handled specially in base types
if (clazz.equals(Content.class)) {
continue
}
BeanTagInfo typeInfo = tagMap.get("default")
String currentType = typeInfo.getTag()
String type = currentType + "-type"
Element complexType = buildClassSchemaType(document, type, clazz)
types.add(complexType)
// create the tag type element for the currentType
Element typeElement = createElement(document, typeInfo.getTag(), type, null, null)
typeElement.appendChild(getDocAnnotation(document, doc, className, null, null));
elements.add(typeElement)
// generate the remaining tag type elements for the rest of the tags of this class
List tagNames = new ArrayList()
tagMap.each { key, tagInfo -> tagNames.add(tagInfo.getTag())
}
if ((additionalSchemaTagsProperties != null) && additionalSchemaTagsProperties.containsKey(className)) {
String[] tagBeanMappings = additionalSchemaTagsProperties.getString(className).split(",")
tagBeanMappings.each { tagBeanMapping ->
String tag = StringUtils.substringBefore(tagBeanMapping, ":")
tagNames.add(tag)
}
}
tagNames.each { tag ->
if (!tag.equals(currentType)) {
Element element = createElement(document, tag, type, null, null)
element.appendChild(getDocAnnotation(document, doc, className, null, null));
elements.add(element)
}
}
}
for (Element element : elements) {
String mixedTypeName = element.getAttribute("name") + "-mixedType"
if (mixedTypes.contains(mixedTypeName)) {
element.setAttribute("type", mixedTypeName)
}
}
}
def Map> buildNameTagMap() {
nameTagMap = new HashMap>()
CustomTagAnnotations.loadTagClasses(scanPackages)
Map beanMap = CustomTagAnnotations.getBeanTags()
beanMap.each { tagName, info ->
String name = info.getBeanClass().getName()
Map existingTags = nameTagMap.get(name)
if (existingTags == null) {
existingTags = new HashMap()
}
if (info.isDefaultTag() || existingTags.isEmpty()) {
info.setDefaultTag(true)
existingTags.put("default", info)
}
if (info.getParent() != null) {
existingTags.put(info.getParent(), info)
}
nameTagMap.put(name, existingTags)
}
return nameTagMap
}
def Element buildClassSchemaType(Document document, String type, Class> clazz) {
boolean isComponent = Component.class.isAssignableFrom(clazz)
boolean isComponentBase = ComponentBase.class.equals(clazz)
Element complexType = document.createElement("xsd:complexType")
complexType.setAttribute("name", type)
Element extension = null
if (isComponent && !isComponentBase) {
Element complexContent = document.createElement("xsd:complexContent")
extension = document.createElement("xsd:extension")
if (isKradSchema()) {
extension.setAttribute("base", "componentAttributes-type")
} else {
extension.setAttribute("base", "krad:componentAttributes-type")
}
complexContent.appendChild(extension)
complexType.appendChild(complexContent)
}
List attributeProperties = new ArrayList()
Element sequence = processClassAttributes(document, clazz, attributeProperties)
// extension for attributes if this is a sub-class of component
if (isComponent && !isComponentBase) {
extension.appendChild(sequence)
} else {
complexType.appendChild(sequence)
}
// add parent attribute to base types (ie, not component child classes)
if (!isComponent || isComponentBase) {
Element parentAttribute = document.createElement("xsd:attribute")
parentAttribute.setAttribute("name", "parent")
parentAttribute.setAttribute("type", "xsd:string")
attributeProperties.add(parentAttribute)
}
// add anyAttribute to allow any arbitrary attribute (ie, dot notation nested property)
Element anyAttribute = document.createElement("xsd:anyAttribute")
anyAttribute.setAttribute("processContents", "skip")
attributeProperties.add(anyAttribute)
// add all the attributes to type
for (Element attribute : attributeProperties) {
if (isComponent && !isComponentBase) {
extension.appendChild(attribute)
} else {
complexType.appendChild(attribute)
}
}
return complexType
}
def Element processClassAttributes(Document document, Class> clazz, List attributeProperties) {
boolean isComponent = Component.class.isAssignableFrom(clazz)
boolean isComponentBase = ComponentBase.class.equals(clazz)
Element sequence = document.createElement("xsd:choice")
sequence.setAttribute("minOccurs", "0")
sequence.setAttribute("maxOccurs", "unbounded")
Map attributes = CustomTagAnnotations.getAttributes(clazz)
if (attributes != null && !attributes.isEmpty()) {
for (BeanTagAttributeInfo aInfo : attributes.values()) {
if (!isComponent || !isComponentBase) {
if (aInfo.getType().equals(BeanTagAttribute.AttributeType.BYTYPE) || aInfo.getType().
equals(BeanTagAttribute.AttributeType.DIRECTORBYTYPE)) {
Element byTypeSequence = buildByTypeChoiceElement(document, aInfo)
sequence.appendChild(byTypeSequence)
continue
}
if (aInfo.getType().equals(BeanTagAttribute.AttributeType.DIRECT)) {
createMixedType(document, aInfo)
Element directRef = document.createElement("xsd:element")
directRef.setAttribute("ref", aInfo.getName())
sequence.appendChild(directRef)
continue
}
// default to anyType
String attrType = "xsd:anyType"
//Process each type of content below by setting a type and special processing flags
if (aInfo.getType().equals(BeanTagAttribute.AttributeType.SINGLEVALUE)) {
attrType = "xsd:string"
} else if (aInfo.getType().equals(BeanTagAttribute.AttributeType.SINGLEBEAN)) {
String attributeClass = aInfo.getValueType().getName()
buildClassChoiceBaseType(document, Class.forName(attributeClass))
if (elementObjects.containsKey(attributeClass)) {
attrType = attributeClass
}
} else if (aInfo.getType().equals(BeanTagAttribute.AttributeType.LISTVALUE) || aInfo.getType().
equals(BeanTagAttribute.AttributeType.SETVALUE)) {
attrType = "list-type"
} else if (aInfo.getType().equals(BeanTagAttribute.AttributeType.MAPVALUE) || aInfo.getType().
equals(BeanTagAttribute.AttributeType.MAPBEAN)) {
attrType = "map-type"
} else if (aInfo.getType().equals(BeanTagAttribute.AttributeType.LISTBEAN) || aInfo.getType().
equals(BeanTagAttribute.AttributeType.SETBEAN)) {
attrType = "listOrSetType"
}
//create the element and documentation
Element element = createElement(document, aInfo.getName(), null, "0", "1");
element.appendChild(getDocAnnotation(document, doc, clazz.getName(), aInfo.getName(),
aInfo.getValueType().getName()));
if (aInfo.getType().equals(BeanTagAttribute.AttributeType.LISTBEAN) || aInfo.getType().
equals(BeanTagAttribute.AttributeType.SETBEAN)) {
Element extensionType = getListOrSetExtension(document, aInfo, attrType)
if (extensionType != null) {
element.appendChild(extensionType)
} else {
element.setAttribute("type", attrType)
}
} else if (aInfo.getType().equals(BeanTagAttribute.AttributeType.ANY)) {
Element anyComplexType = createAnyTypeElement(document)
element.appendChild(anyComplexType)
} else {
element.setAttribute("type", attrType)
}
sequence.appendChild(element)
}
// only append attributes for properties that can be input as string values
boolean useAttribute = !aInfo.getType().equals(BeanTagAttribute.AttributeType.SINGLEBEAN) && !aInfo.
getType().equals(BeanTagAttribute.AttributeType.LISTBEAN) &&
!aInfo.getType().equals(BeanTagAttribute.AttributeType.MAPBEAN) &&
!aInfo.getType().equals(BeanTagAttribute.AttributeType.SETBEAN)
if (useAttribute && (!isComponent || (isComponent && !componentAttributes.containsValue(aInfo)) ||
isComponentBase)) {
Element attribute = document.createElement("xsd:attribute")
attribute.setAttribute("name", aInfo.getName())
attribute.appendChild(getDocAnnotation(document, doc, clazz.getName(), aInfo.getName(),
aInfo.getValueType().getName()));
attributeProperties.add(attribute);
}
}
}
// spring:property element
if (!isComponent || !isComponentBase) {
Element nestedSpringPropertiesElement = document.createElement("xsd:element")
nestedSpringPropertiesElement.setAttribute("ref", "spring:property")
nestedSpringPropertiesElement.setAttribute("minOccurs", "0")
nestedSpringPropertiesElement.setAttribute("maxOccurs", "unbounded")
sequence.appendChild(nestedSpringPropertiesElement)
Element nestedPropertiesElement = document.createElement("xsd:element")
nestedPropertiesElement.setAttribute("ref", "property")
nestedPropertiesElement.setAttribute("minOccurs", "0")
nestedPropertiesElement.setAttribute("maxOccurs", "unbounded")
sequence.appendChild(nestedPropertiesElement)
}
return sequence
}
def void createMixedType(Document document, BeanTagAttributeInfo aInfo) {
String mixedTypeName = aInfo.getName() + "-mixedType"
if (mixedTypes.contains(mixedTypeName)) {
return
}
String attributeClass = aInfo.getValueType().getName()
if (aInfo.getValueType().isInterface()) {
attributeClass = aInfo.getValueType().getName() + "Base"
}
Map tagMap = nameTagMap.get(attributeClass)
Element complexType = document.createElement("xsd:complexType")
complexType.setAttribute("name", aInfo.getName() + "-mixedType")
Element complexContent = document.createElement("xsd:complexContent")
Element extension = document.createElement("xsd:extension")
BeanTagInfo typeInfo = tagMap.get("default")
String defaultType = typeInfo.getTag()
extension.setAttribute("base", defaultType + "-type")
complexContent.appendChild(extension)
complexType.appendChild(complexContent)
buildClassChoiceBaseType(document, Class.forName(attributeClass))
Element tagChoiceElement = buildTypeChoiceElement(document, aInfo, true)
if (tagChoiceElement != null) {
extension.appendChild(tagChoiceElement)
}
types.add(complexType)
mixedTypes.add(mixedTypeName)
}
def Element buildByTypeChoiceElement(Document document, BeanTagAttributeInfo aInfo) {
Element tagChoiceElement = buildTypeChoiceElement(document, aInfo, false)
String attributeName = aInfo.getName()
buildClassChoiceBaseType(document, aInfo.getValueType())
String type = aInfo.getValueType().getName()
if (aInfo.getType().equals(BeanTagAttribute.AttributeType.DIRECTORBYTYPE)) {
createMixedType(document, aInfo)
} else {
Element propertyElement = createElement(document, attributeName, type, "0", "1")
tagChoiceElement.appendChild(propertyElement)
}
return tagChoiceElement
}
def Element buildTypeChoiceElement(Document document, BeanTagAttributeInfo aInfo, boolean forMixed) {
Element tagChoiceElement = document.createElement("xsd:choice")
tagChoiceElement.setAttribute("minOccurs", "0")
tagChoiceElement.setAttribute("maxOccurs", "1")
Class> targetType = aInfo.getValueType()
if (targetType.isInterface()) {
targetType = Class.forName(targetType.getName() + "Base")
}
Map targetTagAttributes = CustomTagAnnotations.getAttributes(targetType)
// if building mixed type we don't want to add the type refs if the value type already
// contains those ref (since mixed type extends the base type)
if (forMixed && !TypeUtils.isSimpleType(targetType) &&
containsByTypeAttribute(targetTagAttributes, targetType)) {
return
}
List added = new ArrayList()
for (String tagClassName : nameTagMap.keySet()) {
Class> tagClazz = Class.forName(tagClassName)
if (!targetType.isAssignableFrom(tagClazz)) {
continue
}
Map tagMap = nameTagMap.get(tagClassName)
for (String key : tagMap.keySet()) {
String tag = tagMap.get(key).getTag()
if (forMixed && (containsAttribute(targetTagAttributes, tag))) {
continue
}
if (!added.contains(tag)) {
added.add(tag)
Element ref = document.createElement("xsd:element")
ref.setAttribute("ref", tag)
tagChoiceElement.appendChild(ref)
}
}
}
return tagChoiceElement
}
def boolean containsByTypeAttribute(Map targetTagAttributes, Class> type) {
boolean hasByTypeAttribue = false
targetTagAttributes.values().each { tagAttribute ->
if ((tagAttribute.getType().equals(BeanTagAttribute.AttributeType.BYTYPE) || tagAttribute.getType().
equals(BeanTagAttribute.AttributeType.DIRECTORBYTYPE)) && type.
isAssignableFrom(tagAttribute.getValueType())) {
hasByTypeAttribue = true
}
}
return hasByTypeAttribue
}
def boolean containsAttribute(Map targetTagAttributes, String attribute) {
boolean hasAttribue = false
targetTagAttributes.values().each { tagAttribute ->
if (StringUtils.equalsIgnoreCase(tagAttribute.name, attribute)) {
hasAttribue = true
}
}
return hasAttribue
}
def Element createElement(Document document, String name, String type, String minOccurs,
String maxOccurs) {
Element element = document.createElement("xsd:element")
element.setAttribute("name", name)
if (minOccurs != null) {
element.setAttribute("minOccurs", minOccurs)
}
if (maxOccurs != null) {
element.setAttribute("maxOccurs", maxOccurs)
}
if (type != null) {
element.setAttribute("type", type)
}
return element;
}
/**
* Fills in the schema documents with the content passed in and writes them out. Multiple schema files and
* includes are used due to a file size limitation of 45k lines in intelliJ for xsd files.*/
def void fillAndWriteSchema() throws TransformerException, IOException {
// create top level schema
Document topLevelDocument = builder.newDocument()
Element schema = getSchemaInstance(topLevelDocument)
Element include = topLevelDocument.createElement("xsd:include")
include.setAttribute("schemaLocation", this.schemaName + "-" + "elements.xsd")
schema.appendChild(include)
topLevelDocument.appendChild(schema)
// start elements document
Document elementsDocument = builder.newDocument()
schema = getSchemaInstance(elementsDocument)
for (Element element : elements) {
schema.appendChild(elementsDocument.importNode(element, true))
}
elementsDocument.appendChild(schema)
// base type documents
List baseTypeDocuments = writeBaseTypesDocuments()
// type documents
List typeDocuments = writeTypesDocuments(baseTypeDocuments)
org.w3c.dom.Node elementsSchema = elementsDocument.getFirstChild()
for (int i = 0; i < typeDocuments.size(); i++) {
//write includes in element document
include = elementsDocument.createElement("xsd:include")
include.setAttribute("schemaLocation", this.schemaName + "-" + "types" + (i + 1) + ".xsd")
elementsSchema.insertBefore(include, elementsSchema.getFirstChild())
}
writeDocument(topLevelDocument, "schema.xsd")
writeDocument(elementsDocument, "elements.xsd")
}
def List writeBaseTypesDocuments() {
Document baseTypesDocument = builder.newDocument()
Element schema = getSchemaInstance(baseTypesDocument)
int startIndex = 0;
int endIndex = 70;
List elementObjectKeys = new ArrayList(elementObjects.keySet())
if (endIndex > elementObjectKeys.size()) {
endIndex = elementObjectKeys.size()
}
List documentsToWrite = new ArrayList()
boolean complete = false
while (!complete) {
List elementObjectSubKeys = elementObjectKeys.subList(startIndex, endIndex)
Element include = baseTypesDocument.createElement("xsd:include")
include.setAttribute("schemaLocation", this.schemaName + "-" + "elements.xsd")
schema.appendChild(include)
// if first base type document add the generic types
if (documentsToWrite.isEmpty() && isKradSchema()) {
addGenericBaseTypes(baseTypesDocument, schema)
}
for (String objectName : elementObjectSubKeys) {
schema.appendChild(baseTypesDocument.importNode(elementObjects[objectName], true))
}
//add to write list
baseTypesDocument.appendChild(schema)
documentsToWrite.add(baseTypesDocument)
//setup next subList indices
startIndex = endIndex
endIndex = endIndex + 30
if (endIndex > elementObjectKeys.size()) {
endIndex = elementObjectKeys.size()
}
if (startIndex == elementObjectKeys.size()) {
complete = true
}
//reset document and schema for next phase
baseTypesDocument = builder.newDocument()
schema = getSchemaInstance(baseTypesDocument)
}
int part = 1;
for (Document document : documentsToWrite) {
writeDocument(document, "baseTypes" + part + ".xsd")
part++
}
return documentsToWrite
}
def addGenericBaseTypes(Document baseTypesDocument, Element schema) {
Element propertySubstitution = baseTypesDocument.createElement("xsd:element")
propertySubstitution.setAttribute("name", "property")
propertySubstitution.setAttribute("type", "spring:propertyType")
schema.appendChild(propertySubstitution)
Element valueElement = baseTypesDocument.createElement("xsd:element")
valueElement.setAttribute("name", "value")
Element valueComplexType = baseTypesDocument.createElement("xsd:complexType")
valueComplexType.setAttribute("mixed", "true")
valueElement.appendChild(valueComplexType)
Element valueChoiceElement = baseTypesDocument.createElement("xsd:choice")
valueChoiceElement.setAttribute("minOccurs", "0")
valueChoiceElement.setAttribute("maxOccurs", "unbounded")
valueComplexType.appendChild(valueChoiceElement)
schema.appendChild(valueElement)
Element beanSubstitution = baseTypesDocument.createElement("xsd:element")
beanSubstitution.setAttribute("name", "bean")
Element beanComplexType = baseTypesDocument.createElement("xsd:complexType")
beanSubstitution.appendChild(beanComplexType)
Element beanComplexContent = baseTypesDocument.createElement("xsd:complexContent")
beanComplexType.appendChild(beanComplexContent)
Element beanExtension = baseTypesDocument.createElement("xsd:extension")
beanExtension.setAttribute("base", "spring:identifiedType")
beanComplexContent.appendChild(beanExtension)
Element beanGroup = baseTypesDocument.createElement("xsd:group")
beanGroup.setAttribute("ref", "spring:beanElements")
beanExtension.appendChild(beanGroup)
Element beanAttributeGroup = baseTypesDocument.createElement("xsd:attributeGroup")
beanAttributeGroup.setAttribute("ref", "spring:beanAttributes")
beanExtension.appendChild(beanAttributeGroup)
schema.appendChild(beanSubstitution)
// add type for custom inc element
Element incElement = baseTypesDocument.createElement("xsd:element")
incElement.setAttribute("name", "inc")
Element incComplexType = baseTypesDocument.createElement("xsd:complexType")
incElement.appendChild(incComplexType)
Element incComplexContent = baseTypesDocument.createElement("xsd:complexContent")
incComplexType.appendChild(incComplexContent)
Element incRestriction = baseTypesDocument.createElement("xsd:restriction")
incRestriction.setAttribute("base", "xsd:anyType")
incComplexContent.appendChild(incRestriction)
Element incAttribute = baseTypesDocument.createElement("xsd:attribute")
incAttribute.setAttribute("name", "compId")
incAttribute.setAttribute("type", "xsd:string")
incRestriction.appendChild(incAttribute)
schema.appendChild(incElement)
// add type for custom ref element
Element refElement = baseTypesDocument.createElement("xsd:element")
refElement.setAttribute("name", "ref")
Element refComplexType = baseTypesDocument.createElement("xsd:complexType")
refElement.appendChild(refComplexType)
Element refComplexContent = baseTypesDocument.createElement("xsd:complexContent")
refComplexType.appendChild(refComplexContent)
Element refRestriction = baseTypesDocument.createElement("xsd:restriction")
refRestriction.setAttribute("base", "xsd:anyType")
refComplexContent.appendChild(refRestriction)
Element refAttribute = baseTypesDocument.createElement("xsd:attribute")
refAttribute.setAttribute("name", "bean")
refAttribute.setAttribute("type", "xsd:string")
refRestriction.appendChild(refAttribute)
schema.appendChild(refElement)
// generic content element type
Element contentElement = baseTypesDocument.createElement("xsd:element")
contentElement.setAttribute("name", "content")
Element contentComplexType = createAnyTypeElement(baseTypesDocument)
contentElement.appendChild(contentComplexType)
Element contentIdAttribute = baseTypesDocument.createElement("xsd:attribute")
contentIdAttribute.setAttribute("name", "id")
contentIdAttribute.setAttribute("type", "xsd:string")
contentComplexType.appendChild(contentIdAttribute)
schema.appendChild(contentElement)
Element entryElement = baseTypesDocument.createElement("xsd:element")
entryElement.setAttribute("name", "entry")
entryElement.setAttribute("type", "spring:entryType")
schema.appendChild(entryElement)
// create map type
Element mapType = baseTypesDocument.createElement("xsd:complexType")
mapType.setAttribute("name", "map-type")
Element mapSequenceContent = baseTypesDocument.createElement("xsd:sequence")
mapType.appendChild(mapSequenceContent)
Element mapSpringEntryElement = baseTypesDocument.createElement("xsd:element")
mapSpringEntryElement.setAttribute("minOccurs", "0")
mapSpringEntryElement.setAttribute("maxOccurs", "unbounded")
mapSpringEntryElement.setAttribute("ref", "spring:entry")
mapSequenceContent.appendChild(mapSpringEntryElement)
Element mapEntryElement = baseTypesDocument.createElement("xsd:element")
mapEntryElement.setAttribute("minOccurs", "0")
mapEntryElement.setAttribute("maxOccurs", "unbounded")
mapEntryElement.setAttribute("ref", "entry")
mapSequenceContent.appendChild(mapEntryElement)
Element mapMergeElement = baseTypesDocument.createElement("xsd:attribute")
mapMergeElement.setAttribute("name", "merge")
mapMergeElement.setAttribute("type", "xsd:boolean")
mapType.appendChild(mapMergeElement)
schema.appendChild(mapType)
//create basicList type
Element basicListType = baseTypesDocument.createElement("xsd:complexType")
basicListType.setAttribute("name", "list-type")
Element basicListSequence = baseTypesDocument.createElement("xsd:sequence")
Element springValueRefElement = baseTypesDocument.createElement("xsd:element")
springValueRefElement.setAttribute("minOccurs", "0")
springValueRefElement.setAttribute("maxOccurs", "unbounded")
springValueRefElement.setAttribute("ref", "spring:value")
basicListSequence.appendChild(springValueRefElement)
Element valueRefElement = baseTypesDocument.createElement("xsd:element")
valueRefElement.setAttribute("minOccurs", "0")
valueRefElement.setAttribute("maxOccurs", "unbounded")
valueRefElement.setAttribute("ref", "value")
basicListSequence.appendChild(valueRefElement)
Element basicListMergeAttribute = baseTypesDocument.createElement("xsd:attribute")
basicListMergeAttribute.setAttribute("name", "merge")
basicListMergeAttribute.setAttribute("type", "xsd:boolean")
basicListType.appendChild(basicListSequence)
basicListType.appendChild(basicListMergeAttribute)
schema.appendChild(basicListType)
// list or set type
Element listOrSetComplexType = baseTypesDocument.createElement("xsd:complexType")
listOrSetComplexType.setAttribute("name", "listOrSetType")
Element listOrSetContentElement = baseTypesDocument.createElement("xsd:complexContent")
listOrSetComplexType.appendChild(listOrSetContentElement)
Element listOrSetExtensionElement = baseTypesDocument.createElement("xsd:extension")
listOrSetExtensionElement.setAttribute("base", "spring:listOrSetType")
listOrSetContentElement.appendChild(listOrSetExtensionElement)
listOrSetExtensionElement.appendChild(basicListMergeAttribute)
schema.appendChild(listOrSetComplexType)
schema.appendChild(baseTypesDocument.importNode(componentBaseElement, true))
}
def List writeTypesDocuments(List baseTypeDocuments) {
Document typesDocument = builder.newDocument()
Element schema = getSchemaInstance(typesDocument)
int startIndex = 0;
int endIndex = 20;
if (endIndex > types.size()) {
endIndex = types.size()
}
List documentsToWrite = new ArrayList()
boolean complete = false
while (!complete) {
List typesSubList = types.subList(startIndex, endIndex)
int part = 1;
for (Document document : baseTypeDocuments) {
Element include = typesDocument.createElement("xsd:include")
include.setAttribute("schemaLocation", this.schemaName + "-" + "baseTypes" + part + ".xsd")
schema.appendChild(include)
part++
}
//add all types
for (Element type : typesSubList) {
schema.appendChild(typesDocument.importNode(type, true))
}
//add to write list
typesDocument.appendChild(schema)
documentsToWrite.add(typesDocument)
//setup next subList indices
startIndex = endIndex
endIndex = endIndex + 20
if (endIndex > types.size()) {
endIndex = types.size()
}
if (startIndex == types.size()) {
complete = true
}
//reset document and schema for next phase
typesDocument = builder.newDocument()
schema = getSchemaInstance(typesDocument)
}
int part = 1;
for (Document document : documentsToWrite) {
writeDocument(document, "types" + part + ".xsd")
part++
}
return documentsToWrite
}
def Element createAnyTypeElement(Document document) {
Element contentComplexType = document.createElement("xsd:complexType")
Element sequenceElement = document.createElement("xsd:sequence")
contentComplexType.appendChild(sequenceElement)
Element anyElement = document.createElement("xsd:any")
anyElement.setAttribute("minOccurs", "0")
anyElement.setAttribute("processContents", "skip")
sequenceElement.appendChild(anyElement)
return contentComplexType
}
/**
* Writes the document out with the provided documentName to the current directory
*
* @param document document to be written
* @param documentName name of document to write to
* @throws TransformerException
* @throws IOException
*/
def void writeDocument(Document document, String documentName) throws TransformerException, IOException {
documentName = this.schemaName + "-" + documentName
this.schemaFileNames.add(documentName)
File file = new File(this.outputPath + "/" + documentName)
LOG.info("Writing schema file: " + file.getPath())
Transformer transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
transformer.transform(new DOMSource(document), new StreamResult(new FileWriter(file)))
}
/**
* Gets a new schema element instance with the krad namespace
*
* @param document the document
* @return schema element with properties/imports filled in
*/
def Element getSchemaInstance(Document document) {
//set up base schema tag
Element schema = document.createElement("xsd:schema")
schema.setAttribute("xmlns", "http://www.kuali.org/" + this.schemaName + "/schema")
schema.setAttribute("targetNamespace", "http://www.kuali.org/" + this.schemaName + "/schema")
schema.setAttribute("elementFormDefault", "qualified")
schema.setAttribute("attributeFormDefault", "unqualified")
if (!isKradSchema()) {
schema.setAttribute("xmlns:krad", "http://www.kuali.org/krad/schema")
}
schema.setAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema")
schema.setAttribute("xmlns:spring", "http://www.springframework.org/schema/beans")
schema.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
schema.setAttribute("xsi:schemaLocation",
"http://www.springframework.org/schema/beans http://www.springframework" +
".org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/util " +
"http://www.springframework.org/schema/util/spring-util-3.1.xsd")
//add spring import
Element springImport = document.createElement("xsd:import")
springImport.setAttribute("namespace", "http://www.springframework.org/schema/beans")
schema.appendChild(springImport)
if (!isKradSchema()) {
Element kradImport = document.createElement("xsd:import")
kradImport.setAttribute("namespace", "http://www.kuali.org/krad/schema")
schema.appendChild(kradImport)
}
return schema
}
def buildClassChoiceBaseType(Document document, Class> clazz) {
if (processedClassChoiceTypes.contains(clazz)) {
return
}
if (TypeUtils.isSimpleType(clazz) || Enum.class.isAssignableFrom(clazz) || clazz.isArray()) {
return
}
Element elementObject = document.createElement("xsd:complexType")
elementObject.setAttribute("name", clazz.getName())
Element choice = document.createElement("xsd:choice")
choice.setAttribute("minOccurs", "0")
choice.setAttribute("maxOccurs", "unbounded")
Element beanElement = document.createElement("xsd:element")
beanElement.setAttribute("ref", "bean")
choice.appendChild(beanElement)
Element ref = document.createElement("xsd:element")
ref.setAttribute("ref", "ref")
choice.appendChild(ref)
Element inc = document.createElement("xsd:element")
inc.setAttribute("ref", "inc")
choice.appendChild(inc)
List added = new ArrayList()
nameTagMap.each { className, tagMap ->
Class> clazzKey = Class.forName(className)
if (clazz.isAssignableFrom(clazzKey)) {
tagMap.each { bean, tagInfo ->
String tag = tagInfo.getTag()
if (!added.contains(tag)) {
added.add(tag)
Element tagElement = document.createElement("xsd:element")
tagElement.setAttribute("ref", tag)
choice.appendChild(tagElement)
}
}
}
}
elementObject.appendChild(choice)
elementObjects[clazz.getName()] = elementObject
processedClassChoiceTypes.addAll(clazz)
}
/**
* Get the documentation annonation element for the class or property information passed in
*
* @param document the document
* @param doc the ResourceBundle documentation resource
* @param className name of the class to get documentation for. If property and property type are not supplied,
* returns the class documentation Element
* @param property (optional) when supplied with propertyType the Element returned will be the property
* documentation
* @param propertyType (optional) must be supplied with property, the property's type
* @return xsd:annotation Element representing the documentation for the class/property
*/
def Element getDocAnnotation(Document document, ResourceBundle doc, String className, String property,
String propertyType) {
try {
Class clazz = Class.forName(className)
Element annotation = document.createElement("xsd:annotation")
Element documentation = document.createElement("xsd:documentation")
documentation.setAttribute("source", clazz.getName())
documentation.setAttribute("xml:lang", "en")
String content = "documentation not available"
if (property == null || propertyType == null) {
if (doc.containsKey(clazz.getName())) {
content = "Backing Class: " + className + "\n\n" + doc.getString(clazz.getName())
}
} else {
int begin = 0
int end = propertyType.length()
if (propertyType.lastIndexOf('.') != -1) {
begin = propertyType.lastIndexOf('.') + 1
}
if (propertyType.indexOf('<') != -1) {
end = propertyType.indexOf('<')
}
String key = clazz.getName() + "|" + property + "|" + propertyType.substring(begin, end)
if (doc.containsKey(key)) {
content = doc.getString(key)
} else {
//find the documentation content for this property on a parent class
boolean foundContent = false
while (!clazz.equals(Object.class) && !foundContent) {
for (Class currentInterface : clazz.getInterfaces()) {
if (currentInterface.getName().startsWith("org.kuali")) {
key = currentInterface.getName() + "|" + property + "|" + propertyType.substring(begin,
end)
foundContent = doc.containsKey(key) && StringUtils.isNotBlank(doc.getString(key))
}
}
if (foundContent) {
break
}
key = clazz.getName() + "|" + property + "|" + propertyType.substring(begin, end)
foundContent = doc.containsKey(key) && StringUtils.isNotBlank(doc.getString(key))
clazz = clazz.getSuperclass()
}
if (foundContent) {
content = doc.getString(key)
}
}
}
content = content.replaceAll("\\", "\n-")
content = content.replaceAll("\\<\\\\ol\\>", "\n")
content = content.replaceAll("\\<\\\\ul\\>", "\n")
content = content.replaceAll("\\<.*?\\>", "")
content = content.replaceAll("\\{\\@code\\s(.*?)\\}", '$1')
content = content.replaceAll("\\{\\@link\\s(.*?)\\}", '$1')
content = content.replaceAll("\\{\\@see\\s(.*?)\\}", '$1')
CDATASection cdata = document.createCDATASection(content)
documentation.appendChild(cdata)
//append doc
annotation.appendChild(documentation)
return annotation
} catch (Exception e) {
throw new RuntimeException("class not found ", e)
}
}
/**
* For list bean types build an extension based on the generic type.
*
* @param document the document
* @param aInfo attribute info for this element
* @param attrType the xsd attribute type for this element
* @return element with a type extension
*/
def Element getListOrSetExtension(Document document, BeanTagAttributeInfo aInfo, String attrType) {
String baseType = null
if (aInfo.getGenericType() instanceof ParameterizedType) {
Type genericParm = ((ParameterizedType) aInfo.getGenericType()).getActualTypeArguments()[0]
if (genericParm instanceof Class>) {
Class> parmClass = (Class>) genericParm
buildClassChoiceBaseType(document, parmClass)
baseType = parmClass.getName()
}
}
if (StringUtils.isBlank(baseType)) {
return null
}
Element complexType = document.createElement("xsd:complexType")
Element simpleContent = document.createElement("xsd:complexContent")
Element extension = document.createElement("xsd:extension")
extension.setAttribute("base", baseType)
Element mergeAttribute = document.createElement("xsd:attribute")
mergeAttribute.setAttribute("name", "merge")
mergeAttribute.setAttribute("type", "xsd:boolean")
extension.appendChild(mergeAttribute)
simpleContent.appendChild(extension)
complexType.appendChild(simpleContent)
return complexType
}
def boolean isKradSchema() {
return KRAD_SCHEMA.equals(this.schemaName)
}
def class OtherSchemaTags {
String schemaName
Map> nameTagMap
public OtherSchemaTags(schemaName, Map> nameTagMap) {
this.schemaName = schemaName
this.nameTagMap = nameTagMap
}
String getSchemaName() {
return schemaName
}
void setSchemaName(String schemaName) {
this.schemaName = schemaName
}
Map> getNameTagMap() {
return nameTagMap
}
void setNameTagMap(Map> nameTagMap) {
this.nameTagMap = nameTagMap
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy