/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.extension.definition; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.StringWriter; import java.util.Collection; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.designer.extension.ExtensionConstants; import org.teiid.designer.extension.properties.ModelExtensionPropertyDefinition; import org.teiid.designer.extension.properties.Translation; import org.w3c.dom.Attr; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; /** * * * @since 8.0 */ public class ModelExtensionDefinitionWriter { private static final String NS_MED_COLON = ExtensionConstants.Namespaces.NS_MED + ":"; //$NON-NLS-1$ /** * Create a Model Extension Definition template, based on the modelExtension.xsd. * * @param med the model extension definition being written (never <code>null</code>) * @return the stream where the definition was written (never <code>null</code>) * @throws IllegalStateException if the definition file is <code>null</code> or if there is a problem creating the stream */ public InputStream writeAsStream( ModelExtensionDefinition med ) throws IllegalStateException { InputStream inputStream = null; try { // Create a temp file for the new mxd File tempFile = File.createTempFile("MxdTemp", ExtensionConstants.DOT_MED_EXTENSION); //$NON-NLS-1$ FileOutputStream tempOutputStream = new FileOutputStream(tempFile); StreamResult streamResult = new StreamResult(tempOutputStream); transform(med, streamResult); inputStream = new FileInputStream(tempFile); } catch (Exception e) { IllegalStateException error = null; if (e instanceof IllegalStateException) { error = (IllegalStateException)e; } else { error = new IllegalStateException(e); } throw error; } return inputStream; } /** * Create a text representation of the specified model extension definition suitable to be saved in a *.mxd file. * * @param med the model extension definition being written (never <code>null</code>) * @return a textual representation of the definition (never <code>null</code>) * @throws IllegalStateException if the definition file is <code>null</code> or if there is a problem parsing the file */ public String writeAsText( ModelExtensionDefinition med ) throws IllegalStateException { StringWriter stringWriter = new StringWriter(); Result streamResult = new StreamResult(stringWriter); transform(med, streamResult); return stringWriter.getBuffer().toString(); } /** * Transforms the Model Extension Definition into the specified XML result. * * @param med the model extension definition being transformed (never <code>null</code>) * @throws Exception if the definition file is <code>null</code> or if there is a problem parsing the file */ private void transform( ModelExtensionDefinition med, Result result ) throws IllegalStateException { CoreArgCheck.isNotNull(med, "med is null"); //$NON-NLS-1$ CoreArgCheck.isNotNull(result, "result is null"); //$NON-NLS-1$ try { // Create a copy ... // NOTE: THIS WORKS IF WE USE THE org.apache.xerces LIBRARY. // The problem is that these other implementations don't write out the // namespace declarations for the imported nodes DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setValidating(false); documentBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", //$NON-NLS-1$ "http://www.w3.org/2001/XMLSchema"); //$NON-NLS-1$ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); DOMImplementation domImpl = documentBuilder.getDOMImplementation(); Document document = domImpl.createDocument(ExtensionConstants.Namespaces.NS_MED_VALUE, ExtensionConstants.Elements.MODEL_EXTENSION, null); // -------------------------------------------------------------- // Get the root Element. // - Set Attributes and add the Description // -------------------------------------------------------------- Element modelExtensionElem = document.getDocumentElement(); setModelExtensionElementAttributes(document, modelExtensionElem, med); // ------------------------------------------------ // Create Element for each extended metaclass name // ------------------------------------------------ String[] extendedMetaclassNames = med.getExtendedMetaclasses(); Element[] extendedMetaclassElems = createExtendedMetaclassElements(document, modelExtensionElem, extendedMetaclassNames); // ---------------------------------------------------------------- // Iterate Extended Metaclass Elements, adding properties for each // ---------------------------------------------------------------- for (int i = 0; i < extendedMetaclassNames.length; i++) { String metaclassName = extendedMetaclassNames[i]; Element metaclassElem = extendedMetaclassElems[i]; // Create the property elements for this metaclass Collection<ModelExtensionPropertyDefinition> properties = med.getPropertyDefinitions(metaclassName); createProperyElements(document, metaclassElem, properties); } // Output the document DOMSource domSource = new DOMSource(document); TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = tFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$ transformer.transform(domSource, result); } catch (Exception e) { IllegalStateException error = null; if (e instanceof IllegalStateException) { error = (IllegalStateException)e; } else { error = new IllegalStateException(e); } throw error; } } /** * Set the Attributes and create the description on the ModelExtensionElement * * @param document the document being worked on * @param extensionElement the model extension element * @param med the med to use in configuring the extension element */ private void setModelExtensionElementAttributes( Document document, Element modelExtensionElem, ModelExtensionDefinition med ) { modelExtensionElem.setAttributeNS(ExtensionConstants.Namespaces.NS_KEY, "xmlns:" + ExtensionConstants.Namespaces.NS_XSI, ExtensionConstants.Namespaces.NS_XSI_VALUE); //$NON-NLS-1$ modelExtensionElem.setAttributeNS(ExtensionConstants.Namespaces.NS_KEY, "xmlns:" + ExtensionConstants.Namespaces.NS_MED, ExtensionConstants.Namespaces.NS_MED_VALUE); //$NON-NLS-1$ // ----------------------------------------- // modelExtensionElement - Attributes // ----------------------------------------- Attr attr = document.createAttribute(ExtensionConstants.Namespaces.NS_SCHEMALOC); attr.setValue(ExtensionConstants.Namespaces.NS_SCHEMALOC_VALUE); modelExtensionElem.setAttributeNode(attr); // Metamodel URI if (!CoreStringUtil.isEmpty(med.getMetamodelUri())) { attr = document.createAttribute(ExtensionConstants.Attributes.METAMODEL_URI); attr.setValue(med.getMetamodelUri()); modelExtensionElem.setAttributeNode(attr); } // Namespace URI if (!CoreStringUtil.isEmpty(med.getNamespaceUri())) { attr = document.createAttribute(ExtensionConstants.Attributes.NAMESPACE_URI); attr.setValue(med.getNamespaceUri()); modelExtensionElem.setAttributeNode(attr); } // Namespace Prefix if (!CoreStringUtil.isEmpty(med.getNamespacePrefix())) { attr = document.createAttribute(ExtensionConstants.Attributes.NAMESPACE_PREFIX); attr.setValue(med.getNamespacePrefix()); modelExtensionElem.setAttributeNode(attr); } // Version attr = document.createAttribute(ExtensionConstants.Attributes.VERSION); attr.setValue(String.valueOf(med.getVersion())); modelExtensionElem.setAttributeNode(attr); // ----------------------------------------- // Description - child element // ----------------------------------------- String medDescription = med.getDescription(); if (medDescription != null && !medDescription.isEmpty()) { Element descriptionElem = document.createElement(NS_MED_COLON + ExtensionConstants.Elements.DESCRIPTION); modelExtensionElem.appendChild(descriptionElem); Text descriptionText = document.createTextNode(med.getDescription()); descriptionElem.appendChild(descriptionText); } // ----------------------------------------- // Model Types - zero or more child elements // ----------------------------------------- for (String modelType : med.getSupportedModelTypes()) { Element modelTypeElem = document.createElement(NS_MED_COLON + ExtensionConstants.Elements.MODEL_TYPE); modelExtensionElem.appendChild(modelTypeElem); Text modelTypeText = document.createTextNode(modelType); modelTypeElem.appendChild(modelTypeText); } } /** * Create Elements for each extended Metaclass and add to the root modelExtensionElement * * @param document the document being worked on * @param rootElem the root element (modelExtensionElement) * @param extendedMetaclassnames the list of metaclass names being extended * @return the list of elements created, which correspond to the supplied names */ private Element[] createExtendedMetaclassElements( Document document, Element rootElem, String[] extendedMetaclassNames ) { Element[] extendedMetaclassElems = new Element[extendedMetaclassNames.length]; Attr attr = document.createAttribute(ExtensionConstants.Namespaces.NS_SCHEMALOC); // For each extended metaclass Name, create an element and append it to the root. for (int i = 0; i < extendedMetaclassNames.length; i++) { String extendedMetaclassName = extendedMetaclassNames[i]; // Create the Element Element extendedMetaclassElem = document.createElement(NS_MED_COLON + ExtensionConstants.Elements.EXTENDED_METACLASS); // Append element to the document root rootElem.appendChild(extendedMetaclassElem); // Set the Name attribute attr = document.createAttribute(ExtensionConstants.Attributes.NAME); attr.setValue(extendedMetaclassName); extendedMetaclassElem.setAttributeNode(attr); // Set element on the returned element array extendedMetaclassElems[i] = extendedMetaclassElem; } return extendedMetaclassElems; } /** * Create the property Elements for the supplied metaclass Element, setting its attributes from the supplied collection of * ModelExtensionPropertyDefinitions * * @param document the document being worked on * @param metaclassElem the metaclass element * @param properties the collection of property defns used to create property elems */ private void createProperyElements( Document document, Element metaclassElem, Collection<ModelExtensionPropertyDefinition> properties ) { // -------------------------------------------------------------------- // Property Elements // Iterate over the collection of ModelExtensionPropertyDefinitions. // -------------------------------------------------------------------- for (ModelExtensionPropertyDefinition propDefn : properties) { Attr attr = document.createAttribute(ExtensionConstants.Namespaces.NS_SCHEMALOC); // -------------------------------------------------------- // Create Property Element and append to Metaclass Element // -------------------------------------------------------- Element propertyElem = document.createElement(NS_MED_COLON + ExtensionConstants.Elements.PROPERTY); metaclassElem.appendChild(propertyElem); // -------------------------------------- // Set the Property Element Attributes // -------------------------------------- // Simple ID Attribute if (!CoreStringUtil.isEmpty(propDefn.getSimpleId())) { attr = document.createAttribute(ExtensionConstants.Attributes.NAME); attr.setValue(propDefn.getSimpleId()); propertyElem.setAttributeNode(attr); } // Type Attribute if (!CoreStringUtil.isEmpty(propDefn.getRuntimeType())) { attr = document.createAttribute(ExtensionConstants.Attributes.TYPE); attr.setValue(propDefn.getRuntimeType()); propertyElem.setAttributeNode(attr); } // Default Value Attribute if (!CoreStringUtil.isEmpty(propDefn.getDefaultValue())) { attr = document.createAttribute(ExtensionConstants.Attributes.DEFAULT_VALUE); attr.setValue(propDefn.getDefaultValue()); propertyElem.setAttributeNode(attr); } // Fixed Value Attribute if (!CoreStringUtil.isEmpty(propDefn.getFixedValue())) { attr = document.createAttribute(ExtensionConstants.Attributes.FIXED_VALUE); attr.setValue(propDefn.getFixedValue()); propertyElem.setAttributeNode(attr); } // Required Attribute boolean isRequired = propDefn.isRequired(); attr = document.createAttribute(ExtensionConstants.Attributes.REQUIRED); attr.setValue(Boolean.toString(isRequired)); propertyElem.setAttributeNode(attr); // Advanced Attribute boolean isAdvanced = propDefn.isAdvanced(); attr = document.createAttribute(ExtensionConstants.Attributes.ADVANCED); attr.setValue(Boolean.toString(isAdvanced)); propertyElem.setAttributeNode(attr); // Masked Attribute boolean isMasked = propDefn.isMasked(); attr = document.createAttribute(ExtensionConstants.Attributes.MASKED); attr.setValue(Boolean.toString(isMasked)); propertyElem.setAttributeNode(attr); // Indexed Attribute boolean isIndexed = propDefn.shouldBeIndexed(); attr = document.createAttribute(ExtensionConstants.Attributes.INDEX); attr.setValue(Boolean.toString(isIndexed)); propertyElem.setAttributeNode(attr); // ---------------------------------------------- // Create the Property Element child elements - Description, Display, Allowed Values must be processed in that order // since the ordering is enforced by the schema. // ---------------------------------------------- // ------------------------------- // Description Elements // - can be multiple locales // ------------------------------- Collection<Translation> descriptions = propDefn.getDescriptions(); for (Translation descriptionTranslation : descriptions) { String descriptionLocale = descriptionTranslation.getLocale().toString(); String descriptionName = descriptionTranslation.getTranslation(); if (descriptionLocale != null) { Element descrElement = document.createElement(NS_MED_COLON + ExtensionConstants.Elements.DESCRIPTION); // Set the description text on the element. Text descriptionText = document.createTextNode(descriptionName); descrElement.appendChild(descriptionText); // Set the locale attribute on the description element attr = document.createAttribute(ExtensionConstants.Attributes.LOCALE); attr.setValue(descriptionLocale); descrElement.setAttributeNode(attr); // append description element child to the property element propertyElem.appendChild(descrElement); } } // ------------------------------- // Display Name Elements // - can be multiple locales // ------------------------------- Collection<Translation> displayNames = propDefn.getDisplayNames(); for (Translation displayTranslation : displayNames) { String dnLocale = displayTranslation.getLocale().toString(); String dnName = displayTranslation.getTranslation(); if (dnLocale != null) { Element displayElement = document.createElement(NS_MED_COLON + ExtensionConstants.Elements.DISPLAY); // Set the displayName text on the element. Text displayText = document.createTextNode(dnName); displayElement.appendChild(displayText); // Set the locale attribute on the display element attr = document.createAttribute(ExtensionConstants.Attributes.LOCALE); attr.setValue(dnLocale); displayElement.setAttributeNode(attr); // append display element child to the property element propertyElem.appendChild(displayElement); } } // ------------------------------- // Allowable Value elements // ------------------------------- String[] allowedValues = propDefn.getAllowedValues(); for (int i = 0; i < allowedValues.length; i++) { String allowedValueStr = allowedValues[i]; Element allowedValueElement = document.createElement(NS_MED_COLON + ExtensionConstants.Elements.ALLOWED_VALUE); // Set the allowedValue text on the element. Append the element to the property element. Text elemText = document.createTextNode(allowedValueStr); allowedValueElement.appendChild(elemText); // append allowedValue element child to the property element propertyElem.appendChild(allowedValueElement); } } } }