Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve substitutions #15

Merged
merged 1 commit into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

## 3.0.6

- Support default values in substitutions: ${some.property:-default.value}
- Support system properties and environment variables in substitutions: ${sys:some.property} and ${env:some.property}
- Support nested substitutions for default values: ${env:some.property:-${sys:some.property:-default.value}}

## 3.0.5

- Support SLF4J 2.0.+.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ Highly efficient garbage-free logging framework for Java 8+.
Add the following dependencies to your project:

```gradle
implementation 'com.epam.deltix:gflog-api:3.0.5'
runtimeOnly 'com.epam.deltix:gflog-core:3.0.5'
implementation 'com.epam.deltix:gflog-api:3.0.6'
runtimeOnly 'com.epam.deltix:gflog-core:3.0.6'
```

Use the following sample to log a message:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

public final class PropertyUtil {

private static final String SUBSTITUTION_START = "${";
private static final char SUBSTITUTION_END = '}';

private PropertyUtil() {
}

Expand Down Expand Up @@ -161,43 +158,6 @@ public static Duration toDuration(final String value) {
}

public static String substitute(final String value, final Properties properties) {
if (value.contains(SUBSTITUTION_START)) {
final StringBuilder builder = new StringBuilder();

final int length = value.length();
int i = 0;

while (true) {
final int j = value.indexOf(SUBSTITUTION_START, i);
if (j == -1) {
break;
}

final int k = j + SUBSTITUTION_START.length();
final int m = value.indexOf(SUBSTITUTION_END, k);
if (m == -1) {
break;
}

if (i < j) {
builder.append(value, i, j);
}

final String name = value.substring(k, m);
final String substitution = properties.getProperty(name, "");

builder.append(substitution);
i = m + 1;
}

if (i < length) {
builder.append(value, i, length);
}

return builder.toString();
}

return value;
return new StringSubstitution(properties, System.getProperties(), System.getenv()).substitute(value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.epam.deltix.gflog.core.util;

import java.util.Map;
import java.util.Properties;


public final class StringSubstitution {

private static final String SYSTEM_PROPERTY_PREFIX = "sys:";
private static final String ENVIRONMENT_VARIABLE_PREFIX = "env:";

private final Properties defaultProperties;
private final Properties systemProperties;
private final Map<String, String> environmentVariables;

public StringSubstitution(final Properties defaultProperties,
final Properties systemProperties,
final Map<String, String> environmentVariables) {
this.defaultProperties = defaultProperties;
this.systemProperties = systemProperties;
this.environmentVariables = environmentVariables;
}

public String substitute(final String text) {
final StringBuilder builder = new StringBuilder();

for (int index = 0; index < text.length(); ) {
final int position = text.indexOf("${", index);

if (position < 0) {
builder.append(text, index, text.length());
break;
}

builder.append(text, index, position);
index = substitute(text, position, builder);
}

return builder.toString();
}

private int substitute(final String source, int index, final StringBuilder target) {
final int end = source.length();
final int start = index;

index += 2;

while (index < end) {
char c = source.charAt(index);

if (c == '}') {
final String key = source.substring(start + 2, index);
final String value = lookup(key);

if (value == null) {
target.append("${").append(key).append(":#UNRESOLVED#").append("}");
} else {
target.append(value);
}

return index + 1;
}

if (c == ':' && index + 1 < end && source.charAt(index + 1) == '-') {
final String key = source.substring(start + 2, index);
final String value = lookup(key);

final int length = target.length();
final int mid = index + 2;

index = mid;

while (index < end) {
c = source.charAt(index);

if (c == '}') {
if (value != null) {
target.setLength(length);
target.append(value);
}

return index + 1;
}

if (c == '$' && index + 1 < end && source.charAt(index + 1) == '{') {
index = substitute(source, index, target);
continue;
}

target.append(c);
index++;
}

target.insert(length, source, start, mid);
return end;
}

index++;
}

target.append(source, start, end);
return index;
}

private String lookup(final String key) {
if (key.startsWith(SYSTEM_PROPERTY_PREFIX)) {
final String propertyKey = key.substring(SYSTEM_PROPERTY_PREFIX.length());
return systemProperties.getProperty(propertyKey);
}

if (key.startsWith(ENVIRONMENT_VARIABLE_PREFIX)) {
final String environmentKey = key.substring(ENVIRONMENT_VARIABLE_PREFIX.length());
return environmentVariables.get(environmentKey);
}

return defaultProperties.getProperty(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.epam.deltix.gflog.core.util;

import org.junit.Assert;
import org.junit.Test;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

public class StringSubstitutionTest {

@Test
public void testDefaults() {
final Properties defaults = new Properties();
defaults.put("a", "A");
defaults.put("b", "B");
defaults.put("c", "C");

final Properties systems = new Properties();
final Map<String, String> environments = new HashMap<>();
final StringSubstitution substitution = new StringSubstitution(defaults, systems, environments);
final Map<String, String> mapping = new LinkedHashMap<>();

mapping.put("12345", "12345");
mapping.put("${a} ${b} ${c}", "A B C");
mapping.put("${a:-10} ${b:-${c:-100500}}", "A B");
mapping.put("${missing:-5} + ${missing:-10}", "5 + 10");
mapping.put("${missing:-${a}}", "A");
mapping.put("${missing:-${missing2:-${b}}}", "B");
mapping.put("${missing:-${missing2:-${missing3:-${c}}}}", "C");
mapping.put("${a:-5", "${a:-5");
mapping.put(" ${a:-${b} ", " ${a:-B ");
mapping.put(" ${a:-${b:-${c ", " ${a:-${b:-${c ");
mapping.put(" ${a:-${b:-${c} ", " ${a:-${b:-C ");
mapping.put(" ${a:-${b:-${c}} ", " ${a:-B ");
mapping.put("${missing}", "${missing:#UNRESOLVED#}");
mapping.put("${missing:-1 ${a} 2 ${missing2} 3}", "1 A 2 ${missing2:#UNRESOLVED#} 3");
mapping.put("${missing:-}", "");

for (final Map.Entry<String, String> entry : mapping.entrySet()) {
final String text = entry.getKey();
final String expected = entry.getValue();
final String actual = substitution.substitute(text);

Assert.assertEquals(expected, actual);
}
}

@Test
public void testEnvironmentAndSystemSubstitution() {
final Properties defaults = new Properties();
defaults.put("a", "1");
defaults.put("b", "2");
defaults.put("c", "3");

final Properties systems = new Properties();
systems.put("a", "4");
systems.put("b", "5");
systems.put("c", "6");

final Map<String, String> environments = new HashMap<>();
environments.put("a", "7");
environments.put("b", "8");
environments.put("c", "9");

final StringSubstitution substitution = new StringSubstitution(defaults, systems, environments);
final Map<String, String> mapping = new LinkedHashMap<>();

mapping.put("${a} ${sys:a} ${env:a}", "1 4 7");
mapping.put("${missing:-${b}} ${missing:-${sys:b}} ${missing:-${env:b}}", "2 5 8");
mapping.put("${sys:missing:-${b}} ${sys:missing:-${sys:b}} ${sys:missing:-${env:b}}", "2 5 8");
mapping.put("${env:missing:-${b}} ${env:missing:-${sys:b}} ${env:missing:-${env:b}}", "2 5 8");

for (final Map.Entry<String, String> entry : mapping.entrySet()) {
final String text = entry.getKey();
final String expected = entry.getValue();
final String actual = substitution.substitute(text);

Assert.assertEquals(expected, actual);
}
}
}
Loading