3333import java .util .Objects ;
3434import java .util .Properties ;
3535import java .util .Set ;
36+ import java .util .concurrent .CopyOnWriteArrayList ;
3637import java .util .function .Predicate ;
3738import java .util .stream .Stream ;
3839
@@ -143,40 +144,10 @@ public class MavenProject implements Cloneable {
143144
144145 private List <MavenProject > collectedProjects ;
145146
146- /**
147- * A tuple of {@link SourceRoot} properties for which we decide that no duplicated value should exist in a project.
148- * The set of properties that we choose to put in this record may be modified in any future Maven version.
149- * The intent is to detect some configuration errors.
150- */
151- private record SourceKey (ProjectScope scope , Language language , Path directory ) {
152- /**
153- * Converts this key into a source root.
154- * Used for adding a new source when no other information is available.
155- *
156- * @return the source root for the properties of this key.
157- */
158- SourceRoot createSource () {
159- return new DefaultSourceRoot (scope , language , directory );
160- }
161-
162- /**
163- * {@return an error message to report when a conflict is detected}.
164- *
165- * @param baseDir value of {@link #getBaseDirectory()}, in order to make the message shorter
166- */
167- String conflictMessage (Path baseDir ) {
168- return "Directory " + baseDir .relativize (directory )
169- + " is specified twice for the scope \" " + scope .id ()
170- + "\" and language \" " + language .id () + "\" ." ;
171- }
172- }
173-
174147 /**
175148 * All sources of this project. The map includes main and test codes for all languages.
176- * However, we put some restrictions on what information can be repeated.
177- * Those restrictions are expressed in {@link SourceKey}.
178149 */
179- private HashMap < SourceKey , SourceRoot > sources = new LinkedHashMap <>(); // Need access to the `clone()` method.
150+ private List < SourceRoot > sources = new CopyOnWriteArrayList <>();
180151
181152 @ Deprecated
182153 private ArtifactRepository releaseArtifactRepository ;
@@ -358,11 +329,12 @@ public DependencyManagement getDependencyManagement() {
358329 * @since 4.0.0
359330 */
360331 public void addSourceRoot (SourceRoot source ) {
361- var key = new SourceKey ( source . scope (), source . language (), source . directory ());
362- SourceRoot current = sources . putIfAbsent ( key , source );
363- if ( current != null && ! current . equals ( source )) {
364- throw new IllegalArgumentException ( key . conflictMessage ( getBaseDirectory ()) );
332+ if ( sources . contains ( source )) {
333+ throw new IllegalArgumentException ( "Directory " + getBaseDirectory (). relativize ( source . directory ())
334+ + " is specified twice for the scope \" " + source . scope (). id ()
335+ + " \" and language \" " + source . language (). id () + " \" ." );
365336 }
337+ sources .add (source );
366338 }
367339
368340 /**
@@ -381,8 +353,7 @@ public void addSourceRoot(SourceRoot source) {
381353 */
382354 public void addSourceRoot (ProjectScope scope , Language language , Path directory ) {
383355 directory = getBaseDirectory ().resolve (directory ).normalize ();
384- var key = new SourceKey (scope , language , directory );
385- sources .computeIfAbsent (key , SourceKey ::createSource );
356+ addSourceRoot (new DefaultSourceRoot (scope , language , directory ));
386357 }
387358
388359 /**
@@ -402,8 +373,7 @@ public void addSourceRoot(ProjectScope scope, Language language, String director
402373 directory = directory .trim ();
403374 if (!directory .isBlank ()) {
404375 Path path = getBaseDirectory ().resolve (directory ).normalize ();
405- var key = new SourceKey (scope , language , path );
406- sources .computeIfAbsent (key , SourceKey ::createSource );
376+ addSourceRoot (scope , language , path );
407377 }
408378 }
409379 }
@@ -432,7 +402,7 @@ public void addTestCompileSourceRoot(String path) {
432402 * @see #addSourceRoot(SourceRoot)
433403 */
434404 public Collection <SourceRoot > getSourceRoots () {
435- return Collections .unmodifiableCollection (sources . values () );
405+ return Collections .unmodifiableCollection (sources );
436406 }
437407
438408 /**
@@ -449,7 +419,7 @@ public Collection<SourceRoot> getSourceRoots() {
449419 * @since 4.0.0
450420 */
451421 public Stream <SourceRoot > getEnabledSourceRoots (ProjectScope scope , Language language ) {
452- Stream <SourceRoot > s = sources .values (). stream ().filter (SourceRoot ::enabled );
422+ Stream <SourceRoot > s = sources .stream ().filter (SourceRoot ::enabled );
453423 if (scope != null ) {
454424 s = s .filter ((source ) -> scope .equals (source .scope ()));
455425 }
@@ -1249,7 +1219,7 @@ protected void setAttachedArtifacts(List<Artifact> attachedArtifacts) {
12491219 */
12501220 @ Deprecated
12511221 private void setSourceRootDirs (ProjectScope scope , Language language , List <String > roots ) {
1252- sources .values (). removeIf ((source ) -> scope .equals (source .scope ()) && language .equals (source .language ()));
1222+ sources .removeIf ((source ) -> scope .equals (source .scope ()) && language .equals (source .language ()));
12531223 Path directory = getBaseDirectory ();
12541224 for (String root : roots ) {
12551225 addSourceRoot (new DefaultSourceRoot (scope , language , directory .resolve (root )));
@@ -1335,7 +1305,7 @@ private void deepCopy(MavenProject project) {
13351305 // This property is not handled like others as we don't use public API.
13361306 // The whole implementation of this `deepCopy` method may need revision,
13371307 // but it would be the topic for a separated commit.
1338- sources = ( HashMap < SourceKey , SourceRoot >) project .sources . clone ( );
1308+ sources = new CopyOnWriteArrayList <>( project .sources );
13391309
13401310 if (project .getModel () != null ) {
13411311 setModel (project .getModel ().clone ());
0 commit comments