Skip to content

Commit 9f35444

Browse files
committed
Add Value.resolve and support simple text sustitutions
1 parent 7c1bb85 commit 9f35444

4 files changed

Lines changed: 143 additions & 1 deletion

File tree

jooby/src/main/java/io/jooby/Env.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
116
package io.jooby;
217

318
import javax.annotation.Nonnull;

jooby/src/main/java/io/jooby/Value.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929
import java.util.LinkedHashSet;
3030
import java.util.List;
3131
import java.util.Map;
32+
import java.util.NoSuchElementException;
3233
import java.util.Optional;
3334
import java.util.Set;
3435
import java.util.TreeMap;
3536
import java.util.function.BiConsumer;
37+
import java.util.function.BiFunction;
3638
import java.util.function.Function;
3739
import java.util.stream.Collectors;
3840

@@ -584,6 +586,75 @@ default <T> T to(Reified<T> type) {
584586
return map;
585587
}
586588

589+
default String resolve(@Nonnull String text) {
590+
return resolve(text, "${", "}");
591+
}
592+
593+
default String resolve(@Nonnull String text, boolean ignoreMissing) {
594+
return resolve(text, "${", "}", ignoreMissing);
595+
}
596+
597+
default String resolve(@Nonnull String text, @Nonnull String startDelim,
598+
@Nonnull String endDelim) {
599+
return resolve(text, startDelim, endDelim, false);
600+
}
601+
602+
default String resolve(@Nonnull String text, @Nonnull String startDelim, @Nonnull String endDelim,
603+
boolean ignoreMissing) {
604+
if (text.length() == 0) {
605+
return "";
606+
}
607+
608+
BiFunction<Integer, BiFunction<Integer, Integer, RuntimeException>, RuntimeException> err = (
609+
start, ex) -> {
610+
String snapshot = text.substring(0, start);
611+
int line = Math.max(1, (int) snapshot.chars().filter(ch -> ch == '\n').count());
612+
int column = start - snapshot.lastIndexOf('\n');
613+
return ex.apply(line, column);
614+
};
615+
616+
StringBuilder buffer = new StringBuilder();
617+
int offset = 0;
618+
int start = text.indexOf(startDelim);
619+
while (start >= 0) {
620+
int end = text.indexOf(endDelim, start + startDelim.length());
621+
if (end == -1) {
622+
throw err.apply(start, (line, column) -> new IllegalArgumentException(
623+
"found '" + startDelim + "' expecting '" + endDelim + "' at " + line + ":"
624+
+ column));
625+
}
626+
buffer.append(text.substring(offset, start));
627+
String key = text.substring(start + startDelim.length(), end);
628+
String value;
629+
try {
630+
// seek
631+
String[] path = key.split("\\.");
632+
Value src = path[0].equals(name()) ? this : get(path[0]);
633+
for (int i = 1; i < path.length; i++) {
634+
src = src.get(path[i]);
635+
}
636+
value = src.value();
637+
} catch (Err.Missing x) {
638+
if (ignoreMissing) {
639+
value = text.substring(start, end + endDelim.length());
640+
} else {
641+
throw err.apply(start, (line, column) -> new NoSuchElementException(
642+
"Missing " + startDelim + key + endDelim + " at " + line + ":" + column));
643+
}
644+
}
645+
buffer.append(value);
646+
offset = end + endDelim.length();
647+
start = text.indexOf(startDelim, offset);
648+
}
649+
if (buffer.length() == 0) {
650+
return text;
651+
}
652+
if (offset < text.length()) {
653+
buffer.append(text.substring(offset));
654+
}
655+
return buffer.toString();
656+
}
657+
587658
/* ***********************************************************************************************
588659
* Factory methods
589660
* ***********************************************************************************************
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.jooby;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.NoSuchElementException;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
public class ValueResolveTest {
10+
11+
@Test
12+
public void resolveOne() {
13+
Value value = Value.value("foo", "bar");
14+
assertEquals("bar", value.resolve("${foo}"));
15+
assertEquals("- bar", value.resolve("- ${foo}"));
16+
assertEquals("bar-", value.resolve("${foo}-"));
17+
assertEquals("-", value.resolve("-"));
18+
assertEquals("", value.resolve(""));
19+
}
20+
21+
@Test
22+
public void resolveComplexWithoutRoot() {
23+
Value value = Value.object(null)
24+
.put("foo.bar", "baz");
25+
assertEquals("baz", value.resolve("${foo.bar}"));
26+
assertEquals("-baz-", value.resolve("-${foo.bar}-"));
27+
}
28+
29+
@Test
30+
public void resolveComplex() {
31+
Value value = Value.object(null)
32+
.put("firstname", "Pedro")
33+
.put("lastname", "Picapiedra");
34+
assertEquals("Hi Pedro Picapiedra!", value.resolve("Hi ${firstname} ${lastname}!"));
35+
}
36+
37+
@Test
38+
public void resolveComplexWithRoot() {
39+
Value value = Value.object("foo")
40+
.put("bar", "baz");
41+
assertEquals("baz", value.resolve("${foo.bar}"));
42+
assertEquals("-baz-", value.resolve("-${foo.bar}-"));
43+
}
44+
45+
@Test
46+
public void resolveMissing() {
47+
try {
48+
Value value = Value.value("x", "y");
49+
assertEquals("bar", value.resolve("${foo}"));
50+
} catch (NoSuchElementException x) {
51+
assertEquals("Missing ${foo} at 1:1", x.getMessage());
52+
}
53+
54+
Value value = Value.value("x", "y");
55+
assertEquals("${foo}", value.resolve("${foo}", true));
56+
}
57+
}

jooby/src/test/java/io/jooby/ValueTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,6 @@ public void verifyExceptionMessage() {
376376

377377
assertMessage(Err.Missing.class, () -> Value.value("foo", "bar").get(1).value(),
378378
"Missing value: 'foo.1'");
379-
380379
}
381380

382381
public static <T extends Throwable> void assertMessage(Class<T> expectedType,

0 commit comments

Comments
 (0)