Skip to content

Commit 217be64

Browse files
authored
Merge pull request #16 from antkorwin/support-for-interfaces
Support for interfaces
2 parents 71ab287 + bfbdac0 commit 217be64

File tree

4 files changed

+183
-34
lines changed

4 files changed

+183
-34
lines changed

README.adoc

Lines changed: 87 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@
22

33
# Better Strings - Java String Interpolation
44

5-
image:https://travis-ci.com/antkorwin/better-strings.svg?branch=master["Build Status", link="https://travis-ci.com/antkorwin/better-strings"]
5+
image:https://travis-ci.com/antkorwin/better-strings.svg?branch=master["Build Status",link="https://travis-ci.com/antkorwin/better-strings"]
66
image:https://codecov.io/gh/antkorwin/better-strings/branch/master/graph/badge.svg[link ="https://codecov.io/gh/antkorwin/better-strings"]
77
image:https://maven-badges.herokuapp.com/maven-central/com.antkorwin/better-strings/badge.svg[link="https://search.maven.org/search?q=g:com.antkorwin%20AND%20a:better-strings"]
88

99
The Java Plugin to use string interpolation for Java (like in Kotlin).
1010
Supports Java 8, 9, 10, 11, ...
1111

12-
1312
## Motivation
1413

15-
In the latest JEPs https://openjdk.java.net/jeps/355, we have the only expectation of the RAW string literals,
16-
but there is nothing about the string interpolation.
14+
In the latest JEPs https://openjdk.java.net/jeps/355, we have the only expectation of the RAW string literals, but there is nothing about the string interpolation.
1715

1816
And it’s so sad, that we need writing code like this in the 2020 year:
1917

20-
[source, java]
18+
[source,java]
2119
----
2220
int a = 3;
2321
int b = 4;
@@ -29,7 +27,7 @@ just to print the string: `3 + 4 = 7`
2927

3028
of course, we can use a `var` since Java 10:
3129

