Skip to content

Commit

Permalink
Merge pull request #103 from pgelinas/fix-issue-98
Browse files Browse the repository at this point in the history
Updated PR #100
  • Loading branch information
Henrik Lundgren committed Dec 12, 2012
2 parents 5c54ba4 + c90aa04 commit 6b4e4f1
Showing 1 changed file with 137 additions and 127 deletions.
264 changes: 137 additions & 127 deletions org.ektorp/src/main/java/org/ektorp/impl/QueryResultParser.java
Expand Up @@ -22,84 +22,89 @@
* @author Pascal Gélinas (rewrite for issue #98)
*/
public class QueryResultParser<T> {
private static final String NOT_FOUND_ERROR = "not_found";
private static final String NOT_FOUND_ERROR = "not_found";
private static final String ROWS_FIELD_NAME = "rows";
private static final String VALUE_FIELD_NAME = "value";
private static final String ID_FIELD_NAME = "id";
private static final String ERROR_FIELD_NAME = "error";
private static final String KEY_FIELD_NAME = "key";
private static final String INCLUDED_DOC_FIELD_NAME = "doc";
private static final String TOTAL_ROWS_FIELD_NAME = "total_rows";
private static final String OFFSET_FIELD_NAME = "offset";

private int totalRows = -1;
private int offset = -1;
private List<T> rows;

private String firstId;
private JsonNode firstKey;

private String lastId;
private JsonNode lastKey;

private final ObjectMapper mapper;
private final Class<T> type;
private boolean ignoreNotFound;

public QueryResultParser(Class<T> type, ObjectMapper mapper) {
this.type = type;
this.mapper = mapper;
}

public void parseResult(InputStream json) throws IOException {
JsonParser jp = mapper.getFactory().createJsonParser(json);

if (jp.nextToken() != JsonToken.START_OBJECT) {
throw new DbAccessException("Expected data to start with an Object");
}

Map<String,String> errorFields = new HashMap<String, String>();
// Issue #98: Can't assume order of JSON fields.
while(jp.nextValue() != JsonToken.END_OBJECT){
String currentName = jp.getCurrentName();
if(OFFSET_FIELD_NAME.equals(currentName)){
offset = jp.getIntValue();
} else if(TOTAL_ROWS_FIELD_NAME.equals(currentName)){
totalRows = jp.getIntValue();
} else if (ROWS_FIELD_NAME.equals(currentName)){
if(totalRows == -1){
rows = new ArrayList<T>();
}else{
rows = new ArrayList<T>(totalRows);
}
parseRows(jp);
}else{
// Handle cloudant errors.
errorFields.put(jp.getCurrentName(), jp.getText());
}
}

if(!errorFields.isEmpty()){
JsonNode error = mapper.convertValue(errorFields, JsonNode.class);
private static final String VALUE_FIELD_NAME = "value";
private static final String ID_FIELD_NAME = "id";
private static final String ERROR_FIELD_NAME = "error";
private static final String KEY_FIELD_NAME = "key";
private static final String INCLUDED_DOC_FIELD_NAME = "doc";
private static final String TOTAL_ROWS_FIELD_NAME = "total_rows";
private static final String OFFSET_FIELD_NAME = "offset";

private int totalRows = -1;
private int offset = -1;
private List<T> rows;

private String firstId;
private JsonNode firstKey;

private String lastId;
private JsonNode lastKey;

private final ObjectMapper mapper;
private final Class<T> type;
private boolean ignoreNotFound;

public QueryResultParser(Class<T> type, ObjectMapper mapper) {
this.type = type;
this.mapper = mapper;
}

public void parseResult(InputStream json) throws IOException {
JsonParser jp = mapper.getFactory().createParser(json);

try {
parseResult(jp);
} finally {
jp.close();
}
}

private void parseResult(JsonParser jp) throws IOException {
if (jp.nextToken() != JsonToken.START_OBJECT) {
throw new DbAccessException("Expected data to start with an Object");
}

Map<String, String> errorFields = new HashMap<String, String>();
// Issue #98: Can't assume order of JSON fields.
while (jp.nextValue() != JsonToken.END_OBJECT) {
String currentName = jp.getCurrentName();
if (OFFSET_FIELD_NAME.equals(currentName)) {
offset = jp.getIntValue();
} else if (TOTAL_ROWS_FIELD_NAME.equals(currentName)) {
totalRows = jp.getIntValue();
} else if (ROWS_FIELD_NAME.equals(currentName)) {
rows = new ArrayList<T>();
parseRows(jp);
} else {
// Handle cloudant errors.
errorFields.put(jp.getCurrentName(), jp.getText());
}
}

if (!errorFields.isEmpty()) {
JsonNode error = mapper.convertValue(errorFields, JsonNode.class);
throw new DbAccessException(error.toString());
}
}
}
}

private void parseRows(JsonParser jp) throws IOException{
if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
private void parseRows(JsonParser jp) throws IOException {
if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
throw new DbAccessException("Expected rows to start with an Array");
}

// Parses the first row that isn't an error row to find out which field to use (doc or value).
String dataField = null;
while (dataField == null && jp.nextToken() == JsonToken.START_OBJECT) {

// Parses the first row that isn't an error row to find out which field
// to use (doc or value).
String dataField = null;
while (dataField == null && jp.nextToken() == JsonToken.START_OBJECT) {
Row row = jp.readValueAs(Row.class);
if (row.error != null) {
if (!ignoreError(row.error)){
if (!ignoreError(row.error)) {
throw new ViewResultException(row.key, row.error);
}
continue;
}
}
if (row.doc != null) {
dataField = INCLUDED_DOC_FIELD_NAME;
rows.add(mapper.readValue(row.doc.traverse(), type));
Expand All @@ -110,86 +115,91 @@ private void parseRows(JsonParser jp) throws IOException{
firstId = row.id;
firstKey = row.key;
}
// After the while, either we point at END_ARRAY but we have no dataField (all rows were error),
// or we point at an END_OBJECT (end of a row) and have determined which data field to use.
if(dataField == null) return;

// Parse all the remaining rows; jp points at START_OBJECT except after the last row
while(jp.nextToken() != JsonToken.END_ARRAY){
String currentId = null;
JsonNode currentKey = null;
// After the while, either we point at END_ARRAY but we have no
// dataField (all rows were error),
// or we point at an END_OBJECT (end of a row) and have determined which
// data field to use.
if (dataField == null)
return;

// Parse all the remaining rows; jp points at START_OBJECT except after
// the last row
while (jp.nextToken() != JsonToken.END_ARRAY) {
String currentId = null;
JsonNode currentKey = null;
String error = null;
T value = null;
// Parse the fields of a row; jp points at a value token except after the last field.
while(jp.nextValue() != JsonToken.END_OBJECT){
String currentName = jp.getCurrentName();
if(ID_FIELD_NAME.equals(currentName)){
currentId = jp.getText();
} else if (KEY_FIELD_NAME.equals(currentName)){
currentKey = jp.readValueAsTree();
} else if(dataField.equals(currentName)){
value = jp.readValueAs(type);
} else if(ERROR_FIELD_NAME.equals(currentName)){
T value = null;
// Parse the fields of a row; jp points at a value token except
// after the last field.
while (jp.nextValue() != JsonToken.END_OBJECT) {
String currentName = jp.getCurrentName();
if (ID_FIELD_NAME.equals(currentName)) {
currentId = jp.getText();
} else if (KEY_FIELD_NAME.equals(currentName)) {
currentKey = jp.readValueAsTree();
} else if (dataField.equals(currentName)) {
value = jp.readValueAs(type);
} else if (ERROR_FIELD_NAME.equals(currentName)) {
error = jp.getText();
} else {
// Skip fields value that are of no interest to us.
jp.skipChildren();
}
}
if (error != null && !ignoreError(error)){
}
if (error != null && !ignoreError(error)) {
throw new ViewResultException(currentKey, error);
}
// If the current row is an error row, then value will be null
if(value != null){
lastId = currentId;
lastKey = currentKey;
rows.add(value);
}
}
}
}
// If the current row is an error row, then value will be null
if (value != null) {
lastId = currentId;
lastKey = currentKey;
rows.add(value);
}
}
}

private boolean ignoreError(String error) {
return ignoreNotFound && NOT_FOUND_ERROR.equals(error);
}

public int getTotalRows() {
return totalRows;
}
public int getTotalRows() {
return totalRows;
}

public int getOffset() {
return offset;
}
public int getOffset() {
return offset;
}

public List<T> getRows() {
return rows;
}
public String getLastId() {
public List<T> getRows() {
return rows;
}

public String getLastId() {
return lastId;
}
public JsonNode getLastKey() {

public JsonNode getLastKey() {
return lastKey;
}
public String getFirstId() {

public String getFirstId() {
return firstId;
}
public JsonNode getFirstKey() {

public JsonNode getFirstKey() {
return firstKey;
}

public void setIgnoreNotFound(boolean ignoreNotFound) {
this.ignoreNotFound = ignoreNotFound;
}

@JsonAutoDetect(fieldVisibility=Visibility.ANY)
private static class Row {
private String id;
private JsonNode key;
private JsonNode value;
private JsonNode doc;
private String error;
}
public void setIgnoreNotFound(boolean ignoreNotFound) {
this.ignoreNotFound = ignoreNotFound;
}

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
private static class Row {
private String id;
private JsonNode key;
private JsonNode value;
private JsonNode doc;
private String error;
}
}

0 comments on commit 6b4e4f1

Please sign in to comment.