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