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