package net.sitemorph.protostore;
import com.google.common.collect.Sets;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Message;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
/**
* Iterator that uses builder fields to index a prepared statement.
*
* Assumes that the prepared statement uses the field list from the builder
*
* @author dak
*
* TODO implement type converter interface for type mappers and allow
* registration to allow arbitrary type mapping.
*/
public class DbFieldIterator<T extends Message> implements CrudIterator<T> {
private final ResultSet resultSet;
private Message.Builder prototype;
public DbFieldIterator(Message.Builder builder, ResultSet resultSet) {
// nasty setup required
prototype = builder;
this.resultSet = resultSet;
}
public static String getCrudFieldList(Descriptor descriptor, String alias,
FieldDescriptor... skip) {
StringBuilder result = new StringBuilder();
List<FieldDescriptor> fields = descriptor.getFields();
Set<FieldDescriptor> skipSet = Sets.newHashSet();
for (int i = 0; null != skip && i < skip.length; i++) {
skipSet.add(skip[i]);
}
for (FieldDescriptor field : fields) {
if (skipSet.contains(field)) {
continue;
}
if (null != alias) {
result.append(alias)
.append(".");
}
result.append(field.getName())
.append(", ");
}
result.delete(result.length() - 2, result.length());
return result.toString();
}
public static String getCrudFieldList(Descriptor descriptor,
FieldDescriptor... skip) {
return getCrudFieldList(descriptor, null, skip);
}
@SuppressWarnings("unchecked")
@Override
public T next() throws CrudException {
Message.Builder next = prototype.clone();
try {
resultSet.next();
Descriptor descriptor = prototype.getDescriptorForType();
int offset = 1;
for (FieldDescriptor field : descriptor.getFields()) {
Object value;
switch (field.getType()) {
case DOUBLE :
value = resultSet.getDouble(offset++);
break;
case FLOAT :
value = resultSet.getFloat(offset++);
break;
case INT64:
case SINT64:
case SFIXED64:
case UINT64:
case FIXED64 :
value = resultSet.getLong(offset++);
break;
case SINT32:
case UINT32:
case SFIXED32:
case FIXED32:
case INT32:
value = resultSet.getInt(offset++);
break;
case BOOL:
value = resultSet.getBoolean(offset++);
break;
case STRING:
value = resultSet.getString(offset++);
break;
case ENUM :
String key = resultSet.getString(offset++);
EnumValueDescriptor enumDescriptor =
field.getEnumType().findValueByName(key);
if (null != key && null == enumDescriptor) {
throw new CrudException("Error finding enum " +
field.getEnumType().getName() + " value " + key);
}
value = enumDescriptor;
break;
case BYTES :
byte[] data = resultSet.getBytes(offset++);
if (null != data && 0 < data.length) {
value = ByteString.copyFrom(data);
} else {
value = null;
}
break;
//case GROUP:
//case MESSAGE:
default:
throw new CrudException("Unsupported proto field type: " +
field.getType().name());
}
if (resultSet.wasNull() || null == value) {
next.clearField(field);
} else {
next.setField(field, value);
}
}
return (T) next.build();
} catch (SQLException e) {
throw new CrudException("Error reading proto field", e);
}
}
@Override
public boolean hasNext() throws CrudException {
try {
boolean forward = resultSet.next();
resultSet.previous();
return forward;
} catch (SQLException e) {
throw new CrudException("Error checking for next crud object", e);
}
}
@Override
public void close() throws CrudException {
try {
resultSet.close();
} catch (SQLException e) {
throw new CrudException("Error closing crud iterator", e);
}
}
}