package com.github.eyce9000.iem.webreports.relevance;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.joda.time.DateTime;
import com.bigfix.schemas.relevance.ResultList;
import com.github.eyce9000.iem.api.relevance.DataType;
import com.github.eyce9000.iem.api.relevance.QueryResultColumn;
import com.github.eyce9000.iem.api.relevance.RelevanceException;
import com.github.eyce9000.iem.api.relevance.SessionRelevanceQuery;
import com.github.eyce9000.iem.api.relevance.handlers.HandlerException;
import com.github.eyce9000.iem.api.relevance.handlers.RawResultHandler;
public class ResultParser {
private JAXBContext jc;
private Unmarshaller unmarshaller;
private boolean columnsProcessed;
public ResultParser() throws JAXBException{
jc = JAXBContext.newInstance(ResultList.class);
unmarshaller = jc.createUnmarshaller();
}
public void parse(SessionRelevanceQuery srq,InputStream in,RawResultHandler handler) throws XMLStreamException, JAXBException, RelevanceException, HandlerException{
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
XMLEventReader reader = factory.createXMLEventReader(new BufferedReader(new InputStreamReader(in)));
boolean inResults = false;
while(reader.hasNext()){
reader.nextEvent();
XMLEvent event = reader.peek();
if(event==null)
break;
if(event.isStartElement()){
StartElement el = event.asStartElement();
String localName = el.getName().getLocalPart();
if(localName.equals("sessionToken")){
JAXBElement<String> element = unmarshaller.unmarshal(reader,String.class);
if(handler instanceof TokenHandler){
((TokenHandler)handler).onToken(element.getValue());
}
}
if(localName.equals("faultstring")){
JAXBElement<String> element = unmarshaller.unmarshal(reader,String.class);
handler.onError(new RelevanceException(element.getValue()));
throw new RelevanceException(element.getValue());
}
if(localName.equals("error")){
JAXBElement<String> element = unmarshaller.unmarshal(reader,String.class);
handler.onError(new RelevanceException(element.getValue()));
throw new RelevanceException(element.getValue());
}
if(localName.equals("results")){
inResults = true;
}
else if(inResults){
Map<String,Object> result = processElement(srq,el,reader);
try{
handler.onResult(result);
}
catch(Exception e){
throw new HandlerException(e);
}
}
}
if(event.isEndElement()){
EndElement el = event.asEndElement();
String localName = el.getName().getLocalPart();
if(localName.equals("results"))
inResults = false;
}
}
}
private Map<String,Object> processElement(SessionRelevanceQuery srq,StartElement el,XMLEventReader reader) throws JAXBException{
ResultList row = null;
JAXBElement element = null;
String localName = el.getName().getLocalPart();
if(localName.equals("String"))
element = unmarshaller.unmarshal(reader,String.class);
else if(localName.equals("Boolean"))
element = unmarshaller.unmarshal(reader,Boolean.class);
else if(localName.equals("Integer"))
element = unmarshaller.unmarshal(reader,Integer.class);
else if(localName.equals("DateTime"))
element = unmarshaller.unmarshal(reader,XMLGregorianCalendar.class);
else if(localName.equals("FloatingPoint"))
element = unmarshaller.unmarshal(reader,Double.class);
else if(localName.equals("Tuple")){
JAXBElement<ResultList> jb = unmarshaller.unmarshal(reader, ResultList.class);
row = jb.getValue();
}
if(row==null){
row = new ResultList();
row.getBooleanOrIntegerOrString().add(element.getValue());
}
return processResultList(row,srq);
}
private Map<String,Object> processResultList(ResultList result,SessionRelevanceQuery srq){
if(!columnsProcessed){
processColumns(srq,result);
columnsProcessed = true;
}
List<QueryResultColumn> columns = srq.getColumns();
Map<String,Object> hash = new HashMap<String,Object>();
List<Object> row = result.getBooleanOrIntegerOrString();
for(int colNum=0; colNum<columns.size(); colNum++){
QueryResultColumn column = columns.get(colNum);
Object value;
Object rawValue = row.get(colNum);
if(rawValue instanceof BigDecimal)
value = ((BigDecimal)rawValue).doubleValue();
else if(rawValue instanceof BigInteger)
value = ((BigInteger)rawValue).intValue();
else if(rawValue instanceof XMLGregorianCalendar){
value = new DateTime(((XMLGregorianCalendar)rawValue).toGregorianCalendar().getTimeInMillis()).toDate();
}
else
value = rawValue;
hash.put(column.getName(),value);
}
return hash;
}
private void processColumns(SessionRelevanceQuery srq,ResultList result){
List<QueryResultColumn> columns = srq.getColumns();
List<Object> firstRow = result.getBooleanOrIntegerOrString();
int diff = firstRow.size() - columns.size();
if(diff > 0){
List<QueryResultColumn> newColumns = new ArrayList<QueryResultColumn>(columns);
for(int i=0; i<firstRow.size(); i++){
if(i>=columns.size()){
newColumns.add(new QueryResultColumn("col"+i,DataType.string));
}
}
columns = newColumns;
}
else if (diff < 0){
while(columns.size() > firstRow.size()){
columns.remove(columns.size()-1);
}
}
srq.setColumns(columns);
}
public static abstract class TokenHandler implements RawResultHandler{
private RawResultHandler handler;
public TokenHandler(RawResultHandler handler){
this.handler = handler;
}
public abstract void onToken(String token);
@Override
public void onError(Exception ex) {
handler.onError(ex);
}
@Override
public void onResult(Map<String, Object> rawResult) throws Exception {
handler.onResult(rawResult);
}
@Override
public void onComplete(long timestamp) {
handler.onComplete(timestamp);
}
}
}