ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Selector.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for SDL3 and C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include <Objectively/Hash.h>
29#include <Objectively/Array.h>
30#include <Objectively/Set.h>
31
32#include "Log.h"
33#include "Selector.h"
34#include "View.h"
35
36#define _Class _Selector
37
38#pragma mark - Object
39
43static void dealloc(Object *self) {
44
45 Selector *this = (Selector *) self;
46
47 release(this->sequences);
48 free(this->rule);
49
50 super(Object, self, dealloc);
51}
52
56static String *description(const Object *self) {
57
58 const Selector *this = (Selector *) self;
59
60 return str(this->rule);
61}
62
66static int hash(const Object *self) {
67
68 Selector *this = (Selector *) self;
69
70 return HashForCString(HASH_SEED, this->rule);
71}
72
76static bool isEqual(const Object *self, const Object *other) {
77
78 if (super(Object, self, isEqual, other)) {
79 return true;
80 }
81
82 if (other && $(other, isKindOfClass, _Selector())) {
83
84 const Selector *this = (Selector *) self;
85 const Selector *that = (Selector *) other;
86
87 return strcmp(this->rule, that->rule) == 0;
88 }
89
90 return false;
91}
92
93#pragma mark - Selector
94
98static int specificity(const Selector *selector) {
99
100 int specificity = 0;
101
102 for (size_t i = 0; i < selector->sequences->count; i++) {
103 SelectorSequence *sequence = $(selector->sequences, objectAtIndex, i);
104
105 for (size_t j = 0; j < sequence->simpleSelectors->count; j++) {
106 SimpleSelector *simpleSelector = $(sequence->simpleSelectors, objectAtIndex, j);
107
108 switch (simpleSelector->type) {
110 specificity += 100;
111 break;
114 specificity += 10;
115 break;
117 specificity += 1;
118 Class *clazz = classForName(simpleSelector->pattern);
119 if (clazz) {
120 while (clazz) {
121 if (clazz == _View()) {
122 break;
123 }
124 specificity += 1;
125 clazz = clazz->def.superclass;
126 }
127 if (clazz != _View()) {
128 MVC_LogError("Class `%s` in Selector `%s` does not extend View\n",
129 simpleSelector->pattern, selector->rule);
130 }
131 } else {
132 MVC_LogWarn("Class `%s` in Selector `%s` not found\n",
133 simpleSelector->pattern, selector->rule);
134 }
135
136 break;
137 }
138 default:
139 break;
140 }
141 }
142 }
143
144 return specificity;
145}
146
151static Order compareTo(const Selector *self, const Selector *other) {
152
153 assert(other);
154
155 if (self->specificity < other->specificity) {
156 return OrderAscending;
157 } else if (self->specificity > other->specificity) {
158 return OrderDescending;
159 }
160
161 return OrderSame;
162}
163
168static void enumerateSelection(const Selector *self, View *view, ViewEnumerator enumerator, ident data) {
169
170 assert(enumerator);
171
172 Set *selection = $(self, select, view);
173 assert(selection);
174
175 Array *array = $(selection, allObjects);
176 assert(array);
177
178 for (size_t i = 0; i < array->count; i++) {
179 enumerator($(array, objectAtIndex, i), data);
180 }
181
182 release(array);
183 release(selection);
184}
185
190static Selector *initWithRule(Selector *self, const char *rule) {
191
192 self = (Selector *) super(Object, self, init);
193 if (self) {
194
195 self->rule = strtrim(rule);
196 assert(self->rule);
197
198 self->sequences = $$(SelectorSequence, parse, self->rule);
199 assert(self->sequences->count);
200
201 self->specificity = specificity(self);
202 }
203
204 return self;
205}
206
210typedef struct {
211 const Array *sequences;
212 size_t sequence;
213 bool match;
214} Match;
215
221static void _matchesViewEnumerator(View *view, ident data);
222
227static bool _matchesView(const View *view, Match *match) {
228
229 const SelectorSequence *sequence = $(match->sequences, objectAtIndex, match->sequence);
230
231 if ($(sequence, matchesView, view)) {
232
233 switch (sequence->left) {
235 match->match = true;
236 break;
237
239 match->sequence--;
241 break;
242
244 match->sequence--;
246 break;
247
249 match->sequence--;
251 break;
252
254 match->sequence--;
256 break;
257
259 break;
260 }
261 }
262
263 return match->match;
264}
265
269static void _matchesViewEnumerator(View *view, ident data) {
270 _matchesView(view, (Match *) data);
271}
272
277static bool matchesView(const Selector *self, const View *view) {
278
279 assert(view);
280
281 return _matchesView(view, &(Match) {
282 .sequences = self->sequences,
283 .sequence = self->sequences->count - 1
284 });
285}
286
291static Array *parse(const char *rules) {
292
293 Array *selectors = $$(Array, arrayWithCapacity, 4);
294 assert(selectors);
295
296 if (rules) {
297
298 const char *c = rules;
299 while (*c) {
300 const size_t size = strcspn(c, ",");
301 if (size) {
302 char *rule = calloc(1, size + 1);
303 assert(rule);
304
305 strncpy(rule, c, size);
306
307 Selector *selector = $(alloc(Selector), initWithRule, rule);
308 assert(selector);
309
310 $(selectors, addObject, selector);
311
312 release(selector);
313 free(rule);
314 }
315 c += size;
316 c += strspn(c, ", \t\n");
317 }
318 }
319
320 return (Array *) selectors;
321}
322
326typedef struct {
327 const Array *sequences;
328 size_t sequence;
331} Selection;
332
338static void __selectEnumerator(View *view, ident data);
339
343static Set *__select(View *view, Selection *selection) {
344
345 const SelectorSequence *sequence = $(selection->sequences, objectAtIndex, selection->sequence);
346
347 if ($(sequence, matchesView, view)) {
348
349 switch (sequence->right) {
351 break;
352
354 selection->sequence++;
355 $(view, enumerateDescendants, __selectEnumerator, selection);
356 break;
357
359 selection->sequence++;
360 $(view, enumerateSubviews, __selectEnumerator, selection);
361 break;
362
364 selection->sequence++;
365 $(view, enumerateSiblings, __selectEnumerator, selection);
366 break;
367
369 selection->sequence++;
370 $(view, enumerateAdjacent, __selectEnumerator, selection);
371 break;
372
374 $(selection->selection, addObject, view);
375 break;
376 }
377 }
378
379 $(view, enumerateSubviews, __selectEnumerator, selection);
380
381 return (Set *) selection->selection;
382}
383
387static void __selectEnumerator(View *view, ident data) {
388 __select(view, (Selection *) data);
389}
390
395static Set *_select(const Selector *self, View *view) {
396
397 assert(view);
398
399 return __select(view, &(Selection) {
400 .sequences = self->sequences,
401 .selection = $$(Set, set),
402 });
403}
404
412static void _selectFirstEnumerator(View *view, ident data);
413
417static void _selectFirst(Selection *selection, View *view) {
418
419 const SelectorSequence *sequence = $(selection->sequences, objectAtIndex, selection->sequence);
420
421 if ($(sequence, matchesView, view)) {
422
423 switch (sequence->right) {
425 break;
426
428 selection->sequence++;
429 $(view, enumerateDescendants, __selectEnumerator, selection);
430 break;
431
433 selection->sequence++;
434 $(view, enumerateSubviews, __selectEnumerator, selection);
435 break;
436
438 selection->sequence++;
439 $(view, enumerateSiblings, __selectEnumerator, selection);
440 break;
441
443 selection->sequence++;
444 $(view, enumerateAdjacent, __selectEnumerator, selection);
445 break;
446
448 selection->first = view;
449 return;
450 }
451 }
452
453 $(view, enumerateSubviews, _selectFirstEnumerator, selection);
454}
455
459static void _selectFirstEnumerator(View *view, ident data) {
460 _selectFirst((Selection *) data, view);
461}
462
467static View *selectFirst(const Selector *self, View *view) {
468
469 assert(view);
470
471 Selection selection = {
472 .sequences = self->sequences,
473 };
474
475 _selectFirst(&selection, view);
476 return selection.first;
477}
478
479#pragma mark - Class lifecycle
480
484static void initialize(Class *clazz) {
485
486 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
487 ((ObjectInterface *) clazz->interface)->description = description;
488 ((ObjectInterface *) clazz->interface)->hash = hash;
489 ((ObjectInterface *) clazz->interface)->isEqual = isEqual;
490
491 ((SelectorInterface *) clazz->interface)->enumerateSelection = enumerateSelection;
492 ((SelectorInterface *) clazz->interface)->compareTo = compareTo;
493 ((SelectorInterface *) clazz->interface)->initWithRule = initWithRule;
494 ((SelectorInterface *) clazz->interface)->matchesView = matchesView;
495 ((SelectorInterface *) clazz->interface)->parse = parse;
496 ((SelectorInterface *) clazz->interface)->select = _select;
497 ((SelectorInterface *) clazz->interface)->selectFirst = selectFirst;
498}
499
504Class *_Selector(void) {
505 static Class *clazz;
506 static Once once;
507
508 do_once(&once, {
509 clazz = _initialize(&(const ClassDef) {
510 .name = "Selector",
511 .superclass = _Object(),
512 .instanceSize = sizeof(Selector),
513 .interfaceOffset = offsetof(Selector, interface),
514 .interfaceSize = sizeof(SelectorInterface),
516 });
517 });
518
519 return clazz;
520}
521
522#undef _Class
static View * init(View *self)
Definition Box.c:67
static SDL_Size size(const Image *self)
Definition Image.c:181
View logging facilities via SDL_Log.
#define MVC_LogWarn(fmt,...)
Definition Log.h:54
#define MVC_LogError(fmt,...)
Definition Log.h:57
static bool _matchesView(const View *view, Match *match)
Recursive View match predicate, invoked directly by matchesView and indirectly, via _matchesViewEnume...
Definition Selector.c:227
static void enumerateSelection(const Selector *self, View *view, ViewEnumerator enumerator, ident data)
Definition Selector.c:168
static bool isEqual(const Object *self, const Object *other)
Definition Selector.c:76
static Selector * initWithRule(Selector *self, const char *rule)
Definition Selector.c:190
static Set * _select(const Selector *self, View *view)
Definition Selector.c:395
Class * _Selector(void)
Definition Selector.c:504
static int specificity(const Selector *selector)
Definition Selector.c:98
static Order compareTo(const Selector *self, const Selector *other)
Definition Selector.c:151
static void _matchesViewEnumerator(View *view, ident data)
ViewEnumerator adapter for _matchesView, so it may be passed to the View::enumerate* family of method...
Definition Selector.c:269
static Array * parse(const char *rules)
Definition Selector.c:291
static String * description(const Object *self)
Definition Selector.c:56
static void __selectEnumerator(View *view, ident data)
ViewEnumerator adapter for __select, so it may be passed to the View::enumerate* family of methods,...
Definition Selector.c:387
static void _selectFirstEnumerator(View *view, ident data)
ViewEnumerator adapter for _selectFirst, so it may be passed to the View::enumerate* family of method...
Definition Selector.c:459
static void _selectFirst(Selection *selection, View *view)
Recursively selects the first View by iterating the SelectorSequences in the given Selection.
Definition Selector.c:417
static void dealloc(Object *self)
Definition Selector.c:43
static Set * __select(View *view, Selection *selection)
Recursively selects Views by iterating the SelectorSequences in the given Selection.
Definition Selector.c:343
static View * selectFirst(const Selector *self, View *view)
Definition Selector.c:467
static void initialize(Class *clazz)
Definition Selector.c:484
static bool matchesView(const Selector *self, const View *view)
Definition Selector.c:277
static int hash(const Object *self)
Definition Selector.c:66
The Selector type.
@ SequenceCombinatorTerminal
@ SequenceCombinatorAdjacent
@ SequenceCombinatorNone
@ SequenceCombinatorChild
@ SequenceCombinatorDescendent
@ SequenceCombinatorSibling
@ SimpleSelectorTypePseudo
@ SimpleSelectorTypeId
@ SimpleSelectorTypeClass
@ SimpleSelectorTypeType
void(* ViewEnumerator)(View *view, ident data)
A function type for View enumeration.
Definition Types.h:56
Class * _View(void)
Definition View.c:2067
static void enumerateAncestors(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:782
static void enumerateSubviews(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:846
static void enumerateSuperview(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:860
static void enumerateAdjacent(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:762
static void enumerateSiblings(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:826
static void enumerateDescendants(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:808
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
A context for View matching.
Definition Selector.c:210
bool match
Definition Selector.c:213
const Array * sequences
Definition Selector.c:211
size_t sequence
Definition Selector.c:212
A context for View selection.
Definition Selector.c:326
const Array * sequences
Definition Selector.c:327
size_t sequence
Definition Selector.c:328
View * first
Definition Selector.c:330
Set * selection
Definition Selector.c:329
Selectors are comprised of one or more SelectorSequences.
Definition Selector.h:49
int specificity
The specificity.
Definition Selector.h:76
char * rule
The rule, as provided by the user.
Definition Selector.h:70
bool matchesView(const Selector *self, View *view)
Array * sequences
The sequences.
Definition Selector.h:65
SelectorSequences are comprised of one or more SimpleSelectors.
SequenceCombinator right
SequenceCombinator left
The combinators.
Array * simpleSelectors
The SimpleSelectors comprising this SelectorSequence.
The SimpleSelector type.
SimpleSelectorType type
The SimpleSelectorType.
char * pattern
The pattern, as provided by the user.
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:134
void enumerateSelection(View *self, const char *rule, ViewEnumerator enumerator, ident data)
Enumerates all Views in the selection matched by rule, applying enumerator to each.
Definition View.c:795