32-
[source, java]
30+
[source,java]
3331
----
3432
var a = 3;
3533
var b = 4;
@@ -42,7 +40,7 @@ But this code is still sad =(
4240

4341
### Using variables in string literals
4442

45-
[source, java]
43+
[source,java]
4644
----
4745
var a = 3;
4846
var b = 4;
@@ -53,24 +51,26 @@ prints: `3 + 4 = 7`
5351

5452
### Using expressions
5553

56-
[source, java]
54+
[source,java]
5755
----
5856
var a = 3;
5957
var b = 4;
6058
System.out.println("flag = ${a > b ? true : false}");
6159
----
60+
6261
prints: `flag = false`
6362

64-
[source, java]
63+
[source,java]
6564
----
6665
var a = 3;
6766
System.out.println("pow = ${a * a}");
6867
----
68+
6969
prints: `pow = 9`
7070

7171
### Using functions
7272

73-
[source, java]
73+
[source,java]
7474
----
7575
@Test
7676
void functionCall() {
@@ -85,21 +85,83 @@ long factorial(int n) {
8585
return fact;
8686
}
8787
----
88+
8889
prints: `fact(5) = 120`
8990

91+
### Using string interpolation in class fields
92+
93+
you can use better-string for string interpolation in class fields, for example:
94+
95+
[source,java]
96+
----
97+
public class Test {
98+
public String field = "${3+4}";
99+
public String getField(){
100+
return "field = ${field}";
101+
}
102+
}
103+
----
104+
105+
`new Test().getField()` prints : `field = 7`
106+
107+
### Using string interpolation in default methods of interfaces
108+
109+
also you can use string interpolation with default methods in interfaces like this:
110+
111+
[source,java]
112+
----
113+
public interface InterfaceWithDefaultMethod {
114+
default String sum(){
115+
return "sum = ${1+2}";
116+
}
117+
}
118+
119+
public class Test implements InterfaceWithDefaultMethod {
120+
public String test() {
121+
return sum();
122+
}
123+
}
124+
----
125+
126+
The result of `new Test().test()` is `sum = 3`
127+
128+
### Using string interpolation in enums
129+
130+
In addition you can use string interpolation for code of enums:
131+
132+
[source, java]
133+
----
134+
public enum EnumCode {
135+
FIRST,
136+
SECOND,
137+
THIRD;
138+
139+
@Override
140+
public String toString() {
141+
return "value: ${this.name()}, order: ${this.ordinal() + 1}";
142+
}
143+
}
144+
----
145+
146+
`EnumCode.THIRD.toString();` should print: `value: THIRD, order: 3`
147+
148+
### Limitations
149+
150+
It's impossible to use the string interpolation within annotations value.
151+
It provides compatibility with spring framework properties injecting by the `@Value` annotation.
152+
90153

91154
### Disclaimer
92155

93156
NOTE: Keep in mind that this feature should be used carefully.
94-
You shouldn't write too much code inside string literals
95-
because it is too difficult to maintain and maybe not obvious for debugging.
157+
You shouldn't write too much code inside string literals because it is too difficult to maintain and maybe not obvious for debugging.
96158

97159

98160
## Getting started
99161

100162
You need to add the following dependency:
101163

102-
[source, xml]
164+
[source,xml]
103165
----
104166
<dependency>
105167
<groupId>com.antkorwin</groupId>
@@ -114,7 +176,7 @@ And you can use string interpolation anywhere in your code.
114176

115177
To skip the string interpolation for class, method or field you can use the `@DisabledStringInterpolation` annotation:
116178

117-
[source, java]
179+
[source,java]
118180
----
119181
@DisabledStringInterpolation
120182
class Foo {
@@ -126,66 +188,58 @@ class Foo {
126188

127189
this code prints: `${a+b}`
128190

129-
Also, you can use the following workaround
130-
to escape string interpolation locally in your code:
191+
Also, you can use the following workaround to escape string interpolation locally in your code:
131192

132-
[source, java]
193+
[source,java]
133194
----
134195
System.out.println("${'$'}{a+b}");
135196
----
136197

137198
the result is : `${a+b}`
138199

139-
140200
## How to control the generated code
141201

142-
Better Strings is a Java Annotation Processor,
143-
but it does not process specific annotations, it makes AST modification of your code while javac compiling it.
202+
Better Strings is a Java Annotation Processor, but it does not process specific annotations, it makes AST modification of your code while javac compiling it.
144203

145204
By default, each `${...}` occurrence translates into an invocation of `String#valueOf`.
146205
For instance, a string:
147206

148-
[source, java]
207+
[source,java]
149208
----
150209
"Result: ${obj}.method() = ${obj.method()}"
151210
----
152211

153212
will yield:
154213

155-
[source, java]
214+
[source,java]
156215
----
157216
"Result: "
158217
+ String.valueOf(obj)
159218
+ ".method() = "
160219
+ String.valueOf(obj.method())
161220
----
162221

163-
Under certain circumstances (e.g. with certain static code analyzers), however,
164-
it might be preferred that the generated code contains an explicit `toString` invocation for each `${...}` occurrence containing a non-null value.
222+
Under certain circumstances (e.g. with certain static code analyzers), however, it might be preferred that the generated code contains an explicit `toString` invocation for each `${...}` occurrence containing a non-null value.
165223
This can be controlled with `-AcallToStringExplicitlyInInterpolations` compiler option, which will instead make the above string translate into:
166224

167-
[source, java]
225+
[source,java]
168226
----
169227
"Result: "
170228
+ (java.util.Objects.nonNull(obj) ? java.util.Objects.requireNonNull(obj).toString() : "null")
171229
+ ".method() = "
172230
+ (java.util.Objects.nonNull(obj.method()) ? java.util.Objects.requireNonNull(obj.method()).toString() : "null")
173231
----
174232

175-
NOTE: this causes the inner part of each `${...}` to be evaluated twice,
176-
which might be problematic if the expression is side-effecting, non-deterministic or expensive to compute.
177-
233+
NOTE: this causes the inner part of each `${...}` to be evaluated twice, which might be problematic if the expression is side-effecting, non-deterministic or expensive to compute.
178234

179235
## How to use with other annotation processors
180236

181-
If you need to use multiple annotation processors (for example `better-strings` with `lombok` or `mapstruct`)
182-
and the order of processing is necessary for you then you can set the order in your building tool.
237+
If you need to use multiple annotation processors (for example `better-strings` with `lombok` or `mapstruct`) and the order of processing is necessary for you then you can set the order in your building tool.
183238

184-
In maven, you should declare dependencies as usually,
185-
then describe annotation processors in the configuration of the `maven-compiler-plugin`
239+
In maven, you should declare dependencies as usually, then describe annotation processors in the configuration of the `maven-compiler-plugin`
186240
in the build section:
187241

188-
[source, xml]
242+
[source,xml]
189243
----
190244
<plugin>
191245
<groupId>org.apache.maven.plugins</groupId>

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<groupId>com.antkorwin</groupId>
66
<artifactId>better-strings</artifactId>
77
<packaging>jar</packaging>
8-
<version>0.4</version>
8+
<version>0.5-SNAPSHOT</version>
99

1010
<!-- region Info -->
1111
<name>BetterStrings Java Plugin</name>

src/main/java/com/antkorwin/betterstrings/BetterStringsProcessor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
6666

6767
private boolean isClassOrEnum(Element codeElement) {
6868
return codeElement.getKind() == ElementKind.CLASS ||
69+
codeElement.getKind() == ElementKind.INTERFACE ||
6970
codeElement.getKind() == ElementKind.ENUM;
7071
}
7172

src/test/java/com/antkorwin/betterstrings/BetterStringsProcessorTest.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@ void createNewClassInExpression() {
8383
assertThat(result).isEqualTo("password = 1234");
8484
}
8585

86+
@Test
87+
void interpolationInFields() {
88+
@Language("Java") String classCode = "public class Test { " +
89+
" public String field = \"${3+4}\";" +
90+
" public String getField(){ " +
91+
" return \"${field}\";" +
92+
" }" +
93+
"}";
94+
95+
Object result = instantiatedTestClass(classCode).invoke("getField");
96+
assertThat(result).isEqualTo("7");
97+
}
98+
8699
@Nested
87100
class DisabledAnnotationTests {
88101

@@ -237,6 +250,87 @@ void useEnumValue() {
237250

238251
assertThat(result).isEqualTo("SECOND = 7");
239252
}
253+
254+
255+
256+
@Test
257+
void enumToString() {
258+
@Language("Java") String enumCode = "public enum EnumCode {" +
259+
" FIRST," +
260+
" SECOND," +
261+
" THIRD;" +
262+
" @Override" +
263+
" public String toString() {" +
264+
" return \"value: ${this.name()}, order: ${this.ordinal() + 1}\";" +
265+
" }"+
266+
"}";
267+
@Language("Java") String classCode = "public class Test { " +
268+
" public static String test(){ " +
269+
" return EnumCode.THIRD.toString();" +
270+
" }" +
271+
"}";
272+
273+
Object result = new CompileTest().classCode("Test", classCode)
274+
.classCode("EnumCode", enumCode)
275+
.processor(new BetterStringsProcessor())
276+
.compile()
277+
.loadClass("Test")
278+
.invokeStatic("test");
279+
280+
assertThat(result).isEqualTo("value: THIRD, order: 3");
281+
}
282+
}
283+
284+
@Nested
285+
class Interfaces {
286+
287+
@Test
288+
void useInDefaultMethod() {
289+
@Language("Java") String interfaceCode = "public interface InterfaceWithDefault {" +
290+
" default String sum(){" +
291+
" return \"${1+2}\";" +
292+
" }" +
293+
"}";
294+
@Language("Java") String classCode = "public class Test implements InterfaceWithDefault {" +
295+
" public String test() {" +
296+
" return sum();" +
297+
" }" +
298+
"}";
299+
300+
Object result = new CompileTest().classCode("Test", classCode)
301+
.classCode("InterfaceWithDefault", interfaceCode)
302+
.processor(new BetterStringsProcessor())
303+
.compile()
304+
.createClass("Test")
305+
.invoke("test");
306+
307+
assertThat(result).isEqualTo("3");
308+
}
309+
310+
@Test
311+
void useInFields() {
312+
@Language("Java") String interfaceCode = "public interface InterfaceWithDefault {" +
313+
" int a = 2;" +
314+
" String b = \"${a*2}\";" +
315+
" default String mul(){" +
316+
" return b;" +
317+
" }" +
318+
"}";
319+
@Language("Java") String classCode = "public class Test implements InterfaceWithDefault {" +
320+
" public String test() {" +
321+
" return mul();" +
322+
" }" +
323+
"}";
324+
325+
Object result = new CompileTest().classCode("Test", classCode)
326+
.classCode("InterfaceWithDefault", interfaceCode)
327+
.processor(new BetterStringsProcessor())
328+
.compile()
329+
.createClass("Test")
330+
.invoke("test");
331+
332+
assertThat(result).isEqualTo("4");
333+
}
240334
}
241335

242336
@Nested

0 commit comments

Comments
 (0)