use of com.google.template.soy.types.ListType in project closure-templates by google.
the class JsType method forSoyType.
/**
* Returns a {@link JsType} corresponding to the given {@link SoyType}
*
* <p>TODO(lukes): consider adding a cache for all the computed types. The same type is probably
* accessed many many times.
*
* @param soyType the soy type
* @param isIncrementalDom whether or not this is for incremental dom.
*/
static JsType forSoyType(SoyType soyType, boolean isIncrementalDom) {
switch(soyType.getKind()) {
case NULL:
return NULL_OR_UNDEFINED_TYPE;
case ANY:
return ANY_TYPE;
case UNKNOWN:
return UNKNOWN_TYPE;
case BOOL:
return BOOLEAN_TYPE;
case PROTO_ENUM:
SoyProtoEnumType enumType = (SoyProtoEnumType) soyType;
String enumTypeName = enumType.getNameForBackend(SoyBackendKind.JS_SRC);
return builder().addType("number").addType(enumTypeName).addRequire(GoogRequire.create(enumTypeName)).setPredicate(GOOG_IS_NUMBER).build();
case FLOAT:
case INT:
return NUMBER_TYPE;
case STRING:
return STRING_OR_UNSANITIZED_TEXT;
case ATTRIBUTES:
if (isIncrementalDom) {
// idom has a different strategy for handling these
return IDOM_ATTRIBUTES;
}
// fall through
case HTML:
if (isIncrementalDom) {
// idom has a different strategy for handling these
return IDOM_HTML;
}
// fall-through
case CSS:
case JS:
case URI:
case TRUSTED_RESOURCE_URI:
return STRICT_TYPES.get(((SanitizedType) soyType).getContentKind());
case LIST:
ListType listType = (ListType) soyType;
if (listType.getElementType().getKind() == SoyType.Kind.ANY) {
return RAW_ARRAY_TYPE;
}
JsType element = forSoyType(listType.getElementType(), isIncrementalDom);
return builder().addType("!Array<" + element.typeExpr() + ">").addRequires(element.getGoogRequires()).setPredicate(GOOG_IS_ARRAY).build();
case LEGACY_OBJECT_MAP:
{
LegacyObjectMapType mapType = (LegacyObjectMapType) soyType;
if (mapType.getKeyType().getKind() == SoyType.Kind.ANY && mapType.getValueType().getKind() == SoyType.Kind.ANY) {
return RAW_OBJECT_TYPE;
}
JsType keyTypeName = forSoyType(mapType.getKeyType(), isIncrementalDom);
JsType valueTypeName = forSoyType(mapType.getValueType(), isIncrementalDom);
return builder().addType(String.format("!Object<%s,%s>", keyTypeName.typeExpr(), valueTypeName.typeExpr())).addRequires(keyTypeName.getGoogRequires()).addRequires(valueTypeName.getGoogRequires()).setPredicate(GOOG_IS_OBJECT).build();
}
case MAP:
{
MapType mapType = (MapType) soyType;
SoyType keyType = mapType.getKeyType();
SoyType.Kind keyKind = keyType.getKind();
Preconditions.checkState(MapType.isAllowedKeyType(keyType));
// Soy key type of string should translate to a JS key type of string.
// forSoyType(StringType.getInstance()) normally translates to
// string|!goog.soy.data.UnsanitizedText, but ES6 Maps always use instance equality for
// lookups. Using UnsanitizedText instances as keys in Soy maps would cause unexpected
// behavior (usually a failed map lookup), so don't generate signatures that allow it.
JsType keyTypeName = keyKind == SoyType.Kind.STRING ? STRING_TYPE : forSoyType(keyType, isIncrementalDom);
JsType valueTypeName = forSoyType(mapType.getValueType(), isIncrementalDom);
return builder().addType(String.format("!soy.map.Map<%s,%s>", keyTypeName.typeExpr(), valueTypeName.typeExpr())).addRequires(keyTypeName.getGoogRequires()).addRequires(valueTypeName.getGoogRequires()).addRequire(GoogRequire.create("soy.map")).setPredicate(TypePredicate.NO_OP).build();
}
case PROTO:
final SoyProtoType protoType = (SoyProtoType) soyType;
final String protoTypeName = protoType.getNameForBackend(SoyBackendKind.JS_SRC);
// isn't clear that this is very useful for users.
return builder().addType(protoTypeName).addRequire(GoogRequire.create(protoTypeName)).addCoercionStrategy(ValueCoercionStrategy.PROTO).setPredicate(new TypePredicate() {
@Override
public Optional<CodeChunk.WithValue> maybeCheck(CodeChunk.WithValue value, Generator codeGenerator) {
return Optional.of(value.instanceof_(JsRuntime.protoConstructor(protoType)));
}
}).build();
case RECORD:
{
RecordType recordType = (RecordType) soyType;
if (recordType.getMembers().isEmpty()) {
return RAW_OBJECT_TYPE;
}
Builder builder = builder();
Map<String, String> members = new LinkedHashMap<>();
for (Map.Entry<String, SoyType> member : recordType.getMembers().entrySet()) {
JsType forSoyType = forSoyType(member.getValue(), isIncrementalDom);
builder.addRequires(forSoyType.getGoogRequires());
members.put(member.getKey(), forSoyType.typeExprForRecordMember(/* isOptional= */
false));
}
return builder.addType("{" + Joiner.on(", ").withKeyValueSeparator(": ").join(members) + ",}").setPredicate(GOOG_IS_OBJECT).build();
}
case UNION:
{
UnionType unionType = (UnionType) soyType;
Builder builder = builder();
final Set<JsType> types = new LinkedHashSet<>();
final boolean isNullable = unionType.isNullable();
// handle null first so that if other type tests dereference the param they won't fail
if (isNullable) {
builder.addTypes(NULL_OR_UNDEFINED_TYPE.typeExpressions);
builder.addCoercionStrategy(ValueCoercionStrategy.NULL);
types.add(NULL_OR_UNDEFINED_TYPE);
}
for (SoyType member : unionType.getMembers()) {
if (member.getKind() == Kind.NULL) {
// handled above
continue;
}
JsType memberType = forSoyType(member, isIncrementalDom);
builder.addRequires(memberType.extraRequires);
builder.addTypes(memberType.typeExpressions);
builder.addCoercionStrategies(memberType.coercionStrategies);
types.add(memberType);
}
return builder.setPredicate(new TypePredicate() {
@Override
public Optional<CodeChunk.WithValue> maybeCheck(CodeChunk.WithValue value, Generator codeGenerator) {
CodeChunk.WithValue result = null;
// this automatically.
for (JsType memberType : types) {
Optional<CodeChunk.WithValue> typeAssertion = memberType.getTypeAssertion(value, codeGenerator);
if (!typeAssertion.isPresent()) {
return Optional.absent();
}
if (result == null) {
result = typeAssertion.get();
} else {
result = result.or(typeAssertion.get(), codeGenerator);
}
}
return Optional.of(result);
}
}).build();
}
default:
throw new AssertionError("unhandled soytype: " + soyType);
}
}
use of com.google.template.soy.types.ListType in project closure-templates by google.
the class GenerateParseInfoVisitor method findProtoTypesRecurse.
/**
* Recursively search for protocol buffer types within the given type.
*
* @param type The type to search.
* @param protoTypes Output set.
*/
private static void findProtoTypesRecurse(SoyType type, SortedSet<String> protoTypes) {
if (type.getKind() == SoyType.Kind.PROTO) {
protoTypes.add(((SoyProtoType) type).getDescriptorExpression());
} else if (type.getKind() == SoyType.Kind.PROTO_ENUM) {
protoTypes.add(((SoyProtoEnumType) type).getDescriptorExpression());
} else {
switch(type.getKind()) {
case UNION:
for (SoyType member : ((UnionType) type).getMembers()) {
findProtoTypesRecurse(member, protoTypes);
}
break;
case LIST:
{
ListType listType = (ListType) type;
findProtoTypesRecurse(listType.getElementType(), protoTypes);
break;
}
case LEGACY_OBJECT_MAP:
{
LegacyObjectMapType mapType = (LegacyObjectMapType) type;
findProtoTypesRecurse(mapType.getKeyType(), protoTypes);
findProtoTypesRecurse(mapType.getValueType(), protoTypes);
break;
}
case RECORD:
{
RecordType recordType = (RecordType) type;
for (SoyType fieldType : recordType.getMembers().values()) {
findProtoTypesRecurse(fieldType, protoTypes);
}
break;
}
default:
break;
}
}
}
use of com.google.template.soy.types.ListType in project closure-templates by google.
the class CheckProtoInitCallsPass method checkProto.
private void checkProto(ProtoInitNode node, SoyProtoType soyType) {
// Check that all proto required fields are present.
// TODO(user): Consider writing a soyProtoTypeImpl.getRequiredFields()
Set<String> givenParams = Sets.newHashSet(node.getParamNames());
for (FieldDescriptor field : soyType.getDescriptor().getFields()) {
if (field.isRequired() && !givenParams.contains(field.getName())) {
errorReporter.report(node.getSourceLocation(), MISSING_REQUIRED_FIELD, field.getName());
}
}
ImmutableSet<String> fields = soyType.getFieldNames();
for (int i = 0; i < node.numChildren(); i++) {
String fieldName = node.getParamNames().get(i);
ExprNode expr = node.getChild(i);
// Check that each arg exists in the proto.
if (!fields.contains(fieldName)) {
String extraErrorMessage = SoyErrors.getDidYouMeanMessageForProtoFields(fields, fieldName);
errorReporter.report(expr.getSourceLocation(), FIELD_DOES_NOT_EXIST, fieldName, extraErrorMessage);
continue;
}
// Check that the arg type is not null and that it matches the expected field type.
SoyType argType = expr.getType();
if (argType.equals(NullType.getInstance())) {
errorReporter.report(expr.getSourceLocation(), NULL_ARG_TYPE, fieldName);
}
SoyType fieldType = soyType.getFieldType(fieldName);
// Let args with unknown or error types pass
if (argType.equals(UnknownType.getInstance()) || argType.equals(ErrorType.getInstance())) {
return;
}
// Same for List<?>, for repeated fields
if (fieldType.getKind() == Kind.LIST && argType.getKind() == Kind.LIST) {
SoyType argElementType = ((ListType) argType).getElementType();
if (argElementType == null || argElementType.equals(UnknownType.getInstance())) {
return;
}
}
SoyType expectedType = SoyTypes.makeNullable(fieldType);
if (!expectedType.isAssignableFrom(argType)) {
errorReporter.report(expr.getSourceLocation(), ARGUMENT_TYPE_MISMATCH, fieldName, expectedType, argType);
}
}
}
use of com.google.template.soy.types.ListType in project closure-templates by google.
the class ResolveExpressionTypesVisitorTest method testTypeParser.
@Test
public void testTypeParser() {
SoyType type = parseSoyType("string");
assertThat(type).isEqualTo(StringType.getInstance());
type = parseSoyType("int");
assertThat(type).isEqualTo(IntType.getInstance());
type = parseSoyType("list<any>");
assertThat(type.getKind()).isEqualTo(SoyType.Kind.LIST);
assertThat(((ListType) type).getElementType()).isEqualTo(AnyType.getInstance());
type = parseSoyType("map<string, ?>");
assertThat(type.getKind()).isEqualTo(Kind.MAP);
assertThat(((MapType) type).getKeyType()).isEqualTo(StringType.getInstance());
assertThat(((MapType) type).getValueType()).isEqualTo(UnknownType.getInstance());
}
use of com.google.template.soy.types.ListType in project closure-templates by google.
the class SoyExpressionTest method testListExpression.
@Test
public void testListExpression() {
ListType list = ListType.of(IntType.getInstance());
assertThatExpression(forList(list, constantNull(Type.getType(List.class))).coerceToBoolean()).evaluatesTo(false);
assertThatExpression(forList(list, constantNull(Type.getType(List.class))).box()).evaluatesTo(null);
assertThatExpression(forList(list, MethodRef.IMMUTABLE_LIST_OF.get(0).invoke()).coerceToBoolean()).evaluatesTo(true);
// SoyList uses Object identity for equality so we can't really assert on the value.
assertThatExpression(forList(list, MethodRef.IMMUTABLE_LIST_OF.get(0).invoke()).box()).evaluatesToInstanceOf(ListImpl.class);
}
Aggregations