blob: 9665b25ffd8149d0d9ae1f01217fc9412e3b9564 [file] [log] [blame]
/*
* 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.solr.handler.extraction;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.index.IndexableField;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.RawResponseWriter;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.response.TabularResponseWriter;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.StrField;
import org.apache.solr.search.ReturnFields;
public class XLSXResponseWriter extends RawResponseWriter {
@Override
public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
// throw away arraywriter just to satisfy super requirements; we're grabbing
// all writes before they go to it anyway
XLSXWriter w = new XLSXWriter(new CharArrayWriter(), req, rsp);
LinkedHashMap<String,String> reqNamesMap = new LinkedHashMap<>();
LinkedHashMap<String,Integer> reqWidthsMap = new LinkedHashMap<>();
Iterator<String> paramNamesIter = req.getParams().getParameterNamesIterator();
while (paramNamesIter.hasNext()) {
String nextParam = paramNamesIter.next();
if (nextParam.startsWith("colname.")) {
String field = nextParam.substring("colname.".length());
reqNamesMap.put(field, req.getParams().get(nextParam));
} else if (nextParam.startsWith("colwidth.")) {
String field = nextParam.substring("colwidth.".length());
reqWidthsMap.put(field, req.getParams().getInt(nextParam));
}
}
try {
w.writeResponse(out, reqNamesMap, reqWidthsMap);
} finally {
w.close();
}
}
@Override
public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
}
}
class XLSXWriter extends TabularResponseWriter {
static class SerialWriteWorkbook {
SXSSFWorkbook swb;
Sheet sh;
XSSFCellStyle headerStyle;
int rowIndex;
Row curRow;
int cellIndex;
SerialWriteWorkbook() {
this.swb = new SXSSFWorkbook(100);
this.sh = this.swb.createSheet();
this.rowIndex = 0;
this.headerStyle = (XSSFCellStyle)swb.createCellStyle();
this.headerStyle.setFillBackgroundColor(IndexedColors.BLACK.getIndex());
//solid fill
this.headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font headerFont = swb.createFont();
headerFont.setFontHeightInPoints((short)14);
headerFont.setBold(true);
headerFont.setColor(IndexedColors.WHITE.getIndex());
this.headerStyle.setFont(headerFont);
}
void addRow() {
curRow = sh.createRow(rowIndex++);
cellIndex = 0;
}
void setHeaderRow() {
curRow.setHeightInPoints((short)21);
}
//sets last created cell to have header style
void setHeaderCell() {
curRow.getCell(cellIndex - 1).setCellStyle(this.headerStyle);
}
//set the width of the most recently created column
void setColWidth(int charWidth) {
//width in poi is units of 1/256th of a character width for some reason
this.sh.setColumnWidth(cellIndex - 1, 256*charWidth);
}
void writeCell(String value) {
Cell cell = curRow.createCell(cellIndex++);
cell.setCellValue(value);
}
void flush(OutputStream out) {
try {
swb.write(out);
} catch (IOException e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String stacktrace = sw.toString();
}finally {
swb.dispose();
}
}
}
private SerialWriteWorkbook wb = new SerialWriteWorkbook();
static class XLField {
String name;
SchemaField sf;
}
private Map<String,XLField> xlFields = new LinkedHashMap<String,XLField>();
public XLSXWriter(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) {
super(writer, req, rsp);
}
public void writeResponse(OutputStream out, LinkedHashMap<String, String> colNamesMap,
LinkedHashMap<String, Integer> colWidthsMap) throws IOException {
Collection<String> fields = getFields();
for (String field : fields) {
if (!returnFields.wantsField(field)) {
continue;
}
if (field.equals("score")) {
XLField xlField = new XLField();
xlField.name = "score";
xlFields.put("score", xlField);
continue;
}
if (shouldSkipField(field)) {
continue;
}
SchemaField sf = schema.getFieldOrNull(field);
if (sf == null) {
FieldType ft = new StrField();
sf = new SchemaField(field, ft);
}
XLField xlField = new XLField();
xlField.name = field;
xlField.sf = sf;
xlFields.put(field, xlField);
}
wb.addRow();
//write header
for (XLField xlField : xlFields.values()) {
String printName = xlField.name;
int colWidth = 14;
String niceName = colNamesMap.get(xlField.name);
if (niceName != null) {
printName = niceName;
}
Integer niceWidth = colWidthsMap.get(xlField.name);
if (niceWidth != null) {
colWidth = niceWidth.intValue();
}
writeStr(xlField.name, printName, false);
wb.setColWidth(colWidth);
wb.setHeaderCell();
}
wb.setHeaderRow();
wb.addRow();
writeResponse(rsp.getResponse());
wb.flush(out);
wb = null;
}
@Override
public void close() throws IOException {
super.close();
}
//NOTE: a document cannot currently contain another document
@SuppressWarnings({"rawtypes"})
List tmpList;
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public void writeSolrDocument(String name, SolrDocument doc, ReturnFields returnFields, int idx ) throws IOException {
if (tmpList == null) {
tmpList = new ArrayList(1);
tmpList.add(null);
}
for (XLField xlField : xlFields.values()) {
Object val = doc.getFieldValue(xlField.name);
int nVals = val instanceof Collection ? ((Collection)val).size() : (val==null ? 0 : 1);
if (nVals == 0) {
writeNull(xlField.name);
continue;
}
if ((xlField.sf != null && xlField.sf.multiValued()) || nVals > 1) {
Collection values;
// normalize to a collection
if (val instanceof Collection) {
values = (Collection)val;
} else {
tmpList.set(0, val);
values = tmpList;
}
writeArray(xlField.name, values.iterator());
} else {
// normalize to first value
if (val instanceof Collection) {
Collection values = (Collection)val;
val = values.iterator().next();
}
writeVal(xlField.name, val);
}
}
wb.addRow();
}
@Override
public void writeStr(String name, String val, boolean needsEscaping) throws IOException {
wb.writeCell(val);
}
@Override
public void writeArray(String name, @SuppressWarnings({"rawtypes"})Iterator val) throws IOException {
StringBuffer output = new StringBuffer();
while (val.hasNext()) {
Object v = val.next();
if (v instanceof IndexableField) {
IndexableField f = (IndexableField)v;
if (v instanceof Date) {
output.append(((Date) val).toInstant().toString()).append("; ");
} else {
output.append(f.stringValue()).append("; ");
}
} else {
output.append(v.toString()).append("; ");
}
}
if (output.length() > 0) {
output.deleteCharAt(output.length()-1);
output.deleteCharAt(output.length()-1);
}
writeStr(name, output.toString(), false);
}
@Override
public void writeNull(String name) throws IOException {
wb.writeCell("");
}
@Override
public void writeInt(String name, String val) throws IOException {
wb.writeCell(val);
}
@Override
public void writeLong(String name, String val) throws IOException {
wb.writeCell(val);
}
@Override
public void writeBool(String name, String val) throws IOException {
wb.writeCell(val);
}
@Override
public void writeFloat(String name, String val) throws IOException {
wb.writeCell(val);
}
@Override
public void writeDouble(String name, String val) throws IOException {
wb.writeCell(val);
}
@Override
public void writeDate(String name, String val) throws IOException {
wb.writeCell(val);
}
}