use of com.google.template.soy.types.SoyProtoType 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.SoyProtoType in project closure-templates by google.
the class GenerateParseInfoVisitor method visitSoyFileNode.
@Override
protected void visitSoyFileNode(SoyFileNode node) {
if (node.getSoyFileKind() != SoyFileKind.SRC) {
// don't generate code for deps
return;
}
String javaClassName = soyFileToJavaClassNameMap.get(node);
// Collect the following:
// + all the public basic templates (non-private, non-delegate) in a map from the
// upper-underscore template name to the template's node,
// + all the param keys from all templates (including private),
// + for each param key, the list of templates that list it directly.
// + for any params whose type is a proto, get the proto name and Java class name.
LinkedHashMap<String, TemplateNode> publicBasicTemplateMap = Maps.newLinkedHashMap();
List<String> deltemplates = new ArrayList<>();
Set<String> allParamKeys = Sets.newHashSet();
SetMultimap<String, TemplateNode> paramKeyToTemplatesMultimap = LinkedHashMultimap.create();
SortedSet<String> protoTypes = Sets.newTreeSet();
for (TemplateNode template : node.getChildren()) {
if (template.getVisibility() == Visibility.PUBLIC && template instanceof TemplateBasicNode) {
publicBasicTemplateMap.put(convertToUpperUnderscore(template.getPartialTemplateName().substring(1)), template);
}
if (template instanceof TemplateDelegateNode) {
deltemplates.add("\"" + template.getTemplateName() + "\"");
}
for (TemplateParam param : template.getAllParams()) {
if (!param.isInjected()) {
allParamKeys.add(param.name());
paramKeyToTemplatesMultimap.put(param.name(), template);
}
if (param instanceof HeaderParam) {
SoyType paramType = ((HeaderParam) param).type();
findProtoTypesRecurse(paramType, protoTypes);
}
}
// Field access nodes need special handling to ensure that extension references are handled.
for (FieldAccessNode fieldAccess : SoyTreeUtils.getAllNodesOfType(template, FieldAccessNode.class)) {
SoyType baseType = fieldAccess.getBaseExprChild().getType();
if (baseType.getKind() == SoyType.Kind.PROTO) {
FieldDescriptor desc = ((SoyProtoType) baseType).getFieldDescriptor(fieldAccess.getFieldName());
if (desc.isExtension()) {
protoTypes.add(ProtoUtils.getTofuExtensionImport(desc));
}
}
}
// Add enums
for (GlobalNode global : SoyTreeUtils.getAllNodesOfType(template, GlobalNode.class)) {
if (global.isResolved() && global.getType().getKind() == SoyType.Kind.PROTO_ENUM) {
protoTypes.add(((SoyProtoEnumType) global.getType()).getDescriptorExpression());
}
}
// Add proto init
for (ProtoInitNode protoInit : SoyTreeUtils.getAllNodesOfType(template, ProtoInitNode.class)) {
if (protoInit.getType().getKind() == SoyType.Kind.PROTO) {
protoTypes.add(((SoyProtoType) protoInit.getType()).getDescriptorExpression());
}
}
}
// allParamKeysMap is a map from upper-underscore key to original key.
SortedMap<String, String> allParamKeysMap = Maps.newTreeMap();
for (String key : allParamKeys) {
String upperUnderscoreKey = convertToUpperUnderscore(key);
// stage of the compiler.
while (allParamKeysMap.containsKey(upperUnderscoreKey)) {
upperUnderscoreKey = upperUnderscoreKey + "_";
}
allParamKeysMap.put(upperUnderscoreKey, key);
// Updates the convertedIdents here, since we might have changed the value by adding
// prepending underscores. Without this, the generated SoyTemplateInfo still use the
// old mapping and will fail.
convertedIdents.put(key, upperUnderscoreKey);
}
ilb = new IndentedLinesBuilder(2);
// ------ Header. ------
ilb.appendLine("// This file was automatically generated from ", node.getFileName(), ".");
ilb.appendLine("// Please don't edit this file by hand.");
ilb.appendLine();
ilb.appendLine("package ", javaPackage, ";");
ilb.appendLine();
ilb.appendLine("import com.google.common.collect.ImmutableList;");
ilb.appendLine("import com.google.common.collect.ImmutableMap;");
ilb.appendLine("import com.google.common.collect.ImmutableSortedSet;");
if (!protoTypes.isEmpty()) {
ilb.appendLine("import com.google.protobuf.Descriptors.GenericDescriptor;");
}
ilb.appendLine("import com.google.template.soy.parseinfo.SoyFileInfo;");
ilb.appendLine("import com.google.template.soy.parseinfo.SoyTemplateInfo;");
// ------ Class start. ------
ilb.appendLine();
ilb.appendLine();
appendJavadoc(ilb, "Soy parse info for " + node.getFileName() + ".", true, false);
ilb.appendLine("public final class ", javaClassName, " extends SoyFileInfo {");
ilb.increaseIndent();
// ------ Constant for namespace. ------
ilb.appendLine();
ilb.appendLine();
ilb.appendLine("/** This Soy file's namespace. */");
ilb.appendLine("public static final String __NAMESPACE__ = \"", node.getNamespace(), "\";");
// ------ Proto types map. ------
if (!protoTypes.isEmpty()) {
ilb.appendLine();
ilb.appendLine();
ilb.appendLine("/** Protocol buffer types used by these templates. */");
ilb.appendLine("@Override public ImmutableList<GenericDescriptor> getProtoDescriptors() {");
ilb.increaseIndent();
// Note we use fully-qualified names instead of imports to avoid potential collisions.
List<String> defaultInstances = Lists.newArrayList();
defaultInstances.addAll(protoTypes);
appendListOrSetHelper(ilb, "return ImmutableList.<GenericDescriptor>of", defaultInstances);
ilb.appendLineEnd(";");
ilb.decreaseIndent();
ilb.appendLine("}");
}
// ------ Template names. ------
ilb.appendLine();
ilb.appendLine();
ilb.appendLine("public static final class TemplateName {");
ilb.increaseIndent();
ilb.appendLine("private TemplateName() {}");
ilb.appendLine();
for (Entry<String, TemplateNode> templateEntry : publicBasicTemplateMap.entrySet()) {
StringBuilder javadocSb = new StringBuilder();
javadocSb.append("The full template name of the ").append(templateEntry.getValue().getPartialTemplateName()).append(" template.");
appendJavadoc(ilb, javadocSb.toString(), false, true);
ilb.appendLine("public static final String ", templateEntry.getKey(), " = \"", templateEntry.getValue().getTemplateName(), "\";");
}
ilb.decreaseIndent();
ilb.appendLine("}");
// ------ Params. ------
ilb.appendLine();
ilb.appendLine();
ilb.appendLine("/**");
ilb.appendLine(" * Param names from all templates in this Soy file.");
ilb.appendLine(" */");
ilb.appendLine("public static final class Param {");
ilb.increaseIndent();
ilb.appendLine("private Param() {}");
ilb.appendLine();
for (Map.Entry<String, String> paramEntry : allParamKeysMap.entrySet()) {
String upperUnderscoreKey = paramEntry.getKey();
String key = paramEntry.getValue();
StringBuilder javadocSb = new StringBuilder();
javadocSb.append("Listed by ");
boolean isFirst = true;
for (TemplateNode template : paramKeyToTemplatesMultimap.get(key)) {
if (isFirst) {
isFirst = false;
} else {
javadocSb.append(", ");
}
javadocSb.append(buildTemplateNameForJavadoc(node, template));
}
javadocSb.append('.');
appendJavadoc(ilb, javadocSb.toString(), false, true);
ilb.appendLine("public static final String ", upperUnderscoreKey, " = \"", key, "\";");
}
ilb.decreaseIndent();
ilb.appendLine("}");
// ------ Templates. ------
for (TemplateNode template : publicBasicTemplateMap.values()) {
visit(template);
}
// ------ Constructor. ------
ilb.appendLine();
ilb.appendLine();
ilb.appendLine("private ", javaClassName, "() {");
ilb.increaseIndent();
ilb.appendLine("super(");
ilb.increaseIndent(2);
ilb.appendLine("\"", node.getFileName(), "\",");
ilb.appendLine("\"", node.getNamespace(), "\",");
// Templates.
List<String> itemSnippets = Lists.newArrayList();
itemSnippets.addAll(publicBasicTemplateMap.keySet());
appendImmutableList(ilb, "<SoyTemplateInfo>", itemSnippets);
ilb.appendLineEnd(",");
// CSS names.
SortedMap<String, CssTagsPrefixPresence> cssNameMap = new CollectCssNamesVisitor().exec(node);
ImmutableMap.Builder<String, String> cssTagPrefixes = ImmutableMap.builder();
for (Map.Entry<String, CssTagsPrefixPresence> entry : cssNameMap.entrySet()) {
cssTagPrefixes.put("\"" + entry.getKey() + "\"", "CssTagsPrefixPresence." + entry.getValue().name());
}
appendImmutableMap(ilb, "<String, CssTagsPrefixPresence>", cssTagPrefixes.build());
ilb.appendLineEnd(",");
appendImmutableList(ilb, "<String>", deltemplates);
ilb.appendLineEnd(");");
ilb.decreaseIndent(2);
ilb.decreaseIndent();
ilb.appendLine("}");
// ------ Singleton instance and its getter. ------
ilb.appendLine();
ilb.appendLine();
ilb.appendLine("private static final ", javaClassName, " __INSTANCE__ =");
ilb.increaseIndent(2);
ilb.appendLine("new ", javaClassName, "();");
ilb.decreaseIndent(2);
ilb.appendLine();
ilb.appendLine("public static ", javaClassName, " getInstance() {");
ilb.increaseIndent();
ilb.appendLine("return __INSTANCE__;");
ilb.decreaseIndent();
ilb.appendLine("}");
// ------ Class end. ------
ilb.appendLine();
ilb.decreaseIndent();
ilb.appendLine("}");
generatedFiles.put(javaClassName + ".java", ilb.toString());
ilb = null;
}
use of com.google.template.soy.types.SoyProtoType in project closure-templates by google.
the class TranslateExprNodeVisitor method genCodeForFieldAccess.
/**
* Generates the code for a field access, e.g. {@code .foo} or {@code .getFoo()}.
*
* @param baseType The type of the object that contains the field.
* @param fieldAccessNode The field access node.
* @param fieldName The field name.
*/
private FieldAccess genCodeForFieldAccess(SoyType baseType, FieldAccessNode fieldAccessNode, String fieldName) {
Preconditions.checkNotNull(baseType);
// type, and then see if they all agree.
if (baseType.getKind() == SoyType.Kind.UNION) {
// TODO(msamuel): We will need to generate fallback code for each variant.
UnionType unionType = (UnionType) baseType;
FieldAccess fieldAccess = null;
for (SoyType memberType : unionType.getMembers()) {
if (memberType.getKind() != SoyType.Kind.NULL) {
FieldAccess fieldAccessForType = genCodeForFieldAccess(memberType, fieldAccessNode, fieldName);
if (fieldAccess == null) {
fieldAccess = fieldAccessForType;
} else if (!fieldAccess.equals(fieldAccessForType)) {
errorReporter.report(fieldAccessNode.getSourceLocation(), UNION_ACCESSOR_MISMATCH, fieldName, baseType);
}
}
}
return fieldAccess;
}
if (baseType.getKind() == SoyType.Kind.PROTO) {
SoyProtoType protoType = (SoyProtoType) baseType;
FieldDescriptor desc = protoType.getFieldDescriptor(fieldName);
Preconditions.checkNotNull(desc, "Error in proto %s, field not found: %s", protoType.getDescriptor().getFullName(), fieldName);
return FieldAccess.protoCall(fieldName, desc);
}
return FieldAccess.id(fieldName);
}
use of com.google.template.soy.types.SoyProtoType in project closure-templates by google.
the class EvalVisitor method visitProtoInitNode.
@Override
protected SoyValue visitProtoInitNode(ProtoInitNode node) {
// The downcast is safe because if it was anything else, compilation would have already failed.
SoyProtoType soyProto = (SoyProtoType) node.getType();
ImmutableList<String> paramNames = node.getParamNames();
SoyProtoValueImpl.Builder builder = new SoyProtoValueImpl.Builder(soyProto.getDescriptor());
for (int i = 0; i < node.numChildren(); i++) {
SoyValue visit = visit(node.getChild(i));
// null means don't assign
if (visit instanceof NullData || visit instanceof UndefinedData) {
continue;
}
builder.setField(paramNames.get(i), visit);
}
return builder.build();
}
use of com.google.template.soy.types.SoyProtoType in project closure-templates by google.
the class TranslateExprNodeVisitor method visitProtoInitNode.
@Override
protected CodeChunk.WithValue visitProtoInitNode(ProtoInitNode node) {
SoyProtoType type = (SoyProtoType) node.getType();
CodeChunk.WithValue proto = new_(protoConstructor(type)).call();
if (node.numChildren() == 0) {
// If there's no further structure to the proto, no need to declare a variable.
return proto;
}
CodeChunk.WithValue protoVar = codeGenerator.declarationBuilder().setRhs(proto).build().ref();
ImmutableList.Builder<CodeChunk> initialStatements = ImmutableList.builder();
for (int i = 0; i < node.numChildren(); i++) {
String fieldName = node.getParamName(i);
FieldDescriptor fieldDesc = type.getFieldDescriptor(fieldName);
CodeChunk.WithValue fieldValue = visit(node.getChild(i));
if (ProtoUtils.isSanitizedContentField(fieldDesc)) {
CodeChunk.WithValue sanitizedContentPackFn = sanitizedContentToProtoConverterFunction(fieldDesc.getMessageType());
fieldValue = fieldDesc.isRepeated() ? GOOG_ARRAY_MAP.call(fieldValue, sanitizedContentPackFn) : sanitizedContentPackFn.call(fieldValue);
}
if (fieldDesc.isExtension()) {
CodeChunk.WithValue extInfo = extensionField(fieldDesc);
initialStatements.add(protoVar.dotAccess("setExtension").call(extInfo, fieldValue));
} else if (fieldDesc.isMapField()) {
// Protocol buffer in JS does not generate setters for map fields. To construct a proto map
// field, we first save a reference to the empty instance using the getter, and then load
// it with the contents of the SoyMap.
String getFn = "get" + LOWER_CAMEL.to(UPPER_CAMEL, fieldName);
CodeChunk.WithValue protoMap = protoVar.dotAccess(getFn).call();
CodeChunk.WithValue protoMapVar = codeGenerator.declarationBuilder().setRhs(protoMap).build().ref();
if (ProtoUtils.isSanitizedContentMap(fieldDesc)) {
CodeChunk.WithValue sanitizedContentPackFn = sanitizedContentToProtoConverterFunction(ProtoUtils.getMapValueMessageType(fieldDesc));
fieldValue = SOY_NEWMAPS_TRANSFORM_VALUES.call(fieldValue, sanitizedContentPackFn);
}
initialStatements.add(SOY_MAP_POPULATE.call(protoMapVar, fieldValue));
} else {
String setFn = "set" + LOWER_CAMEL.to(UPPER_CAMEL, fieldName);
initialStatements.add(protoVar.dotAccess(setFn).call(fieldValue));
}
}
return protoVar.withInitialStatements(initialStatements.build());
}
Aggregations