/******************************************************************************* * Copyright (c) 2008 Ralf Ebert * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Ralf Ebert - initial API and implementation *******************************************************************************/ package com.swtxml.tinydom; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Stack; import org.apache.commons.lang.StringUtils; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.swtxml.definition.IAttributeDefinition; import com.swtxml.definition.INamespaceDefinition; import com.swtxml.definition.INamespaceResolver; import com.swtxml.definition.ITagDefinition; import com.swtxml.definition.ITagScope; import com.swtxml.util.lang.CollectionUtils; import com.swtxml.util.parser.ParseException; public class TinyDomSaxHandler extends DefaultHandler { private final String xmlFilename; private Locator locator; private Tag root; private final INamespaceResolver namespaceResolver; private final Map<String, INamespaceDefinition> namespaces = new HashMap<String, INamespaceDefinition>(); TinyDomSaxHandler(INamespaceResolver namespaceResolver, String xmlFilename) { this.namespaceResolver = namespaceResolver; this.xmlFilename = xmlFilename; } private Stack<Tag> parserStack = new Stack<Tag>(); @Override public void startElement(String namespace, String localName, String qName, Attributes attributes) throws SAXException { INamespaceDefinition namespaceDefinition = getNamespace(namespace); ITagDefinition tagDefinition = namespaceDefinition.getTag(localName); if (tagDefinition == null) { throw new ParseException("Unknown tag \"" + localName + "\" for namespace \"" + namespace + "\", allowed are: " + CollectionUtils.sortedToString(namespaceDefinition.getTagNames())); } Map<INamespaceDefinition, Map<IAttributeDefinition, String>> attributeMap = processAttributes( namespaceDefinition, tagDefinition, attributes); Tag tag = new Tag(namespaceDefinition, tagDefinition, attributeMap, parserStack.isEmpty() ? null : parserStack.peek(), getLocationInfo()); ITagDefinition parentTag = tag.getParent() != null ? tag.getParent().getTagDefinition() : ITagDefinition.ROOT; if (tagDefinition instanceof ITagScope && !((ITagScope) tagDefinition).isAllowedIn(parentTag)) { throw new ParseException("Tag " + tag.getName() + " is not allowed in " + parentTag.getName()); } if (root == null) { this.root = tag; } parserStack.push(tag); } private Map<INamespaceDefinition, Map<IAttributeDefinition, String>> processAttributes( INamespaceDefinition tagNamespace, ITagDefinition tagDefinition, Attributes attributes) { Map<INamespaceDefinition, Map<IAttributeDefinition, String>> attributeNsMap = new HashMap<INamespaceDefinition, Map<IAttributeDefinition, String>>(); for (int i = 0; i < attributes.getLength(); i++) { String uri = attributes.getURI(i); INamespaceDefinition attributeNamespace = !StringUtils.isEmpty(uri) ? getNamespace(uri) : tagNamespace; Map<IAttributeDefinition, String> attributeMap = attributeNsMap.get(attributeNamespace); if (attributeMap == null) { attributeMap = new HashMap<IAttributeDefinition, String>(); attributeNsMap.put(attributeNamespace, attributeMap); } String name = attributes.getLocalName(i); String value = attributes.getValue(i); IAttributeDefinition attributeDefinition; if (attributeNamespace.equals(tagNamespace)) { attributeDefinition = tagDefinition.getAttribute(name); } else { attributeDefinition = attributeNamespace.getForeignAttribute(name); if (attributeDefinition instanceof ITagScope && !((ITagScope) attributeDefinition).isAllowedIn(tagDefinition)) { throw new ParseException("Attribute " + attributes.getQName(i) + " is not allowed for tag \"" + tagDefinition.getName() + "\""); } } if (attributeDefinition == null) { throw new ParseException("Unknown attribute \"" + attributes.getQName(i) + "\" for tag \"" + tagDefinition.getName() + "\" (available are: " + CollectionUtils.sortedToString(tagDefinition.getAttributeNames()) + ")"); } attributeMap.put(attributeDefinition, value); } if (attributeNsMap.isEmpty()) { return Collections.emptyMap(); } return attributeNsMap; } private INamespaceDefinition getNamespace(String namespaceUri) { INamespaceDefinition namespace = namespaces.get(namespaceUri); if (namespace == null) { namespace = namespaceResolver.resolveNamespace(namespaceUri); if (namespace == null) { throw new ParseException("Unknown namespace: " + namespaceUri); } namespaces.put(namespaceUri, namespace); } return namespace; } public String getLocationInfo() { return xmlFilename + ((locator != null) ? " [line " + locator.getLineNumber() + "] " : ""); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { parserStack.pop(); } @Override public void setDocumentLocator(Locator locator) { this.locator = locator; } public Tag getRoot() { return root; } }