/*
* Copyright 2015 Hewlett-Packard Development Company, L.P.
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
*/
package com.hp.autonomy.frontend.find.idol.export.service;
import com.autonomy.aci.client.services.AciErrorException;
import com.autonomy.aci.client.services.ProcessorException;
import com.autonomy.aci.client.services.impl.AbstractStAXProcessor;
import com.autonomy.aci.client.services.impl.ErrorProcessor;
import com.hp.autonomy.frontend.find.core.export.service.PlatformDataExportStrategy;
import com.hp.autonomy.searchcomponents.core.config.FieldInfo;
import com.hp.autonomy.searchcomponents.core.config.FieldType;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Stack;
import java.util.stream.Collectors;
@SuppressWarnings("serial")
class ExportQueryResponseProcessor extends AbstractStAXProcessor<Void> {
private static final String HIT_NODE_NAME = "autn:hit";
private static final String CONTENT_NODE_NAME = "autn:content";
private static final Map<String, IdolMetadataNode> METADATA_NODES = new HashMap<>();
static {
for (final IdolMetadataNode metadataNode : IdolMetadataNode.values()) {
METADATA_NODES.put(metadataNode.getNodeName(), metadataNode);
}
}
private final PlatformDataExportStrategy exportStrategy;
private final OutputStream outputStream;
private final Collection<FieldInfo<?>> fieldInfoList;
private final Collection<String> selectedFieldIds;
ExportQueryResponseProcessor(final PlatformDataExportStrategy exportStrategy, final OutputStream outputStream, final Collection<FieldInfo<?>> fieldInfoList, final Collection<String> selectedFieldIds) {
this.outputStream = outputStream;
this.fieldInfoList = new ArrayList<>(fieldInfoList);
this.selectedFieldIds = new ArrayList<>(selectedFieldIds);
this.exportStrategy = exportStrategy;
}
@Override
public Void process(final XMLStreamReader aciResponse) throws AciErrorException, ProcessorException {
try {
if (isErrorResponse(aciResponse)) {
setErrorProcessor(new ErrorProcessor());
processErrorResponse(aciResponse);
}
while (aciResponse.hasNext()) {
aciResponse.next();
if (aciResponse.isStartElement()) {
if (HIT_NODE_NAME.equals(aciResponse.getLocalName())) {
parseHit(aciResponse);
}
}
}
} catch (final XMLStreamException | IOException e) {
throw new ProcessorException("Error parsing data", e);
}
return null;
}
private void parseHit(final XMLStreamReader aciResponse) throws XMLStreamException, IOException {
final Map<String, List<String>> valueMap = new HashMap<>();
boolean hitEnd = false;
while (aciResponse.hasNext() && !hitEnd) {
aciResponse.next();
if (aciResponse.isStartElement()) {
final String nodeName = aciResponse.getLocalName();
if (CONTENT_NODE_NAME.equals(nodeName)) {
parseContent(aciResponse, valueMap);
} else {
conditionallyAddMetadataValueToExportMap(aciResponse, valueMap, nodeName);
}
} else if (aciResponse.isEndElement()) {
hitEnd = HIT_NODE_NAME.equals(aciResponse.getLocalName());
}
}
final Collection<String> values = fieldInfoList.stream()
.map(fieldInfo -> exportStrategy.combineValues(valueMap.get(fieldInfo.getId())))
.collect(Collectors.toList());
exportStrategy.exportRecord(outputStream, values);
}
private void parseContent(final XMLStreamReader aciResponse, final Map<String, List<String>> valueMap) throws XMLStreamException {
boolean contentEnd = false;
final Stack<String> nodes = new Stack<>();
while (aciResponse.hasNext() && !contentEnd) {
aciResponse.next();
if (aciResponse.isStartElement()) {
final String nodeName = aciResponse.getLocalName();
nodes.push(nodeName);
conditionallyAddFieldValueToExportMap(aciResponse, valueMap, nodeName, nodes);
} else if (aciResponse.isEndElement()) {
contentEnd = CONTENT_NODE_NAME.equals(aciResponse.getLocalName());
if (!contentEnd) {
nodes.pop();
}
}
}
}
private void conditionallyAddMetadataValueToExportMap(final XMLStreamReader aciResponse, final Map<String, List<String>> valueMap, final String nodeName) throws XMLStreamException {
final Optional<? extends FieldInfo<?>> maybeFieldInfo = exportStrategy.getFieldInfoForMetadataNode(nodeName, METADATA_NODES, selectedFieldIds);
if (maybeFieldInfo.isPresent()) {
final FieldInfo<?> fieldInfo = maybeFieldInfo.get();
addValueToMap(aciResponse, valueMap, fieldInfo);
}
}
private void conditionallyAddFieldValueToExportMap(final XMLStreamReader aciResponse, final Map<String, List<String>> valueMap, final String nodeName, final Stack<String> nodes) throws XMLStreamException {
final String nodePath = CollectionUtils.isEmpty(nodes) ? nodeName : String.join("/", nodes);
final Optional<? extends FieldInfo<?>> maybeFieldInfo = exportStrategy.getFieldInfoForNode(nodePath, selectedFieldIds);
if (maybeFieldInfo.isPresent()) {
final FieldInfo<?> fieldInfo = maybeFieldInfo.get();
addValueToMap(aciResponse, valueMap, fieldInfo);
nodes.pop();
}
}
private void addValueToMap(final XMLStreamReader aciResponse, final Map<String, List<String>> valueMap, final FieldInfo<?> fieldInfo) throws XMLStreamException {
final String id = fieldInfo.getId();
final FieldType fieldType = fieldInfo.getType();
final Object rawValue = parseValue(aciResponse, fieldType);
final String value = StringUtils.defaultString(exportStrategy.getDisplayValue(fieldInfo, (Serializable) rawValue));
if (!valueMap.containsKey(id)) {
valueMap.put(id, new ArrayList<>());
}
valueMap.get(id).add(value);
}
private Object parseValue(final XMLStreamReader aciResponse, final FieldType fieldType) throws XMLStreamException {
return fieldType.parseValue(fieldType.getType(), aciResponse.getElementText().trim());
}
}