/**
  * Perform {@link com.android.manifmerger.ManifestMerger2.SystemProperty} injection.
  *
  * @param mergingReport to log actions and errors.
  * @param xmlDocument the xml document to inject into.
  */
 protected void performSystemPropertiesInjection(
     MergingReport.Builder mergingReport, XmlDocument xmlDocument) {
   for (SystemProperty systemProperty : SystemProperty.values()) {
     String propertyOverride = mSystemPropertyResolver.getValue(systemProperty);
     if (propertyOverride != null) {
       systemProperty.addTo(mergingReport.getActionRecorder(), xmlDocument, propertyOverride);
     }
   }
 }
  private void visit(
      @NonNull ManifestMerger2.MergeType mergeType,
      @NonNull XmlElement xmlElement,
      @NonNull KeyBasedValueResolver<String> valueProvider,
      @NonNull MergingReport.Builder mergingReportBuilder) {

    for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) {

      StringBuilder resultString = new StringBuilder();
      String inputString = xmlAttribute.getValue();
      Matcher matcher = PATTERN.matcher(inputString);
      if (matcher.matches()) {
        while (matcher.matches()) {
          String placeholderValue = valueProvider.getValue(matcher.group(2));
          // whatever precedes the placeholder key is added back to the string.
          resultString.append(matcher.group(1));
          if (placeholderValue == null) {
            // if this is a library, ignore the failure
            MergingReport.Record.Severity severity =
                mergeType == ManifestMerger2.MergeType.LIBRARY
                    ? MergingReport.Record.Severity.INFO
                    : MergingReport.Record.Severity.ERROR;

            xmlAttribute.addMessage(
                mergingReportBuilder,
                severity,
                String.format(
                    "Attribute %1$s at %2$s requires a placeholder substitution"
                        + " but no value for <%3$s> is provided.",
                    xmlAttribute.getId(), xmlAttribute.printPosition(), matcher.group(2)));
            // we add back the placeholder key, since this is not an error for libraries
            resultString.append("${");
            resultString.append(matcher.group(2));
            resultString.append("}");
          } else {
            // record the attribute set
            mergingReportBuilder
                .getActionRecorder()
                .recordAttributeAction(
                    xmlAttribute,
                    PositionImpl.UNKNOWN,
                    Actions.ActionType.INJECTED,
                    null /* attributeOperationType */);

            // substitute the placeholder key with its value.
            resultString.append(placeholderValue);
          }
          // the new input string is the tail of the previous match, as it may contain
          // more placeholders to substitute.
          inputString = matcher.group(3);
          // reset the pattern matching with that new string to test for more placeholders
          matcher = PATTERN.matcher(inputString);
        }
        // append the last remainder (without placeholders) in the result string.
        resultString.append(inputString);
        xmlAttribute.getXml().setValue(resultString.toString());
      }
    }
    for (XmlElement childElement : xmlElement.getMergeableElements()) {
      visit(mergeType, childElement, valueProvider, mergingReportBuilder);
    }
  }