1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.rat.configuration;
20
21 import java.io.IOException;
22 import java.io.Writer;
23 import java.lang.reflect.InvocationTargetException;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.HashSet;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.SortedSet;
30 import java.util.UUID;
31 import java.util.function.Predicate;
32
33 import org.apache.commons.lang3.StringUtils;
34 import org.apache.rat.ImplementationException;
35 import org.apache.rat.ReportConfiguration;
36 import org.apache.rat.analysis.IHeaderMatcher;
37 import org.apache.rat.api.RatException;
38 import org.apache.rat.config.parameters.ComponentType;
39 import org.apache.rat.config.parameters.Description;
40 import org.apache.rat.configuration.builders.MatcherRefBuilder;
41 import org.apache.rat.license.ILicense;
42 import org.apache.rat.license.ILicenseFamily;
43 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
44 import org.apache.rat.report.xml.writer.IXmlWriter;
45 import org.apache.rat.report.xml.writer.XmlWriter;
46
47
48
49
50 public class XMLConfigurationWriter {
51
52 private final ReportConfiguration configuration;
53
54 private final Set<String> matchers;
55
56 private final Set<String> licenseChildren;
57
58
59
60
61
62 public XMLConfigurationWriter(final ReportConfiguration configuration) {
63 this.configuration = configuration;
64 this.matchers = new HashSet<>();
65 licenseChildren = new HashSet<>(Arrays.asList(XMLConfig.LICENSE_CHILDREN));
66 }
67
68 private Predicate<Description> attributeFilter(final Description parent) {
69 return d -> {
70 if (d.getType() == ComponentType.PARAMETER) {
71 return switch (parent.getType()) {
72 case MATCHER -> !XMLConfig.isInlineNode(parent.getCommonName(), d.getCommonName());
73 case LICENSE -> !licenseChildren.contains(d.getCommonName());
74 default -> true;
75 };
76 }
77 return false;
78 };
79 }
80
81
82
83
84
85
86 public void write(final Writer plainWriter) throws RatException {
87 write(new XmlWriter(plainWriter));
88 }
89
90
91
92
93
94
95 public void write(final IXmlWriter writer) throws RatException {
96 if (configuration.listFamilies() != LicenseFilter.NONE || configuration.listLicenses() != LicenseFilter.NONE) {
97 try {
98 writer.openElement(XMLConfig.ROOT);
99
100
101 SortedSet<ILicenseFamily> families = configuration.getLicenseFamilies(configuration.listFamilies());
102 if (!families.isEmpty()) {
103 writer.openElement(XMLConfig.FAMILIES);
104 for (ILicenseFamily family : families) {
105 writeFamily(writer, family);
106 }
107 writer.closeElement();
108 }
109
110
111 SortedSet<ILicense> licenses = configuration.getLicenses(configuration.listLicenses());
112 if (!licenses.isEmpty()) {
113 writer.openElement(XMLConfig.LICENSES);
114 for (ILicense license : licenses) {
115 writeDescription(writer, license.getDescription(), license);
116 }
117 writer.closeElement();
118 }
119
120
121 writer.openElement(XMLConfig.APPROVED);
122 for (String family : configuration.getLicenseCategories(LicenseFilter.APPROVED)) {
123 writer.openElement(XMLConfig.APPROVED).attribute(XMLConfig.ATT_LICENSE_REF, family.trim())
124 .closeElement();
125 }
126 writer.closeElement();
127
128
129 MatcherBuilderTracker tracker = MatcherBuilderTracker.instance();
130 writer.openElement(XMLConfig.MATCHERS);
131 for (Class<?> clazz : tracker.getClasses()) {
132 writer.openElement(XMLConfig.MATCHER).attribute(XMLConfig.ATT_CLASS_NAME, clazz.getCanonicalName())
133 .closeElement();
134 }
135 writer.closeElement();
136
137 writer.closeElement();
138 } catch (IOException e) {
139 throw new RatException(e);
140 }
141 }
142 }
143
144 private void writeFamily(final IXmlWriter writer, final ILicenseFamily family) throws RatException {
145 try {
146 writer.openElement(XMLConfig.FAMILY).attribute(XMLConfig.ATT_ID, family.getFamilyCategory().trim())
147 .attribute(XMLConfig.ATT_NAME, family.getFamilyName());
148 writer.closeElement();
149 } catch (IOException e) {
150 throw new RatException(e);
151 }
152 }
153
154 private void writeDescriptions(final IXmlWriter writer, final Collection<Description> descriptions, final IHeaderMatcher component)
155 throws RatException {
156 for (Description description : descriptions) {
157 writeDescription(writer, description, component);
158 }
159 }
160
161 private void writeChildren(final IXmlWriter writer, final Description description, final IHeaderMatcher component)
162 throws RatException {
163 writeAttributes(writer, description.filterChildren(attributeFilter(component.getDescription())), component);
164 writeDescriptions(writer, description.filterChildren(attributeFilter(component.getDescription()).negate()),
165 component);
166 }
167
168 private void writeAttributes(final IXmlWriter writer, final Collection<Description> descriptions, final IHeaderMatcher component)
169 throws RatException {
170 for (Description d : descriptions) {
171 try {
172 writeAttribute(writer, d, component);
173 } catch (IOException e) {
174 throw new RatException(e);
175 }
176 }
177 }
178
179 private void writeComment(final IXmlWriter writer, final Description description) throws IOException {
180 if (StringUtils.isNotBlank(description.getDescription())) {
181 writer.comment(description.getDescription().replace("-->", "-–>"));
182 }
183 }
184
185 private void writeAttribute(final IXmlWriter writer, final Description description, final IHeaderMatcher component)
186 throws IOException {
187 String paramValue = description.getParamValue(component);
188 if (paramValue != null) {
189 writer.attribute(description.getCommonName(), paramValue);
190 }
191 }
192
193
194 @SuppressWarnings("unchecked")
195 void writeDescription(final IXmlWriter writer, final Description desc, final IHeaderMatcher comp) throws RatException {
196 Description description = desc;
197 IHeaderMatcher component = comp;
198 try {
199 switch (description.getType()) {
200 case MATCHER:
201
202 Optional<Description> id = description.childrenOfType(ComponentType.PARAMETER).stream()
203 .filter(i -> XMLConfig.ATT_ID.equals(i.getCommonName())).findFirst();
204
205
206 if (id.isPresent()) {
207 String matcherId = id.get().getParamValue(component);
208
209 if (matchers.contains(matcherId)) {
210 component = new MatcherRefBuilder.IHeaderMatcherProxy(matcherId, null);
211 description = component.getDescription();
212 } else {
213 matchers.add(matcherId);
214 }
215
216 try {
217 UUID.fromString(matcherId);
218 description.getChildren().remove(XMLConfig.ATT_ID);
219 } catch (IllegalArgumentException expected) {
220 if (description.getCommonName().equals("spdx")) {
221 description.getChildren().remove(XMLConfig.ATT_ID);
222 }
223 }
224 }
225
226
227 Optional<Description> resource = description.childrenOfType(ComponentType.PARAMETER).stream()
228 .filter(i -> XMLConfig.ATT_RESOURCE.equals(i.getCommonName())).findFirst();
229 if (resource.isPresent()) {
230 String resourceStr = resource.get().getParamValue(component);
231 if (StringUtils.isNotBlank(resourceStr)) {
232 description.getChildren().remove("enclosed");
233 }
234 }
235 writeComment(writer, description);
236 writer.openElement(description.getCommonName());
237 writeChildren(writer, description, component);
238 writer.closeElement();
239 break;
240 case LICENSE:
241 writer.openElement(XMLConfig.LICENSE);
242 writeChildren(writer, description, component);
243 writer.closeElement();
244 break;
245 case PARAMETER:
246 if ("id".equals(description.getCommonName())) {
247 try {
248 String paramId = description.getParamValue(component);
249
250 if (paramId != null) {
251 UUID.fromString(paramId);
252 return;
253 }
254 } catch (IllegalArgumentException expected) {
255
256 }
257 }
258 if (description.getChildType() == String.class) {
259
260 boolean inline = XMLConfig.isInlineNode(component.getDescription().getCommonName(),
261 description.getCommonName());
262 String s = description.getParamValue(component);
263 if (StringUtils.isNotBlank(s)) {
264 if (!inline) {
265 writer.openElement(description.getCommonName());
266 }
267 writer.content(description.getParamValue(component));
268 if (!inline) {
269 writer.closeElement();
270 }
271 }
272 } else {
273 try {
274 if (description.isCollection()) {
275 for (IHeaderMatcher matcher : (Collection<IHeaderMatcher>) description
276 .getter(component.getClass()).invoke(component)) {
277 writeDescription(writer, matcher.getDescription(), matcher);
278 }
279 } else {
280 IHeaderMatcher matcher = (IHeaderMatcher) description.getter(component.getClass())
281 .invoke(component);
282 writeDescription(writer, matcher.getDescription(), matcher);
283 }
284 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
285 | NoSuchMethodException | SecurityException | RatException e) {
286 throw new ImplementationException(e);
287 }
288 }
289 break;
290 case BUILD_PARAMETER:
291
292 break;
293 }
294 } catch (IOException e) {
295 throw new RatException(e);
296 }
297 }
298 }