code: @Nullable
      public static String replaceMacro( @CheckForNull String s, @Nonnull Map<String,String> properties) {
          return replaceMacro(s, new VariableResolver.ByMap<>(properties));
      }
docs: Replaces the occurrence of '$key' by {@code properties.get('key')}.
  
  <p>
  Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)