/**
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.ambari.view.hive2.resources.uploads.parsers.xml;
import org.apache.ambari.view.hive2.resources.uploads.parsers.EndOfDocumentException;
import org.apache.ambari.view.hive2.resources.uploads.parsers.RowMapIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.*;
import java.io.IOException;
import java.util.LinkedHashMap;
/**
* assumes XML of following format
* <table>
* <row>
* <col name="col1Name">row1-col1-Data</col>
* <col name="col2Name">row1-col2-Data</col>
* <col name="col3Name">row1-col3-Data</col>
* <col name="col4Name">row1-col4-Data</col>
* </row>
* <row>
* <col name="col1Name">row2-col1-Data</col>
* <col name="col2Name">row2-col2-Data</col>
* <col name="col3Name">row2-col3-Data</col>
* <col name="col4Name">row2-col4-Data</col>
* </row>
* </table>
*/
class XMLIterator implements RowMapIterator {
protected final static Logger LOG =
LoggerFactory.getLogger(XMLIterator.class);
private LinkedHashMap<String, String> nextObject = null;
private static final String TAG_TABLE = "table";
private static final String TAG_ROW = "row";
private static final String TAG_COL = "col";
private boolean documentStarted = false;
private XMLEventReader reader;
public XMLIterator(XMLEventReader reader) throws IOException {
this.reader = reader;
try {
nextObject = readNextObject(this.reader);
} catch (EndOfDocumentException e) {
LOG.debug("error : {}", e);
} catch (XMLStreamException e) {
throw new IOException(e);
}
}
@Override
public boolean hasNext() {
return null != nextObject;
}
public LinkedHashMap<String, String> peek() {
return nextObject;
}
@Override
public LinkedHashMap<String, String> next() {
LinkedHashMap<String, String> currObject = nextObject;
try {
nextObject = readNextObject(this.reader);
} catch (IOException e) {
LOG.error("Exception occured while reading the next row from XML : {} ", e);
nextObject = null;
} catch (EndOfDocumentException e) {
LOG.debug("End of XML document reached with next character ending the XML.");
nextObject = null;
} catch (XMLStreamException e) {
LOG.error("Exception occured while reading the next row from XML : {} ", e);
nextObject = null;
}
return currObject;
}
@Override
public void remove() {
// no operation.
LOG.info("No operation when remove called.");
}
private LinkedHashMap<String, String> readNextObject(XMLEventReader reader) throws IOException, EndOfDocumentException, XMLStreamException {
LinkedHashMap<String, String> row = new LinkedHashMap<>();
boolean objectStarted = false;
String currentName = null;
while (true) {
XMLEvent event = reader.nextEvent();
switch (event.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
StartElement startElement = event.asStartElement();
String qName = startElement.getName().getLocalPart();
LOG.debug("startName : {}" , qName);
switch (qName) {
case TAG_TABLE:
if (documentStarted) {
throw new IllegalArgumentException("Cannot have a <table> tag nested inside another <table> tag");
} else {
documentStarted = true;
}
break;
case TAG_ROW:
if (objectStarted) {
throw new IllegalArgumentException("Cannot have a <row> tag nested inside another <row> tag");
} else {
objectStarted = true;
}
break;
case TAG_COL:
if (!objectStarted) {
throw new IllegalArgumentException("Stray tag " + qName);
}
Attribute nameAttr = startElement.getAttributeByName( new QName("name"));
if( null == nameAttr ){
throw new IllegalArgumentException("Missing name attribute in col tag.");
}
currentName = nameAttr.getValue();
break;
default:
throw new IllegalArgumentException("Illegal start tag " + qName + " encountered.");
}
break;
case XMLStreamConstants.END_ELEMENT:
EndElement endElement = event.asEndElement();
String name = endElement.getName().getLocalPart();
LOG.debug("endName : {}", name);
switch (name) {
case TAG_TABLE:
if (!documentStarted) {
throw new IllegalArgumentException("Stray </table> tag.");
}
throw new EndOfDocumentException("End of XML document.");
case TAG_ROW:
if (!objectStarted) {
throw new IllegalArgumentException("Stray </row> tag.");
}
return row;
case TAG_COL:
if (!objectStarted) {
throw new IllegalArgumentException("Stray tag " + name);
}
currentName = null;
break;
default:
throw new IllegalArgumentException("Illegal start ending " + name + " encountered.");
}
break;
case XMLStreamConstants.CHARACTERS:
Characters characters = event.asCharacters();
if (characters.isWhiteSpace() && currentName == null)
break;
String data = characters.getData();
LOG.debug("character data : {}", data);
if (currentName == null) {
throw new IllegalArgumentException("Illegal characters outside any tag : " + data);
} else {
String oldData = row.get(currentName);
if (null != oldData) {
data = oldData + data;
}
row.put(currentName, data);
}
break;
}
}
}
}