diff options
Diffstat (limited to 'src/com/memberwebs/ldapxml/map/LXSAXHandler.java')
-rw-r--r-- | src/com/memberwebs/ldapxml/map/LXSAXHandler.java | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/src/com/memberwebs/ldapxml/map/LXSAXHandler.java b/src/com/memberwebs/ldapxml/map/LXSAXHandler.java new file mode 100644 index 0000000..d81c240 --- /dev/null +++ b/src/com/memberwebs/ldapxml/map/LXSAXHandler.java @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2004, Nate Nielsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Nate Nielsen <nielsen@memberwebs.com> + * + */ + +package com.memberwebs.ldapxml.map; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + + +/** + * Parses an XML file into an in memory LX map representation. + * + * @author nielsen@memberwebs.com + * @version 0.5 + */ +class LXSAXHandler + extends DefaultHandler +{ + private static final String EL_ROOT = "lxmap"; + private static final String EL_ENTRY = "entry"; + private static final String EL_CLASS = "class"; + private static final String EL_ELEMENT = "element"; + private static final String EL_ATTRIBUTE = "attribute"; + private static final String EL_RENAME = "rename"; + private static final String EL_IGNORE = "ignore"; + private static final String AT_NAME = "name"; + private static final String AT_CLASS = "class"; + private static final String AT_MODE = "mode"; + private static final String AT_NAMESPACE = "namespace"; + private static final String AT_NS = "ns"; + private static final String AT_PLACE = "place"; + private static final String AT_HOOK = "hook"; + private static final String AT_ROOT = "root"; + private static final String VAL_INCLUSIVE = "inclusive"; + private static final String VAL_EXCLUSIVE = "exclusive"; + private static final String VAL_INLINE = "inline"; + private static final String VAL_BLOCK = "block"; + private static final String AT_VERSION = "version"; + private static final String VAL_VERSION = "1.0"; + + private static final String INTERNAL_ERROR = "LX Map parser invalid internal state"; + + // The root of the map being built + private LXMap m_map; + + // The current LX map object being acted on. + private LXBase m_cur; + + // Stack of LX objects + private Stack m_elStack; + + // public (XML) to private (LDAP) name mappings + private Hashtable m_nameMap; + + // All the LDAP attributes required for this map + private HashSet m_nameSet; + + /** + * Constructs a new LXSAXHandler object. + */ + public LXSAXHandler() + { + m_elStack = new Stack(); + m_nameMap = new Hashtable(); + m_nameSet = null; + } + + /** + * Called at the start of the XML document. + */ + public void startDocument() + throws SAXException + { + m_cur = null; + m_map = null; + m_nameMap.clear(); + m_nameSet = new HashSet(); + } + + /** + * Called at the end of the XML document. + */ + public void endDocument() + throws SAXException + { + // The current LX object should be null as we're back + // to the root. If not then something went wrong. + if(m_cur != null) + throw new SAXException(new LXMapException(INTERNAL_ERROR)); + } + + + /** + * Called when a new element is encountered in the XML file. + * + * @param uri The element namespace + * @param localName The plain element name. + * @param qName The fully quallified element name. + * @param attributes The element's attributes. + */ + public void startElement(String uri, String localName, String qName, + Attributes attributes) + throws SAXException + { + try + { + // Push the current LX object on a stack so it can + // be popped as we encounter the end of elements. + + // Since null doesn't work with the collection classes + // we use 'this' to represent it. (See endElement) + if(m_cur == null) + m_elStack.push(this); + else + m_elStack.push(m_cur); + + // Now depending on what kind of LX Object we have around + // we hand of parsing to different functions. + if(m_cur == null) + { + rootHandler(uri, localName, qName, attributes); + } + else if(m_cur instanceof LXMap) + { + entryHandler(uri, localName, qName, attributes); + } + else if(m_cur instanceof LXEntry) + { + if(!classHandler(uri, localName, qName, attributes)) + actionHandler(uri, localName, qName, attributes); + } + else if(m_cur instanceof LXClass) + { + if(!attributeHandler(uri, localName, qName, attributes)) + actionHandler(uri, localName, qName, attributes); + } + else if(m_cur instanceof LXAttribute) + { + actionHandler(uri, localName, qName, attributes); + } + else + throw new LXMapException(INTERNAL_ERROR); + + } + catch(LXMapException e) + { + throw new SAXException(e); + } + } + + /** + * Called when the end of an element is encountered. + * + * @param uri The namespace of this element. + * @param localName The plain name of this element. + * @param qName The fully qualified name of this element. + */ + public void endElement(String uri, String localName, String qName) + throws SAXException + { + // Add the current element to the naming stack + if(m_cur instanceof LXAttribute) + addName(m_cur); + + // Pop the current LX object off the stack + Object obj = m_elStack.pop(); + if(obj == null) + throw new SAXException(new LXMapException(INTERNAL_ERROR)); + + // If it's set to 'this' then it's actually + // 'null' (See startElement) + if(obj == this) + m_cur = null; + else + m_cur = (LXBase)obj; + } + + /** + * Handles elements at the root document level. + * + * @param uri The element namespace + * @param localname The plain element name. + * @param qName The fully quallified element name. + * @param attributes The element's attributes. + * @return Returns true if element was processed. + */ + private boolean rootHandler(String uri, String localName, String qName, + Attributes attributes) + throws LXMapException + { + if(qName.equals(EL_ROOT)) + { + if(!attributes.getValue(AT_VERSION).equals(VAL_VERSION)) + throw new LXMapException("Invalid LX Map version"); + + m_map = new LXMap(m_nameMap, m_nameSet); + m_cur = m_map; + + nameAttrHandler(attributes, AT_ROOT); + commonAttrHandler(attributes); + + + // Put some exceptions into the name map + m_nameMap.put("class", "objectClass"); + + String prefix = m_cur.getPrefix(); + if(prefix != null) + m_nameMap.put(prefix + ":class", "objectClass"); + + + return true; + } + + return false; + } + + /** + * Handles entry elements at the map root level. + * + * @param uri The element namespace + * @param localname The plain element name. + * @param qName The fully quallified element name. + * @param attributes The element's attributes. + * @return Returns true if element was processed. + */ + private boolean entryHandler(String uri, String localName, String qName, + Attributes attributes) + throws LXMapException + { + if(qName.equals(EL_ENTRY)) + { + LXEntry entry = new LXEntry(m_cur); + m_cur = entry; + nameAttrHandler(attributes, AT_CLASS); + commonAttrHandler(attributes); + return true; + } + + return false; + } + + /** + * Handles class elements at the map entry level. + * + * @param uri The element namespace + * @param localname The plain element name. + * @param qName The fully quallified element name. + * @param attributes The element's attributes. + * @return Returns true if element was processed. + */ + private boolean classHandler(String uri, String localName, String qName, + Attributes attributes) + throws LXMapException + { + if(qName.equals(EL_CLASS)) + { + LXClass cls = new LXClass(m_cur); + m_cur = cls; + nameAttrHandler(attributes, null); + commonAttrHandler(attributes); + classAttrHandler(attributes); + return true; + } + + return false; + } + + /** + * Handles attribute elements at the map class level. + * + * @param uri The element namespace + * @param localname The plain element name. + * @param qName The fully quallified element name. + * @param attributes The element's attributes. + * @return Returns true if element was processed. + */ + private boolean attributeHandler(String uri, String localName, String qName, + Attributes attributes) + throws LXMapException + { + if(qName.equals(EL_ATTRIBUTE)) + { + LXAttribute attr = new LXAttribute(m_cur, false); + m_cur = attr; + nameAttrHandler(attributes, null); + return true; + } + else if(qName.equals(EL_ELEMENT)) + { + LXAttribute attr = new LXAttribute(m_cur, true); + m_cur = attr; + nameAttrHandler(attributes, null); + return true; + } + + return false; + } + + /** + * Handles action elements at various levels. + * + * @param uri The element namespace + * @param localname The plain element name. + * @param qName The fully quallified element name. + * @param attributes The element's attributes. + * @return Returns true if element was processed. + */ + private boolean actionHandler(String uri, String localName, String qName, + Attributes attributes) + throws LXMapException + { + if(qName.equals(EL_IGNORE)) + { + m_cur.m_isUseable = false; + return true; + } + else if(qName.equals(EL_RENAME)) + { + m_cur.m_xmlName = attributes.getValue(AT_NAME); + + if(m_cur.m_xmlName == null) + throw new LXMapException("LX map element requires a '" + AT_NAME + "' attribute"); + } + + return false; + } + + /** + * Handles an element's name. + * + * @param attributes The element's attributes. + */ + private void nameAttrHandler(Attributes attributes, String attr) + throws LXMapException + { + if(attr == null) + attr = AT_NAME; + + m_cur.setName(attributes.getValue(attr)); + + if(m_cur.getName() == null) + throw new LXMapException("LX map element requires a '" + attr + "' attribute"); + } + + /** + * Handles an element's namespace and other basic info. + * + * @param attributes The element's attributes. + */ + private void commonAttrHandler(Attributes attributes) + throws LXMapException + { + m_cur.m_nameSpace = attributes.getValue(AT_NAMESPACE); + m_cur.m_ns = attributes.getValue(AT_NS); + m_cur.m_hook = attributes.getValue(AT_HOOK); + } + + /** + * Handles various attributes for class elements. + * + * @param attributes The element's attributes. + */ + private void classAttrHandler(Attributes attributes) + throws LXMapException + { + if(!(m_cur instanceof LXClass)) + throw new LXMapException(INTERNAL_ERROR); + + LXClass cls = (LXClass)m_cur; + + String inc = attributes.getValue(AT_MODE); + if(inc == null || inc.equals(VAL_EXCLUSIVE)) + { + cls.m_isInclusive = false; + } + else if(inc.equals(VAL_INCLUSIVE)) + { + // We don't know the list of names + m_nameSet = null; + cls.m_isInclusive = true; + } + else + throw new LXMapException("LX map invalid value for '" + AT_MODE + "' attribute"); + + String place = attributes.getValue(AT_PLACE); + if(place != null) + { + if(place.equals(VAL_INLINE)) + cls.m_isInline = true; + else if(place.equals(VAL_BLOCK)) + cls.m_isInline = false; + else + throw new LXMapException("LX map invalid value for '" + AT_PLACE + "' attribute"); + } + } + + /** + * Get the resulting LX Map. + * + * @return The map. + */ + public final LXMap getMap() + { + return m_map; + } + + /** + * Get public (XML) to private (LDAP) name mappings + * for the first entry. + * + * @return The name map. + */ + public final Map getNameMap() + { + return m_nameMap; + } + + /** + * Gets a listing of the minimum set of attributes required + * to satisfy this map, or null when not known + */ + public final Set getNameSet() + { + return m_nameSet; + } + + /** + * Add an LX object to the name map. This adds both + * the prefixed name as well as the local name. + * + * @param base The LX object whose name to add. + */ + private void addName(LXBase base) + { + String name = base.getName(); + if(base.m_xmlName != null && !m_nameMap.contains(base.m_xmlName)) + m_nameMap.put(base.m_xmlName, name); + + String xmlName = base.getXmlName(); + if(xmlName != null && !m_nameMap.contains(xmlName)) + m_nameMap.put(xmlName, name); + + if(m_nameSet != null) + m_nameSet.add(name); + } +} |