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(); }
private CodeBlock singleAdapterFor(ProtoType type) { CodeBlock.Builder result = CodeBlock.builder(); if (type.isScalar()) { result.add("$T.$L", ADAPTER, type.simpleName().toUpperCase(Locale.US)); } else if (type.isMap()) { throw new IllegalArgumentException("Cannot create single adapter for map type " + type); } else { AdapterConstant adapterConstant = profile.getAdapter(type); if (adapterConstant != null) { result.add("$T.$L", adapterConstant.className, adapterConstant.memberName); } else { result.add("$T.ADAPTER", typeName(type)); } } return result.build(); }
@Test public void messageToString() throws Exception { ProtoType person = ProtoType.get("squareup.protos.person.Person"); assertThat(person.toString()).isEqualTo("squareup.protos.person.Person"); ProtoType phoneType = person.nestedType("PhoneType"); assertThat(phoneType.toString()).isEqualTo("squareup.protos.person.Person.PhoneType"); }
/** Creates a map type. */ ProtoType(ProtoType keyType, ProtoType valueType, String string) { checkNotNull(keyType, "keyType == null"); checkNotNull(valueType, "valueType == null"); checkNotNull(string, "string == null"); checkArgument( keyType.isScalar() && !keyType.equals(BYTES) && !keyType.equals(DOUBLE) && !keyType.equals( FLOAT), "map key must be non-byte, non-floating point scalar: %s", keyType); this.isScalar = false; this.string = string; this.isMap = true; this.keyType = keyType; // TODO restrict what's allowed here this.valueType = valueType; }
public static ProtoType get(String name) { ProtoType scalar = SCALAR_TYPES.get(name); if (scalar != null) return scalar; if (name == null || name.isEmpty() || name.contains("#")) { throw new IllegalArgumentException("unexpected name: " + name); } if (name.startsWith("map<") && name.endsWith(">")) { int comma = name.indexOf(','); if (comma == -1) throw new IllegalArgumentException("expected ',' in map type: " + name); ProtoType key = get(name.substring(4, comma).trim()); ProtoType value = get(name.substring(comma + 1, name.length() - 1).trim()); return new ProtoType(key, value, name); } return new ProtoType(false, name); }
private TypeName fieldType(Field field) { ProtoType type = field.type(); if (type.isMap()) { return ParameterizedTypeName.get(ClassName.get(Map.class), typeName(type.keyType()), typeName(type.valueType())); } TypeName messageType = typeName(type); return field.isRepeated() ? listOf(messageType) : messageType; }
private void mark(ProtoType type) { // Mark the map type as it's non-scalar and transitively reachable. if (type.isMap()) { marks.mark(type); // Map key type is always scalar. No need to mark it. type = type.valueType(); } if (marks.mark(type)) { queue.add(type); // The transitive dependencies of this type must be visited. } }
/** Returns the type or null if it doesn't exist. */ public Type get(ProtoType protoType) { return protoTypeNames.get(protoType.toString()); }
private MethodSpec messageToString(NameAllocator nameAllocator, MessageType type) { NameAllocator localNameAllocator = nameAllocator.clone(); MethodSpec.Builder result = MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(PUBLIC) .returns(String.class); String builderName = localNameAllocator.newName("builder"); result.addStatement("$1T $2N = new $1T()", StringBuilder.class, builderName); for (Field field : type.fieldsAndOneOfFields()) { String fieldName = nameAllocator.get(field); if (field.isRepeated() || field.type().isMap()) { result.addCode("if (!$N.isEmpty()) ", fieldName); } else if (!field.isRequired()) { result.addCode("if ($N != null) ", fieldName); } if (field.isRedacted()) { result.addStatement("$N.append(\", $N=██\")", builderName, field.name()); } else { result.addStatement("$N.append(\", $N=\").append($L)", builderName, field.name(), fieldName); } } result.addStatement("return builder.replace(0, 2, \"$L{\").append('}').toString()", type.type().simpleName()); return result.build(); }
private String adapterString(ProtoType type) { if (type.isScalar()) { return ProtoAdapter.class.getName() + '#' + type.toString().toUpperCase(Locale.US); } AdapterConstant adapterConstant = profile.getAdapter(type); if (adapterConstant != null) { return reflectionName(adapterConstant.className) + "#" + adapterConstant.memberName; } return reflectionName((ClassName) typeName(type)) + "#ADAPTER"; }
@Test public void enclosingTypeOrPackage() throws Exception { assertThat(ProtoType.STRING.enclosingTypeOrPackage()).isNull(); ProtoType person = ProtoType.get("squareup.protos.person.Person"); assertThat(person.enclosingTypeOrPackage()).isEqualTo("squareup.protos.person"); ProtoType phoneType = person.nestedType("PhoneType"); assertThat(phoneType.enclosingTypeOrPackage()).isEqualTo("squareup.protos.person.Person"); }
@Test public void simpleName() throws Exception { ProtoType person = ProtoType.get("squareup.protos.person.Person"); assertThat(person.simpleName()).isEqualTo("Person"); }
@Test public void nestedType() throws Exception { assertThat(ProtoType.get("squareup.protos.person.Person").nestedType("PhoneType")) .isEqualTo(ProtoType.get("squareup.protos.person.Person.PhoneType")); }
private MethodSpec messageHashCode(NameAllocator nameAllocator, MessageType type) { NameAllocator localNameAllocator = nameAllocator.clone(); String resultName = localNameAllocator.newName("result"); MethodSpec.Builder result = MethodSpec.methodBuilder("hashCode") .addAnnotation(Override.class) .addModifiers(PUBLIC) .returns(int.class); List<Field> fields = type.fieldsAndOneOfFields(); if (fields.isEmpty()) { result.addStatement("return unknownFields().hashCode()"); return result.build(); } result.addStatement("int $N = super.hashCode", resultName); result.beginControlFlow("if ($N == 0)", resultName); result.addStatement("$N = unknownFields().hashCode()", resultName); for (Field field : fields) { String fieldName = localNameAllocator.get(field); result.addCode("$1N = $1N * 37 + ", resultName); if (field.isRepeated() || field.isRequired() || field.type().isMap()) { result.addStatement("$L.hashCode()", fieldName); } else { result.addStatement("($1L != null ? $1L.hashCode() : 0)", fieldName); } } result.addStatement("super.hashCode = $N", resultName); result.endControlFlow(); result.addStatement("return $N", resultName); return result.build(); }
@Test public void linkMessage() throws Exception { Schema schema = new RepoBuilder() .add("message.proto", "" + "import \"foo.proto\";\n" + "message Message {\n" + " optional foo_package.Foo field = 1;\n" + " map<string, foo_package.Bar> bars = 2;\n" + "}\n") .add("foo.proto", "" + "package foo_package;\n" + "message Foo {\n" + "}\n" + "message Bar {\n" + "}\n") .schema(); MessageType message = (MessageType) schema.getType("Message"); Field field = message.field("field"); assertThat(field.type()).isEqualTo(schema.getType("foo_package.Foo").type()); ProtoType bars = message.field("bars").type(); assertThat(bars.keyType()).isEqualTo(ProtoType.STRING); assertThat(bars.valueType()).isEqualTo(schema.getType("foo_package.Bar").type()); }
private static void putAll(Map<ProtoType, ClassName> wireToJava, String javaPackage, ClassName enclosingClassName, List<Type> types) { for (Type type : types) { ClassName className = enclosingClassName != null ? enclosingClassName.nestedClass(type.type().simpleName()) : ClassName.get(javaPackage, type.type().simpleName()); wireToJava.put(type.type(), className); putAll(wireToJava, javaPackage, className, type.nestedTypes()); } }