/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.nifi.processors.standard.util; import java.util.concurrent.atomic.AtomicInteger; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class XmlSplitterSaxParser extends DefaultHandler { private final XmlElementNotifier notifier; private final AtomicInteger splitDepth; private final StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); public Locator locator; private final AtomicInteger depth = new AtomicInteger(0); public XmlSplitterSaxParser(final XmlElementNotifier notifier) { this.notifier = notifier; this.splitDepth = new AtomicInteger(1); } public XmlSplitterSaxParser(final XmlElementNotifier notifier, int splitDepth) { this.notifier = notifier; this.splitDepth = new AtomicInteger(splitDepth); } @Override public void characters(final char[] ch, final int start, final int length) throws SAXException { for (int i = start; i < start + length; i++) { char c = ch[i]; switch (c) { case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '&': sb.append("&"); break; case '\'': sb.append("'"); break; case '"': sb.append("""); break; default: sb.append(c); break; } } } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { // Add the element end tag. sb.append("</").append(qName).append(">"); // We have finished processing this element. Decrement the depth. int newDepth = depth.decrementAndGet(); // If we have now returned to level 1, we have finished processing // a 2nd-level element. Send notification with the XML text and // erase the String Builder so that we can start // processing a new 2nd-level element. if (newDepth == splitDepth.get()) { String elementTree = sb.toString(); notifier.onXmlElementFound(elementTree); // Reset the StringBuilder to just the XML prolog. sb.setLength(38); } } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException { // Increment the current depth because start a new XML element. int newDepth = depth.incrementAndGet(); // Output the element and its attributes if it is // not the root element. if (newDepth > splitDepth.get()) { sb.append("<"); sb.append(qName); int attCount = atts.getLength(); for (int i = 0; i < attCount; i++) { String attName = atts.getQName(i); String attValue = atts.getValue(i); sb.append(" ").append(attName).append("=").append("\"").append(attValue).append("\""); } sb.append(">"); } } }