Skip to content

Commit a4d62d9

Browse files
committed
[#95] Added possibility to add resource files via fluent api (acts as alias for resources, shadows existing files)
1 parent 5b3f416 commit a4d62d9

11 files changed

Lines changed: 520 additions & 93 deletions

File tree

cute/src/main/java/io/toolisticon/cute/CompileTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public static CompilationResult compile(CuteApi.CompilerTestBB compileTestConfig
246246

247247

248248
// Configure java compilation task
249-
CompileTestFileManager javaFileManager = new CompileTestFileManager(stdJavaFileManager);
249+
CompileTestFileManager javaFileManager = new CompileTestFileManager(stdJavaFileManager, compileTestConfiguration.resourceFiles());
250250

251251
JavaCompiler.CompilationTask compilationTask = compiler.getTask(
252252
null,

cute/src/main/java/io/toolisticon/cute/CompileTestFileManager.java

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
package io.toolisticon.cute;
22

3-
import javax.tools.FileObject;
4-
import javax.tools.ForwardingJavaFileManager;
5-
import javax.tools.JavaFileManager;
6-
import javax.tools.JavaFileObject;
7-
import javax.tools.SimpleJavaFileObject;
8-
import javax.tools.StandardJavaFileManager;
93
import java.io.ByteArrayInputStream;
104
import java.io.ByteArrayOutputStream;
115
import java.io.IOException;
@@ -22,12 +16,26 @@
2216
import java.util.List;
2317
import java.util.Map;
2418

19+
import javax.tools.FileObject;
20+
import javax.tools.ForwardingJavaFileManager;
21+
import javax.tools.JavaFileManager;
22+
import javax.tools.JavaFileObject;
23+
import javax.tools.SimpleJavaFileObject;
24+
import javax.tools.StandardJavaFileManager;
25+
import javax.tools.StandardLocation;
26+
27+
import io.toolisticon.cute.CuteApi.ResourceFileBB;
28+
2529
/**
2630
* Forwarding file manager to be able to test for generated sources and resources
2731
*/
2832
class CompileTestFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
2933

3034

35+
/**
36+
* The file object cache that is used to store generated classes and resources.
37+
* @param <T> The type of cached file objects, must be either FileObject or JavaFileObject
38+
*/
3139
public static class FileObjectCache<T extends FileObject> {
3240

3341
final Map<URI, T> fileObjectCache = new HashMap<>();
@@ -54,15 +62,46 @@ public boolean isEmpty() {
5462
}
5563

5664

65+
}
66+
67+
/**
68+
* This internal class is used to provide resources from class path via a custom package.
69+
* Internally this works like an alias.
70+
* It hides any pre-existing resources with uri.
71+
*/
72+
public static class ProvidedResourceFilesCache{
73+
74+
75+
final Map<URI, FileObject> resourcesMap = new HashMap<>();
76+
77+
ProvidedResourceFilesCache (List<ResourceFileBB> resourceFiles) {
78+
79+
if (resourceFiles != null) {
80+
resourceFiles.stream().filter(e -> e.getRelativeName() != null).forEach( e -> resourcesMap.put(uriForFileObject(StandardLocation.CLASS_PATH, e.targetPackageName(), e.getRelativeName()), FileObjectUtils.forPassedInResource(e)));
81+
}
82+
83+
}
84+
85+
FileObject get(String packageName, String relativeName) {
86+
URI uri = uriForFileObject(StandardLocation.CLASS_PATH, packageName,relativeName);
87+
return this.resourcesMap.get(uri);
88+
}
89+
90+
91+
5792
}
5893

5994

6095
private final FileObjectCache<InMemoryOutputJavaFileObject> generatedJavaFileObjectCache = new FileObjectCache<>();
6196
private final FileObjectCache<InMemoryOutputFileObject> generatedFileObjectCache = new FileObjectCache<>();
97+
private final ProvidedResourceFilesCache providedResourceFilesCache;
6298

6399

64-
public CompileTestFileManager(StandardJavaFileManager standardJavaFileManager) {
100+
public CompileTestFileManager(StandardJavaFileManager standardJavaFileManager, List<CuteApi.ResourceFileBB> resourceFiles) {
65101
super(standardJavaFileManager);
102+
103+
// configure cache for resource files
104+
providedResourceFilesCache = new ProvidedResourceFilesCache(resourceFiles);
66105

67106
}
68107

@@ -124,7 +163,18 @@ public FileObject getFileForInput(Location location, String packageName, String
124163
} else {
125164
throw new IllegalArgumentException(Constants.Messages.IAE_CANNOT_FIND_FILEOBJECT.produceMessage(uri.toString()));
126165
}
166+
} else if (StandardLocation.CLASS_PATH.equals(location)) {
167+
168+
// Try to get it from provided resource file cache
169+
FileObject providedResource = this.providedResourceFilesCache.get(packageName, relativeName);
170+
171+
if (providedResource != null) {
172+
return providedResource;
173+
}
174+
127175
}
176+
177+
// use standard manager if file haven't been found so far
128178
return super.getFileForInput(location, packageName, relativeName);
129179
}
130180

@@ -138,8 +188,17 @@ public FileObject getFileForInput(Location location, String packageName, String
138188
* @return true if JavaFileObject exists, otherwise false
139189
*/
140190
public boolean existsExpectedJavaFileObject(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) {
141-
142-
return this.generatedJavaFileObjectCache.contains(uriForJavaFileObject(location, className, kind));
191+
192+
if (location.isOutputLocation()) {
193+
return this.generatedJavaFileObjectCache.contains(uriForJavaFileObject(location, className, kind));
194+
}
195+
196+
try {
197+
return super.getJavaFileForInput(location, className, kind) != null;
198+
} catch (IOException e) {
199+
throw new IllegalStateException("Something went wrong while accessing the default FileManager", e);
200+
}
201+
143202

144203
}
145204

@@ -152,10 +211,24 @@ public boolean existsExpectedJavaFileObject(JavaFileManager.Location location, S
152211
* @return true if FileObject exists, otherwise false
153212
*/
154213
public boolean existsExpectedFileObject(JavaFileManager.Location location, String packageName, String relativeName) {
155-
156-
return this.generatedFileObjectCache.contains(uriForFileObject(location, packageName, relativeName));
157-
158-
214+
215+
// return true if file could be found in resource cache, otherwise check regular file manager
216+
if (StandardLocation.CLASS_PATH.equals(location) && this.providedResourceFilesCache.get(packageName, relativeName) != null) {
217+
return true;
218+
}
219+
220+
// otherwise check the generated file object cache
221+
if (location.isOutputLocation()) {
222+
return this.generatedFileObjectCache.contains(uriForFileObject(location, packageName, relativeName));
223+
}
224+
225+
// use standard file manager for input files
226+
try {
227+
return super.getFileForInput(location, packageName, relativeName) != null;
228+
} catch (IOException e) {
229+
throw new IllegalStateException("Something went wrong while accessing the default FileManager", e);
230+
}
231+
159232
}
160233

161234

@@ -166,7 +239,7 @@ interface OutputStreamCallback {
166239
}
167240

168241

169-
private static URI uriForFileObject(Location location, String packageName, String relativeName) {
242+
static URI uriForFileObject(Location location, String packageName, String relativeName) {
170243
StringBuilder uri = new StringBuilder("mem://").append(location.getName()).append('/');
171244
if (!packageName.isEmpty()) {
172245
uri.append(packageName.replace('.', '/')).append('/');
@@ -304,4 +377,5 @@ public void write(byte[] b) throws IOException {
304377
outputStreamCallback.setContent(this.toByteArray());
305378
}
306379
}
380+
307381
}

cute/src/main/java/io/toolisticon/cute/CuteApi.java

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
import io.toolisticon.fluapigen.api.FluentApiRoot;
1717
import io.toolisticon.fluapigen.api.MappingAction;
1818
import io.toolisticon.fluapigen.api.TargetBackingBean;
19+
import io.toolisticon.fluapigen.validation.api.FluentApiValidator;
1920
import io.toolisticon.fluapigen.validation.api.NotNull;
21+
import io.toolisticon.fluapigen.validation.api.Validator;
2022

2123
import javax.annotation.processing.Processor;
2224
import javax.lang.model.element.Element;
@@ -57,12 +59,13 @@ public interface CompilerTestBB {
5759

5860
UnitTestType getPassInType();
5961

60-
6162
List<Class<? extends Processor>> processors();
6263

6364
List<String> compilerOptions();
6465

6566
Set<JavaFileObject> sourceFiles();
67+
68+
List<ResourceFileBB> resourceFiles();
6669

6770
Set<String> modules();
6871

@@ -122,6 +125,34 @@ default List<String> getNormalizedCompilerOptions() {
122125

123126
}
124127

128+
@FluentApiBackingBean
129+
public interface ResourceFileBB {
130+
131+
@FluentApiBackingBeanField("targetPackageName")
132+
String targetPackageName();
133+
134+
@FluentApiBackingBeanField("resource")
135+
String resource();
136+
137+
138+
/**
139+
* Gets the relative file name of the resource
140+
* @return
141+
*/
142+
default String getRelativeName() {
143+
144+
try {
145+
return new File(ResourceFileBB.class.getResource(resource()).toURI()).getName();
146+
} catch (URISyntaxException e) {
147+
return null;
148+
}
149+
150+
}
151+
}
152+
153+
154+
155+
125156
@FluentApiBackingBean
126157
public interface ExceptionCheckBB {
127158

@@ -442,7 +473,21 @@ default BlackBoxTestFinalGivenInterface andSourceFilesFromFolders(String ... fol
442473
return andSourceFiles(files.toArray(new String[0]));
443474
}
444475

445-
476+
/**
477+
* Adds a resource file to a specific package in the class path.
478+
* By doing that the passed resource file will be available with it's original name in the passed target package.
479+
* This shadows existing resource files in CLASS_PATH location!
480+
* !!! WARNING - This will work in the tests but it's not guaranteed that your annotation processor will work out of the box with all the different kind of build tools or compilers out there !!!
481+
* @param targetPackageName The target package name to use
482+
* @param resource The resource to use
483+
* @return the next fluent interface
484+
*/
485+
@FluentApiInlineBackingBeanMapping("resourceFiles")
486+
BlackBoxTestFinalGivenInterface andResourceFile(
487+
@FluentApiBackingBeanMapping(action = MappingAction.SET, value = "targetPackageName" , target = TargetBackingBean.INLINE) String targetPackageName,
488+
@FluentApiBackingBeanMapping(action = MappingAction.SET, value = "resource" , target = TargetBackingBean.INLINE) @ResourceIsFileAndExists String resource
489+
);
490+
446491
}
447492

448493
@FluentApiInterface(CompilerTestBB.class)
@@ -620,6 +665,23 @@ default UnitTestGivenInterface useSourceFilesFromFolders(String ... folders) {
620665
return useSourceFiles(files.toArray(new String[0]));
621666
}
622667

668+
669+
670+
/**
671+
* Adds a resource file to a specific package in the class path.
672+
* By doing that the passed resource file will be available with it's original name in the passed target package.
673+
* This shadows existing resource files in CLASS_PATH location!
674+
* !!! WARNING - This will work in the tests but it's not guaranteed that your annotation processor will work out of the box with all the different kind of build tools or compilers out there !!!
675+
* @param targetPackageName The target package name to use
676+
* @param resource The resource to use
677+
* @return the next fluent interface
678+
*/
679+
@FluentApiInlineBackingBeanMapping("resourceFiles")
680+
UnitTestGivenInterface useResourceFile(
681+
@FluentApiBackingBeanMapping(action = MappingAction.SET, value = "targetPackageName" , target = TargetBackingBean.INLINE) String targetPackageName,
682+
@FluentApiBackingBeanMapping(action = MappingAction.SET, value = "resource" , target = TargetBackingBean.INLINE) @ResourceIsFileAndExists String resource
683+
);
684+
623685

624686
/**
625687
* Traverse to when section to pass in an Element or a processor instant and to define the unit test scenario.
@@ -1325,6 +1387,40 @@ public <T extends FileObject> GeneratedFileObjectMatcher createMatcher(T expecte
13251387
*/
13261388
protected abstract <T extends FileObject> GeneratedFileObjectMatcher createMatcher(T expectedFileObject);
13271389
}
1390+
1391+
// --------------------------------------------------------------------
1392+
// Validators
1393+
// --------------------------------------------------------------------
1394+
1395+
1396+
@FluentApiValidator(value = CuteApi.ResourceIsFileAndExists.ValidatorImpl.class)
1397+
public @interface ResourceIsFileAndExists {
1398+
1399+
class ValidatorImpl implements Validator<String> {
1400+
1401+
1402+
@Override
1403+
public boolean validate(String resourceFile) {
1404+
URL resourceURL = CuteApi.class.getResource(resourceFile);
1405+
if (resourceURL == null) {
1406+
throw new IllegalArgumentException("Passed resource '" + resourceFile + " doesn't exist");
1407+
}
1408+
1409+
1410+
try {
1411+
if (new File(resourceURL.toURI()).isDirectory()) {
1412+
throw new IllegalArgumentException("Passed resource '" + resourceFile + " is a directory, but must be a file.");
1413+
}
1414+
} catch (URISyntaxException e) {
1415+
return false;
1416+
}
1417+
1418+
return true;
1419+
}
1420+
1421+
}
1422+
1423+
}
13281424

13291425
// --------------------------------------------------------------------
13301426
// Commands

0 commit comments

Comments
 (0)