Skip to content

Commit 3e3f36c

Browse files
authored
Merge pull request #67 from polyapi/EN_#4386_fix-simple-name-collisions
EN #4386 fix SDK to build without name collisions
2 parents 58c0e96 + a6cf5c2 commit 3e3f36c

File tree

4 files changed

+112
-26
lines changed

4 files changed

+112
-26
lines changed

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
##
4+
## [0.15.5] - 2025-06-04
5+
6+
### Added
7+
8+
- `classFqn` helper to compute fully-qualified names
9+
- import-filter to drop any illegal import strings
10+
- `typeRef` helper to pick simple name or FQN and avoid duplicates
11+
- `lastSegment` helper to filter out a context’s own class from imports
12+
13+
### Changed
14+
15+
- Updated `ResolvedContext.hbs` so that all generated-type references go through `typeRef` (simple names only when safe)
16+
- Updated constructor loops in `ResolvedContext.hbs` to call proxy methods with `classFqn` (true FQNs)
17+
18+
### Fixed
19+
20+
- “already defined in this compilation unit” compile errors—SDK now builds without name collisions
21+
22+
##
323
## [0.15.4] - 2024-11-15
424

525
### Added

polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/generation/PolyObjectResolverService.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.ArrayList;
66
import java.util.HashSet;
77
import java.util.List;
8+
import java.util.Objects;
89
import java.util.Optional;
910
import java.util.Set;
1011
import java.util.function.Function;
@@ -43,6 +44,8 @@
4344
@Slf4j
4445
public class PolyObjectResolverService {
4546
private final JsonSchemaParser jsonSchemaParser;
47+
private static final Pattern VALID_IMPORT =
48+
Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)+$");
4649

