private SoyMsgPart compactPlural(SoyMsgPluralPart plural) { // Plural variable names tend to be repeated across templates, such as "count". return new SoyMsgPluralPart( intern(plural.getPluralVarName()), plural.getOffset(), compactCases(plural.getCases(), DEFAULT_PLURAL_CASE_SPEC)); }
/** * Render a {@code {plural}} part of a message. Most of the complexity is handled by {@link * SoyMsgPluralPart#lookupCase} all this needs to do is apply the placeholders to all the * children. */ private static void renderPlural( @Nullable ULocale locale, SoyMsgPluralPart plural, Map<String, Object> placeholders, Appendable out) throws IOException { long pluralValue = getPlural(placeholders, plural.getPluralVarName()); for (SoyMsgPart casePart : plural.lookupCase(pluralValue, locale)) { if (casePart instanceof SoyMsgPlaceholderPart) { writePlaceholder((SoyMsgPlaceholderPart) casePart, placeholders, out); } else if (casePart instanceof SoyMsgRawTextPart) { writeRawText((SoyMsgRawTextPart) casePart, out); } else if (casePart instanceof SoyMsgPluralRemainderPart) { out.append(String.valueOf(pluralValue - plural.getOffset())); } else { // Plural parts will not have nested plural/select parts. So, this is an error. throw new AssertionError("unexpected part: " + casePart); } } }
private void putPluralPartIntoMap( MsgNode originalMsg, Map<String, Function<Expression, Statement>> placeholderNameToPutStatement, SoyMsgPluralPart plural) { MsgPluralNode repPluralNode = originalMsg.getRepPluralNode(plural.getPluralVarName()); if (!placeholderNameToPutStatement.containsKey(plural.getPluralVarName())) { Label reattachPoint = new Label(); Expression value = placeholderCompiler.compileToInt(repPluralNode.getExpr(), reattachPoint); placeholderNameToPutStatement.put( plural.getPluralVarName(), putToMapFunction(plural.getPluralVarName(), value, reattachPoint)); } // Recursively visit plural cases for (Case<SoyMsgPluralCaseSpec> caseOrDefault : plural.getCases()) { putPlaceholdersIntoMap(originalMsg, caseOrDefault.parts(), placeholderNameToPutStatement); } }
/** * Appends the text representation of a plural part to a StringBuilder. * @param stringBuilder The StringBuilder to append to. * @param soyMsgPluralPart The SoyMsgPluralPart to add. * @param doUseBracedPhs Whether to use braced placeholders. */ private static void appendPluralToStringBuilder( StringBuilder stringBuilder, SoyMsgPluralPart soyMsgPluralPart, boolean doUseBracedPhs) { stringBuilder.append(IcuSyntaxUtils.getPluralOpenString( soyMsgPluralPart.getPluralVarName(), soyMsgPluralPart.getOffset())); for (Pair<SoyMsgPluralCaseSpec, List<SoyMsgPart>> pluralCase : soyMsgPluralPart.getCases()) { SoyMsgPluralCaseSpec pluralCaseSpec = pluralCase.first; stringBuilder.append(IcuSyntaxUtils.getPluralCaseOpenString( pluralCaseSpec.getType() == SoyMsgPluralCaseSpec.Type.OTHER ? null : pluralCaseSpec.getExplicitValue())); appendMsgPartsToTcStringBuilder(stringBuilder, pluralCase.second, doUseBracedPhs); stringBuilder.append(IcuSyntaxUtils.getPluralCaseCloseString()); } stringBuilder.append(IcuSyntaxUtils.getPluralCloseString()); }
/** * Builds the list of SoyMsgParts for the given MsgPluralNode. * * @param msgPluralNode The plural node parsed from the Soy source. * @param msgNode The MsgNode containing 'msgPluralNode'. * @return A SoyMsgPluralPart. */ private static SoyMsgPluralPart buildMsgPartForPlural( MsgPluralNode msgPluralNode, MsgNode msgNode) { // This is the list of the cases. ImmutableList.Builder<SoyMsgPart.Case<SoyMsgPluralCaseSpec>> pluralCases = ImmutableList.builder(); for (CaseOrDefaultNode child : msgPluralNode.getChildren()) { ImmutableList<SoyMsgPart> caseMsgParts = buildMsgPartsForChildren((MsgBlockNode) child, msgNode); SoyMsgPluralCaseSpec caseSpec; if (child instanceof MsgPluralCaseNode) { caseSpec = new SoyMsgPluralCaseSpec(((MsgPluralCaseNode) child).getCaseNumber()); } else if (child instanceof MsgPluralDefaultNode) { caseSpec = new SoyMsgPluralCaseSpec(Type.OTHER); } else { throw new AssertionError("Unidentified node under a plural node."); } pluralCases.add(SoyMsgPart.Case.create(caseSpec, caseMsgParts)); } return new SoyMsgPluralPart( msgNode.getPluralVarName(msgPluralNode), msgPluralNode.getOffset(), pluralCases.build()); }
/** * Private helper for {@code convertMsgPartsToEmbeddedIcuSyntax()} to convert a plural part. * * @param newMsgPartsBuilder The new msg parts being built. * @param currRawTextSb The collector for the current raw text, which hasn't yet been turned into * a SoyMsgRawTextPart and added to newMsgPartsBuilder because it might not be complete. * @param origPluralPart The plural part to convert. */ private static void convertPluralPartHelper( Builder<SoyMsgPart> newMsgPartsBuilder, StringBuilder currRawTextSb, SoyMsgPluralPart origPluralPart) { currRawTextSb.append( getPluralOpenString(origPluralPart.getPluralVarName(), origPluralPart.getOffset())); for (Case<SoyMsgPluralCaseSpec> pluralCase : origPluralPart.getCases()) { currRawTextSb.append(getPluralCaseOpenString(pluralCase.spec())); convertMsgPartsHelper( newMsgPartsBuilder, currRawTextSb, pluralCase.parts(), /* isInPlrselPart= */ true); currRawTextSb.append(getPluralCaseCloseString()); } currRawTextSb.append(getPluralCloseString()); }
private void putPluralPartIntoMap( Expression mapExpression, MsgNode originalMsg, Map<String, Statement> placeholderNameToPutStatement, SoyMsgPluralPart plural) { MsgPluralNode repPluralNode = originalMsg.getRepPluralNode(plural.getPluralVarName()); if (!placeholderNameToPutStatement.containsKey(plural.getPluralVarName())) { Label reattachPoint = new Label(); Expression value = soyNodeCompiler.compileToInt(repPluralNode.getExpr(), reattachPoint); placeholderNameToPutStatement.put( plural.getPluralVarName(), putToMap(mapExpression, plural.getPluralVarName(), value) .labelStart(reattachPoint) .withSourceLocation(repPluralNode.getSourceLocation())); } // Recursively visit plural cases for (Case<SoyMsgPluralCaseSpec> caseOrDefault : plural.getCases()) { putPlaceholdersIntoMap( mapExpression, originalMsg, caseOrDefault.parts(), placeholderNameToPutStatement); } }
/** * Builds the list of SoyMsgParts for the given MsgPluralNode. * * @param msgPluralNode The plural node parsed from the Soy source. * @param msgNode The MsgNode containing 'msgPluralNode'. * @return A SoyMsgPluralPart. */ private static SoyMsgPluralPart buildMsgPartForPlural( MsgPluralNode msgPluralNode, MsgNode msgNode) { // This is the list of the cases. ImmutableList.Builder<SoyMsgPart.Case<SoyMsgPluralCaseSpec>> pluralCases = ImmutableList.builder(); for (CaseOrDefaultNode child : msgPluralNode.getChildren()) { ImmutableList<SoyMsgPart> caseMsgParts = buildMsgPartsForChildren(child, msgNode); SoyMsgPluralCaseSpec caseSpec; if (child instanceof MsgPluralCaseNode) { caseSpec = new SoyMsgPluralCaseSpec(((MsgPluralCaseNode) child).getCaseNumber()); } else if (child instanceof MsgPluralDefaultNode) { caseSpec = new SoyMsgPluralCaseSpec(Type.OTHER); } else { throw new AssertionError("Unidentified node under a plural node."); } pluralCases.add(SoyMsgPart.Case.create(caseSpec, caseMsgParts)); } return new SoyMsgPluralPart( msgNode.getPluralVarName(msgPluralNode), msgPluralNode.getOffset(), pluralCases.build()); }
/** * Private helper for {@code convertMsgPartsToEmbeddedIcuSyntax()} to convert a plural part. * * @param newMsgPartsBuilder The new msg parts being built. * @param currRawTextSb The collector for the current raw text, which hasn't yet been turned into * a SoyMsgRawTextPart and added to newMsgPartsBuilder because it might not be complete. * @param origPluralPart The plural part to convert. */ private static void convertPluralPartHelper( Builder<SoyMsgPart> newMsgPartsBuilder, StringBuilder currRawTextSb, SoyMsgPluralPart origPluralPart) { currRawTextSb.append( getPluralOpenString(origPluralPart.getPluralVarName(), origPluralPart.getOffset())); for (Case<SoyMsgPluralCaseSpec> pluralCase : origPluralPart.getCases()) { currRawTextSb.append(getPluralCaseOpenString(pluralCase.spec())); convertMsgPartsHelper( newMsgPartsBuilder, currRawTextSb, pluralCase.parts(), /* isInPlrselPart= */ true); currRawTextSb.append(getPluralCaseCloseString()); } currRawTextSb.append(getPluralCloseString()); }
private SoyMsgPart compactPlural(SoyMsgPluralPart plural) { // Plural variable names tend to be repeated across templates, such as "count". return new SoyMsgPluralPart( intern(plural.getPluralVarName()), plural.getOffset(), compactCases(plural.getCases(), DEFAULT_PLURAL_CASE_SPEC)); }
/** * Generates code bits for a {@code MsgPluralNode} subtree inside a message. * * @param pluralPart A node of type {@code SoyMsgPluralPart}. * @param msgNode The enclosing {@code MsgNode} object. * @param codeGenInfo Data structure holding information on placeholder names, plural variable * names, and select variable names to be used for message code generation. */ private void genGoogMsgCodeForPluralNode( SoyMsgPluralPart pluralPart, MsgNode msgNode, GoogMsgPlaceholderCodeGenInfo codeGenInfo) { // we need to always traverse into children in case any of the cases have unique placeholder MsgPluralNode reprNode = msgNode.getRepPluralNode(pluralPart.getPluralVarName()); if (!codeGenInfo.pluralsAndSelects.contains(pluralPart.getPluralVarName())) { codeGenInfo.pluralsAndSelects.put( pluralPart.getPluralVarName(), translateExpr(reprNode.getExpr())); } for (SoyMsgPart.Case<SoyMsgPluralCaseSpec> child : pluralPart.getCases()) { genGoogMsgCodeForChildren(child.parts(), msgNode, codeGenInfo); } }
} else if (first instanceof SoyMsgPluralPart) { SoyMsgPluralPart pluralPart = (SoyMsgPluralPart) first; long pluralValue = getPlural(pluralPart.getPluralVarName()); parts = pluralPart.lookupCase(pluralValue, locale); remainder = pluralValue - pluralPart.getOffset(); } else { break;
/** * Builds the list of SoyMsgParts for the given MsgPluralNode. * @param msgPluralNode The plural node parsed from the Soy source. * @param msgNode The MsgNode containing 'msgPluralNode'. * @return A SoyMsgPluralPart. */ private static SoyMsgPluralPart buildMsgPartForPlural( MsgPluralNode msgPluralNode, MsgNode msgNode) { // This is the list of the cases. List<Pair<SoyMsgPluralCaseSpec, List<SoyMsgPart>>> pluralCases = Lists.newArrayList(); for (CaseOrDefaultNode child : msgPluralNode.getChildren()) { List<SoyMsgPart> caseMsgParts = buildMsgPartsForChildren(child, msgNode); SoyMsgPluralCaseSpec caseSpec; if (child instanceof MsgPluralCaseNode) { caseSpec = new SoyMsgPluralCaseSpec(((MsgPluralCaseNode) child).getCaseNumber()); } else if (child instanceof MsgPluralDefaultNode) { caseSpec = new SoyMsgPluralCaseSpec("other"); } else { throw new AssertionError("Unidentified node under a plural node."); } pluralCases.add(Pair.of(caseSpec, caseMsgParts)); } return new SoyMsgPluralPart( msgNode.getPluralVarName(msgPluralNode), msgPluralNode.getOffset(), pluralCases); }
} else if (part instanceof SoyMsgPluralPart) { SoyMsgPluralPart pluralPart = (SoyMsgPluralPart) part; List<Expression> caseExprs = new ArrayList<>(pluralPart.getCases().size()); for (Case<SoyMsgPluralCaseSpec> item : pluralPart.getCases()) { Expression spec; if (item.spec().getType() == Type.EXPLICIT) { constant(pluralPart.getPluralVarName()), constant(pluralPart.getOffset()), BytecodeUtils.asList(caseExprs)); } else if (part instanceof SoyMsgPluralRemainderPart) {
private PyStringExpr pyFuncForPluralMsg() { SoyMsgPluralPart pluralPart = (SoyMsgPluralPart) msgParts.get(0); MsgPluralNode pluralNode = msgNode.getRepPluralNode(pluralPart.getPluralVarName()); Map<PyExpr, PyExpr> nodePyVarToPyExprMap = collectVarNameListAndToPyExprMap(); Map<PyExpr, PyExpr> caseSpecStrToMsgTexts = new LinkedHashMap<>(); for (Case<SoyMsgPluralCaseSpec> pluralCase : pluralPart.getCases()) { caseSpecStrToMsgTexts.put( new PyStringExpr("'" + pluralCase.spec() + "'"), new PyStringExpr( "'" + processMsgPartsHelper(pluralCase.parts(), escaperForIcuSection) + "'")); } prepareFunc .addArg(msgId) .addArg(PyExprUtils.convertMapToPyExpr(caseSpecStrToMsgTexts)) .addArg(PyExprUtils.convertIterableToPyTupleExpr(nodePyVarToPyExprMap.keySet())); // Translates {@link MsgPluralNode#pluralExpr} into a Python lookup expression. // Note that pluralExpr represent the Soy expression inside the attributes of a plural tag. PyExpr pluralPyExpr = translateToPyExprVisitor.exec(pluralNode.getExpr()); return renderFunc .addArg(prepareFunc.asPyExpr()) .addArg(pluralPyExpr) .addArg(PyExprUtils.convertMapToPyExpr(nodePyVarToPyExprMap)) .asPyStringExpr(); }
MsgPluralNode repPluralNode = msgNode.getRepPluralNode(pluralPart.getPluralVarName()); double correctPluralValue; ExprRootNode pluralExpr = repPluralNode.getExpr(); List<SoyMsgPart> caseParts = pluralPart.lookupCase((int) correctPluralValue, locale); appendPluralRemainder(correctPluralValue - pluralPart.getOffset());
} else if (part instanceof SoyMsgPluralPart) { SoyMsgPluralPart pluralPart = (SoyMsgPluralPart) part; List<Expression> caseExprs = new ArrayList<>(pluralPart.getCases().size()); for (Case<SoyMsgPluralCaseSpec> item : pluralPart.getCases()) { Expression spec; if (item.spec().getType() == Type.EXPLICIT) { constant(pluralPart.getPluralVarName()), constant(pluralPart.getOffset()), BytecodeUtils.asList(caseExprs)); } else if (part instanceof SoyMsgPluralRemainderPart) {
private PyStringExpr pyFuncForPluralMsg() { SoyMsgPluralPart pluralPart = (SoyMsgPluralPart) msgParts.get(0); MsgPluralNode pluralNode = msgNode.getRepPluralNode(pluralPart.getPluralVarName()); Map<PyExpr, PyExpr> nodePyVarToPyExprMap = collectVarNameListAndToPyExprMap(); Map<PyExpr, PyExpr> caseSpecStrToMsgTexts = new LinkedHashMap<>(); for (Case<SoyMsgPluralCaseSpec> pluralCase : pluralPart.getCases()) { caseSpecStrToMsgTexts.put( new PyStringExpr("'" + pluralCase.spec() + "'"), new PyStringExpr( "'" + processMsgPartsHelper(pluralCase.parts(), escaperForIcuSection) + "'")); } prepareFunc .addArg(msgId) .addArg(PyExprUtils.convertMapToPyExpr(caseSpecStrToMsgTexts)) .addArg(PyExprUtils.convertIterableToPyTupleExpr(nodePyVarToPyExprMap.keySet())); // Translates {@link MsgPluralNode#pluralExpr} into a Python lookup expression. // Note that pluralExpr represent the Soy expression inside the attributes of a plural tag. PyExpr pluralPyExpr = translateToPyExprVisitor.exec(pluralNode.getExpr()); return renderFunc .addArg(prepareFunc.asPyExpr()) .addArg(pluralPyExpr) .addArg(PyExprUtils.convertMapToPyExpr(nodePyVarToPyExprMap)) .asPyStringExpr(); }