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 "WOScriptedComponent.h"
23#include <NGObjWeb/WOTemplateBuilder.h>
24#include <NGScripting/NGScriptLanguage.h>
25#include "common.h"
26
27@interface NSObject(misc)
28- (void)applyStandardClasses;
29- (BOOL)isScriptFunction;
30
31- (id)callScriptFunction:(NSString *)_name withObject:(id)_arg0;
32- (id)evaluateScript:(NSString *)_script source:(NSString *)_s line:(int)_line;
33
34@end
35
36@interface WOComponent(UsedPrivates)
37- (id)initWithName:(NSString *)_cname
38  template:(WOTemplate *)_template
39  inContext:(WOContext *)_ctx;
40@end
41
42@interface WOComponentScript(UsedPrivates)
43- (id)initScriptWithComponent:(id)_comp;
44@end
45
46@implementation WOScriptedComponent
47
48static BOOL logScriptKVC     = NO;
49static BOOL logScriptInit    = NO;
50static BOOL logScriptDealloc = NO;
51
52+ (void)initialize {
53  static BOOL didInit = NO;
54  if (!didInit) {
55    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
56    didInit = YES;
57    logScriptKVC     = [ud boolForKey:@"WOLogScriptKVC"];
58    logScriptInit    = [ud boolForKey:@"WOLogScriptInit"];
59    logScriptDealloc = [ud boolForKey:@"WOLogScriptDealloc"];
60  }
61}
62
63- (id)initWithName:(NSString *)_cname
64  template:(WOTemplate *)_template
65  inContext:(WOContext *)_ctx
66{
67  if ((self = [super initWithName:_cname template:_template inContext:_ctx])) {
68    self->script = [[_template componentScript] retain];
69
70    self->language =
71      [[NGScriptLanguage languageWithName:[self->script language]] retain];
72    if (self->language == nil) {
73      [self logWithFormat:
74              @"did not find engine for script language %@",
75	      [self->script language]];
76      RELEASE(self);
77      return nil;
78    }
79
80    if ((self->shadow = [self->language createShadowForMaster:self]) == nil) {
81      [self logWithFormat:
82              @"could not create shadow for component in language %@",
83	      self->language];
84      RELEASE(self);
85      return nil;
86    }
87
88    if ([self->shadow respondsToSelector:@selector(applyStandardClasses)])
89      [(id)self->shadow applyStandardClasses];
90
91    [self->script initScriptWithComponent:self];
92
93    if (logScriptInit)
94      [self logWithFormat:@"created scripted component: %@", self];
95  }
96  return self;
97}
98
99- (void)dealloc {
100  if (logScriptDealloc)
101    [self logWithFormat:@"will dealloc scripted component: %@", self];
102
103  [self->shadow invalidateShadow]; /* ensure shadow is dead ;-) */
104  RELEASE(self->shadow);
105  RELEASE(self->language);
106  RELEASE(self->script);
107  RELEASE(self->template);
108  [super dealloc];
109}
110
111/* accessors */
112
113- (void)setTemplate:(id)_template {
114  ASSIGN(self->template, _template);
115}
116- (WOElement *)_woComponentTemplate {
117  return self->template;
118}
119
120- (BOOL)isScriptedComponent {
121  return YES;
122}
123
124/* scripting */
125
126- (id)evaluateScript:(NSString *)_script language:(NSString *)_lang
127  source:(NSString *)_src line:(unsigned)_line
128{
129  return [self->shadow evaluateScript:_script source:_src line:_line];
130}
131
132/* notification mapping */
133
134- (void)awake {
135  [super awake];
136
137  if ([self->shadow hasFunctionNamed:@"awake"])
138    [self->shadow callScriptFunction:@"awake"];
139}
140- (void)sleep {
141  if ([self->shadow hasFunctionNamed:@"sleep"])
142    [self->shadow callScriptFunction:@"sleep"];
143
144  //[self debugWithFormat:@"vars: %@", [self variableDictionary]];
145  [super sleep];
146}
147
148/* script properties */
149
150- (BOOL)takeValue:(id)_value forJSPropertyNamed:(NSString *)_key {
151  NSAssert1(self->shadow, @"missing shadow for component %@", self);
152  [self->shadow setObject:_value forKey:_key];
153  return YES;
154}
155- (id)valueForJSPropertyNamed:(NSString *)_key {
156  [self debugWithFormat:@"value for prop %@", _key];
157  NSAssert1(self->shadow, @"missing shadow for component %@", self);
158  return [self->shadow objectForKey:_key];
159}
160
161/* extra variables */
162
163- (void)setObject:(id)_obj forKey:(NSString *)_key {
164  //[self debugWithFormat:@"setObject:%@ forKey:%@", _obj, _key];
165  NSAssert1(self->shadow, @"missing shadow for component %@", self);
166  [self->shadow setObject:_obj forKey:_key];
167}
168- (id)objectForKey:(NSString *)_key {
169  NSAssert1(self->shadow, @"missing shadow for component %@", self);
170  return [self->shadow objectForKey:_key];
171}
172
173/* key-value coding */
174
175- (void)takeValue:(id)_value forKey:(NSString *)_key {
176  NSString *funcName;
177  id       func;
178  unsigned len;
179  unsigned char *buf;
180
181  len = [_key cStringLength];
182  buf = malloc(len + 4);
183  [_key getCString:&(buf[3])];
184  buf[0] = 's'; buf[1] = 'e'; buf[2] = 't';
185  buf[len + 3] = '\0';
186  if (len > 0) buf[3] = toupper(buf[3]);
187  funcName = [NSString stringWithCString:buf length:(len + 3)];
188  free(buf);
189
190  if ((func = [self->shadow objectForKey:funcName])) {
191    if ([func isScriptFunction]) {
192      id result;
193
194      if (logScriptKVC) {
195	[self logWithFormat:@"KVC: for key %@ call %@(%@)",
196	        _key, funcName, _value];
197      }
198
199      result = [self->shadow callScriptFunction:funcName withObject:_value];
200    }
201    else {
202      [self logWithFormat:
203	      @"KVC: object stored at '%@' is not a function, "
204	      @"could not set value for key %@ !",
205	      funcName, _key];
206    }
207  }
208  else {
209    if (logScriptKVC) {
210      [self logWithFormat:@"KVC: assign %@=%@ (no func '%@')",
211	      _key, _value, funcName];
212    }
213
214    [self->shadow setObject:_value forKey:_key];
215  }
216}
217
218- (id)valueForKey:(NSString *)_key {
219  id obj;
220
221  //[self logWithFormat:@"script: valueForKey:%@", _key];
222
223  if ((obj = [self->shadow objectForKey:_key])) {
224    if ([obj isScriptFunction]) {
225      if (logScriptKVC) {
226	[self logWithFormat:@"KVC: for key %@ call %@",
227	        _key, _key];
228      }
229      obj = [self->shadow callScriptFunction:_key];
230    }
231    else {
232      if (logScriptKVC) {
233	[self logWithFormat:@"KVC: valueForKey(%@): %@",
234	        _key, obj];
235      }
236    }
237    return obj;
238  }
239  else {
240    if (logScriptKVC) {
241      [self logWithFormat:@"KVC: get value for key %@ from WOComponent",
242	      _key];
243    }
244    return [super valueForKey:_key];
245  }
246}
247
248/* logging */
249
250- (NSString *)loggingPrefix {
251  return [NSString stringWithFormat:@"script<%@>[0x%p]", [self name], self];
252}
253
254@end /* WOScriptedComponent */
255
256@implementation NSObject(ScriptFunc)
257
258- (BOOL)isScriptFunction {
259  return NO;
260}
261
262@end /* NSObject(ScriptFunc) */
263