diff --git a/protobuf/pom.xml b/protobuf/pom.xml
index 185db965b..7c78af94b 100644
--- a/protobuf/pom.xml
+++ b/protobuf/pom.xml
@@ -47,6 +47,10 @@ abstractions.
jackson-annotations
test
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
diff --git a/protobuf/src/main/java/com/fasterxml/jackson/dataformat/protobuf/schema/DescriptorLoader.java b/protobuf/src/main/java/com/fasterxml/jackson/dataformat/protobuf/schema/DescriptorLoader.java
new file mode 100644
index 000000000..ad282f4ca
--- /dev/null
+++ b/protobuf/src/main/java/com/fasterxml/jackson/dataformat/protobuf/schema/DescriptorLoader.java
@@ -0,0 +1,82 @@
+package com.fasterxml.jackson.dataformat.protobuf.schema;
+
+
+import com.fasterxml.jackson.dataformat.protobuf.ProtobufMapper;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Class used for loading protobuf descriptors (from .desc files
+ * or equivalent sources), to construct FileDescriptorSet.
+ */
+public class DescriptorLoader
+{
+ private final String DESCRIPTOR_PROTO = "/descriptor.proto";
+ private ProtobufMapper descriptorMapper;
+ private ProtobufSchema descriptorFileSchema;
+
+ /**
+ * Standard loader instance that is usually used for loading descriptor file.
+ */
+ public final static DescriptorLoader std = new DescriptorLoader();
+
+ public DescriptorLoader() {}
+
+
+ /**
+ * Public API
+ */
+
+ public FileDescriptorSet load(URL url) throws IOException
+ {
+ return _loadFileDescriptorSet(url.openStream());
+ }
+
+ public FileDescriptorSet load(File f) throws IOException
+ {
+ return _loadFileDescriptorSet(new FileInputStream(f));
+ }
+
+ public FileDescriptorSet load(InputStream in) throws IOException
+ {
+ return _loadFileDescriptorSet(in);
+ }
+
+ public FileDescriptorSet fromBytes(byte[] descriptorBytes) throws IOException
+ {
+ return _loadFileDescriptorSet(new ByteArrayInputStream(descriptorBytes));
+ }
+
+ protected FileDescriptorSet _loadFileDescriptorSet(InputStream in) throws IOException
+ {
+ try {
+ if (descriptorMapper == null) {
+ createDescriptorMapper();
+ }
+ return descriptorMapper.readerFor(FileDescriptorSet.class)
+ .with(descriptorFileSchema)
+ .readValue(in);
+ }
+ finally {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ }
+ }
+ }
+
+ private void createDescriptorMapper() throws IOException
+ {
+ // read Descriptor Proto
+ descriptorMapper = new ProtobufMapper();
+ InputStream in = getClass().getResourceAsStream(DESCRIPTOR_PROTO);
+ descriptorFileSchema = ProtobufSchemaLoader.std.load(in, "FileDescriptorSet");
+ in.close();
+ }
+}
diff --git a/protobuf/src/main/java/com/fasterxml/jackson/dataformat/protobuf/schema/FileDescriptorSet.java b/protobuf/src/main/java/com/fasterxml/jackson/dataformat/protobuf/schema/FileDescriptorSet.java
new file mode 100644
index 000000000..186b17d87
--- /dev/null
+++ b/protobuf/src/main/java/com/fasterxml/jackson/dataformat/protobuf/schema/FileDescriptorSet.java
@@ -0,0 +1,492 @@
+package com.fasterxml.jackson.dataformat.protobuf.schema;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.squareup.protoparser.DataType;
+import com.squareup.protoparser.EnumConstantElement;
+import com.squareup.protoparser.EnumElement;
+import com.squareup.protoparser.FieldElement;
+import com.squareup.protoparser.MessageElement;
+import com.squareup.protoparser.OptionElement;
+import com.squareup.protoparser.ProtoFile;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class FileDescriptorSet
+{
+ public FileDescriptorProto[] file;
+
+ public FileDescriptorProto findFileByName(String fileName)
+ {
+ for (FileDescriptorProto fdp : file) {
+ if (fdp.name.equals(fileName)) {
+ return fdp;
+ }
+ }
+ throw new IllegalArgumentException(fileName + " not found");
+ }
+
+ public ProtobufSchema forFirstType()
+ {
+ FileDescriptorProto fdp0 = file[0];
+ ProtoFile protoFile = buildProtoFile(fdp0.name);
+ NativeProtobufSchema nps = NativeProtobufSchema.construct(protoFile);
+ return nps.forFirstType();
+ }
+
+ public ProtobufSchema forType(String rootTypeName)
+ {
+ for (FileDescriptorProto fdp : file) {
+ for (DescriptorProto dp : fdp.message_type) {
+ if (dp.name.equals(rootTypeName)) {
+ System.out.println(dp);
+ ProtoFile protoFile = buildProtoFile(fdp.name);
+ NativeProtobufSchema nps = NativeProtobufSchema.construct(protoFile);
+ return nps.forType(rootTypeName);
+ }
+ }
+ }
+ throw new IllegalArgumentException(rootTypeName + " not found");
+ }
+
+ private ProtoFile buildProtoFile(String fileName)
+ {
+ FileDescriptorProto fdp = findFileByName(fileName);
+ ProtoFile.Builder builder = ProtoFile.builder(fdp.name);
+ builder.syntax(fdp.getSyntax());
+ builder.packageName(fdp.packageName);
+
+ // dependency file names.
+ if (fdp.dependency != null) {
+ for (String dependency : fdp.dependency) {
+ FileDescriptorProto dep = findFileByName(dependency);
+ for (DescriptorProto dp : dep.message_type) {
+ MessageElement me = dp.buildMessageElement();
+ builder.addType(me);
+ }
+ }
+ }
+
+ // FIXME: public dependency file names.
+// if (fdp.public_dependency) {
+// for (DescriptorProto dp : fdp.public_dependency) {
+// String dep = fdp.getDependency();
+// builder.addPublicDependency(dep);
+// }
+// }
+
+ // types
+ for (DescriptorProto dp : fdp.message_type) {
+ MessageElement me = dp.buildMessageElement();
+ builder.addType(me);
+ }
+
+ // FIXME: implement following features
+ // services
+ // extendDeclarations
+ // options
+
+ return builder.build();
+ }
+
+
+ // POJOs for the .desc file Protobuf
+ static class FileDescriptorProto
+ {
+ public String name;
+ @JsonProperty("package")
+ public String packageName;
+ public String[] dependency;
+ public int[] public_dependency;
+ public int[] weak_dependency;
+ public DescriptorProto[] message_type;
+ public EnumDescriptorProto[] enum_type;
+ public ServiceDescriptorProto[] service;
+ public FieldDescriptorProto[] extension;
+ public FileOptions options;
+ public SourceCodeInfo source_code_info;
+ public String syntax;
+
+ public ProtoFile.Syntax getSyntax()
+ {
+ if (syntax == null) {
+ return ProtoFile.Syntax.PROTO_2;
+ }
+ return ProtoFile.Syntax.valueOf(syntax);
+ }
+ }
+
+ static class DescriptorProto
+ {
+ public String name;
+ public FieldDescriptorProto[] field;
+ public FieldDescriptorProto[] extension;
+ public DescriptorProto[] nested_type;
+ public EnumDescriptorProto[] enum_type;
+
+ static class ExtensionRange
+ {
+ public int start;
+ public int end;
+ }
+
+ public ExtensionRange[] extension_range;
+ public OneofDescriptorProto[] oneof_decl;
+ public MessageOptions options;
+
+ static class ReservedRange
+ {
+ public int start; // Inclusive.
+ public int end; // Exclusive.
+ }
+
+ public ReservedRange[] reserved_range;
+ public String[] reserved_name;
+
+ public MessageElement buildMessageElement()
+ {
+ MessageElement.Builder messageElementBuilder = MessageElement.builder();
+ messageElementBuilder.name(name);
+ // fields
+ if (field != null) {
+ for (FieldDescriptorProto f : field) {
+ DataType dataType;
+ String fieldName = f.name;
+ FieldDescriptorProto.Type type = f.type;
+ FieldElement.Label label = f.getLabel();
+ // message and enum fields are named fields
+ if (type.equals(FieldDescriptorProto.Type.TYPE_MESSAGE)
+ || type.equals(FieldDescriptorProto.Type.TYPE_ENUM)) {
+ String fullyQualifiedtypeName = f.type_name; // fully qualified name including package name.
+ String typeName = fullyQualifiedtypeName.substring(fullyQualifiedtypeName.indexOf(".", 2) + 1);
+ dataType = DataType.NamedType.create(typeName);
+ } else {
+ dataType = f.getDataType();
+ }
+
+ // build field
+ FieldElement.Builder fieldBuilder = FieldElement
+ .builder()
+ .name(fieldName)
+ .type(dataType)
+ .label(label)
+ .tag(f.number);
+
+ // add field options to the field
+ if (f.json_name != null) {
+ OptionElement.Kind kind = OptionElement.Kind.STRING;
+ OptionElement option = OptionElement.create("json_name", kind, f.json_name);
+ fieldBuilder.addOption(option);
+ }
+
+ if (f.options != null) {
+ if (f.options.packed) {
+ OptionElement.Kind kind = OptionElement.Kind.STRING;
+ OptionElement option = OptionElement.create("packed", kind, "true");
+ fieldBuilder.addOption(option);
+ }
+ }
+ // add the field to the message
+ messageElementBuilder.addField(fieldBuilder.build());
+ }
+ }
+
+ // message type declarations
+ if (nested_type != null) {
+ for (DescriptorProto n : nested_type) {
+ messageElementBuilder.addType(n.buildMessageElement());
+ }
+ }
+
+ // enum declarations
+ if (enum_type != null) {
+ for (EnumDescriptorProto e : enum_type) {
+ EnumElement.Builder nestedEnumElement = EnumElement
+ .builder()
+ .name(e.name);
+ for (EnumValueDescriptorProto v : e.value) {
+ EnumConstantElement.Builder c = EnumConstantElement.builder()
+ .name(v.name)
+ .tag(v.number);
+ nestedEnumElement.addConstant(c.build());
+ }
+ messageElementBuilder.addType(nestedEnumElement.build());
+ }
+ }
+ return messageElementBuilder.build();
+ }
+
+ }
+
+ static class FieldDescriptorProto
+ {
+ enum Type
+ {
+ TYPE_DOUBLE,
+ TYPE_FLOAT,
+ TYPE_INT64,
+ TYPE_UINT64,
+ TYPE_INT32,
+ TYPE_FIXED64,
+ TYPE_FIXED32,
+ TYPE_BOOL,
+ TYPE_STRING,
+ TYPE_GROUP,
+ TYPE_MESSAGE,
+ TYPE_BYTES,
+ TYPE_UINT32,
+ TYPE_ENUM,
+ TYPE_SFIXED32,
+ TYPE_SFIXED64,
+ TYPE_SINT32,
+ TYPE_SINT64
+ }
+
+ enum Label
+ {
+ LABEL_OPTIONAL,
+ LABEL_REQUIRED,
+ LABEL_REPEATED
+ }
+
+ public String name;
+ public int number;
+ public Label label;
+ public Type type;
+ public String type_name;
+ public String extendee;
+ public String default_value;
+ public int oneof_index;
+ public String json_name;
+ public FieldOptions options;
+
+ static private Map scalarTypeMap = new HashMap();
+ static private Map