]> err.no Git - sope/blob - sope-appserver/WEExtensions/WETableCalcMatrix.m
code reorgs
[sope] / sope-appserver / WEExtensions / WETableCalcMatrix.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "WETableCalcMatrix.h"
23 #include "common.h"
24
25 typedef struct {
26   NSMutableArray *items;
27 } MatrixEntry;
28
29 typedef struct {
30   unsigned x;
31   unsigned y;
32 } MatrixCoord;
33
34 typedef struct {
35   id *objects;
36 } MatrixThread;
37
38 typedef enum { WERow, WEColumn } WEOrientation;
39
40 static NSNull *null = nil;
41
42 @implementation WETableCalcMatrixSpan
43
44 + (id)spanWithObject:(id)_obj range:(NSRange *)_range {
45   id span;
46   span = [[WETableCalcMatrixSpan alloc] initWithObject:_obj range:_range];
47   return [span autorelease];
48 }
49 - (id)initWithObject:(id)_obj range:(NSRange *)_range {
50   self->object = [_obj retain];
51   self->range  = *_range;
52   return self;
53 }
54 - (void)dealloc {
55   [self->object release];
56   [super dealloc];
57 }
58
59 /* accessors */
60
61 - (id)object {
62   return self->object;
63 }
64 - (NSRange)range {
65   return self->range;
66 }
67
68 /* calculations */
69
70 - (BOOL)startsAtIndex:(unsigned)_idx {
71   return (_idx == self->range.location) ? YES : NO;
72 }
73 - (BOOL)occupiesIndex:(unsigned)_idx {
74   if (_idx < self->range.location)
75     return NO;
76   if (_idx >= (self->range.location + self->range.length))
77     return NO;
78   return YES;
79 }
80 - (unsigned)size {
81   return self->range.length;
82 }
83
84 /* description */
85
86 - (NSString *)description {
87   return [NSString stringWithFormat:
88                      @"<0x%08X[%@]: object=0x%08X start=%d len=%d>",
89                      self, NSStringFromClass([self class]),
90                      [self object],
91                      self->range.location,
92                      self->range.length];
93 }
94
95 @end /* WETableCalcMatrixSpan */
96
97 @interface WETableCalcMatrixStripe : NSObject
98 {
99 @private
100   unsigned           size;
101   MatrixThread       *threads;
102   short              threadCount;
103   WETableCalcMatrix *matrix;     /* non-retained */
104   id                 delegate;    /* non-retained */
105 }
106
107 - (unsigned)threadCount;
108 - (NSArray *)threads;
109 - (NSArray *)threadSpans;
110
111 /* modification */
112
113 - (void)addObject:(id)_obj atPositions:(unsigned *)_pos count:(unsigned)_c;
114
115 @end
116
117 @implementation WETableCalcMatrixStripe
118
119 + (void)initialize {
120   if (null == nil) null = [[NSNull null] retain];
121 }
122
123 - (id)initWithSize:(unsigned)_h
124   matrix:(WETableCalcMatrix *)_matrix
125   delegate:(id)_delegate
126 {
127   self->size   = _h;
128   self->matrix = _matrix;
129
130   if ([_delegate respondsToSelector:
131                    @selector(tableCalcMatrix:spanForObject:range:)])
132     self->delegate = _delegate;
133   
134   return self;
135 }
136 - (void)dealloc {
137   if (self->threads) {
138     unsigned i;
139     
140     for (i = 0; i < (unsigned)self->threadCount; i++) {
141       if (self->threads[i].objects) {
142         unsigned j;
143
144         for (j = 0; j < self->size; j++)
145           [self->threads[i].objects[j] release];
146         free(self->threads[i].objects);
147       }
148     }
149     free(self->threads);
150   }
151   [super dealloc];
152 }
153
154 /* accessors */
155
156 - (unsigned)threadCount {
157   return self->threadCount;
158 }
159
160 - (NSArray *)threadAtIndex:(unsigned)_idx {
161   id       *objs;
162   unsigned i;
163   NSArray  *result;
164   
165   objs = calloc(self->size, sizeof(id));
166   NSAssert(objs, @"could not allocate memory ..");
167   NSAssert(self->size > 0, @"invalid size ..");
168   
169   for (i = 0; i < self->size; i++) {
170     objs[i] = self->threads[_idx].objects[i];
171     
172     if (objs[i] == nil)
173       objs[i] = null;
174   }
175   result = [NSArray arrayWithObjects:objs count:self->size];
176   free(objs);
177   return result;
178 }
179 - (NSArray *)spansAtIndex:(unsigned)_idx {
180   id       *spans;
181   unsigned i, spanCount;
182   NSArray  *result;
183   
184   spans = calloc(self->size, sizeof(id));
185   
186   for (i = 0, spanCount = 0; i < self->size; ) {
187     WETableCalcMatrixSpan *span;
188     id      obj;
189     NSRange r;
190     
191     span = nil;
192     obj  = self->threads[_idx].objects[i];
193
194     if ((i + 1) == self->size) {
195       /* last entry */
196       r.location = i;
197       r.length   = 1;
198       i++;
199     }
200     else {
201       /* look ahead for similiar entries */
202       unsigned j;
203       
204       r.location = i;
205       r.length   = 0;
206
207       for (j = i + 1; j < self->size; j++) {
208         id nextObj;
209         
210         nextObj = self->threads[_idx].objects[j];
211         if (nextObj != obj)
212           break;
213       }
214       r.length = (j - i);
215
216       /* continue at end of this object */
217       i = j;
218     }
219
220     span = nil;
221     if (self->delegate) {
222       span = [self->delegate tableCalcMatrix:self->matrix
223                              spanForObject:obj
224                              range:r];
225     }
226     if (span == nil) {
227       span = [[WETableCalcMatrixSpan alloc] initWithObject:obj range:&r];
228       AUTORELEASE(span);
229     }
230
231     spans[spanCount] = span;
232     spanCount++;
233   }
234   result = [NSArray arrayWithObjects:spans count:spanCount];
235   free(spans);
236   return result;
237 }
238
239 - (NSArray *)threads {
240   if (self->threadCount == 0)
241     return nil;
242   if (self->threadCount == 1)
243     return [NSArray arrayWithObject:[self threadAtIndex:0]];
244   
245   {
246     id       threadArrays[self->threadCount];
247     unsigned i;
248
249     for (i = 0; i < (unsigned)self->threadCount; i++)
250       threadArrays[i] = [self threadAtIndex:i];
251     
252     return [NSArray arrayWithObjects:threadArrays count:self->threadCount];
253   }
254 }
255 - (NSArray *)threadSpans {
256   if (self->threadCount == 0)
257     return nil;
258   else if (self->threadCount == 1)
259     return [NSArray arrayWithObject:[self spansAtIndex:0]];
260   else {
261     NSArray  *threadArrays[self->threadCount];
262     unsigned i;
263     
264     for (i = 0; i < (unsigned)self->threadCount; i++) {
265       threadArrays[i] = [self spansAtIndex:i];
266     }
267     
268     return [NSArray arrayWithObjects:threadArrays count:self->threadCount];
269   }
270 }
271
272 /* addition */
273
274 - (unsigned)threadForPositions:(unsigned *)_pos count:(unsigned)_c {
275   unsigned i;
276   void *tmp;
277   
278   if (self->threadCount == 0) {
279     self->threads  = calloc(1, sizeof(MatrixThread));
280     self->threads[0].objects = calloc(self->size, sizeof(id));
281     self->threadCount = 1;
282     return 0;
283   }
284   
285   /* check each column */
286   for (i = 0; i < (unsigned)self->threadCount; i++) {
287     unsigned       j;
288     MatrixThread *column;
289     BOOL           ok;
290     
291     column = &(self->threads[i]);
292     
293     /* check each required position in column */
294     for (j = 0, ok = YES; j < _c; j++) {
295       unsigned requiredPos;
296       
297       requiredPos = _pos[j];
298       NSAssert(requiredPos < self->size, @"index to high ..");
299       
300       if (column->objects[requiredPos] != nil) {
301         /* position already assigned */
302         ok = NO;
303         break;
304       }
305     }
306     if (ok) {
307       /* all required position available, return column */
308       return i;
309     }
310     /* check next column */
311   }
312
313   /* all available threads are full, make new one .. */
314   tmp = self->threads;
315   self->threads = calloc(self->threadCount + 1, sizeof(MatrixThread));
316   memcpy(self->threads, tmp, self->threadCount * sizeof(MatrixThread));
317   self->threads[self->threadCount].objects = calloc(self->size, sizeof(id));
318   self->threadCount++;
319   return (self->threadCount - 1);
320 }
321
322 - (void)addObject:(id)_obj atPositions:(unsigned *)_pos count:(unsigned)_c {
323   unsigned thread;
324   unsigned i;
325
326   if (_c == 0) return;
327   
328   /* find row */
329   thread = [self threadForPositions:_pos count:_c];
330   NSAssert(thread < (unsigned)self->threadCount, @"invalid idx");
331   
332   /* place object */
333   for (i = 0; i < _c; i++) {
334     unsigned requiredIdx;
335     
336     requiredIdx = _pos[i];
337     
338 #if DEBUG
339     NSAssert(requiredIdx < self->size, @"index to high ..");
340     NSAssert3(self->threads[thread].objects[requiredIdx] == nil,
341               @"index %i is already marked (by=0x%08X, my=0x%08X) !",
342               requiredIdx, self->threads[thread].objects[requiredIdx], _obj);
343 #endif
344     
345     self->threads[thread].objects[requiredIdx] = RETAIN(_obj);
346   }
347 }
348
349 @end
350
351 @interface WETableCalcMatrixPositionArray : NSObject
352 { /* mutable array of matrix coordinates */
353 @private
354   unsigned    count;
355   MatrixCoord *positions;
356 }
357
358 - (void)addPosition:(unsigned)_x:(unsigned)_y;
359 - (void)checkForDuplicates;
360
361 /* narrow set to row or column */
362 - (unsigned *)indicesInColumn:(unsigned)_x count:(unsigned *)_count;
363 - (unsigned *)indicesInRow:(unsigned)_y    count:(unsigned *)_count;
364
365 @end
366
367 @implementation WETableCalcMatrixPositionArray
368
369 - (void)dealloc {
370   if (self->positions) free(self->positions);
371   [super dealloc];
372 }
373
374 - (void)checkForDuplicates {
375   unsigned j;
376   
377   for (j = 0; j < self->count; j++) {
378     unsigned i;
379
380     for (i = 0; i < j; i++) {
381       NSAssert4(!((self->positions[j].x) == self->positions[i].x &&
382                  (self->positions[j].y) == self->positions[i].y),
383                 @"duplicate coordinate at %d and %d: %d/%d !",
384                 j, i, self->positions[j].x, self->positions[j].y);
385     }
386   }
387 }
388
389 - (void)addPosition:(unsigned)_x:(unsigned)_y {
390   if (self->positions == NULL) {
391     self->positions = calloc(1, sizeof(MatrixCoord));
392     self->positions[0].x = _x;
393     self->positions[0].y = _y;
394     self->count = 1;
395   }
396   else {
397     unsigned oldCount = self->count;
398     void *tmp;
399     
400     tmp = self->positions;
401     self->positions = calloc(oldCount + 1, sizeof(MatrixCoord));
402     memcpy(self->positions, tmp, (oldCount * sizeof(MatrixCoord)));
403     
404     self->positions[oldCount].x = _x;
405     self->positions[oldCount].y = _y;
406     self->count++;
407   }
408 }
409
410 - (unsigned *)indicesIn:(WEOrientation)o index:(unsigned)_idx
411   count:(unsigned *)_count
412 {
413   unsigned i, rowCount;
414   unsigned *pos, *p;
415   
416   /* first count */
417   for (i = 0, rowCount = 0; i < self->count; i++) {
418     unsigned j;
419     
420     j = (o == WEColumn) ? self->positions[i].x : self->positions[i].y;
421     if (j == _idx)
422       rowCount++;
423   }
424   if (rowCount == 0)
425     return NULL;
426   
427   /* then copy */
428   pos = calloc(rowCount, sizeof(unsigned));
429   *_count = rowCount;
430   
431   for (i = 0, p = pos; i < self->count; i++) {
432     unsigned j;
433     
434     j = (o == WEColumn) ? self->positions[i].x : self->positions[i].y;
435     
436     if (j == _idx) {
437       *p = (o == WEColumn)
438         ? self->positions[i].y
439         : self->positions[i].x;
440       p++;
441     }
442   }
443   return pos;
444 }
445
446 - (unsigned *)indicesInRow:(unsigned)_y count:(unsigned *)_count {
447   return [self indicesIn:WERow index:_y count:_count];
448 }
449 - (unsigned *)indicesInColumn:(unsigned)_x count:(unsigned *)_count {
450   return [self indicesIn:WEColumn index:_x count:_count];
451 }
452
453 - (NSString *)description {
454   return [NSString stringWithFormat:@"<%08X[%@]: count=%d>",
455                      self, NSStringFromClass([self class]),
456                      self->count];
457 }
458
459 @end /* WETableCalcMatrixPositionArray */
460
461 @implementation WETableCalcMatrix
462
463 + (int)version {
464   return 0;
465 }
466
467 static inline MatrixEntry *entryAt(WETableCalcMatrix *self, unsigned x, 
468                                    unsigned y) {
469   return self->matrix +
470          (x * self->height * sizeof(MatrixEntry)) +
471          (y * sizeof(MatrixEntry));
472 }
473
474 - (id)initWithSize:(unsigned)_width:(unsigned)_height {
475   if (_width == 0 || _height == 0) {
476     [self logWithFormat:@"ERROR: specified invalid matrix dimensions: %ix%i",
477             _width, _height];
478     [self release];
479     return nil;
480   }
481   
482   NSAssert(_width > 0 && _height > 0, @"invalid args ..");
483   self->width  = _width;
484   self->height = _height;
485   self->matrix = (void *)calloc(_width * _height, sizeof(MatrixEntry));
486   memset(self->matrix, 0, _width * _height * sizeof(MatrixEntry));
487
488   self->objToPos = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
489                                     NSObjectMapValueCallBacks,
490                                     64);
491   
492   return self;
493 }
494 - (void)dealloc {
495   [self removeAllObjects];
496
497   if (self->objToPos)
498     NSFreeMapTable(self->objToPos);
499   
500   if (self->matrix)
501     free(self->matrix);
502   
503   [super dealloc];
504 }
505
506 /* accessors */
507
508 - (unsigned)width {
509   return self->width;
510 }
511 - (unsigned)height {
512   return self->height;
513 }
514
515 - (void)setDelegate:(id)_delegate {
516   self->delegate = _delegate;
517   
518   self->columnCheck =
519     [_delegate respondsToSelector:
520                  @selector(tableCalcMatrix:shouldProcessColumn:forObject:)];
521   self->rowCheck =
522     [_delegate respondsToSelector:
523                  @selector(tableCalcMatrix:shouldProcessRow:forObject:)];
524 }
525 - (id)delegate {
526   return self->delegate;
527 }
528
529 /* clearing the structure */
530
531 - (void)removeAllObjects {
532   unsigned y, x;
533   
534   if (self->objToPos) {
535     NSResetMapTable(self->objToPos);
536     self->objToPos = NULL;
537   }
538   
539   if (self->matrix == NULL)
540     return;
541   
542   for (y = 0; y < self->height; y++) {
543     for (x = 0; x < self->width; x++) {
544       MatrixEntry *e;
545       
546       e = entryAt(self, x, y);
547       
548       if (e->items == nil) 
549         continue;
550       
551       [e->items release]; e->items = nil;
552     }
553   }
554 }
555
556 /* queries */
557
558 - (BOOL)object:(id)_obj possibleInRow:(unsigned)_y {
559   /* optimization method, can always return 'YES' */
560   if (self->rowCheck) {
561     return [self->delegate tableCalcMatrix:self
562                            shouldProcessRow:_y
563                            forObject:_obj];
564   }
565   return YES;
566 }
567
568 - (BOOL)object:(id)_obj possibleInColumn:(unsigned)_x {
569   /* optimization method, can always return 'YES' */
570   if (self->columnCheck) {
571     return [self->delegate tableCalcMatrix:self
572                            shouldProcessColumn:_x
573                            forObject:_obj];
574   }
575   return YES;
576 }
577
578 - (BOOL)object:(id)_obj matchesCellAt:(unsigned)_x:(unsigned)_y {
579   return [self->delegate tableCalcMatrix:self
580                          shouldPlaceObject:_obj
581                          atPosition:_x:_y];
582 }
583
584 /* adding object to structure */
585
586 - (void)addObject:(id)_obj toCellAt:(unsigned)_x:(unsigned)_y {
587   WETableCalcMatrixPositionArray *positions;
588   MatrixEntry *e;
589   
590   if ((positions = NSMapGet(self->objToPos, _obj)) == nil) {
591     positions = [[WETableCalcMatrixPositionArray alloc] init];
592     NSMapInsert(self->objToPos, _obj, positions);
593     RELEASE(positions);
594   }
595   
596   [positions checkForDuplicates];
597   
598   e = entryAt(self, _x, _y);
599   
600   if (e->items == nil)
601     e->items = [[NSMutableArray alloc] init];
602   
603   [e->items addObject:_obj];
604
605   [positions checkForDuplicates];
606   [positions addPosition:_x:_y];
607   [positions checkForDuplicates];
608 }
609
610 /* placing objects */
611
612 - (void)placeObject:(id)_object {
613   unsigned y, x;
614
615   if (NSMapGet(self->objToPos, _object)) {
616     //NSLog(@"already placed object %@ !", _object);
617     return;
618   }
619   
620   if (self->rowCheck) {
621     for (y = 0; y < self->height; y++) {
622       if (![self object:_object possibleInRow:y])
623         continue;
624
625       for (x = 0; x < self->width; x++) {
626         if ([self object:_object matchesCellAt:x:y]) {
627           /* add to cell x:y */
628           [self addObject:_object toCellAt:x:y];
629         }
630       }
631     }
632   }
633   else {
634     for (x = 0; x < self->width; x++) {
635       if (![self object:_object possibleInColumn:x])
636         continue;
637     
638       for (y = 0; y < self->height; y++) {
639         if ([self object:_object matchesCellAt:x:y]) {
640           /* add to cell x:y */
641           [self addObject:_object toCellAt:x:y];
642         }
643       }
644     }
645   }
646 }
647
648 - (void)placeObjects:(NSArray *)_objects {
649   unsigned oc, i;
650   
651   if ((oc = [_objects count]) == 0)
652     return;
653   
654   for (i = 0; i < oc; i++)
655     [self placeObject:[_objects objectAtIndex:i]];
656 }
657
658 /* formatting */
659
660 - (NSArray *)objectsInColumn:(unsigned)_x {
661   unsigned     y;
662   NSMutableSet *set;
663   NSArray      *result;
664
665   set = [[NSMutableSet alloc] init];
666   
667   for (y = 0; y < self->height; y++) {
668     MatrixEntry *e;
669     
670     e = entryAt(self, _x, y);
671     if (e->items)
672       [set addObjectsFromArray:e->items];
673   }
674   result = [set allObjects];
675   RELEASE(set);
676   return result;
677 }
678 - (NSArray *)objectsInRow:(unsigned)_y {
679   unsigned     x;
680   NSMutableSet *set;
681   NSArray      *result;
682
683   set = [[NSMutableSet alloc] init];
684   
685   for (x = 0; x < self->width; x++) {
686     MatrixEntry *e;
687     
688     e = entryAt(self, x, _y);
689     if (e->items)
690       [set addObjectsFromArray:e->items];
691   }
692   result = [set allObjects];
693   RELEASE(set);
694   return result;
695 }
696
697 - (NSArray *)spansOfColumn:(unsigned)_x {
698   WETableCalcMatrixStripe *stripe;
699   NSEnumerator  *objects;
700   id            object;
701   
702   stripe = [[WETableCalcMatrixStripe alloc]
703                                      initWithSize:self->height
704                                      matrix:self
705                                      delegate:self->delegate];
706   stripe = [stripe autorelease];
707   
708   objects = [[self objectsInColumn:_x] objectEnumerator];
709   while ((object = [objects nextObject]) != nil) {
710     WETableCalcMatrixPositionArray *pos;
711     unsigned *indices;
712     unsigned idxCount;
713     
714     pos = NSMapGet(self->objToPos, object);
715     [pos checkForDuplicates];
716     
717     indices = [pos indicesInColumn:_x count:&idxCount];
718     NSAssert(indices, @"available in column, but no indices ?");
719
720     [stripe addObject:object atPositions:indices count:idxCount];
721   }
722   
723   return [stripe threadSpans];
724 }
725 - (NSArray *)spansOfRow:(unsigned)_y {
726   WETableCalcMatrixStripe *stripe;
727   NSEnumerator  *objects;
728   id            object;
729   
730   stripe = [[WETableCalcMatrixStripe alloc]
731                                      initWithSize:self->width
732                                      matrix:self
733                                      delegate:self->delegate];
734   stripe = [stripe autorelease];
735   
736   objects = [[self objectsInRow:_y] objectEnumerator];
737   while ((object = [objects nextObject])) {
738     WETableCalcMatrixPositionArray *pos;
739     unsigned *indices;
740     unsigned idxCount;
741     
742     pos = NSMapGet(self->objToPos, object);
743     [pos checkForDuplicates];
744     
745     indices = [pos indicesInRow:_y count:&idxCount];
746     NSAssert(indices, @"available in column, but no indices ?");
747     
748     [stripe addObject:object atPositions:indices count:idxCount];
749   }
750   
751   return [stripe threadSpans];
752 }
753
754 - (NSArray *)columnSpans {
755   id       objs[self->width];
756   unsigned i;
757   
758   for (i = 0; i < self->width; i++) {
759     objs[i] = [self spansOfColumn:i];
760     if (objs[i] == nil) objs[i] = [NSArray array];
761   }
762   return [NSArray arrayWithObjects:objs count:self->width];
763 }
764 - (NSArray *)rowSpans {
765   id       objs[self->height];
766   unsigned i;
767   
768   for (i = 0; i < self->height; i++) {
769     objs[i] = [self spansOfRow:i];
770     if (objs[i] == nil) objs[i] = [NSArray array];
771   }
772   return [NSArray arrayWithObjects:objs count:self->height];
773 }
774
775 /* counting */
776
777 - (unsigned)widthOfColumn:(unsigned)_x {
778   unsigned y;
779   unsigned count;
780   
781   for (y = 0, count = 0; y < self->height; y++) {
782     MatrixEntry *e;
783
784     e = entryAt(self, _x, y);
785     if ([e->items count] > count)
786       count = [e->items count];
787   }
788   return count;
789 }
790
791 - (unsigned)countOfColumn:(unsigned)_x {
792   return [[self objectsInColumn:_x] count];
793 }
794
795 @end /* WETableCalcMatrix */