blob: 5e5f0105754d0b447c0414cb089858766cb3fc0d [file] [log] [blame]
Nils Diewald0e6992a2015-04-14 20:13:52 +00001/**
2 * Document group criterion
3 */
4define([
5 'vc/jsonld',
6 'vc/unspecified',
7 'vc/doc',
8 'util'
9], function (jsonldClass,
10 unspecClass,
11 docClass) {
12
13 var _validGroupOpRE = new RegExp("^(?:and|or)$");
14
15 var docGroupClass = {
16 _ldType : "docGroup",
17
18 create : function (parent, json) {
19 var obj = Object.create(jsonldClass).upgradeTo(this);
20 obj._operands = [];
21 obj.fromJson(json);
22 if (parent !== undefined)
23 obj._parent = parent;
24 return obj;
25 },
26
27 newAfter : function (obj) {
28 for (var i = 0; i < this._operands.length; i++) {
29 if (this._operands[i] === obj) {
30 var operand = unspecClass.create(this);
31 this._operands.splice(i + 1, 0, operand);
32 return this.update();
33 };
34 };
35 },
36
37 // The doc is already set in the group
38 _duplicate : function (operand) {
39 if (operand.ldType() !== 'doc')
40 return null;
41
42 for (var i = 0; i < this._operands.length; i++) {
43 var op = this.getOperand(i);
44 if (op.ldType() === 'doc'
45 && operand.key() === op.key()
46 && operand.matchop() === op.matchop()
47 && operand.value() === op.value()) {
48 return op;
49 };
50 };
51 return null;
52 },
53
54 append : function (operand) {
55
56 // Append unspecified object
57 if (operand === undefined) {
58
59 // Be aware of cyclic structures!
60 operand = unspecClass.create(this);
61 this._operands.push(operand);
62 return operand;
63 };
64
65 switch (operand["@type"]) {
66
67 case undefined:
68 // No @type defined
69 if (operand["ldType"] !== undefined) {
70 if (operand.ldType() !== 'doc' &&
71 operand.ldType() !== 'docGroup') {
72 KorAP.log(812, "Operand not supported in document group");
73 return;
74 };
75
76 // Be aware of cyclic structures!
77 operand.parent(this);
78
79 var dupl = this._duplicate(operand);
80 if (dupl === null) {
81 this._operands.push(operand);
82 return operand;
83 };
84 return dupl;
85 };
86
87 KorAP.log(701, "JSON-LD group has no @type attribute");
88 return;
89
90 case "koral:doc":
91 // Be aware of cyclic structures!
92 var doc = docClass.create(this, operand);
93 if (doc === undefined)
94 return;
95 var dupl = this._duplicate(doc);
96 if (dupl === null) {
97 this._operands.push(doc);
98 return doc;
99 };
100 return dupl;
101
102 case "koral:docGroup":
103 // Be aware of cyclic structures!
104 var docGroup = docGroupClass.create(this, operand);
105 if (docGroup === undefined)
106 return;
107
108 // Flatten group
109 if (docGroup.operation() === this.operation()) {
110 for (var op in docGroup.operands()) {
111 op = docGroup.getOperand(op);
112 var dupl = this._duplicate(op);
113 if (dupl === null) {
114 this._operands.push(op);
115 op.parent(this);
116 };
117 };
118 docGroup._operands = [];
119 docGroup.destroy();
120 return this;
121 };
122 this._operands.push(docGroup);
123 return docGroup;
124
125 default:
126 KorAP.log(812, "Operand not supported in document group");
127 return;
128 };
129 },
130
131 update : function () {
132 // There is only one operand in group
133
134 if (this._operands.length === 1) {
135
136 var parent = this.parent();
137 var op = this.getOperand(0);
138
139 // This will prevent destruction of
140 // the operand
141 this._operands = [];
142
143 // Parent is a group
144 if (parent.ldType() !== null)
145 return parent.replaceOperand(this, op).update();
146
147 // Parent is vc
148 else {
149 this.destroy();
150 // Cyclic madness
151 parent.root(op);
152 op.parent(parent);
153 return parent.root();
154 };
155 };
156
157 if (this._element === undefined)
158 return this;
159
160 var group = this._element;
161 group.setAttribute('data-operation', this.operation());
162
163 _removeChildren(group);
164
165 // Append operands
166 for (var i = 0; i < this._operands.length; i++) {
167 group.appendChild(
168 this.getOperand(i).element()
169 );
170 };
171
172 // Set operators
173 var op = this.operators(
174 this.operation() == 'and' ? false : true,
175 this.operation() == 'or' ? false : true,
176 true
177 );
178
179 group.appendChild(op.element());
180
181 return this;
182 },
183
184 element : function () {
185 if (this._element !== undefined)
186 return this._element;
187
188 this._element = document.createElement('div');
189 this._element.setAttribute('class', 'docGroup');
190
191 // Update the object - including optimization
192 this.update();
193
194 return this._element;
195 },
196
197 operation : function (op) {
198 if (arguments.length === 1) {
199 if (_validGroupOpRE.test(op)) {
200 this._op = op;
201 }
202 else {
203 KorAP.log(810, "Unknown operation type");
204 return;
205 };
206 };
207 return this._op || 'and';
208 },
209
210 operands : function () {
211 return this._operands;
212 },
213
214 getOperand : function (index) {
215 return this._operands[index];
216 },
217
218 // Replace operand
219 replaceOperand : function (oldOp, newOp) {
220
221 for (var i = 0; i < this._operands.length; i++) {
222 if (this._operands[i] === oldOp) {
223
224 // Just insert a doc or ...
225 if (newOp.ldType() === "doc" ||
226 newOp.ldType() === "non" ||
227 // ... insert a group of a different operation
228 // (i.e. "and" in "or"/"or" in "and")
229 newOp.operation() != this.operation()) {
230 this._operands[i] = newOp;
231 newOp.parent(this);
232 }
233
234 // Flatten group
235 else {
236 // Remove old group
237 this._operands.splice(i, 1);
238
239 // Inject new operands
240 for (var op in newOp.operands().reverse()) {
241 op = newOp.getOperand(op);
242 this._operands.splice(i, 0, op);
243 op.parent(this);
244 };
245 // Prevent destruction of operands
246 newOp._operands = [];
247 newOp.destroy();
248 };
249 oldOp.destroy();
250 return this;
251 }
252 };
253 return false;
254 },
255
256 // Delete operand from group
257 delOperand : function (obj) {
258 for (var i = 0; i < this._operands.length; i++) {
259 if (this._operands[i] === obj) {
260
261 // Delete identified operand
262 this._operands.splice(i,1);
263
264 // Destroy object for cyclic references
265 obj.destroy();
266
267 return this;
268 };
269 };
270
271 // Operand not found
272 return undefined;
273 },
274
275 // Deserialize from json
276 fromJson : function (json) {
277 if (json === undefined)
278 return this;
279
280 if (json["@type"] === undefined) {
281 KorAP.log(701, "JSON-LD group has no @type attribute");
282 return;
283 };
284
285 if (json["operation"] === undefined ||
286 typeof json["operation"] !== 'string') {
287 KorAP.log(811, "Document group expects operation");
288 return;
289 };
290
291 var operation = json["operation"];
292
293 this.operation(operation.replace(/^operation:/,''));
294
295 if (json["operands"] === undefined ||
296 !(json["operands"] instanceof Array)) {
297 KorAP.log(704, "Operation needs operand list")
298 return;
299 };
300
301 // Add all documents
302 for (var i in json["operands"]) {
303 var operand = json["operands"][i];
304 this.append(operand);
305 };
306
307 return this;
308 },
309
310 toJson : function () {
311 var opArray = new Array();
312 for (var i = 0; i < this._operands.length; i++) {
313 if (this._operands[i].ldType() !== 'non')
314 opArray.push(this._operands[i].toJson());
315 };
316 return {
317 "@type" : "koral:" + this.ldType(),
318 "operation" : "operation:" + this.operation(),
319 "operands" : opArray
320 };
321 },
322
323 toQuery : function (brackets) {
324 var list = this._operands
325 .filter(function (op) {
326 return op.ldType() !== 'non';
327 })
328 .map(function (op) {
329 return (op.ldType() === 'docGroup') ?
330 op.toQuery(true) :
331 op.toQuery();
332 });
333
334 if (list.length === 1)
335 return list.join('');
336 else {
337 var str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
338 return brackets ? '(' + str + ')' : str;
339 };
340 }
341 };
342 return docGroupClass;
343});