void validate(Linker linker) { linker = linker.withContext(this); if (isPacked() && !isPackable(linker, type)) { linker.addError("packed=true not permitted on %s", type); } if (extension && isRequired()) { linker.addError("extension fields cannot be required", type); } linker.validateImport(location(), type); }
register(type); Linker linker = withContext(protoFile); for (Extend extend : protoFile.extendList()) { extend.link(linker); Linker linker = withContext(protoFile); for (Type type : protoFile.types()) { type.link(linker); Linker linker = withContext(protoFile); protoFile.linkOptions(linker); for (Type type : protoFile.types()) { addImports(sink, protoFile.imports(), publicImports); addImports(sink, protoFile.publicImports(), publicImports); Linker linker = withContext(protoFile); for (Type type : protoFile.types()) { type.validate(linker);
void link(Linker linker) { linker = linker.withContext(this); protoType = linker.resolveMessageType(name); Type type = linker.get(protoType); if (type != null) { ((MessageType) type).addExtensionFields(fields); } }
private ProtoType resolveType(String name, boolean messageOnly) { ProtoType type = ProtoType.get(name); if (type.isScalar()) { if (messageOnly) { addError("expected a message but was %s", name); } return type; } if (type.isMap()) { if (messageOnly) { addError("expected a message but was %s", name); } ProtoType keyType = resolveType(type.keyType().toString(), false); ProtoType valueType = resolveType(type.valueType().toString(), false); return new ProtoType(keyType, valueType, name); } Type resolved = resolve(name, protoTypeNames); if (resolved == null) { addError("unable to resolve %s", name); return ProtoType.BYTES; // Just return any placeholder. } if (messageOnly && !(resolved instanceof MessageType)) { addError("expected a message but was %s", name); return ProtoType.BYTES; // Just return any placeholder. } return resolved.type(); }
Map<ProtoMember, Object> canonicalizeOption( Linker linker, ProtoType extensionType, OptionElement option) { Type type = linker.get(extensionType); if (!(type instanceof MessageType)) { return null; // No known extensions for the given extension type. String packageName = linker.packageName(); if (path == null && packageName != null) { lastProtoType = field.type(); last = nested; field = linker.dereference(field, path[i]); if (field == null) { return null; // Unable to dereference this path segment.
int tag = field.tag(); if (!Util.isValidTag(tag)) { withContext(field).addError("tag is out of range: %s", tag); withContext(field).addError("tag %s is reserved (%s)", tag, reserved.location()); withContext(field).addError("name '%s' is reserved (%s)", field.name(), reserved.location()); index++, field.name(), field.location())); addError("%s", error); index++, field.name(), field.location())); addError("%s", error);
void validateImport(Location location, ProtoType type) { // Map key type is always scalar. No need to validate it. if (type.isMap()) type = type.valueType(); if (type.isScalar()) return; String path = location.path(); String requiredImport = get(type).location().path(); if (!path.equals(requiredImport) && !imports.containsEntry(path, requiredImport)) { addError("%s needs to import %s", path, requiredImport); } }
ImmutableMap.Builder<ProtoMember, Object> result = ImmutableMap.builder(); OptionElement option = (OptionElement) value; Field field = linker.dereference(context, option.name()); if (field == null) { linker.addError("unable to resolve option %s on %s", option.name(), context.type()); } else { ProtoMember protoMember = ProtoMember.get(context.type(), field); for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) { String name = (String) entry.getKey(); Field field = linker.dereference(context, name); if (field == null) { linker.addError("unable to resolve option %s on %s", name, context.type()); } else { ProtoMember protoMember = ProtoMember.get(context.type(), field);
private void validateTagUniqueness(Linker linker) { Multimap<Integer, EnumConstant> tagToConstant = LinkedHashMultimap.create(); for (EnumConstant constant : constants) { tagToConstant.put(constant.tag(), constant); } for (Map.Entry<Integer, Collection<EnumConstant>> entry : tagToConstant.asMap().entrySet()) { if (entry.getValue().size() > 1) { StringBuilder error = new StringBuilder(); error.append(String.format("multiple enum constants share tag %s:", entry.getKey())); int index = 1; for (EnumConstant constant : entry.getValue()) { error.append(String.format("\n %s. %s (%s)", index++, constant.name(), constant.location())); } linker.addError("%s", error); } } }
/** Add all paths in {@code paths} to {@code sink}, plus their public imports, recursively. */ private void addImports(Collection<String> sink, Collection<String> paths, Multimap<String, String> publicImports) { for (String path : paths) { if (sink.add(path)) { addImports(sink, publicImports.get(path), publicImports); } } }
/** Returns a new linker that uses {@code context} to resolve type names and report errors. */ Linker withContext(Object context) { return new Linker(this, context); }
private void register(Type type) { protoTypeNames.put(type.type().toString(), type); for (Type nestedType : type.nestedTypes()) { register(nestedType); } }
private boolean isPackable(Linker linker, ProtoType type) { return !type.equals(ProtoType.STRING) && !type.equals(ProtoType.BYTES) && !(linker.get(type) instanceof MessageType); }
/** Returns the field named {@code field} on the message type of {@code self}. */ Field dereference(Field self, String field) { if (field.startsWith("[") && field.endsWith("]")) { field = field.substring(1, field.length() - 1); } Type type = protoTypeNames.get(self.type().toString()); if (type instanceof MessageType) { MessageType messageType = (MessageType) type; Field messageField = messageType.field(field); if (messageField != null) return messageField; Map<String, Field> typeExtensions = messageType.extensionFieldsMap(); Field extensionField = resolve(field, typeExtensions); if (extensionField != null) return extensionField; } return null; // Unable to traverse this field path. }
<T> T resolve(String name, Map<String, T> map) { if (name.startsWith(".")) { // If name starts with a '.', the rest of it is fully qualified. T result = map.get(name.substring(1)); if (result != null) return result; } else { // We've got a name suffix, like 'Person' or 'protos.Person'. Start the search from with the // longest prefix like foo.bar.Baz.Quux, shortening the prefix until we find a match. String prefix = resolveContext(); while (!prefix.isEmpty()) { T result = map.get(prefix + '.' + name); if (result != null) return result; // Strip the last nested class name or package name from the end and try again. int dot = prefix.lastIndexOf('.'); prefix = dot != -1 ? prefix.substring(0, dot) : ""; } T result = map.get(name); if (result != null) return result; } return null; }
void link(Linker linker) { linker = linker.withContext(this); protoType = linker.resolveMessageType(name); Type type = linker.get(protoType); if (type != null) { ((MessageType) type).addExtensionFields(fields); } }
private ProtoType resolveType(String name, boolean messageOnly) { ProtoType type = ProtoType.get(name); if (type.isScalar()) { if (messageOnly) { addError("expected a message but was %s", name); } return type; } if (type.isMap()) { if (messageOnly) { addError("expected a message but was %s", name); } ProtoType keyType = resolveType(type.keyType().toString(), false); ProtoType valueType = resolveType(type.valueType().toString(), false); return new ProtoType(keyType, valueType, name); } Type resolved = resolve(name, protoTypeNames); if (resolved == null) { addError("unable to resolve %s", name); return ProtoType.BYTES; // Just return any placeholder. } if (messageOnly && !(resolved instanceof MessageType)) { addError("expected a message but was %s", name); return ProtoType.BYTES; // Just return any placeholder. } return resolved.type(); }
Map<ProtoMember, Object> canonicalizeOption( Linker linker, ProtoType extensionType, OptionElement option) { Type type = linker.get(extensionType); if (!(type instanceof MessageType)) { return null; // No known extensions for the given extension type. String packageName = linker.packageName(); if (path == null && packageName != null) { lastProtoType = field.type(); last = nested; field = linker.dereference(field, path[i]); if (field == null) { return null; // Unable to dereference this path segment.
void validate(Linker linker) { if (!Util.isValidTag(start()) || !Util.isValidTag(end())) { linker.withContext(this).addError("tags are out of range: %s to %s", start(), end()); } } }