1
2
3
4
5
6
7
8
9
10
11
12
13
14 package eu.fbk.rdfpro;
15
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.function.Predicate;
19
20 import javax.annotation.Nullable;
21
22 import org.openrdf.model.BNode;
23 import org.openrdf.model.Literal;
24 import org.openrdf.model.Resource;
25 import org.openrdf.model.Statement;
26 import org.openrdf.model.URI;
27 import org.openrdf.model.Value;
28 import org.openrdf.model.impl.URIImpl;
29 import org.openrdf.rio.RDFHandlerException;
30
31 import eu.fbk.rdfpro.util.Hash;
32 import eu.fbk.rdfpro.util.Scripting;
33 import eu.fbk.rdfpro.util.Statements;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 @FunctionalInterface
57 public interface Mapper {
58
59
60 URI BYPASS_KEY = new URIImpl("rdfpro:bypass");
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 Value[] map(Statement statement) throws RDFHandlerException;
78
79
80
81
82
83
84
85
86
87
88
89 public static Mapper bypass(final Mapper mapper, @Nullable final Predicate<Statement> predicate) {
90 if (predicate != null) {
91 final Value[] bypass = new Value[] { BYPASS_KEY };
92 return new Mapper() {
93
94 @Override
95 public Value[] map(final Statement statement) throws RDFHandlerException {
96 if (predicate.test(statement)) {
97 return bypass;
98 } else {
99 return mapper.map(statement);
100 }
101 }
102 };
103
104 } else {
105 return mapper;
106 }
107 }
108
109
110
111
112
113
114
115
116
117
118
119 public static Mapper filter(final Mapper mapper, @Nullable final Predicate<Statement> predicate) {
120 if (predicate != null) {
121 final Value[] empty = new Value[0];
122 return new Mapper() {
123
124 @Override
125 public Value[] map(final Statement statement) throws RDFHandlerException {
126 if (predicate.test(statement)) {
127 return mapper.map(statement);
128 } else {
129 return empty;
130 }
131 }
132 };
133 } else {
134 return mapper;
135 }
136 }
137
138
139
140
141
142
143
144
145
146
147 public static Mapper concat(final Mapper... mappers) {
148 return new Mapper() {
149
150 @Override
151 public Value[] map(final Statement statement) throws RDFHandlerException {
152 final List<Value> keys = new ArrayList<>(mappers.length);
153 for (int i = 0; i < mappers.length; ++i) {
154 for (final Value key : mappers[i].map(statement)) {
155 if (!keys.contains(key)) {
156 keys.add(key);
157 }
158 }
159 }
160 return keys.toArray(new Value[keys.size()]);
161 }
162
163 };
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 public static Mapper select(final String components) {
180
181 final String comp = components.trim().toLowerCase();
182
183 if (comp.equals("e")) {
184 return new Mapper() {
185
186 @Override
187 public Value[] map(final Statement statement) throws RDFHandlerException {
188 if (statement.getObject() instanceof Resource) {
189 return new Value[] { statement.getSubject(), statement.getObject() };
190 } else {
191 return new Value[] { statement.getSubject() };
192 }
193 }
194
195 };
196 }
197
198 int num = 0;
199 for (int i = 0; i < comp.length(); ++i) {
200 final char c = comp.charAt(i);
201 final int b = c == 's' ? 0x8 : c == 'p' ? 0x4 : c == 'o' ? 0x2 : c == 'c' ? 0x1 : -1;
202 if (b < 0 || (num & b) != 0) {
203 throw new IllegalArgumentException("Invalid components '" + components + "'");
204 }
205 num = num | b;
206 }
207 final int mask = num;
208
209 if (mask == 0x08 || mask == 0x04 || mask == 0x02 || mask == 0x01) {
210 return new Mapper() {
211
212 @Override
213 public Value[] map(final Statement statement) throws RDFHandlerException {
214 switch (mask) {
215 case 0x08:
216 return new Value[] { statement.getSubject() };
217 case 0x04:
218 return new Value[] { statement.getPredicate() };
219 case 0x02:
220 return new Value[] { statement.getObject() };
221 case 0x01:
222 return new Value[] { statement.getContext() };
223 default:
224 throw new Error();
225 }
226 }
227
228 };
229 }
230
231 return new Mapper() {
232
233 private final boolean hasSubj = (mask & 0x80) != 0;
234
235 private final boolean hasPred = (mask & 0x40) != 0;
236
237 private final boolean hasObj = (mask & 0x20) != 0;
238
239 private final boolean hasCtx = (mask & 0x10) != 0;
240
241 @Override
242 public Value[] map(final Statement statement) throws RDFHandlerException {
243
244 int header = 0;
245 int count = 0;
246
247 if (hasSubj) {
248 final int bits = classify(statement.getSubject());
249 header |= bits << 24;
250 count += bits & 0xF;
251 }
252 if (hasPred) {
253 final int bits = classify(statement.getPredicate());
254 header |= bits << 16;
255 count += bits & 0xF;
256 }
257 if (hasObj) {
258 final int bits = classify(statement.getObject());
259 header |= bits << 8;
260 count += bits & 0xF;
261 }
262 if (hasCtx) {
263 final int bits = classify(statement.getContext());
264 header |= bits;
265 count += bits & 0xF;
266 }
267
268 final String[] strings = new String[count];
269 int index = 0;
270 strings[index++] = Integer.toString(header);
271 if (hasSubj) {
272 index = add(strings, index, statement.getSubject());
273 }
274 if (hasPred) {
275 index = add(strings, index, statement.getPredicate());
276 }
277 if (hasObj) {
278 index = add(strings, index, statement.getObject());
279 }
280 if (hasCtx) {
281 index = add(strings, index, statement.getContext());
282 }
283
284 final String hash = Hash.murmur3(strings).toString();
285 return new Value[] { Statements.VALUE_FACTORY.createBNode(hash) };
286 }
287
288 private int classify(final Value value) {
289 if (value == null) {
290 return 0;
291 } else if (value instanceof BNode) {
292 return 0x11;
293 } else if (value instanceof URI) {
294 return 0x21;
295 }
296 final Literal l = (Literal) value;
297 if (l.getLanguage() != null) {
298 return 0x52;
299 } else if (l.getDatatype() != null) {
300 return 0x42;
301 } else {
302 return 0x31;
303 }
304 }
305
306 private int add(final String[] strings, int index, final Value value) {
307 if (value instanceof URI || value instanceof BNode) {
308 strings[index++] = value.stringValue();
309 } else if (value instanceof Literal) {
310 final Literal l = (Literal) value;
311 strings[index++] = l.getLabel();
312 if (l.getLanguage() != null) {
313 strings[index++] = l.getLanguage();
314 } else if (l.getDatatype() != null) {
315 strings[index++] = l.getDatatype().stringValue();
316 }
317 }
318 return index;
319 }
320
321 };
322 }
323
324
325
326
327
328
329
330
331
332
333 @Nullable
334 static Mapper parse(@Nullable final String expression) {
335 if (expression == null) {
336 return null;
337 } else if (Scripting.isScript(expression)) {
338 return Scripting.compile(Mapper.class, expression, "q");
339 } else {
340 return select(expression);
341 }
342 }
343
344 }