1+ /*
2+ * Copyright 2009-2026 the original author or authors.
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+ package org .metaeffekt .core .inventory .processor .report ;
17+
18+ import lombok .extern .slf4j .Slf4j ;
19+ import org .apache .commons .lang3 .StringUtils ;
20+ import org .apache .tools .ant .Project ;
21+ import org .apache .tools .ant .taskdefs .Copy ;
22+ import org .apache .tools .ant .types .FileSet ;
23+ import org .metaeffekt .core .inventory .processor .model .Artifact ;
24+ import org .metaeffekt .core .inventory .processor .model .Constants ;
25+ import org .metaeffekt .core .inventory .processor .model .Inventory ;
26+ import org .metaeffekt .core .inventory .processor .model .LicenseMetaData ;
27+ import org .metaeffekt .core .inventory .processor .report .configuration .ReportConfigurationParameters ;
28+
29+ import java .io .File ;
30+ import java .util .HashSet ;
31+ import java .util .Set ;
32+
33+ import static org .metaeffekt .core .inventory .processor .model .Constants .*;
34+
35+ @ Slf4j
36+ public class AnnexResourceProcessor {
37+ private final Inventory inventory ;
38+ private final Inventory referenceInventory ;
39+ private final ReportConfigurationParameters configParams ;
40+
41+ private final File referenceInventoryDir ;
42+ private final String referenceComponentPath ;
43+ private final String referenceLicensePath ;
44+
45+ private final File targetComponentDir ;
46+ private final File targetLicenseDir ;
47+
48+ public AnnexResourceProcessor (Inventory inventory , Inventory referenceInventory ,
49+ ReportConfigurationParameters configParams ,
50+ File referenceInventoryDir , String referenceComponentPath , String referenceLicensePath ,
51+ File targetComponentDir , File targetLicenseDir ) {
52+ this .inventory = inventory ;
53+ this .referenceInventory = referenceInventory ;
54+ this .configParams = configParams ;
55+ this .referenceInventoryDir = referenceInventoryDir ;
56+ this .referenceComponentPath = referenceComponentPath ;
57+ this .referenceLicensePath = referenceLicensePath ;
58+ this .targetComponentDir = targetComponentDir ;
59+ this .targetLicenseDir = targetLicenseDir ;
60+ }
61+
62+ public boolean execute () {
63+ // Enrich the local inventory with reference data
64+ enrichInventory ();
65+
66+ // Perform folder creation
67+ final Set <String > reportedSourceFolders = new HashSet <>();
68+ boolean missingFiles = false ;
69+
70+ for (Artifact artifact : inventory .getArtifacts ()) {
71+ if (!artifact .isEnabledForDistribution ()) {
72+ continue ;
73+ }
74+
75+ final String componentName = artifact .getComponent ();
76+ final String sourceLicense = artifact .getLicense ();
77+
78+ if (StringUtils .isBlank (sourceLicense ) || StringUtils .isBlank (componentName )) {
79+ continue ;
80+ }
81+
82+ final String version = artifact .getVersion ();
83+ boolean isArtifactVersionWildcard = isWildcard (version ) ||
84+ STRING_TRUE .equalsIgnoreCase (artifact .get (Constants .KEY_WILDCARD_MATCH ));
85+
86+ boolean isUndefinedVersion = version == null ;
87+
88+ // Resolve license metadata from the now-enriched inventory
89+ final LicenseMetaData matchingLicenseMetaData = inventory .
90+ findMatchingLicenseMetaData (componentName , sourceLicense , version );
91+
92+ String effectiveLicense = artifact .getLicense ();
93+ if (matchingLicenseMetaData != null ) {
94+ effectiveLicense = matchingLicenseMetaData .deriveLicenseInEffect ();
95+ }
96+ // Normalize license string for folder splitting
97+ effectiveLicense = effectiveLicense .replaceAll ("\\ s*,\\ s*" , "|" );
98+
99+ final String versionUnspecificComponentFolder = LicenseMetaData .deriveComponentFolderName (componentName );
100+ final String versionSpecificComponentFolder = LicenseMetaData .deriveComponentFolderName (componentName , version );
101+
102+ final String sourcePath = (isArtifactVersionWildcard || isUndefinedVersion ) ?
103+ versionUnspecificComponentFolder : versionSpecificComponentFolder ;
104+
105+ final String targetPath = (isArtifactVersionWildcard || isUndefinedVersion ) ?
106+ versionUnspecificComponentFolder : versionSpecificComponentFolder ;
107+
108+ // Copy logic
109+ if (targetComponentDir != null ) {
110+ missingFiles |= checkAndCopyComponentFolder (sourcePath ,
111+ new File (targetComponentDir , targetPath ), reportedSourceFolders );
112+ }
113+
114+ if (targetLicenseDir != null ) {
115+ for (String licenseInEffect : effectiveLicense .split ("\\ |" )) {
116+ final String licenseFolderName = LicenseMetaData .deriveLicenseFolderName (licenseInEffect );
117+ File licenseTargetDir = new File (targetLicenseDir , licenseFolderName );
118+
119+ missingFiles |= checkAndCopyLicenseFolder (licenseFolderName ,
120+ licenseTargetDir , reportedSourceFolders );
121+
122+ missingFiles |= checkAndCopyComponentFolder (sourcePath ,
123+ new File (licenseTargetDir , targetPath ), reportedSourceFolders );
124+ }
125+ }
126+ }
127+ return !missingFiles ;
128+ }
129+
130+ private void enrichInventory () {
131+ if (referenceInventory != null ) {
132+ log .debug ("Enriching inventory with reference inventory data." );
133+ inventory .inheritLicenseMetaData (referenceInventory , false );
134+ inventory .filterLicenseMetaData ();
135+ inventory .inheritLicenseData (referenceInventory , false );
136+ }
137+ }
138+
139+ private boolean isWildcard (String version ) {
140+ return ASTERISK .equalsIgnoreCase (version ) ||
141+ (version != null && version .startsWith (VERSION_PLACHOLDER_PREFIX ) && version .endsWith (VERSION_PLACHOLDER_SUFFIX ));
142+ }
143+
144+ private boolean checkAndCopyLicenseFolder (String folderName , File targetDir , Set <String > reported ) {
145+ File sourceDir = new File (referenceInventoryDir , referenceLicensePath );
146+ return performCopy (sourceDir , folderName , targetDir , reported , configParams .isFailOnMissingLicenseFile (), "[missing license file]" );
147+ }
148+
149+ private boolean checkAndCopyComponentFolder (String folderName , File targetDir , Set <String > reported ) {
150+ File sourceDir = new File (referenceInventoryDir , referenceComponentPath );
151+ return performCopy (sourceDir , folderName , targetDir , reported , configParams .isFailOnMissingComponentFiles (), "[missing component specific license file]" );
152+ }
153+
154+ private boolean performCopy (File sourceRootDir , String folderName , File targetDir , Set <String > reported , boolean failFlag , String errorMsg ) {
155+ File sourceFolder = new File (sourceRootDir , folderName );
156+ if (sourceFolder .exists ()) {
157+ copyFolderContent (sourceRootDir , folderName , targetDir );
158+ return false ;
159+ } else {
160+ if (!reported .contains (folderName )) {
161+ if (failFlag ) {
162+ log .error ("{} in folder [{}]" , errorMsg , folderName );
163+ } else if (reported .size () <= 10 ) {
164+ log .warn ("{} in folder [{}]" , errorMsg , folderName );
165+ }
166+ reported .add (folderName );
167+ return true ;
168+ }
169+ return false ;
170+ }
171+ }
172+
173+ private void copyFolderContent (File sourceRootDir , String sourcePath , File targetDir ) {
174+ log .info ("copied {} from {} to {}" , sourceRootDir , sourcePath , targetDir );
175+ Copy copy = new Copy ();
176+ copy .setProject (new Project ());
177+ FileSet fileSet = new FileSet ();
178+ fileSet .setDir (new File (sourceRootDir , sourcePath ));
179+ fileSet .setIncludes ("**/*" );
180+ copy .setIncludeEmptyDirs (false );
181+ copy .setFailOnError (true );
182+ copy .setOverwrite (true );
183+ copy .addFileset (fileSet );
184+ copy .setTodir (targetDir );
185+ copy .perform ();
186+ }
187+ }
0 commit comments