ProtobufMessage.java
package com.fasterxml.jackson.dataformat.protobuf.schema;
import java.util.*;
import com.fasterxml.jackson.core.SerializableString;
public class ProtobufMessage
{
private final static ProtobufField[] NO_FIELDS = new ProtobufField[0];
// Let's allow reasonable sized lookup arrays
private final static int MAX_FIELD_INDEX_SIZE = 200;
// private final static int[] NO_INTS = new int[0];
protected final String _name;
/**
* Array that contains actual fields, in declaration order.
* Note that while array is assigned in constructor, the contents
* may be lazily added within, but they must be completed
* before {@link #init(ProtobufField)} is called.
*/
protected final ProtobufField[] _fields;
// note: assigned on init()
protected FieldLookup _fieldsByName;
/**
* Arrays of fields indexed by id (offset by <code>_idOffset</code>), if
* fields ids are in contiguous (enough) range.
*/
protected ProtobufField[] _fieldsById;
protected ProtobufField _firstField;
protected int _idOffset = -1;
public ProtobufMessage(String name, ProtobufField[] fields)
{
_name = name;
_fields = fields;
}
/**
* Method called right after finishing actual construction of this
* message definition. Needed because assignment to fields is dynamic,
* and setup is NOT complete when constructor exits.
*/
public void init(ProtobufField first)
{
_firstField = first;
_fieldsByName = FieldLookup.construct(_fields);
// Let's see, as well, whether we can create a direct lookup index.
// Note that fields have been sorted by caller already.
int len = _fields.length;
if (len > 0) {
int firstId = _fields[0].id;
int lastId = _fields[len-1].id;
if (firstId > lastId) {
throw new IllegalStateException("Internal error: first id ("+firstId+") > last id ("
+lastId+")");
}
int size = lastId - firstId + 1;
if (size <= MAX_FIELD_INDEX_SIZE) {
_idOffset = firstId;
_fieldsById = new ProtobufField[size];
for (ProtobufField f : _fields) {
// another sanity check for fun
int index = f.id - _idOffset;
if (_fieldsById[index] != null) {
throw new IllegalStateException("Internal error: collision for message of type '"
+_name+"' for id "+f.id);
}
_fieldsById[index] = f;
}
}
}
}
public static ProtobufMessage bogusMessage(String desc) {
ProtobufMessage bogus = new ProtobufMessage(desc, NO_FIELDS);
bogus.init(null);
return bogus;
}
public ProtobufField firstField() { return _firstField; }
public ProtobufField firstIf(String name) {
ProtobufField f = _firstField;
if (f != null && name.equals(f.name)) {
return f;
}
// regardless, find the field
return _fieldsByName.findField(name);
}
public int getFieldCount() { return _fields.length; }
public String getName() { return _name; }
public ProtobufField field(String name) {
return _fieldsByName.findField(name);
}
// !!! TODO: optimize?
public ProtobufField field(int id)
{
// Can we just index it?
int idOffset = _idOffset;
if (idOffset >= 0) {
int index = id - idOffset;
if ((index < _fieldsById.length) && (index >= 0)) {
return _fieldsById[index];
}
}
// if not, brute force works
for (int i = 0, len = _fields.length; i < len; ++i) {
ProtobufField f = _fields[i];
if (f.id == id) {
return f;
}
}
// not found? that's ok with us, but caller may mind
return null;
}
public ProtobufField field(SerializableString name) {
return _fieldsByName.findField(name.getValue());
}
public String fieldsAsString() {
return Arrays.asList(_fields).toString();
}
public Iterable<ProtobufField> fields() {
return Arrays.asList(_fields);
}
}