Skip to content

Commit 47df1a9

Browse files
Copilotlaeubi
andcommitted
Add new Resources API with Path-based methods
Co-authored-by: laeubi <[email protected]>
1 parent 5c1d60c commit 47df1a9

File tree

3 files changed

+336
-0
lines changed

3 files changed

+336
-0
lines changed

src/main/java/org/codehaus/plexus/build/BuildContext.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ public interface BuildContext {
3535
*
3636
* @param relpath is path relative to build context basedir
3737
* @return a boolean.
38+
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#hasDelta(java.nio.file.Path)} instead
3839
*/
40+
@Deprecated
3941
boolean hasDelta(String relpath);
4042

4143
/**
@@ -45,7 +47,9 @@ public interface BuildContext {
4547
* @since 0.0.5
4648
* @param file a {@link java.io.File} object.
4749
* @return a boolean.
50+
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#hasDelta(java.nio.file.Path)} instead
4851
*/
52+
@Deprecated
4953
boolean hasDelta(File file);
5054

5155
/**
@@ -54,15 +58,19 @@ public interface BuildContext {
5458
*
5559
* @param relpaths paths relative to build context basedir
5660
* @return a boolean.
61+
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#hasDelta(java.nio.file.Path)} instead
5762
*/
63+
@Deprecated
5864
boolean hasDelta(List<String> relpaths);
5965

6066
/**
6167
* Indicates that the file or folder content has been modified during the build.
6268
*
6369
* @see #newFileOutputStream(File)
6470
* @param file a {@link java.io.File} object.
71+
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#refresh(java.nio.file.Path)} instead
6572
*/
73+
@Deprecated
6674
void refresh(File file);
6775

6876
/**
@@ -78,7 +86,9 @@ public interface BuildContext {
7886
* @param file a {@link java.io.File} object.
7987
* @return a {@link java.io.OutputStream} object.
8088
* @throws java.io.IOException if any.
89+
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#newOutputStream(java.nio.file.Path)} instead
8190
*/
91+
@Deprecated
8292
OutputStream newFileOutputStream(File file) throws IOException;
8393

8494
/**
@@ -236,6 +246,8 @@ public interface BuildContext {
236246
* @param target a {@link java.io.File} object.
237247
* @param source a {@link java.io.File} object.
238248
* @return a boolean.
249+
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#isUptodate(java.nio.file.Path, java.nio.file.Path)} instead
239250
*/
251+
@Deprecated
240252
boolean isUptodate(File target, File source);
241253
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
Copyright (c) 2008 Sonatype, Inc. All rights reserved.
3+
4+
This program is licensed to you under the Apache License Version 2.0,
5+
and you may not use this file except in compliance with the Apache License Version 2.0.
6+
You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
8+
Unless required by applicable law or agreed to in writing,
9+
software distributed under the Apache License Version 2.0 is distributed on an
10+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.codehaus.plexus.build.resources;
14+
15+
import javax.inject.Inject;
16+
import javax.inject.Named;
17+
import javax.inject.Singleton;
18+
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.io.OutputStream;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
25+
import org.codehaus.plexus.build.BuildContext;
26+
27+
/**
28+
* Default implementation of the Resources interface.
29+
* <p>
30+
* This implementation delegates to the BuildContext for compatibility with existing
31+
* build infrastructure. It provides a transition path from the File-based API to the
32+
* modern Path-based API.
33+
* </p>
34+
*/
35+
@Named("default")
36+
@Singleton
37+
public class DefaultResources implements Resources {
38+
39+
private final BuildContext buildContext;
40+
41+
/**
42+
* Creates a new DefaultResources instance.
43+
*
44+
* @param buildContext the BuildContext to which operations will be delegated
45+
*/
46+
@Inject
47+
public DefaultResources(BuildContext buildContext) {
48+
this.buildContext = buildContext;
49+
}
50+
51+
@Override
52+
public boolean hasDelta(Path file) {
53+
if (file == null) {
54+
return false;
55+
}
56+
return buildContext.hasDelta(file.toFile());
57+
}
58+
59+
@Override
60+
public void refresh(Path file) {
61+
if (file != null) {
62+
buildContext.refresh(file.toFile());
63+
}
64+
}
65+
66+
@Override
67+
public OutputStream newOutputStream(Path file) throws IOException {
68+
if (file == null) {
69+
throw new IllegalArgumentException("file cannot be null");
70+
}
71+
return buildContext.newFileOutputStream(file.toFile());
72+
}
73+
74+
@Override
75+
public OutputStream newOutputStream(Path file, boolean derived) throws IOException {
76+
OutputStream outputStream = newOutputStream(file);
77+
if (derived) {
78+
// Mark the file as derived after creating the output stream
79+
// The marking happens here so that implementations can wrap the stream
80+
// if needed to defer the marking until after successful write
81+
markDerived(file);
82+
}
83+
return outputStream;
84+
}
85+
86+
@Override
87+
public boolean isUptodate(Path target, Path source) {
88+
if (target == null || source == null) {
89+
return false;
90+
}
91+
return buildContext.isUptodate(target.toFile(), source.toFile());
92+
}
93+
94+
@Override
95+
public Path getPath(String relpath) {
96+
if (relpath == null) {
97+
throw new IllegalArgumentException("relpath cannot be null");
98+
}
99+
// Get the basedir from the build context by checking a known file
100+
// The BuildContext API doesn't expose basedir directly, so we need to work around this
101+
// For the default implementation, we'll resolve against the current working directory
102+
// Custom implementations should override this to use the actual basedir
103+
return java.nio.file.Paths.get(relpath);
104+
}
105+
106+
@Override
107+
public void markDerived(Path file) {
108+
// No-op in the default implementation
109+
// Custom implementations (e.g., IDE integrations) can override this
110+
// to provide actual derived file tracking
111+
}
112+
113+
@Override
114+
public void copy(Path source, Path target) throws IOException {
115+
if (source == null) {
116+
throw new IllegalArgumentException("source cannot be null");
117+
}
118+
if (target == null) {
119+
throw new IllegalArgumentException("target cannot be null");
120+
}
121+
122+
// Only copy if the target is not up-to-date with the source
123+
if (!isUptodate(target, source)) {
124+
// Ensure parent directory exists
125+
Path parentDir = target.getParent();
126+
if (parentDir != null && !Files.exists(parentDir)) {
127+
Files.createDirectories(parentDir);
128+
}
129+
130+
// Copy using Files API and newOutputStream to ensure proper change detection
131+
try (InputStream in = Files.newInputStream(source);
132+
OutputStream out = newOutputStream(target)) {
133+
byte[] buffer = new byte[8192];
134+
int bytesRead;
135+
while ((bytesRead = in.read(buffer)) != -1) {
136+
out.write(buffer, 0, bytesRead);
137+
}
138+
}
139+
}
140+
}
141+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
Copyright (c) 2008 Sonatype, Inc. All rights reserved.
3+
4+
This program is licensed to you under the Apache License Version 2.0,
5+
and you may not use this file except in compliance with the Apache License Version 2.0.
6+
You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
8+
Unless required by applicable law or agreed to in writing,
9+
software distributed under the Apache License Version 2.0 is distributed on an
10+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.codehaus.plexus.build.resources;
14+
15+
import java.io.IOException;
16+
import java.io.OutputStream;
17+
import java.nio.file.Path;
18+
19+
/**
20+
* <p>Resources interface.</p>
21+
* <p>
22+
* This API provides a modern, flexible way to manage build resources using Path-based methods.
23+
* It separates resource management concerns from logging/messaging functionality and provides
24+
* better control over file operations during the build process.
25+
* </p>
26+
* <p>
27+
* Example usage:
28+
* </p>
29+
* <pre>
30+
* // Convert relative path to absolute Path
31+
* Path sourceFile = resources.getPath("src/main/java/Example.java");
32+
* Path targetFile = resources.getPath("target/classes/Example.class");
33+
*
34+
* // Copy source to target only if needed
35+
* resources.copy(sourceFile, targetFile);
36+
*
37+
* // Mark generated file as derived
38+
* resources.markDerived(targetFile);
39+
* </pre>
40+
*/
41+
public interface Resources {
42+
43+
/**
44+
* Returns <code>true</code> if the file has changed since last build.
45+
* <p>
46+
* <strong>Important:</strong> This is a "best effort" hint and should primarily be used
47+
* for user-editable source files where you want to detect changes. For generated
48+
* files or when there is a clear input/output relationship, prefer using
49+
* {@link #isUptodate(Path, Path)} instead, as it handles cases where target files
50+
* may be deleted or modified outside of the build process.
51+
* </p>
52+
*
53+
* @param file the file to check for changes
54+
* @return <code>true</code> if the file has changed or if the file is not under basedir
55+
*/
56+
boolean hasDelta(Path file);
57+
58+
/**
59+
* Indicates that the file or folder content has been modified during the build.
60+
* <p>
61+
* This method should be called when file content is modified through means other
62+
* than {@link #newOutputStream(Path)} or {@link #newOutputStream(Path, boolean)}.
63+
* Files changed using OutputStreams returned by those methods do not need to be
64+
* explicitly refreshed.
65+
* </p>
66+
*
67+
* @param file the file that was modified
68+
*/
69+
void refresh(Path file);
70+
71+
/**
72+
* Returns a new OutputStream that writes to the specified file.
73+
* <p>
74+
* Files changed using OutputStream returned by this method do not need to be
75+
* explicitly refreshed using {@link #refresh(Path)}.
76+
* </p>
77+
* <p>
78+
* As an optimization, OutputStreams created by incremental build contexts will
79+
* attempt to avoid writing to the file if the file content has not changed.
80+
* This ensures that file timestamps are only updated when actual changes occur,
81+
* which is important for incremental build performance.
82+
* </p>
83+
*
84+
* @param file the file to write to
85+
* @return an OutputStream for writing to the file
86+
* @throws IOException if an I/O error occurs
87+
*/
88+
OutputStream newOutputStream(Path file) throws IOException;
89+
90+
/**
91+
* Returns a new OutputStream that writes to the specified file and optionally
92+
* marks it as derived.
93+
* <p>
94+
* Files changed using OutputStream returned by this method do not need to be
95+
* explicitly refreshed using {@link #refresh(Path)}.
96+
* </p>
97+
* <p>
98+
* As an optimization, OutputStreams created by incremental build contexts will
99+
* attempt to avoid writing to the file if the file content has not changed.
100+
* </p>
101+
* <p>
102+
* When <code>derived</code> is <code>true</code>, the file is marked as generated/derived,
103+
* which can be used by IDEs to warn users if they attempt to edit the file.
104+
* This is useful for code generators that will overwrite any manual changes on
105+
* subsequent builds.
106+
* </p>
107+
*
108+
* @param file the file to write to
109+
* @param derived <code>true</code> to mark the file as derived/generated
110+
* @return an OutputStream for writing to the file
111+
* @throws IOException if an I/O error occurs
112+
*/
113+
OutputStream newOutputStream(Path file, boolean derived) throws IOException;
114+
115+
/**
116+
* Returns <code>true</code> if the target file exists and is up-to-date compared
117+
* to the source file.
118+
* <p>
119+
* More specifically, this method returns <code>true</code> when both target and
120+
* source files exist, do not have changes since last incremental build, and the
121+
* target file was last modified later than the source file. Returns <code>false</code>
122+
* in all other cases.
123+
* </p>
124+
* <p>
125+
* This method should be preferred over {@link #hasDelta(Path)} when there is a
126+
* clear input/output relationship between files, as it properly handles cases
127+
* where the target file may be deleted or modified outside of the build process.
128+
* </p>
129+
*
130+
* @param target the target/output file
131+
* @param source the source/input file
132+
* @return <code>true</code> if the target is up-to-date with the source
133+
*/
134+
boolean isUptodate(Path target, Path source);
135+
136+
/**
137+
* Converts a relative path string to an absolute Path based on the build context basedir.
138+
* <p>
139+
* This method is useful for converting relative paths (e.g., "src/main/java/Example.java")
140+
* to absolute Paths that can be used with other methods in this interface.
141+
* </p>
142+
*
143+
* @param relpath the relative path string
144+
* @return the absolute Path resolved against the build context basedir
145+
*/
146+
Path getPath(String relpath);
147+
148+
/**
149+
* Marks a file as derived (generated) by the build process.
150+
* <p>
151+
* This is useful for code generators and other build plugins that create files
152+
* that should not be edited manually. IDEs can use this information to warn
153+
* users if they attempt to modify derived files, as such changes will typically
154+
* be overwritten on the next build.
155+
* </p>
156+
* <p>
157+
* Note: In the default implementation, this is a no-op. Custom implementations
158+
* (e.g., IDE integrations) may provide actual derived file tracking.
159+
* </p>
160+
*
161+
* @param file the file to mark as derived
162+
*/
163+
void markDerived(Path file);
164+
165+
/**
166+
* Copies the source file to the target file if the target is not up-to-date.
167+
* <p>
168+
* This is a convenience method that combines {@link #isUptodate(Path, Path)}
169+
* checking with file copying using {@link #newOutputStream(Path)}. The copy
170+
* only occurs when the target does not exist or is not up-to-date with the source.
171+
* </p>
172+
* <p>
173+
* The implementation uses the Files API to open an input stream from the source
174+
* and copies it to an output stream obtained from {@link #newOutputStream(Path)},
175+
* ensuring that the target's timestamp is only updated if the content actually changes.
176+
* </p>
177+
*
178+
* @param source the source file to copy from
179+
* @param target the target file to copy to
180+
* @throws IOException if an I/O error occurs during the copy operation
181+
*/
182+
void copy(Path source, Path target) throws IOException;
183+
}

0 commit comments

Comments
 (0)