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. } MessageType messageType = (MessageType) type; String[] path; Field field = messageType.field(option.name()); if (field != null) { // This is an option declared by descriptor.proto. path = new String[] {option.name()}; } else { // This is an option declared by an extension. Map<String, Field> extensionsForType = messageType.extensionFieldsMap(); path = resolveFieldPath(option.name(), extensionsForType.keySet()); String packageName = linker.packageName(); if (path == null && packageName != null) { // If the path couldn't be resolved, attempt again by prefixing it with the package name. path = resolveFieldPath(packageName + "." + option.name(), extensionsForType.keySet()); } if (path == null) { return null; // Unable to find the root of this field path. } field = extensionsForType.get(path[0]); } Map<ProtoMember, Object> result = new LinkedHashMap<>(); Map<ProtoMember, Object> last = result; ProtoType lastProtoType = messageType.type(); for (int i = 1; i < path.length; i++) { Map<ProtoMember, Object> nested = new LinkedHashMap<>(); last.put(ProtoMember.get(lastProtoType, field), nested); lastProtoType = field.type(); last = nested; field = linker.dereference(field, path[i]); if (field == null) { return null; // Unable to dereference this path segment. } } last.put( ProtoMember.get(lastProtoType, field), canonicalizeValue(linker, field, option.value())); return result; }
private Object canonicalizeValue(Linker linker, Field context, Object value) { if (value instanceof OptionElement) { 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); result.put(protoMember, canonicalizeValue(linker, field, option.value())); } return coerceValueForField(context, result.build()); } if (value instanceof Map) { ImmutableMap.Builder<ProtoMember, Object> result = ImmutableMap.builder(); 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); result.put(protoMember, canonicalizeValue(linker, field, entry.getValue())); } } return coerceValueForField(context, result.build()); } if (value instanceof List) { ImmutableList.Builder<Object> result = ImmutableList.builder(); for (Object element : (List<?>) value) { result.addAll((List) canonicalizeValue(linker, context, element)); } return coerceValueForField(context, result.build()); } if (value instanceof String) { return coerceValueForField(context, value); } throw new IllegalArgumentException("Unexpected option value: " + value); }