/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.service.externaldata;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.qp.operator.Cursor;
import com.foundationdb.qp.operator.RowCursor;
import com.foundationdb.qp.row.Row;
import com.foundationdb.server.types.FormatOptions;
import com.foundationdb.server.types.value.ValueSource;
import com.foundationdb.util.AkibanAppender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonRowWriter
{
private static final Logger logger = LoggerFactory.getLogger(JsonRowWriter.class);
private final RowTracker tracker;
public JsonRowWriter(RowTracker tracker) {
this.tracker = tracker;
}
public boolean writeRows(Cursor cursor, AkibanAppender appender, String prefix, WriteRow rowWriter, FormatOptions options) {
try {
cursor.openTopLevel();
return writeRowsFromOpenCursor(cursor, appender, prefix, rowWriter, options);
}
finally {
cursor.closeTopLevel();
}
}
public boolean writeRowsFromOpenCursor(RowCursor cursor, AkibanAppender appender, String prefix, WriteRow rowWriter, FormatOptions options) {
tracker.reset();
final int minDepth = tracker.getMinDepth();
final int maxDepth = tracker.getMaxDepth();
int depth = minDepth - 1;
Row row;
while ((row = cursor.next()) != null) {
logger.trace("Row {}", row);
tracker.beginRow(row);
int rowDepth = tracker.getRowDepth();
boolean begun = false;
if (depth >= rowDepth) {
if (tracker.isSameRowType())
begun = true;
do {
appender.append((depth > rowDepth || !begun) ? "}]" : "}");
depth--;
tracker.popRowType();
} while (depth >= rowDepth);
}
if (rowDepth > maxDepth)
continue;
assert (rowDepth == depth+1);
depth = rowDepth;
tracker.pushRowType();
if (begun) {
appender.append(',');
}
else if (depth > minDepth) {
appender.append(",\"");
appender.append(tracker.getRowName());
appender.append("\":[");
}
else {
appender.append(prefix);
}
appender.append('{');
rowWriter.write(row, appender, options);
}
if (depth < minDepth)
return false; // Cursor was empty = not found.
do {
appender.append((depth > minDepth) ? "}]" : "}");
depth--;
tracker.popRowType();
} while (depth >= minDepth);
return true;
}
public static void writeValue(String name, ValueSource value, AkibanAppender appender,
boolean first, FormatOptions options) {
if(!first) {
appender.append(',');
}
appender.append('"');
appender.append(name);
appender.append("\":");
value.getType().formatAsJson(value, appender, options);
}
/**
* Write the name:value pairs of the data from a row into Json format.
* Current implementations take names from the table columns or the
* table's primary key columns.
* @author tjoneslo
*/
public interface WriteRow {
public void write(Row row, AkibanAppender appender, FormatOptions options);
}
public static class WriteTableRow implements WriteRow {
@Override
public void write(Row row, AkibanAppender appender, FormatOptions options) {
List<Column> columns = row.rowType().table().getColumns();
for (int i = 0; i < columns.size(); i++) {
writeValue(columns.get(i).getName(), row.value(i), appender, i == 0, options);
}
}
}
public static class WriteCapturePKRow implements WriteRow {
private Map<Column, ValueSource> pkValues = new HashMap<>();
@Override
public void write(Row row, AkibanAppender appender, FormatOptions options) {
// tables with hidden PK (noPK tables) return no values
if (row.rowType().table().getPrimaryKey() == null) return;
List<IndexColumn> columns = row.rowType().table().getPrimaryKey().getIndex().getKeyColumns();
for (int i = 0; i < columns.size(); i++) {
Column column = columns.get(i).getColumn();
writeValue(column.getName(), row.value(column.getPosition()), appender, i == 0, options);
pkValues.put(column, row.value(column.getPosition()));
}
}
public Map<Column, ValueSource> getPKValues() {
return pkValues;
}
}
}