1/**  <title>NSKeyValueBinding informal protocol reference</title>
2
3   Implementation of KeyValueBinding for GNUStep
4
5   Copyright (C) 2007 Free Software Foundation, Inc.
6
7   Written by:  Chris Farber <chris@chrisfarber.net>
8   Date: 2007
9   Author: Fred Kiefer <fredkiefer@gmx.de>
10   Date: Decembre 2007
11
12   This file is part of the GNUstep GUI Library.
13
14   This library is free software; you can redistribute it and/or
15   modify it under the terms of the GNU Lesser General Public
16   License as published by the Free Software Foundation; either
17   version 2 of the License, or (at your option) any later version.
18
19   This library is distributed in the hope that it will be useful,
20   but WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
22   Lesser General Public License for more details.
23
24   You should have received a copy of the GNU Lesser General Public
25   License along with this library; see the file COPYING.LIB.
26   If not, see <http://www.gnu.org/licenses/> or write to the
27   Free Software Foundation, 51 Franklin Street, Fifth Floor,
28   Boston, MA 02110-1301, USA.
29*/
30
31#import <Foundation/NSArray.h>
32#import <Foundation/NSDebug.h>
33#import <Foundation/NSDictionary.h>
34#import <Foundation/NSEnumerator.h>
35#import <Foundation/NSException.h>
36#import <Foundation/NSInvocation.h>
37#import <Foundation/NSKeyValueObserving.h>
38#import <Foundation/NSKeyValueCoding.h>
39#import <Foundation/NSLock.h>
40#import <Foundation/NSMapTable.h>
41#import <Foundation/NSValue.h>
42#import <Foundation/NSValueTransformer.h>
43#import <GNUstepBase/GSLock.h>
44
45#import "AppKit/NSKeyValueBinding.h"
46#import "GSBindingHelpers.h"
47#import "GSFastEnumeration.h"
48
49@implementation NSObject (NSKeyValueBindingCreation)
50
51+ (void) exposeBinding: (NSString *)binding
52{
53  [GSKeyValueBinding exposeBinding: binding forClass: [self class]];
54}
55
56- (NSArray *) exposedBindings
57{
58  NSMutableArray *exposedBindings = [NSMutableArray array];
59  NSArray *tmp;
60  Class class = [self class];
61
62  while (class && class != [NSObject class])
63    {
64      tmp = [GSKeyValueBinding exposedBindingsForClass: class];
65      if (tmp != nil)
66        {
67          [exposedBindings addObjectsFromArray: tmp];
68        }
69
70      class = [class superclass];
71    }
72
73  return exposedBindings;
74}
75
76- (Class) valueClassForBinding: (NSString *)binding
77{
78  return [NSString class];
79}
80
81- (void) bind: (NSString *)binding
82     toObject: (id)anObject
83  withKeyPath: (NSString *)keyPath
84      options: (NSDictionary *)options
85{
86  if ((anObject == nil)
87      || (keyPath == nil))
88    {
89      NSLog(@"No object or path for binding on %@ for %@", self, binding);
90      return;
91    }
92
93  if ([[self exposedBindings] containsObject: binding])
94    {
95      GSKeyValueBinding *kvb;
96
97      [self unbind: binding];
98      kvb = [[GSKeyValueBinding alloc] initWithBinding: binding
99                                              withName: binding
100                                              toObject: anObject
101                                           withKeyPath: keyPath
102                                               options: options
103                                            fromObject: self];
104      // The binding will be retained in the binding table
105      RELEASE(kvb);
106    }
107  else
108    {
109      NSLog(@"No binding exposed on %@ for %@", self, binding);
110    }
111}
112
113- (NSDictionary *) infoForBinding: (NSString *)binding
114{
115  return [GSKeyValueBinding infoForBinding: binding forObject: self];
116}
117
118- (void) unbind: (NSString *)binding
119{
120  [GSKeyValueBinding unbind: binding forObject: self];
121}
122
123@end
124
125static NSRecursiveLock *bindingLock = nil;
126static NSMapTable *classTable = NULL;      //available bindings
127static NSMapTable *objectTable = NULL;     //bound bindings
128
129typedef enum {
130  GSBindingOperationAnd = 0,
131  GSBindingOperationOr
132} GSBindingOperationKind;
133
134//TODO: document
135BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings,
136    GSBindingOperationKind operationKind);
137
138//TODO: document
139void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
140    NSDictionary *bindings);
141
142@implementation GSKeyValueBinding
143
144+ (void) initialize
145{
146  if (self == [GSKeyValueBinding class])
147    {
148      bindingLock = [GSLazyRecursiveLock new];
149      classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
150          NSObjectMapValueCallBacks, 128);
151      objectTable = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
152          NSObjectMapValueCallBacks, 128);
153    }
154}
155
156+ (void) exposeBinding: (NSString *)binding forClass: (Class)clazz
157{
158  NSMutableArray *bindings;
159
160  [bindingLock lock];
161  bindings = (NSMutableArray *)NSMapGet(classTable, (void*)clazz);
162  if (bindings == nil)
163    {
164      bindings = [[NSMutableArray alloc] initWithCapacity: 5];
165      NSMapInsert(classTable, (void*)clazz, (void*)bindings);
166      RELEASE(bindings);
167    }
168  [bindings addObject: binding];
169  [bindingLock unlock];
170}
171
172+ (NSArray *) exposedBindingsForClass: (Class)clazz
173{
174  NSArray *tmp;
175
176  if (!classTable)
177    return nil;
178
179  [bindingLock lock];
180  tmp = NSMapGet(classTable, (void*)clazz);
181  [bindingLock unlock];
182
183  return tmp;
184}
185
186+ (GSKeyValueBinding *) getBinding: (NSString *)binding
187                         forObject: (id)anObject
188{
189  NSMutableDictionary *bindings;
190  GSKeyValueBinding *theBinding = nil;
191
192  if (!objectTable)
193    return nil;
194
195  [bindingLock lock];
196  bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)anObject);
197  if (bindings != nil)
198    {
199      theBinding = (GSKeyValueBinding*)[bindings objectForKey: binding];
200    }
201  [bindingLock unlock];
202
203  return theBinding;
204}
205
206+ (NSDictionary *) infoForBinding: (NSString *)binding
207                        forObject: (id)anObject
208{
209  GSKeyValueBinding *theBinding;
210
211  theBinding = [self getBinding: binding forObject: anObject];
212  if (theBinding == nil)
213    return nil;
214
215  return theBinding->info;
216}
217
218+ (void) unbind: (NSString *)binding
219      forObject: (id)anObject
220{
221  NSMutableDictionary *bindings;
222  id observedObject;
223  NSString *keyPath;
224  GSKeyValueBinding *theBinding;
225
226  if (!objectTable)
227    return;
228
229  [bindingLock lock];
230  bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)anObject);
231  if (bindings != nil)
232    {
233      theBinding = (GSKeyValueBinding*)[bindings objectForKey: binding];
234      if (theBinding != nil)
235        {
236          observedObject = [theBinding->info objectForKey: NSObservedObjectKey];
237          keyPath = [theBinding->info objectForKey: NSObservedKeyPathKey];
238          [observedObject removeObserver: theBinding forKeyPath: keyPath];
239          [bindings setValue: nil forKey: binding];
240        }
241    }
242  [bindingLock unlock];
243}
244
245+ (void) unbindAllForObject: (id)anObject
246{
247  NSEnumerator *enumerator;
248  NSString *binding;
249  NSDictionary *list;
250
251  if (!objectTable)
252    return;
253
254  [bindingLock lock];
255  list = (NSDictionary *)NSMapGet(objectTable, (void *)anObject);
256  if (list != nil)
257    {
258      NSArray *keys = [list allKeys];
259
260      enumerator = [keys objectEnumerator];
261      while ((binding = [enumerator nextObject]))
262        {
263          [anObject unbind: binding];
264        }
265      NSMapRemove(objectTable, (void *)anObject);
266    }
267  [bindingLock unlock];
268}
269
270- (id) initWithBinding: (NSString *)binding
271              withName: (NSString *)name
272              toObject: (id)dest
273           withKeyPath: (NSString *)keyPath
274               options: (NSDictionary *)options
275            fromObject: (id)source
276{
277  NSMutableDictionary *bindings;
278
279  src = source;
280  if (options == nil)
281    {
282      info = [[NSDictionary alloc] initWithObjectsAndKeys:
283        dest, NSObservedObjectKey,
284        keyPath, NSObservedKeyPathKey,
285        nil];
286    }
287  else
288    {
289      info = [[NSDictionary alloc] initWithObjectsAndKeys:
290        dest, NSObservedObjectKey,
291        keyPath, NSObservedKeyPathKey,
292        options, NSOptionsKey,
293        nil];
294    }
295
296  [dest addObserver: self
297        forKeyPath: keyPath
298        options: NSKeyValueObservingOptionNew
299        context: binding];
300
301  [bindingLock lock];
302  bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)source);
303  if (bindings == nil)
304    {
305      bindings = [NSMutableDictionary new];
306      NSMapInsert(objectTable, (void*)source, (void*)bindings);
307      RELEASE(bindings);
308    }
309  [bindings setObject: self forKey: name];
310  [bindingLock unlock];
311
312  [self setValueFor: binding];
313
314  return self;
315}
316
317- (void)dealloc
318{
319  DESTROY(info);
320  src = nil;
321  [super dealloc];
322}
323
324- (id) destinationValue
325{
326  id newValue;
327  id dest;
328  NSString *keyPath;
329  NSDictionary *options;
330
331  dest = [info objectForKey: NSObservedObjectKey];
332  keyPath = [info objectForKey: NSObservedKeyPathKey];
333  options = [info objectForKey: NSOptionsKey];
334  newValue = [dest valueForKeyPath: keyPath];
335  return [self transformValue: newValue withOptions: options];
336}
337
338- (id) sourceValueFor: (NSString *)binding
339{
340  id newValue;
341  NSDictionary *options;
342
343  options = [info objectForKey: NSOptionsKey];
344  newValue = [src valueForKeyPath: binding];
345  return [self reverseTransformValue: newValue withOptions: options];
346}
347
348- (void) setValueFor: (NSString *)binding
349{
350  id value = [self destinationValue];
351
352  NSDebugLLog(@"NSBinding", @"setValueFor: binding %@, source %@ value %@", binding, src, value);
353  [src setValue: value forKey: binding];
354}
355
356- (void) reverseSetValue: (id)value
357{
358  NSString *keyPath;
359  id dest;
360
361  keyPath = [info objectForKey: NSObservedKeyPathKey];
362  dest = [info objectForKey: NSObservedObjectKey];
363  NSDebugLLog(@"NSBinding", @"reverseSetValue: keyPath %@, dest %@ value %@", keyPath, dest, value);
364  [dest setValue: value forKeyPath: keyPath];
365}
366
367- (void) reverseSetValueFor: (NSString *)binding
368{
369  [self reverseSetValue: [self sourceValueFor: binding]];
370}
371
372- (void) observeValueForKeyPath: (NSString *)keyPath
373                       ofObject: (id)object
374                         change: (NSDictionary *)change
375                        context: (void *)context
376{
377  NSString *binding = (NSString *)context;
378  NSDictionary *options;
379  id newValue;
380
381  if (change != nil)
382    {
383      options = [info objectForKey: NSOptionsKey];
384      newValue = [change objectForKey: NSKeyValueChangeNewKey];
385      newValue = [self transformValue: newValue withOptions: options];
386      NSDebugLLog(@"NSBinding", @"observeValueForKeyPath: binding %@, keyPath %@, source %@ value %@", binding, keyPath, src, newValue);
387      [src setValue: newValue forKey: binding];
388    }
389}
390
391- (id) transformValue: (id)value withOptions: (NSDictionary *)options
392{
393  NSString *valueTransformerName;
394  NSValueTransformer *valueTransformer;
395  NSString *placeholder;
396
397  if (value == NSMultipleValuesMarker)
398    {
399      placeholder = [options objectForKey: NSMultipleValuesPlaceholderBindingOption];
400      if (placeholder == nil)
401        {
402          placeholder = @"Multiple Values";
403        }
404      return placeholder;
405    }
406  if (value == NSNoSelectionMarker)
407    {
408      placeholder = [options objectForKey: NSNoSelectionPlaceholderBindingOption];
409      if (placeholder == nil)
410        {
411          placeholder = @"No Selection";
412        }
413      return placeholder;
414    }
415  if (value == NSNotApplicableMarker)
416    {
417      if ([[options objectForKey: NSRaisesForNotApplicableKeysBindingOption]
418          boolValue])
419        {
420          [NSException raise: NSGenericException
421                      format: @"This binding does not accept not applicable keys"];
422        }
423
424      placeholder = [options objectForKey:
425        NSNotApplicablePlaceholderBindingOption];
426      if (placeholder == nil)
427        {
428          placeholder = @"Not Applicable";
429        }
430      return placeholder;
431    }
432  if (value == nil)
433    {
434      placeholder = [options objectForKey:
435        NSNullPlaceholderBindingOption];
436      return placeholder;
437    }
438
439  valueTransformerName = [options objectForKey:
440    NSValueTransformerNameBindingOption];
441  if (valueTransformerName != nil)
442    {
443      valueTransformer = [NSValueTransformer valueTransformerForName:
444                                                 valueTransformerName];
445    }
446  else
447    {
448      valueTransformer = [options objectForKey:
449                                      NSValueTransformerBindingOption];
450    }
451
452  if (valueTransformer != nil)
453    {
454      if ([value isKindOfClass: [NSArray class]])
455        {
456          NSArray *oldValue = (NSArray *)value;
457          NSMutableArray *newValue = [[NSMutableArray alloc] initWithCapacity: [oldValue count]];
458          id<NSFastEnumeration> enumerator = oldValue;
459
460          FOR_IN (id, obj, enumerator)
461            [newValue addObject: [valueTransformer transformedValue: obj]];
462          END_FOR_IN(enumerator)
463          value = AUTORELEASE(newValue);
464        }
465      else
466        {
467          value = [valueTransformer transformedValue: value];
468        }
469    }
470
471  return value;
472}
473
474- (id) reverseTransformValue: (id)value withOptions: (NSDictionary *)options
475{
476  NSString *valueTransformerName;
477  NSValueTransformer *valueTransformer;
478
479  valueTransformerName = [options objectForKey:
480    NSValueTransformerNameBindingOption];
481  if (valueTransformerName != nil)
482    {
483      valueTransformer = [NSValueTransformer valueTransformerForName:
484                                                 valueTransformerName];
485    }
486  else
487    {
488      valueTransformer = [options objectForKey:
489                                      NSValueTransformerBindingOption];
490    }
491
492  if ((valueTransformer != nil) && [[valueTransformer class]
493                                       allowsReverseTransformation])
494    {
495      value = [valueTransformer reverseTransformedValue: value];
496    }
497
498  return value;
499}
500
501- (NSString*) description
502{
503  return [NSString stringWithFormat:
504                     @"GSKeyValueBinding src (%@) info %@", src, info];
505}
506
507@end
508
509@implementation GSKeyValueOrBinding : GSKeyValueBinding
510
511- (void) setValueFor: (NSString *)binding
512{
513  NSDictionary *bindings;
514  BOOL res;
515
516  if (!objectTable)
517    return;
518
519 [bindingLock lock];
520  bindings = (NSDictionary *)NSMapGet(objectTable, (void *)src);
521  if (!bindings)
522    return;
523
524  res = GSBindingResolveMultipleValueBool(binding, bindings,
525                                          GSBindingOperationOr);
526  [bindingLock unlock];
527  [src setValue: [NSNumber numberWithBool: res] forKey: binding];
528}
529
530- (void) observeValueForKeyPath: (NSString *)keyPath
531                       ofObject: (id)object
532                         change: (NSDictionary *)change
533                        context: (void *)context
534{
535  [self setValueFor: (NSString*)context];
536}
537
538@end
539
540@implementation GSKeyValueAndBinding : GSKeyValueBinding
541
542- (void) setValueFor: (NSString *)binding
543{
544  NSDictionary *bindings;
545  BOOL res;
546
547  if (!objectTable)
548    return;
549
550  [bindingLock lock];
551  bindings = (NSDictionary *)NSMapGet(objectTable, (void *)src);
552  if (!bindings)
553    return;
554
555  res = GSBindingResolveMultipleValueBool(binding, bindings,
556                                          GSBindingOperationAnd);
557  [bindingLock unlock];
558  [src setValue: [NSNumber numberWithBool: res] forKey: binding];
559}
560
561- (void) observeValueForKeyPath: (NSString *)keyPath
562                       ofObject: (id)object
563                         change: (NSDictionary *)change
564                        context: (void *)context
565{
566  [self setValueFor: (NSString*)context];
567}
568
569@end
570
571BOOL NSIsControllerMarker(id object)
572{
573  return [NSMultipleValuesMarker isEqual: object]
574    || [NSNoSelectionMarker isEqual: object]
575    || [NSNotApplicableMarker isEqual: object];
576}
577
578//Helper functions
579BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings,
580    GSBindingOperationKind operationKind)
581{
582  NSString *bindingName;
583  NSDictionary *info;
584  int count = 1;
585  id object;
586  NSString *keyPath;
587  id value;
588  NSDictionary *options;
589  GSKeyValueBinding *theBinding;
590
591  bindingName = key;
592  while ((theBinding = [bindings objectForKey: bindingName]))
593    {
594      info = theBinding->info;
595      object = [info objectForKey: NSObservedObjectKey];
596      keyPath = [info objectForKey: NSObservedKeyPathKey];
597      options = [info objectForKey: NSOptionsKey];
598
599      value = [object valueForKeyPath: keyPath];
600      value = [theBinding transformValue: value withOptions: options];
601      if ([value boolValue] == operationKind)
602        {
603          return operationKind;
604        }
605      bindingName = [NSString stringWithFormat: @"%@%i", key, ++count];
606    }
607  return !operationKind;
608}
609
610void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
611    NSDictionary *bindings)
612{
613  NSString *bindingName;
614  NSDictionary *info;
615  NSDictionary *options;
616  int count = 1;
617  id object;
618  id target;
619  SEL selector;
620  NSString *keyPath;
621  NSInvocation *invocation;
622  GSKeyValueBinding *theBinding;
623
624  theBinding = [bindings objectForKey: targetKey];
625  info = theBinding->info;
626  object = [info objectForKey: NSObservedObjectKey];
627  keyPath = [info objectForKey: NSObservedKeyPathKey];
628  options = [info objectForKey: NSOptionsKey];
629
630  target = [object valueForKeyPath: keyPath];
631  selector = NSSelectorFromString([options objectForKey:
632      NSSelectorNameBindingOption]);
633  if (target == nil || selector == NULL) return;
634
635  invocation = [NSInvocation invocationWithMethodSignature:
636    [target methodSignatureForSelector: selector]];
637  [invocation setSelector: selector];
638
639  bindingName = argumentKey;
640  while ((theBinding = [bindings objectForKey: bindingName]))
641    {
642      info = theBinding->info;
643      object = [info objectForKey: NSObservedObjectKey];
644      keyPath = [info objectForKey: NSObservedKeyPathKey];
645      if ((object = [object valueForKeyPath: keyPath]))
646        {
647          [invocation setArgument: object atIndex: ++count];
648        }
649      bindingName = [NSString stringWithFormat: @"%@%i", argumentKey, count];
650    }
651  [invocation invoke];
652}
653