/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
* Copyright (C) 2012 Eugene Fradkin (eugene.fradkin@gmail.com)
*
* Licensed 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.jkiss.dbeaver.tools.transfer.stream.impl;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
import org.jkiss.dbeaver.model.data.DBDContent;
import org.jkiss.dbeaver.model.data.DBDContentStorage;
import org.jkiss.dbeaver.model.exec.DBCSession;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.tools.transfer.stream.IStreamDataExporterSite;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
/**
* JSON Exporter
*/
public class DataExporterJSON extends StreamExporterAbstract {
public static final String PROP_FORMAT_DATE_ISO = "formatDateISO";
public static final String PROP_PRINT_TABLE_NAME = "printTableName";
private PrintWriter out;
private List<DBDAttributeBinding> columns;
private String tableName;
private int rowNum = 0;
private DateFormat dateFormat;
private boolean printTableName = true;
private boolean formatDateISO = true;
@Override
public void init(IStreamDataExporterSite site) throws DBException
{
super.init(site);
out = site.getWriter();
formatDateISO = CommonUtils.getBoolean(site.getProperties().get(PROP_FORMAT_DATE_ISO), true);
printTableName = CommonUtils.getBoolean(site.getProperties().get(PROP_PRINT_TABLE_NAME), true);
TimeZone tz = TimeZone.getTimeZone("UTC");
dateFormat = new SimpleDateFormat(DBConstants.DEFAULT_ISO_TIMESTAMP_FORMAT);
dateFormat.setTimeZone(tz);
}
@Override
public void dispose()
{
out = null;
super.dispose();
}
@Override
public void exportHeader(DBCSession session) throws DBException, IOException
{
columns = getSite().getAttributes();
tableName = getSite().getSource().getName();
printHeader();
}
private void printHeader()
{
if (printTableName) {
out.write("{\n");
out.write("\"" + escapeJsonString(tableName) + "\": ");
}
out.write("[\n");
}
@Override
public void exportRow(DBCSession session, Object[] row) throws DBException, IOException
{
if (rowNum > 0) {
out.write(",\n");
}
rowNum++;
out.write("\t{\n");
for (int i = 0; i < row.length; i++) {
DBDAttributeBinding column = columns.get(i);
String columnName = column.getName();
out.write("\t\t\"" + escapeJsonString(columnName) + "\" : ");
Object cellValue = row[i];
if (DBUtils.isNullValue(cellValue)) {
writeTextCell(null);
} else if (cellValue instanceof DBDContent) {
// Content
// Inline textual content and handle binaries in some special way
DBDContent content = (DBDContent) cellValue;
try {
DBDContentStorage cs = content.getContents(session.getProgressMonitor());
if (cs != null) {
if (ContentUtils.isTextContent(content)) {
try (Reader in = cs.getContentReader()) {
out.write("\"");
writeCellValue(in);
out.write("\"");
}
} else {
getSite().writeBinaryData(cs);
}
}
}
finally {
content.release();
}
} else {
if (cellValue instanceof Number || cellValue instanceof Boolean) {
out.write(cellValue.toString());
} else if (cellValue instanceof Date && formatDateISO) {
writeTextCell(dateFormat.format(cellValue));
} else {
writeTextCell(super.getValueDisplayString(column, cellValue));
}
}
if (i < row.length - 1) {
out.write(",");
}
out.write("\n");
}
out.write("\t}");
}
@Override
public void exportFooter(DBRProgressMonitor monitor) throws IOException
{
out.write("\n]");
if (printTableName) {
out.write("}");
}
out.write("\n");
}
private void writeTextCell(@Nullable String value)
{
if (value != null) {
out.write("\"" + escapeJsonString(value) + "\"");
} else {
out.write("null");
}
}
private void writeCellValue(Reader reader) throws IOException
{
// Copy reader
char buffer[] = new char[2000];
for (;;) {
int count = reader.read(buffer);
if (count <= 0) {
break;
}
out.write(escapeJsonString(new String(buffer, 0, count)));
}
}
private static String escapeJsonString(String str) {
if (str == null) {
return null;
}
StringBuilder result = new StringBuilder(str.length());
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
switch (c) {
case '\n':
result.append("\\n");
break;
case '\r':
result.append("\\r");
break;
case '\t':
result.append("\\t");
break;
case '\f':
result.append("\\f");
break;
case '\b':
result.append("\\b");
break;
case '"':
case '\\':
case '/':
result.append("\\").append(c);
break;
default:
result.append(c);
break;
}
}
return result.toString();
}
}