/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package org.codice.ddf.transformer.xml.streaming.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.codice.ddf.transformer.xml.streaming.AbstractSaxEventHandler; import org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerUtils; import org.xml.sax.Attributes; import ddf.catalog.data.Attribute; import ddf.catalog.data.AttributeDescriptor; import ddf.catalog.data.Metacard; import ddf.catalog.data.impl.AttributeImpl; import ddf.catalog.data.impl.types.ContactAttributes; import ddf.catalog.data.impl.types.CoreAttributes; /** * A sax event handler used to parse urn:catalog:metacard {@link Metacard}s. By default, handles all elements defined in the {@link XmlSaxEventHandlerImpl#xmlToMetacard} * These defaults can be overridden by passing a different {@link Map} in {@link XmlSaxEventHandlerImpl#setXmlToMetacard(Map)} * {@inheritDoc} */ public class XmlSaxEventHandlerImpl extends AbstractSaxEventHandler { private static Set<AttributeDescriptor> attributeDescriptors = new HashSet<>(); /* * A list of Attributes that is populated during parsing and then returned by getAttributes */ private List<Attribute> attributes; /* * The localName of the XML element that is currently being read */ private String elementBeingRead; /* * A StringBuilder that holds the text value of an XML element, e.g. "foo" in <bar> foo </bar> */ private StringBuilder textDataOfElement; /* * A mapping of XML Element names to Metacard Attribute names */ private Map<String, String> xmlToMetacard; private SaxEventHandlerUtils saxEventHandlerUtils = new SaxEventHandlerUtils(); static { CoreAttributes coreAttributes = new CoreAttributes(); attributeDescriptors.add(coreAttributes.getAttributeDescriptor(Metacard.ID)); attributeDescriptors.add(coreAttributes.getAttributeDescriptor(Metacard.TITLE)); attributeDescriptors.add(coreAttributes.getAttributeDescriptor(Metacard.METADATA)); attributeDescriptors.add(coreAttributes.getAttributeDescriptor(Metacard.DESCRIPTION)); attributeDescriptors.add(new ContactAttributes().getAttributeDescriptor(Metacard.POINT_OF_CONTACT)); } @Override public List<Attribute> getAttributes() { return saxEventHandlerUtils.getCombinedMultiValuedAttributes( getSupportedAttributeDescriptors(), attributes); } @Override public Set<AttributeDescriptor> getSupportedAttributeDescriptors() { return attributeDescriptors; } protected XmlSaxEventHandlerImpl() { xmlToMetacard = new HashMap<>(); /* * Map the XML element names to the metacard attributes */ xmlToMetacard.put(Metacard.TITLE, Metacard.TITLE); xmlToMetacard.put(Metacard.POINT_OF_CONTACT, Metacard.POINT_OF_CONTACT); xmlToMetacard.put(Metacard.DESCRIPTION, Metacard.DESCRIPTION); xmlToMetacard.put("source", Metacard.RESOURCE_URI); } protected XmlSaxEventHandlerImpl(Map<String, String> xmlToMetacardMap) { this.xmlToMetacard = xmlToMetacardMap; } @Override public void startDocument() { textDataOfElement = new StringBuilder(); attributes = new ArrayList<>(); } /** * Takes in a sax event from {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate}, and if it is * in the xmlToMetacardMapping, begin reading it. * * @param uri the URI that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} * @param localName the localName that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} * @param qName the qName that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} * @param attributes the attributes that are passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { /* * If the element name is in the xmlToMetacardMapping, begin elementBeingRead. The data will be in the characters event. */ if (xmlToMetacard.get(localName.toLowerCase()) != null) { elementBeingRead = localName.toLowerCase(); return; } /* * If the attribute "name" is in the xmlToMetacardMapping, begin elementBeingRead. The data will be in the characters event. */ String attribute = attributes.getValue("name"); if (attribute != null && xmlToMetacard.get(attribute) != null) { elementBeingRead = attribute; } } /** * Takes in a sax event from {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate}. If the element is in the xmlToElementMapping, * add it to the attributes list * * @param namespaceURI the namespaceURI that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} * @param localName the localName that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} * @param qName the qName that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} */ @Override public void endElement(String namespaceURI, String localName, String qName) { /* * If the handler is currently "reading" an element this implies the element is in the xmlToElementMapping, add the value of the * textDataOfElement to the attributes list */ if (elementBeingRead != null) { String result = textDataOfElement.toString() .trim(); textDataOfElement.setLength(0); attributes.add(new AttributeImpl(xmlToMetacard.get(elementBeingRead), result)); elementBeingRead = null; } } /** * Takes in a sax event from {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} and adds the characters * to textDataOfElement * * @param ch the ch that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} * @param start the start that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} * @param length the length that is passed in by {@link org.codice.ddf.transformer.xml.streaming.lib.SaxEventHandlerDelegate} */ @Override public void characters(char[] ch, int start, int length) { /* * If the handler is currently "reading" an element, read the characters into the textDataOfElement Stringbuilder */ if (elementBeingRead != null) { textDataOfElement.append(new String(ch, start, length)); } } }