"Fossies" - the Fresh Open Source Software Archive

Member "opengroupware-5.5rc3/WebUI/Common/OGoUIElements/SkyTableView.m" (5 Dec 2015, 20962 Bytes) of package /linux/privat/opengroupware-5.5rc3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Matlab source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "SkyTableView.m" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.5rc2_vs_5.5rc3.

    1 /*
    2   Copyright (C) 2000-2005 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 
   22 /*
   23    > dataSource
   24    > list
   25   <> batchSize
   26   <> selections
   27 
   28   <> item
   29   <> index
   30    > identifier
   31   <> previousItem
   32   <> previousIndex
   33   
   34   <> sortedKey
   35   <> isDescending
   36 
   37   <  groups
   38   <  objectsOfGroup
   39    > showGroupTitle        (default: YES)
   40   
   41    > scrollOnClient
   42   <> autoScroll
   43   <  count           // number of elements in dataSource
   44 
   45    > sortAction
   46    > sortCaseInsensitive    (default: NO)
   47    > showBatchResizeButtons (default: YES)
   48 
   49    > titleString
   50    > footerString
   51 
   52    > cacheTimeout    // seconds
   53 */
   54 
   55 //#define PROFILE 1
   56 
   57 #include <OGoFoundation/OGoComponent.h>
   58 #include <NGExtensions/EOCacheDataSource.h>
   59 #include "common.h"
   60 
   61 @interface SkyTableView : OGoComponent
   62 {
   63 @protected
   64   NSArray        *list;
   65   EODataSource   *dataSource;
   66   NSMutableArray *selections;
   67   id             item;
   68   unsigned       index;
   69   
   70   BOOL           isDescending;
   71   BOOL           scrollOnClient;
   72   int            autoScroll;
   73   NSString       *sortedKey;
   74   unsigned       batchSize;
   75   unsigned       currentBatch;
   76 
   77   NSString *shiftId; // used by shiftClickScript (changed never)
   78   NSString *allId;   // used by selectAllCheckboxScript
   79 
   80   NSString *titleString;
   81   NSString *footerString;
   82 
   83   int indexOfFirst;
   84   int indexOfLast;
   85 
   86   // grouping
   87   NSDictionary   *groupingDict;
   88   NSArray        *groupAttributes;  // array of groupNames
   89   NGBitSet       *showGroupSet;
   90 }
   91 - (void)setList:(NSArray *)_list;
   92 - (NSString *)identifier;
   93 @end
   94 
   95 @interface NSDictionary(SkyTableViewGrouping)
   96 - (NSArray *)flattenedArrayWithHint:(unsigned)_hint andKeys:(NSArray *)_keys;
   97 - (NSArray *)attributesWithHint:(unsigned int)_hint andKeys:(NSArray *)_keys;
   98 - (NGBitSet *)bitSetWithHint:(unsigned int)_hint;
   99 @end
  100 
  101 static NSString *SkyTableView_SelectAllCheckboxesScript = nil;
  102 static NSString *SkyTableView_ShiftClickScript = nil;
  103 
  104 @implementation SkyTableView
  105 
  106 static Class EOCacheDataSourceClass = Nil;
  107 static Class StrClass = Nil;
  108 
  109 + (void)initialize {
  110   NSBundle *bundle;
  111   NSString *path;
  112   static BOOL didInit = NO;
  113   if (didInit) return;
  114   didInit = YES;
  115   
  116   StrClass               = [NSString class];
  117   EOCacheDataSourceClass = [EOCacheDataSource class];
  118 
  119   bundle = [NSBundle bundleForClass:self];
  120   
  121   path = [bundle pathForResource:@"SkyTableView_SelectAllCheckboxesScript"
  122                  ofType:@"js"];
  123   SkyTableView_SelectAllCheckboxesScript =
  124     [[StrClass alloc] initWithContentsOfFile:path];
  125   if (SkyTableView_SelectAllCheckboxesScript == nil)
  126     NSLog(@"WARNING: did not find checkboxes JavaScript for SkyTableView!");
  127   
  128   path = [bundle pathForResource:@"SkyTableView_ShiftClickScript"
  129                  ofType:@"js"];
  130   SkyTableView_ShiftClickScript = 
  131     [[StrClass alloc] initWithContentsOfFile:path];
  132   if (SkyTableView_ShiftClickScript == nil)
  133     NSLog(@"WARNING: did not find shiftclick JavaScript for SkyTableView!");
  134 }
  135 
  136 static inline NSString *_currentId(SkyTableView *self) {
  137   NSArray *tmp;
  138 
  139   tmp = [[[self context] elementID] componentsSeparatedByString:@"."];
  140   
  141   return [tmp componentsJoinedByString:@""];
  142 }
  143 
  144 - (id)init {
  145   if ((self = [super init])) {
  146     self->shiftId         = [_currentId(self) copy];
  147     self->showGroupSet    = [[NGBitSet alloc] initWithCapacity:128];
  148   }
  149   return self;
  150 }
  151 
  152 - (void)dealloc {
  153   [self->list            release];
  154   [self->item            release];
  155   [self->dataSource      release];
  156   [self->selections      release];
  157   [self->allId           release];
  158   [self->shiftId         release];
  159   [self->sortedKey       release];
  160 
  161   [self->titleString     release];
  162   [self->footerString    release];
  163 
  164   [self->groupingDict    release];
  165   [self->groupAttributes release];
  166   [self->showGroupSet    release];
  167   
  168   [super dealloc];
  169 }
  170 
  171 /* sorting */
  172 
  173 - (void)_sortList {
  174   // is only called if self->dataSource== nil
  175   EOSortOrdering *so;
  176   SEL            sel;
  177   BOOL           isInsen;
  178   NSArray        *soArray;
  179 
  180   BEGIN_PROFILE;
  181 
  182   isInsen = ([self canGetValueForBinding:@"sortCaseInsensitive"])
  183     ? [[self valueForBinding:@"sortCaseInsensitive"] boolValue]
  184     : NO;
  185   
  186   sel = (self->isDescending)
  187     ? (isInsen) ? EOCompareCaseInsensitiveDescending : EOCompareDescending
  188     : (isInsen) ? EOCompareCaseInsensitiveAscending  : EOCompareAscending;
  189   
  190   so = [EOSortOrdering sortOrderingWithKey:self->sortedKey selector:sel];
  191   
  192   soArray = [NSArray arrayWithObject:so];
  193   [self setList:[self->list sortedArrayUsingKeyOrderArray:soArray]];
  194   
  195   END_PROFILE;
  196 }
  197 
  198 - (void)_updateFetchSpecification {
  199   EOFetchSpecification *fetchSpec;
  200   EOSortOrdering       *so;
  201   SEL                  sel;
  202   BOOL                 isInsen;
  203 
  204   if (self->sortedKey == nil)
  205     return;
  206 
  207   BEGIN_PROFILE;
  208 
  209   isInsen = ([self canGetValueForBinding:@"sortCaseInsensitive"])
  210     ? [[self valueForBinding:@"sortCaseInsensitive"] boolValue]
  211     : NO;
  212     
  213   sel = (self->isDescending)
  214     ? (isInsen) ? EOCompareCaseInsensitiveDescending : EOCompareDescending
  215     : (isInsen) ? EOCompareCaseInsensitiveAscending  : EOCompareAscending;
  216     
  217   so = [EOSortOrdering sortOrderingWithKey:self->sortedKey selector:sel];
  218     
  219   if ((fetchSpec = [self->dataSource fetchSpecification]) == nil) {
  220     fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName:nil
  221                                       qualifier:nil
  222                                       sortOrderings:
  223                                         [NSArray arrayWithObject:so]];
  224     [self->dataSource setFetchSpecification:fetchSpec];
  225   }
  226   else {
  227     NSArray *sos = nil;
  228 
  229     sos = [NSArray arrayWithObject:so];
  230     
  231     if ([[fetchSpec sortOrderings] isEqual:sos] == NO) {
  232       [fetchSpec setSortOrderings:[NSArray arrayWithObject:so]];
  233       [self->dataSource setFetchSpecification:fetchSpec];
  234     }
  235   }
  236   
  237   END_PROFILE;
  238 }
  239 
  240 - (void)_updateListFromDataSource {
  241   EOFetchSpecification *fetchSpec = nil;
  242   NSArray              *groupings = nil;
  243 
  244   BEGIN_PROFILE;
  245 
  246   if (![self canGetValueForBinding:@"sortAction"])
  247     [self _updateFetchSpecification];
  248   else
  249     [self performParentAction:[self valueForBinding:@"sortAction"]];
  250   
  251   fetchSpec = [self->dataSource fetchSpecification];
  252 
  253   PROFILE_CHECKPOINT("after update fspec");
  254   
  255   if ((groupings = [fetchSpec groupings])) {
  256     unsigned int cnt;
  257     NSArray      *tmp;
  258     EOGrouping   *grouping;
  259     NSArray      *allKeys;
  260 
  261 
  262     tmp = [self->dataSource fetchObjects];
  263     cnt = [tmp count];
  264 
  265     grouping  = [groupings lastObject];
  266     [grouping setSortOrderings:[fetchSpec sortOrderings]];
  267 
  268     [self->groupingDict release]; self->groupingDict = nil;
  269     self->groupingDict = [[tmp arrayGroupedBy:grouping] retain];
  270 
  271     allKeys = [self->groupingDict allKeys];
  272     allKeys = [allKeys sortedArrayUsingSelector:@selector(compare:)];
  273     [self->groupAttributes release]; self->groupAttributes = nil;
  274     self->groupAttributes = 
  275       [[self->groupingDict attributesWithHint:cnt andKeys:allKeys] retain];
  276     
  277     [self setList:[self->groupingDict flattenedArrayWithHint:[tmp count]
  278                              andKeys:allKeys]];
  279     
  280     PROFILE_CHECKPOINT("after groupings");
  281   }
  282   else {
  283     RELEASE(self->groupAttributes); self->groupAttributes = nil;
  284 
  285     [self setList:[self->dataSource fetchObjects]];
  286     
  287     PROFILE_CHECKPOINT("after no groupings");
  288   }
  289   
  290   END_PROFILE;
  291 }
  292 
  293 /* accessors */
  294 
  295 - (void)setIsDescending:(BOOL)_isDescending {
  296   self->isDescending = _isDescending;
  297 }
  298 - (BOOL)isDescending {
  299   return self->isDescending;
  300 }
  301 
  302 - (void)setSortedKey:(NSString *)_sortedKey {
  303   ASSIGN(self->sortedKey, _sortedKey);
  304 }
  305 - (NSString *)sortedKey {
  306   return self->sortedKey;
  307 }
  308 
  309 - (BOOL)scrollOnClient {
  310   return self->scrollOnClient;
  311 }
  312 
  313 - (int)autoScroll {
  314   return self->autoScroll;
  315 }
  316 - (void)setAutoScroll:(int)_autoScroll {
  317   self->autoScroll = _autoScroll;
  318 }
  319 
  320 - (unsigned)batchSize {
  321   return self->batchSize;
  322 }
  323 - (void)setBatchSize:(unsigned)_batchSize {
  324   self->batchSize = _batchSize;
  325 }
  326 
  327 - (void)setList:(NSArray *)_list {
  328   ASSIGN(self->list, _list);
  329 }
  330 
  331 - (NSArray *)list {
  332   return self->list;
  333 }
  334 
  335 - (void)setDataSource:(EODataSource *)_dataSource {
  336   int cacheTimeout = -1;
  337 
  338   if (self->dataSource == _dataSource)
  339     return;
  340 
  341   BEGIN_PROFILE;
  342   
  343   if ([self canGetValueForBinding:@"cacheTimeout"])
  344     cacheTimeout = [self intValueForBinding:@"cacheTimeout"];
  345   
  346   // use a cacheDataSource
  347   if (cacheTimeout > 0) {
  348 #if DEBUG && 0
  349       [self debugWithFormat:@"use cached datasource (timeout=%i)",
  350               cacheTimeout];
  351 #endif
  352       if ([self->dataSource isKindOfClass:EOCacheDataSourceClass] &&
  353           [(EOCacheDataSource *)self->dataSource source] == _dataSource) {
  354         if (cacheTimeout != [(EOCacheDataSource *)self->dataSource timeout])
  355           [(EOCacheDataSource *)self->dataSource setTimeout:cacheTimeout];
  356         return;
  357       }
  358       RELEASE(self->dataSource); self->dataSource = nil;
  359       self->dataSource = [[EOCacheDataSource allocWithZone:[self zone]]
  360                                              initWithDataSource:_dataSource];
  361       
  362       [(EOCacheDataSource *)self->dataSource setTimeout:cacheTimeout];
  363   }
  364   // do not use dataSource
  365   else {
  366     ASSIGN(self->dataSource, _dataSource);
  367   }
  368   
  369   END_PROFILE;
  370 }
  371 
  372 - (void)setSelections:(NSArray *)_selections {
  373   ASSIGN(self->selections, [NSMutableArray arrayWithArray: _selections]);
  374 }
  375 - (NSArray *)selections {
  376   return self->selections;
  377 }
  378 
  379 - (void)setItem:(id)_item {
  380   [self setValue:_item forBinding:@"item"];
  381   ASSIGN(self->item, _item);
  382 }
  383 - (id)item {
  384   return self->item;
  385 }
  386 
  387 - (void)setIndex:(unsigned)_index {
  388   [self setValue:[NSNumber numberWithInt:_index] forBinding:@"index"];
  389   self->index = _index;
  390 }
  391 - (unsigned)index {
  392   return self->index;
  393 }
  394 
  395 // --- grouping -----------------------------------------
  396 
  397 - (NSArray *)groups {
  398   return (self->groupAttributes)
  399     ? [self->groupAttributes objectAtIndex:self->index]
  400     : nil;
  401 }
  402 
  403 - (void)setGroups:(NSArray *)_groups {
  404   [self setValue:[self groups] forBinding:@"groups"];
  405   [self setValue:[self->groupingDict objectForKey:[self groups]]
  406         forBinding:@"objectsOfGroup"];
  407 }
  408 
  409 - (BOOL)showGroup {
  410   return (self->groupAttributes != nil)
  411     ? [self->showGroupSet isMember:self->index]
  412     : YES;
  413 }
  414 - (void)setShowGroup:(BOOL)_bool {
  415   if (_bool)
  416     [self->showGroupSet addMember:self->index];
  417   else
  418     [self->showGroupSet removeMember:self->index];
  419 }
  420 
  421 - (BOOL)showGroupTitle {
  422   return ([self canGetValueForBinding:@"showGroupTitle"])
  423     ? [[self valueForBinding:@"showGroupTitle"] boolValue]
  424     : YES;
  425 }
  426 
  427 // --- checkboxes  --------------------------------------
  428 
  429 - (BOOL)isSelectAllAsCheckBox {
  430   if (self->batchSize > [self->list count])
  431     return YES;
  432 
  433   return NO;
  434 }
  435 
  436 - (BOOL)isAllSelected {
  437   return ([self->list count] == 0)
  438     ? NO
  439     : ([self->selections count] == [self->list count]);
  440 }
  441 
  442 - (id)selectAll {
  443   [self->selections removeAllObjects];
  444   [self->selections addObjectsFromArray:self->list];
  445   
  446   return nil;
  447 }
  448 
  449 - (id)deselectAll {
  450   [self->selections removeAllObjects];
  451 
  452   return nil;
  453 }
  454 
  455 - (BOOL)isChecked {
  456   return [self->selections containsObject:self->item];
  457 }
  458 
  459 - (void)setIsChecked:(BOOL)_flag {
  460   if (_flag && ![self->selections containsObject:self->item])
  461     [self->selections addObject:self->item];
  462   else if (!_flag && [self->selections containsObject:self->item])
  463     [self->selections removeObject:self->item];
  464 }
  465 
  466 - (BOOL)isCheckBoxes {
  467   if (![[self context] isInForm])
  468     return NO;
  469   
  470   return [self hasBinding:@"selections"];
  471   //return (self->selections != nil) ? YES : NO;
  472 }
  473 
  474 - (NSString *)shiftClick {
  475   return [StrClass stringWithFormat:@"shiftClick%@(%d)",
  476                    self->shiftId, self->index];
  477 }
  478 
  479 - (NSString *)allSelect {
  480   return [StrClass stringWithFormat:@"allselect%@()", self->allId];
  481 }
  482 
  483 - (NSString *)checkBoxName {
  484   return [StrClass stringWithFormat:@"%@%@", self->shiftId,
  485                    [self identifier]];
  486 }
  487 
  488 - (NSString *)checkBoxValue {
  489   return [StrClass stringWithFormat:@"%@%d", self->shiftId, self->index];
  490 }
  491 
  492 - (void)setIndexOfFirst:(int)_val {
  493   self->indexOfFirst = _val;
  494 }
  495 - (int)indexOfFirst {
  496   return self->indexOfFirst;
  497 }
  498 - (void)setIndexOfLast:(int)_val {
  499   self->indexOfLast = _val;
  500 }
  501 - (int)indexOfLast {
  502   return self->indexOfLast;
  503 }
  504 
  505 - (NSString *)selectAllCheckboxesScript {
  506   RELEASE(self->allId); self->allId = nil;
  507   self->allId = [_currentId(self) copy];
  508   
  509   return [StrClass stringWithFormat:SkyTableView_SelectAllCheckboxesScript,
  510                    self->allId,
  511                    self->allId,
  512                    self->allId,
  513                    self->allId,
  514                    [self indexOfFirst],
  515                    [self indexOfLast] + 1,
  516                    self->shiftId,
  517                    self->allId];
  518 }
  519 
  520 - (NSString *)shiftClickScript {
  521   return [StrClass stringWithFormat:SkyTableView_ShiftClickScript,
  522                    self->shiftId,self->shiftId, self->shiftId, self->shiftId,
  523                    self->shiftId,self->shiftId];
  524 }
  525 
  526 - (NSString *)markAllCheckboxName {
  527   return [StrClass stringWithFormat:@"markAllCheckbox%@", self->allId];
  528 }
  529 
  530 // --- actions ------------------------------------------
  531 
  532 - (id)tableViewSortAction {
  533   NSString *parentAction = nil;
  534 
  535   parentAction = [self valueForBinding:@"sortAction"];
  536 
  537   if (parentAction != nil) {
  538     if ([self canSetValueForBinding:@"sortedKey"])
  539       [self setValue:self->sortedKey   forBinding:@"sortedKey"];
  540     if ([self canSetValueForBinding:@"isDescending"])
  541       [self setValue:[NSNumber numberWithBool:self->isDescending]
  542             forBinding:@"isDescending"];
  543     
  544     [self performParentAction:parentAction];
  545   }
  546   else if (self->dataSource) {
  547     [self _updateFetchSpecification];
  548   }
  549   else
  550     [self _sortList];
  551 
  552   return nil;
  553 }
  554 
  555 // --- syncing ------------------------------------------
  556 
  557 - (void)setPreviousItem:(id)_previousItem {
  558   [self setValue:_previousItem forBinding:@"previousItem"];
  559 }
  560 
  561 - (void)setPreviousIndex:(unsigned)_previousIndex {
  562   [self setValue:[NSNumber numberWithInt:_previousIndex]
  563         forBinding:@"previousIndex"];
  564 }
  565 
  566 - (void)setCurrentBatch:(unsigned)_batch {
  567   self->currentBatch = _batch;
  568 }
  569 - (unsigned)currentBatch {
  570   return self->currentBatch;
  571 }
  572 
  573 - (void)setIdentifier:(NSString *)_identifier {
  574   [self setValue:_identifier forBinding:@"identifier"];
  575 }
  576 
  577 - (NSString *)identifier {
  578   id tmp;
  579   
  580   tmp = [self valueForBinding:@"identifier"];
  581   return (tmp) ? tmp : [StrClass stringWithFormat:@"%d", self->index];
  582 }
  583 
  584 - (BOOL)showBatchResizeButtons {
  585   if (![self canGetValueForBinding:@"showBatchResizeButtons"])
  586     return YES;
  587 
  588   return [[self valueForBinding:@"showBatchResizeButtons"] boolValue];
  589 }
  590 
  591 - (void)setTitleString:(NSString *)_titleString {
  592   ASSIGN(self->titleString, _titleString);
  593 }
  594 - (NSString *)titleString {
  595   return self->titleString;
  596 }
  597 
  598 - (void)setFooterString:(NSString *)_footerString {
  599   ASSIGN(self->footerString, _footerString);
  600 }
  601 - (NSString *)footerString {
  602   return self->footerString;
  603 }
  604 
  605 - (BOOL)hasFooterString {
  606   return (self->footerString != nil) ? YES : NO;
  607 }
  608 
  609 - (BOOL)hasTitleString {
  610   return (self->titleString != nil) ? YES : NO;
  611 }
  612 
  613 - (void)syncFromParent {
  614 #define getVal(_a_) getBinding(self, @selector(valueForBinding:), _a_)
  615 
  616   id  tmp;
  617   IMP getBinding;
  618 
  619   BEGIN_PROFILE;
  620 
  621   getBinding = [self methodForSelector:@selector(valueForBinding:)];
  622   
  623   [self setDataSource:getVal(@"dataSource")];
  624   [self setSelections:getVal(@"selections")];
  625   
  626   if ([self canGetValueForBinding:@"isDescending"] &&
  627      ([self canSetValueForBinding:@"isDescending"] ||
  628        (self->sortedKey == nil)))
  629     self->isDescending = [getVal(@"isDescending") boolValue];
  630 
  631   if ([self canGetValueForBinding:@"sortedKey"] &&
  632       ([self canSetValueForBinding:@"sortedKey"] || (self->sortedKey == nil)))
  633     [self setSortedKey:getVal(@"sortedKey")];
  634 
  635   [self setTitleString:getVal(@"titleString")];
  636   [self setFooterString:getVal(@"footerString")];  
  637 
  638   if (self->dataSource) {
  639     [self _updateListFromDataSource];
  640   }
  641   else
  642     [self setList:getVal(@"list")];
  643   
  644   [self setValue:[NSNumber numberWithInt:[self->list count]]
  645         forBinding:@"count"];
  646 
  647   self->batchSize      = [getVal(@"batchSize") unsignedIntValue];
  648   tmp = getVal(@"currentBatch");
  649   if (tmp != nil)
  650     self->currentBatch = [tmp unsignedIntValue];
  651   self->scrollOnClient = [getVal(@"scrollOnClient") boolValue];
  652   self->autoScroll     = [getVal(@"autoScroll") intValue];
  653 
  654   END_PROFILE;
  655   
  656 #undef getVal
  657 }
  658 
  659 - (void)syncToParent {
  660 #define setVal(_val_, _b_) \
  661   if ([self canSetValueForBinding:_b_])\
  662     setBinding(self, @selector(setValue:forBinding:), ((_val_)), ((_b_)))
  663   
  664   IMP setBinding;
  665 
  666   BEGIN_PROFILE;
  667   
  668   setBinding = [self methodForSelector:@selector(setValue:forBinding:)];
  669   
  670   setVal(self->selections, @"selections");
  671 
  672   setVal(self->sortedKey,  @"sortedKey");
  673   setVal([NSNumber numberWithBool:self->isDescending], @"isDescending");
  674   setVal([NSNumber numberWithInt:self->batchSize],     @"batchSize");
  675   setVal([NSNumber numberWithInt:self->currentBatch],  @"currentBatch");
  676   setVal([NSNumber numberWithInt:self->autoScroll],    @"autoScroll");
  677 
  678   RELEASE(self->list); self->list = nil;
  679 
  680   END_PROFILE;
  681 
  682 #undef setVal
  683 }
  684 
  685 // --- responder
  686 
  687 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
  688   [self syncFromParent];
  689   [super takeValuesFromRequest:_req inContext:_ctx];
  690   [self syncToParent];
  691 }
  692 
  693 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
  694   id result;
  695 
  696   [self syncFromParent];
  697   result = [super invokeActionForRequest:_req inContext:_ctx];
  698   [self syncToParent];
  699   
  700   return result;
  701 }
  702 
  703 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
  704   BEGIN_PROFILE;
  705   [self syncFromParent];
  706   PROFILE_CHECKPOINT("after sync from parent");
  707   [super appendToResponse:_response inContext:_ctx];
  708   PROFILE_CHECKPOINT("after append to response");
  709   [self syncToParent];
  710   END_PROFILE;
  711 }
  712 
  713 - (BOOL)synchronizesVariablesWithBindings {
  714   return NO;
  715 }
  716 
  717 @end /* SkyTableView */
  718 
  719 #define ProfileComponents NO
  720 
  721 @implementation NSDictionary(TableView)
  722 
  723 - (NSArray *)flattenedArrayWithHint:(unsigned int)_hint 
  724   andKeys:(NSArray *)_keys
  725 {
  726   NSMutableArray *result  = nil;
  727   unsigned int   i, cnt;
  728   NSTimeInterval st     = 0.0;
  729   
  730   if (ProfileComponents)
  731     st = [[NSDate date] timeIntervalSince1970];
  732 
  733   // should be improved
  734   result = [[NSMutableArray alloc] initWithCapacity:_hint]; 
  735 
  736   for (i = 0, cnt = [_keys count]; i < cnt; i++) {
  737     NSString *key;
  738     NSArray  *tmp;
  739 
  740     key = [_keys objectAtIndex:i];
  741     tmp = [self objectForKey:key];
  742     [result addObjectsFromArray:tmp];
  743   }
  744 
  745   if (ProfileComponents) {
  746     NSTimeInterval diff;
  747     diff = [[NSDate date] timeIntervalSince1970] - st;
  748     
  749     printf("NSDictionary.flattenedArray: %0.4fs\n", diff);
  750   }
  751   return result;
  752 }
  753 
  754 - (NSArray *)attributesWithHint:(unsigned int)_hint andKeys:(NSArray *)_keys {
  755   NSMutableArray *result  = nil;
  756   unsigned int   i, cnt;
  757   NSTimeInterval st     = 0.0;
  758   
  759   if (ProfileComponents)
  760     st = [[NSDate date] timeIntervalSince1970];
  761 
  762   result = [[NSMutableArray allocWithZone:[self zone]]
  763                             initWithCapacity:_hint+1];
  764 
  765   for (i = 0, cnt = [_keys count]; i < cnt; i++) {
  766     unsigned j, cnt2;
  767     NSString *key;
  768 
  769     key  = [_keys objectAtIndex:i];
  770 
  771     cnt2 = [[self objectForKey:key] count];
  772     for (j = 0; j < cnt2; j++)
  773       [result addObject:key];
  774   }
  775 
  776   if (ProfileComponents) {
  777     NSTimeInterval diff;
  778     diff = [[NSDate date] timeIntervalSince1970] - st;
  779     
  780     printf("NSDictionary.attributes: %0.4fs\n", diff);
  781   }
  782   
  783   return result;
  784 }
  785 
  786 - (NGBitSet *)bitSetWithHint:(unsigned int)_hint {
  787   NGBitSet     *bitSet  = nil;
  788   NSEnumerator *keyEnum;
  789   NSString     *key;
  790   unsigned int firstPos = 0;
  791   NSTimeInterval st     = 0.0;
  792   
  793   if (ProfileComponents)
  794     st = [[NSDate date] timeIntervalSince1970];
  795  
  796   bitSet = [NGBitSet bitSetWithCapacity:_hint];
  797   
  798   keyEnum = [self keyEnumerator];
  799   while ((key = [keyEnum nextObject])) {
  800     [bitSet addMember:firstPos];
  801     firstPos += [[self objectForKey:key] count];
  802   }
  803 
  804   if (ProfileComponents) {
  805     NSTimeInterval diff;
  806     diff = [[NSDate date] timeIntervalSince1970] - st;
  807     
  808     printf("NSDictionary.bitSet: %0.4fs\n", diff);
  809   }
  810   
  811   return bitSet;
  812 }
  813 
  814 @end /* NSDictionary(TableView) */