/* * JBoss, Home of Professional Open Source * Copyright 2006, JBoss Inc., and others contributors as indicated * by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2005-2006, JBoss Inc. */ package org.jboss.tools.smooks.templating.model; import java.util.Properties; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.milyn.xml.DomUtils; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; /** * Abstract message model builder. * * @author <a href="mailto:tom.fennelly@jboss.com">tom.fennelly@jboss.com</a> */ public abstract class ModelBuilder { public static final String NAMESPACE = "http://www.jboss.org/xsd/tools/smooks"; // TODO: Make same as JBT eclipse extension namespace ?? //$NON-NLS-1$ public static final String REQUIRED = "#required"; //$NON-NLS-1$ public static final String OPTIONAL = "#optional"; //$NON-NLS-1$ private static final String HIDDEN_ELEMENT = "hidden"; //$NON-NLS-1$ public static enum ElementType { simple, complex } private Properties namespaces = new Properties(); /** * Build the message model. * @return The message model. * @throws ModelBuilderException Error building model. */ public abstract Document buildModel() throws ModelBuilderException; /** * Get the namespace prefix-to-URI mappings for this model. * @return The namespace prefix-to-URI mappings for this model. */ public Properties getNamespaces() { return namespaces; } /** * Mark a fragment as being hidden. * <p/> * When hidden, it is illegal to attempt a mapping onto a fragment. * * @param fragment The fragment to be marked as hidden. */ public static void hideFragment(Element fragment) { fragment.setAttributeNS(NAMESPACE, HIDDEN_ELEMENT, "true"); //$NON-NLS-1$ } /** * Unmark a fragment as being hidden. * <p/> * When hidden, it is illegal to attempt a mapping onto a fragment. * * @param fragment The fragment to be unmarked as hidden. */ public static void unhideFragment(Element fragment) { fragment.removeAttributeNS(NAMESPACE, HIDDEN_ELEMENT); } /** * Is the specified node marked as being hidden. * <p/> * When hidden, it is illegal to attempt a mapping onto a fragment. * * @return True if the node is hidden, otherwise false. */ public static boolean isHidden(Node node) { if(node != null) { switch(node.getNodeType()) { case Node.ATTRIBUTE_NODE : return isHidden(node.getParentNode()); case Node.ELEMENT_NODE : if(((Element)node).getAttributeNS(NAMESPACE, HIDDEN_ELEMENT).equals("true")) { //$NON-NLS-1$ return true; } return isHidden(node.getParentNode()); } } return false; } /** * Is the specified node required. * <p/> * For an Element node, this means does it have a minOccurs > 0. For an Attr node, * it means is its value set to "#REQUIRED". * * @return True if the node is hidden, otherwise false. */ public static boolean isRequired(Node node) { if(node != null) { switch(node.getNodeType()) { case Node.ATTRIBUTE_NODE : return (REQUIRED.equals(node.getTextContent()) || XMLConstants.XML_NS_URI.equals(node.getNamespaceURI())); case Node.ELEMENT_NODE : if(getMinOccurs((Element) node) > 0) { return true; } } } return false; } /** * Is the specified node in the reserved namespace. * * @return True if the node is in the reserved namespace, otherwise false. */ public static boolean isInReservedNamespace(Node node) { if(node != null) { return NAMESPACE.equals(node.getNamespaceURI()); } return false; } protected Document createModelInstance() throws ModelBuilderException { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = builderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw new ModelBuilderException("Error constructing DOM DocumentBuilder.", e); //$NON-NLS-1$ } return builder.newDocument(); } public static void setMinMax(Element element, int minOccurs, int maxOccurs) { element.setAttributeNS(NAMESPACE, "smk:minOccurs", Integer.toString(minOccurs)); //$NON-NLS-1$ element.setAttributeNS(NAMESPACE, "smk:maxOccurs", Integer.toString(maxOccurs)); //$NON-NLS-1$ } public static Node getParentNode(Node node) { if(node.getNodeType() == Node.ATTRIBUTE_NODE) { return ((Attr)node).getOwnerElement(); } else { return node.getParentNode(); } } public static int getMinOccurs(Element element) { String minOccurs = element.getAttributeNS(NAMESPACE, "minOccurs"); //$NON-NLS-1$ if(minOccurs.equals("")) { //$NON-NLS-1$ return 1; } return Integer.parseInt(minOccurs); } public static int getMaxOccurs(Element element) { String maxOccurs = element.getAttributeNS(NAMESPACE, "maxOccurs"); //$NON-NLS-1$ if(maxOccurs.equals("")) { //$NON-NLS-1$ return 1; } return Integer.parseInt(maxOccurs); } public static boolean isCollection(Element element) { if(element == null) { return false; } int maxOccurs = getMaxOccurs(element); return (maxOccurs > 1 || maxOccurs == -1); } public static void setElementType(Element element, ElementType type) { element.setAttributeNS(NAMESPACE, "smk:elementType", type.toString()); //$NON-NLS-1$ } public static ElementType getElementType(Element element) { String elementType = element.getAttributeNS(NAMESPACE, "elementType"); //$NON-NLS-1$ if(elementType == null || elementType.length() == 0) { return null; } return ElementType.valueOf(elementType); } /** * Create a compositor Element. * @param document Owner Document node. * @return The compositor Element. */ public static Element createCompositor(Document document) { return document.createElementNS(NAMESPACE, "smk:compositor"); //$NON-NLS-1$ } /** * Is the supplied DOM node the compositor Element. * @param node The DOM node to test. * @return True if the node is the compositor element, otherwise false. */ public static boolean isCompositor(Node node) { if(node.getNodeType() == Node.ELEMENT_NODE) { if(DomUtils.getName((Element)node).equals("compositor")) { //$NON-NLS-1$ if(NAMESPACE.equals(node.getNamespaceURI())) { return true; } } } return false; } /** * Turn on/off enforcement of collection sub-mapping rules. * <p/> * If turned on, the collection mapping must be made on the model collection element (having maxOccurs > 1) * before mappings can be made on sub elements. * * @param element The model element. * @param enforce True if enforcement is to be turned on, otherwise false. */ public static void setEnforceCollectionSubMappingRules(Element element, boolean enforce) { element.setAttributeNS(NAMESPACE, "smk:enforceCollectionSubMappingRules", Boolean.toString(enforce)); //$NON-NLS-1$ } /** * Is collection sub-mapping rules turned on for the supplied model element. * @param element The model element. * @return True if enforcement is turned on, otherwise false. * @see #setEnforceCollectionSubMappingRules(Element, boolean) */ public static boolean getEnforceCollectionSubMappingRules(Element element) { String enforce = element.getAttributeNS(NAMESPACE, "enforceCollectionSubMappingRules"); //$NON-NLS-1$ if(enforce == null || enforce.length() == 0) { return true; } return !enforce.equals("false"); //$NON-NLS-1$ } /** * Does the model element contain child elements. * @param element The element to test. * @return true if the element contains child elements, otherwise false. */ public static boolean hasChildElements(Element element) { NodeList childNodes = element.getChildNodes(); int numChildren = childNodes.getLength(); for(int i = 0; i < numChildren; i++) { if(childNodes.item(i).getNodeType() == Node.ELEMENT_NODE) { return true; } } return false; } /** * Does the model element have child content of any kind. * <p/> * Does it have child elements or attributes. * * @param element The element to test. * @return true if the element contains child content of any kind, otherwise false. */ public static boolean hasChildContent(Element element) { return (element.hasAttributes() || hasChildElements(element)); } /** * Set the model as being strict, or not. * <p/> * A strict model should have cardinality info attached e.g. minOccurs/maxOccurs, required etc. * @param model The model Document node. * @param strict True if the model is strict, otherwise false. */ public static void setStrictModel(Document model, boolean strict) { Element documentElement = model.getDocumentElement(); if(documentElement == null) { throw new IllegalStateException("Call to 'markStrictModel' before the model's root element has been added."); //$NON-NLS-1$ } documentElement.setAttributeNS(NAMESPACE, "smk:strict", Boolean.toString(strict)); //$NON-NLS-1$ } /** * Is the model strict, or not. * <p/> * A strict model should have cardinality info attached e.g. minOccurs/maxOccurs, required etc. * @param model The model Document node. * @return True if the model is strict, otherwise false. */ public static boolean isStrictModel(Document model) { Element documentElement = model.getDocumentElement(); if(documentElement == null) { throw new IllegalStateException("Call to 'isStrictModel' before the model's root element has been added."); //$NON-NLS-1$ } String strict = documentElement.getAttributeNS(NAMESPACE, "strict"); //$NON-NLS-1$ return Boolean.parseBoolean(strict); } }