4750
public PolyObjectResolverService(JsonSchemaParser jsonSchemaParser) {
4851
this.jsonSchemaParser = jsonSchemaParser;
@@ -92,12 +95,21 @@ public ResolvedServerVariableSpecification resolve(ServerVariableSpecification s
9295

9396
public ResolvedContext resolve(Context context) {
9497
Set<String> imports = new HashSet<>();
95-
context.getSubcontexts().stream().map(subcontext -> format("%s.%s", subcontext.getPackageName(), subcontext.getClassName())).forEach(imports::add);
98+
context.getSubcontexts().stream()
99+
.map(subcontext -> format("%s.%s", subcontext.getPackageName(), subcontext.getClassName()))
100+
.filter(s -> !s.isBlank())
101+
.forEach(imports::add);
96102
context.getSpecifications().forEach(specification -> {
97103
ImportsCollectorVisitor importsCollectorVisitor = new ImportsCollectorVisitor(specification.getPackageName(), specification.getClassName(), jsonSchemaParser);
98104
importsCollectorVisitor.doVisit(specification);
99-
imports.addAll(importsCollectorVisitor.getImports());
105+
importsCollectorVisitor.getImports().stream()
106+
.filter(Objects::nonNull)
107+
.filter(s -> !s.isBlank())
108+
.filter(s -> VALID_IMPORT.matcher(s).matches())
109+
.filter(s -> !s.substring(s.lastIndexOf('.') + 1).equals(context.getClassName()))
110+
.forEach(imports::add);
100111
});
112+
101113
return new ResolvedContext(context.getName(), context.getPackageName(), imports, context.getClassName(), context.getSubcontexts().stream().map(this::resolve).toList(), context.getSpecifications().stream().map(specification -> {
102114
PolyObjectResolverVisitor visitor = new PolyObjectResolverVisitor(this);
103115
visitor.doVisit(specification);

polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/template/PolyHandlebars.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.function.BiPredicate;
99
import java.util.function.Function;
10+
import java.lang.reflect.Method;
1011

1112
public class PolyHandlebars extends Handlebars {
1213

@@ -15,6 +16,50 @@ public PolyHandlebars() {
1516
registerSimpleHelper("toCamelCase", StringUtils::toCamelCase);
1617
registerSimpleHelper("toPascalCase", StringUtils::toCamelCase);
1718
registerConditionalHelper("ifIsType", (object, options) -> object.getClass().getSimpleName().equals(options.param(0)));
19+
registerSimpleHelper("lastSegment", (Object fqn) -> {
20+
if (fqn == null) {
21+
return "";
22+
}
23+
String s = fqn.toString();
24+
int idx = s.lastIndexOf('.');
25+
return idx == -1 ? s : s.substring(idx + 1);
26+
});
27+
registerConditionalHelper("eq",
28+
(obj, opts) -> {
29+
Object other = opts.param(0, "");
30+
return obj != null && obj.toString().equals(other == null ? "" : other.toString());
31+
});
32+
registerHelper("typeRef", (Object ctx, Options opts) -> {
33+
if (ctx == null) {
34+
return "";
35+
}
36+
String fqn = ctx.toString();
37+
String parentSimple = opts.param(0, "").toString();
38+
String simple = fqn.substring(fqn.lastIndexOf('.') + 1);
39+
return simple.equals(parentSimple) ? fqn : simple;
40+
});
41+
registerHelper("classFqn", (Object ctx, Options o) -> {
42+
if (ctx == null) return "";
43+
44+
for (String m : new String[]{"getFullClassName", "getFullName"}) {
45+
try {
46+
Method mm = ctx.getClass().getMethod(m);
47+
Object val = mm.invoke(ctx);
48+
if (val != null) return val.toString();
49+
} catch (ReflectiveOperationException ignored) {}
50+
}
51+
52+
try {
53+
Method pm = ctx.getClass().getMethod("getPackageName");
54+
Method cm = ctx.getClass().getMethod("getClassName");
55+
Object pkg = pm.invoke(ctx);
56+
Object cls = cm.invoke(ctx);
57+
if (pkg != null && cls != null) return pkg + "." + cls;
58+
if (cls != null) return cls.toString();
59+
} catch (ReflectiveOperationException ignored) {}
60+
61+
return "";
62+
});
1863
}
1964

2065
private <T> void registerSimpleHelper(String name, Function<T, ?> helper) {

polyapi-maven-plugin/src/main/resources/templates/ResolvedContext.hbs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,68 +8,74 @@ import io.polyapi.client.api.model.PolyEntity;
88
import io.polyapi.client.api.AuthTokenOptions;
99
import io.polyapi.commons.api.model.PolyGeneratedClass;
1010
{{~#each this.imports}}
11+
{{~#unless (eq (lastSegment this) ../className)}}
1112
import {{{this}}};
13+
{{~/unless}}
1214
{{~/each}}
1315

1416
@PolyGeneratedClass
1517
public class {{className}} extends PolyContext {
1618
{{~#each functionSpecifications}}
17-
private final {{this.className}} {{this.name}};
19+
private final {{typeRef (classFqn this) ../className}} {{this.name}};
1820
{{~/each}}
1921
{{~#each standardAuthFunctionSpecifications}}
20-
private final {{this.className}} {{this.name}};
22+
private final {{typeRef (classFqn this) ../className}} {{this.name}};
2123
{{~/each}}
2224
{{~#each subresourceAuthFunctionSpecifications}}
23-
private final {{this.className}} {{this.name}};
25+
private final {{typeRef (classFqn this) ../className}} {{this.name}};
2426
{{~/each}}
2527
{{~#each serverVariableSpecifications}}
26-
public final {{this.className}} {{this.name}};
28+
private final {{typeRef (classFqn this) ../className}} {{this.name}};
2729
{{~/each}}
2830
{{~#each webhookHandlerSpecifications}}
29-
private final {{this.className}} {{this.name}};
31+
private final {{typeRef (classFqn this) ../className}} {{this.name}};
3032
{{~/each}}
3133
{{#each subcontexts}}
32-
public final {{this.className}} {{this.name}};
34+
private final {{typeRef (classFqn this) ../className}} {{this.name}};
3335
{{~/each}}
3436

35-
public {{className}}(PolyProxyFactory proxyFactory, WebSocketClient webSocketClient) {
36-
super(proxyFactory, webSocketClient);
37+
public {{className}}(PolyProxyFactory proxyFactory, WebSocketClient webSocketClient) {
38+
super(proxyFactory, webSocketClient);
3739
{{~#each serverFunctionSpecifications}}
38-
this.{{this.name}} = createServerFunctionProxy({{this.className}}.class);
40+
this.{{this.name}} =
41+
createServerFunctionProxy({{classFqn this}}.class);
3942
{{~/each}}
4043
{{~#each customFunctionSpecifications}}
41-
this.{{this.name}} = createCustomFunctionProxy({{this.className}}.class);
44+
this.{{this.name}} =
45+
createCustomFunctionProxy({{classFqn this}}.class);
4246
{{~/each}}
4347
{{~#each apiFunctionSpecifications}}
44-
this.{{this.name}} = createApiFunctionProxy({{this.className}}.class);
48+
this.{{this.name}} =
49+
createApiFunctionProxy({{classFqn this}}.class);
4550
{{~/each}}
4651
{{~#each subresourceAuthFunctionSpecifications}}
47-
this.{{this.name}} = createSubresourceAuthFunction({{this.className}}.class);
52+
this.{{this.name}} =
53+
createSubresourceAuthFunction({{classFqn this}}.class);
4854
{{~/each}}
4955
{{~#each standardAuthFunctionSpecifications}}
50-
this.{{this.name}} = create{{#if audienceRequired}}Audience{{/if}}TokenAuthFunction({{this.className}}.class);
56+
this.{{this.name}} =
57+
create{{#if audienceRequired}}Audience{{/if}}TokenAuthFunction({{classFqn this}}.class);
5158
{{~/each}}
5259
{{~#each serverVariableSpecifications}}
53-
this.{{this.name}} = createServerVariableHandler({{this.className}}.class);
60+
this.{{this.name}} =
61+
createServerVariableHandler({{classFqn this}}.class);
5462
{{~/each}}
5563
{{~#each webhookHandlerSpecifications}}
56-
this.{{this.name}} = createPolyTriggerProxy({{this.className}}.class);
64+
this.{{this.name}} =
65+
createPolyTriggerProxy({{classFqn this}}.class);
5766
{{~/each}}
5867
{{#each subcontexts}}
59-
this.{{this.name}} = new {{this.className}}(proxyFactory, webSocketClient);
68+
this.{{this.name}} = new {{typeRef (classFqn this) ../className}}(proxyFactory, webSocketClient);
6069
{{~/each}}
6170
}
6271

6372
{{~#each functionSpecifications}}
6473
public {{{this.returnType}}} {{{this.methodSignature}}} {
65-
{{~#if this.returnsValue}}
66-
return
67-
{{~else}}
68-
{{~/if}} this.{{this.name}}.{{this.name}}({{this.paramVariableNames}});
74+
{{#if this.returnsValue}}return {{/if}}this.{{this.name}}.{{this.name}}({{this.paramVariableNames}});
6975
}
7076

71-
public {{{this.className}}} get{{{this.className}}}Function() {
72-
return this.{{{this.name}}};
77+
public {{typeRef (classFqn this) ../className}} get{{this.className}}Function() {
78+
return this.{{this.name}};
7379
}
7480
{{~/each}}
7581

@@ -110,6 +116,9 @@ public class {{className}} extends PolyContext {
110116
{{~#each specifications}}
111117

112118
{{~#ifIsType this "AuthFunctionSpecification"}}
119+
public {{typeRef (classFqn this) ../className}} get{{this.className}}AuthFunction() {
120+
return this.{{this.name}};
121+
}
113122
{{~#if subResource}}
114123
public void {{name}}(String token) {
115124
this.{{name}}.{{name}}(token);
@@ -127,8 +136,8 @@ public class {{className}} extends PolyContext {
127136
}
128137
{{~/if}}
129138

130-
public {{{this.className}}} get{{{this.className}}}AuthFunction() {
131-
return this.{{{this.name}}};
139+
public {{typeRef (classFqn this) ../className}} get{{this.className}}AuthFunction() {
140+
return this.{{this.name}};
132141
}
133142
{{~/ifIsType}}
134143
{{~/each}}

0 commit comments

Comments
 (0)