diff options
Diffstat (limited to 'java/src/main/java/com/google/protobuf/Descriptors.java')
-rw-r--r-- | java/src/main/java/com/google/protobuf/Descriptors.java | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index caae0f77..d65e8b49 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -32,6 +32,7 @@ package com.google.protobuf; import com.google.protobuf.DescriptorProtos.*; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -40,6 +41,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.logging.Logger; import java.io.UnsupportedEncodingException; @@ -123,6 +125,26 @@ public final class Descriptors { return Collections.unmodifiableList(Arrays.asList(publicDependencies)); } + /** The syntax of the .proto file. */ + public enum Syntax { + UNKNOWN("unknown"), + PROTO2("proto2"), + PROTO3("proto3"); + + Syntax(String name) { + this.name = name; + } + private final String name; + } + + /** Get the syntax of the .proto file. */ + public Syntax getSyntax() { + if (Syntax.PROTO3.name.equals(proto.getSyntax())) { + return Syntax.PROTO3; + } + return Syntax.PROTO2; + } + /** * Find a message type in the file by name. Does not find nested types. * @@ -539,6 +561,10 @@ public final class Descriptors { extensions[i].setProto(proto.getExtension(i)); } } + + boolean supportsUnknownEnumValue() { + return getSyntax() == Syntax.PROTO3; + } } // ================================================================= @@ -871,6 +897,11 @@ public final class Descriptors { return (type == Type.STRING) && (getFile().getOptions().getJavaStringCheckUtf8()); } + boolean isMapField() { + return getType() == Type.MESSAGE && isRepeated() + && getMessageType().getOptions().getMapEntry(); + } + // I'm pretty sure values() constructs a new array every time, since there // is nothing stopping the caller from mutating the array. Therefore we // make a static copy here. @@ -1001,6 +1032,11 @@ public final class Descriptors { return getNumber() - other.getNumber(); } + @Override + public String toString() { + return getFullName(); + } + private final int index; private FieldDescriptorProto proto; @@ -1420,6 +1456,64 @@ public final class Descriptors { return file.pool.enumValuesByNumber.get( new DescriptorPool.DescriptorIntPair(this, number)); } + + /** + * Get the enum value for a number. If no enum value has this number, + * construct an EnumValueDescriptor for it. + */ + public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) { + EnumValueDescriptor result = findValueByNumber(number); + if (result != null) { + return result; + } + // The number represents an unknown enum value. + synchronized (this) { + // Descriptors are compared by object identity so for the same number + // we need to return the same EnumValueDescriptor object. This means + // we have to store created EnumValueDescriptors. However, as there + // are potentially 2G unknown enum values, storing all of these + // objects persistently will consume lots of memory for long-running + // services and it's also unnecessary as not many EnumValueDescriptors + // will be used at the same time. + // + // To solve the problem we take advantage of Java's weak references and + // rely on gc to release unused descriptors. + // + // Here is how it works: + // * We store unknown EnumValueDescriptors in a WeakHashMap with the + // value being a weak reference to the descriptor. + // * The descriptor holds a strong reference to the key so as long + // as the EnumValueDescriptor is in use, the key will be there + // and the corresponding map entry will be there. Following-up + // queries with the same number will return the same descriptor. + // * If the user no longer uses an unknown EnumValueDescriptor, + // it will be gc-ed since we only hold a weak reference to it in + // the map. The key in the corresponding map entry will also be + // gc-ed as the only strong reference to it is in the descriptor + // which is just gc-ed. With the key being gone WeakHashMap will + // then remove the whole entry. This way unknown descriptors will + // be freed automatically and we don't need to do anything to + // clean-up unused map entries. + + // Note: We must use "new Integer(number)" here because we don't want + // these Integer objects to be cached. + Integer key = new Integer(number); + WeakReference<EnumValueDescriptor> reference = unknownValues.get(key); + if (reference != null) { + result = reference.get(); + } + if (result == null) { + result = new EnumValueDescriptor(file, this, key); + unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result)); + } + } + return result; + } + + // Used in tests only. + int getUnknownEnumValueDescriptorCount() { + return unknownValues.size(); + } private final int index; private EnumDescriptorProto proto; @@ -1427,6 +1521,8 @@ public final class Descriptors { private final FileDescriptor file; private final Descriptor containingType; private EnumValueDescriptor[] values; + private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues = + new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>(); private EnumDescriptor(final EnumDescriptorProto proto, final FileDescriptor file, @@ -1531,6 +1627,25 @@ public final class Descriptors { file.pool.addSymbol(this); file.pool.addEnumValueByNumber(this); } + + private Integer number; + // Create an unknown enum value. + private EnumValueDescriptor( + final FileDescriptor file, + final EnumDescriptor parent, + final Integer number) { + String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number; + EnumValueDescriptorProto proto = EnumValueDescriptorProto + .newBuilder().setName(name).setNumber(number).build(); + this.index = -1; + this.proto = proto; + this.file = file; + this.type = parent; + this.fullName = parent.getFullName() + '.' + proto.getName(); + this.number = number; + + // Don't add this descriptor into pool. + } /** See {@link FileDescriptor#setProto}. */ private void setProto(final EnumValueDescriptorProto proto) { |