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 init(ProtobufField) is called.
/** * 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 _idOffset), if fields ids are in contiguous (enough) range.
/** * 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.
/** * 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); } }