1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.any23.writer;
19
20 import java.io.OutputStream;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.ServiceConfigurationError;
31 import java.util.ServiceLoader;
32 import java.util.concurrent.CopyOnWriteArrayList;
33 import java.util.concurrent.CopyOnWriteArraySet;
34
35 import org.apache.any23.configuration.Settings;
36 import org.eclipse.rdf4j.rio.RDFFormat;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40
41
42
43
44
45
46 public class WriterFactoryRegistry {
47
48 private static final Logger LOG = LoggerFactory.getLogger(WriterFactoryRegistry.class);
49
50
51
52
53 private static class InstanceHolder {
54 private static final WriterFactoryRegistry instance = new WriterFactoryRegistry();
55 }
56
57 private static final WriterFactory[] EMPTY_WRITERS = new WriterFactory[0];
58
59
60
61
62 private final List<WriterFactory> writers = new CopyOnWriteArrayList<>();
63
64
65
66
67 private final Map<String, List<WriterFactory>> mimeToWriter = Collections.synchronizedMap(new HashMap<>());
68
69
70
71
72 private final Map<String, WriterFactory> idToWriter = new HashMap<>();
73
74 private final List<String> identifiers = new CopyOnWriteArrayList<>();
75
76 private final Collection<String> mimeTypes = new CopyOnWriteArraySet<>();
77
78 public WriterFactoryRegistry() {
79 ServiceLoader<WriterFactory> serviceLoader = java.util.ServiceLoader.load(WriterFactory.class,
80 this.getClass().getClassLoader());
81
82 Iterator<WriterFactory> iterator = serviceLoader.iterator();
83
84
85
86
87 ArrayList<WriterFactory> factories = new ArrayList<>();
88 while (true) {
89 try {
90 if (!iterator.hasNext())
91 break;
92 factories.add(iterator.next());
93 } catch (ServiceConfigurationError error) {
94 LOG.error("Found error loading a WriterFactory", error);
95 }
96 }
97
98 registerAll(factories.toArray(EMPTY_WRITERS));
99 }
100
101
102
103
104
105
106
107
108
109 public static String getIdentifier(WriterFactory writerClass) {
110 return writerClass.getIdentifier();
111 }
112
113
114
115
116
117
118
119
120
121 public static String getMimeType(WriterFactory writerClass) {
122 if (writerClass instanceof TripleWriterFactory) {
123 return ((TripleWriterFactory) writerClass).getTripleFormat().getMimeType();
124 } else if (writerClass instanceof DecoratingWriterFactory) {
125 return null;
126 } else {
127 return reportAndGetCompatFormat(writerClass).getMimeType();
128 }
129 }
130
131
132
133
134 public static WriterFactoryRegistry getInstance() {
135 return InstanceHolder.instance;
136 }
137
138 @SuppressWarnings("deprecation")
139 private static TripleFormat reportAndGetCompatFormat(WriterFactory f) {
140 LOG.warn("{} must implement either {} or {}.", f.getClass(), TripleWriterFactory.class,
141 DecoratingWriterFactory.class);
142 final String mimeType = f.getMimeType();
143 RDFFormat fmt;
144 try {
145 fmt = f.getRdfFormat();
146 } catch (RuntimeException e) {
147 return TripleFormat.of(mimeType, Collections.singleton(mimeType), null, Collections.emptySet(), null,
148 TripleFormat.NONSTANDARD);
149 }
150 if (mimeType == null || fmt.hasDefaultMIMEType(mimeType)) {
151 return TripleFormat.of(fmt);
152 }
153
154 return TripleFormat.of(fmt.getName(), Collections.singleton(mimeType), fmt.getCharset(),
155 fmt.getFileExtensions(), fmt.getStandardURI().stringValue(), TripleFormat.capabilities(fmt));
156 }
157
158 private static TripleWriterFactory getCompatFactory(WriterFactory f) {
159 final TripleFormat format = reportAndGetCompatFormat(f);
160 return new TripleWriterFactory() {
161 @Override
162 public TripleFormat getTripleFormat() {
163 return format;
164 }
165
166 @Override
167 @SuppressWarnings("deprecation")
168 public TripleHandler getTripleWriter(OutputStream os, Settings settings) {
169 return f.getRdfWriter(os);
170 }
171
172 @Override
173 public Settings getSupportedSettings() {
174 return Settings.of();
175 }
176
177 @Override
178 public String getIdentifier() {
179 return f.getIdentifier();
180 }
181 };
182 }
183
184
185
186
187
188
189
190
191
192
193 public void register(WriterFactory f) {
194 if (f == null)
195 throw new NullPointerException("writerClass cannot be null.");
196 registerAll(new WriterFactory[] { f });
197 }
198
199 private void registerAll(WriterFactory[] factories) {
200 final int count = factories.length;
201 if (count == 0) {
202 return;
203 }
204 final HashMap<String, ArrayList<WriterFactory>> mimes = new HashMap<>();
205 final String[] ids = new String[count];
206
207 for (int i = 0; i < count; i++) {
208 WriterFactory f = factories[i];
209 if (!(f instanceof BaseWriterFactory<?>)) {
210
211 f = factories[i] = getCompatFactory(f);
212 }
213 final String id = ids[i] = f.getIdentifier();
214 if (id == null || id.trim().isEmpty()) {
215 throw new IllegalArgumentException("Invalid identifier returned by writer " + f);
216 }
217 if (f instanceof TripleWriterFactory) {
218 String mimeType = ((TripleWriterFactory) f).getTripleFormat().getMimeType();
219 if (mimeType == null || mimeType.trim().isEmpty()) {
220 throw new IllegalArgumentException("Invalid MIME type returned by writer " + f);
221 }
222 mimes.computeIfAbsent(mimeType, k -> new ArrayList<>()).add(f);
223 }
224 }
225
226 final List<String> idList = Arrays.asList(ids);
227 final List<WriterFactory> factoryList = Arrays.asList(factories);
228 final Map<String, WriterFactory> idToWriter;
229 synchronized (idToWriter = this.idToWriter) {
230 for (int i = 0; i < count; i++) {
231 String id = ids[i];
232 if (idToWriter.putIfAbsent(id, factories[i]) != null) {
233 idToWriter.keySet().removeAll(idList.subList(0, i));
234 throw new IllegalArgumentException("The writer identifier is already declared: " + id);
235 }
236 }
237 }
238
239 writers.addAll(factoryList);
240 identifiers.addAll(idList);
241 for (Map.Entry<String, ArrayList<WriterFactory>> entry : mimes.entrySet()) {
242 String mimeType = entry.getKey();
243 mimeTypes.add(mimeType);
244 mimeToWriter.computeIfAbsent(mimeType, k -> new CopyOnWriteArrayList<>()).addAll(entry.getValue());
245 }
246 }
247
248
249
250
251
252
253
254
255
256 public boolean hasIdentifier(String id) {
257 synchronized (idToWriter) {
258 return idToWriter.containsKey(id);
259 }
260 }
261
262
263
264
265 public List<String> getIdentifiers() {
266
267 return Collections.unmodifiableList(identifiers);
268 }
269
270
271
272
273 public Collection<String> getMimeTypes() {
274
275 return Collections.unmodifiableCollection(mimeTypes);
276 }
277
278
279
280
281 public List<WriterFactory> getWriters() {
282
283 return Collections.unmodifiableList(writers);
284 }
285
286
287
288
289
290
291
292
293
294 public WriterFactory getWriterByIdentifier(String id) {
295 synchronized (idToWriter) {
296 return idToWriter.get(id);
297 }
298 }
299
300
301
302
303
304
305
306
307
308 public Collection<WriterFactory> getWritersByMimeType(String mimeType) {
309
310
311 List<WriterFactory> list = mimeToWriter.get(mimeType);
312 return list != null ? Collections.unmodifiableList(list) : Collections.emptyList();
313 }
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331 @Deprecated
332 public FormatWriter getWriterInstanceByIdentifier(String id, OutputStream os) {
333 return Objects.requireNonNull(getWriterByIdentifier(id), "Cannot find writer with id " + id).getRdfWriter(os);
334 }
335
336 }