blob: 83e3bbc2678326f3447518b489a25a464fb7e272 [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001#include "generate_java.h"
2#include "AST.h"
3#include "Type.h"
Alexey Zaytsev0aa7fe62008-10-21 23:52:01 +04004#include <string.h>
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07005#include <stdio.h>
The Android Open Source Projectb7986892009-01-09 17:51:23 -08006#include <stdlib.h>
7#include <string.h>
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07008
9// =================================================
10class VariableFactory
11{
12public:
13 VariableFactory(const string& base); // base must be short
14 Variable* Get(Type* type);
15 Variable* Get(int index);
16private:
17 vector<Variable*> m_vars;
18 string m_base;
19 int m_index;
20};
21
22VariableFactory::VariableFactory(const string& base)
23 :m_base(base),
24 m_index(0)
25{
26}
27
28Variable*
29VariableFactory::Get(Type* type)
30{
31 char name[100];
32 sprintf(name, "%s%d", m_base.c_str(), m_index);
33 m_index++;
34 Variable* v = new Variable(type, name);
35 m_vars.push_back(v);
36 return v;
37}
38
39Variable*
40VariableFactory::Get(int index)
41{
42 return m_vars[index];
43}
44
45// =================================================
46class StubClass : public Class
47{
48public:
49 StubClass(Type* type, Type* interfaceType);
50 virtual ~StubClass();
51
52 Variable* transact_code;
53 Variable* transact_data;
54 Variable* transact_reply;
55 Variable* transact_flags;
56 SwitchStatement* transact_switch;
57private:
58 void make_as_interface(Type* interfaceType);
59};
60
61StubClass::StubClass(Type* type, Type* interfaceType)
62 :Class()
63{
64 this->comment = "/** Local-side IPC implementation stub class. */";
65 this->modifiers = PUBLIC | ABSTRACT | STATIC;
66 this->what = Class::CLASS;
67 this->type = type;
68 this->extends = BINDER_NATIVE_TYPE;
69 this->interfaces.push_back(interfaceType);
70
71 // descriptor
72 Field* descriptor = new Field(STATIC | FINAL | PRIVATE,
73 new Variable(STRING_TYPE, "DESCRIPTOR"));
74 descriptor->value = "\"" + interfaceType->QualifiedName() + "\"";
75 this->elements.push_back(descriptor);
76
77 // ctor
78 Method* ctor = new Method;
79 ctor->modifiers = PUBLIC;
80 ctor->comment = "/** Construct the stub at attach it to the "
81 "interface. */";
82 ctor->name = "Stub";
83 ctor->statements = new StatementBlock;
84 MethodCall* attach = new MethodCall(THIS_VALUE, "attachInterface",
85 2, THIS_VALUE, new LiteralExpression("DESCRIPTOR"));
86 ctor->statements->Add(attach);
87 this->elements.push_back(ctor);
88
89 // asInterface
90 make_as_interface(interfaceType);
91
92 // asBinder
93 Method* asBinder = new Method;
94 asBinder->modifiers = PUBLIC;
95 asBinder->returnType = IBINDER_TYPE;
96 asBinder->name = "asBinder";
97 asBinder->statements = new StatementBlock;
98 asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
99 this->elements.push_back(asBinder);
100
101 // onTransact
102 this->transact_code = new Variable(INT_TYPE, "code");
103 this->transact_data = new Variable(PARCEL_TYPE, "data");
104 this->transact_reply = new Variable(PARCEL_TYPE, "reply");
105 this->transact_flags = new Variable(INT_TYPE, "flags");
106 Method* onTransact = new Method;
Xavier Ducrohet7ea9d792009-08-03 19:51:54 -0700107 onTransact->modifiers = PUBLIC | OVERRIDE;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700108 onTransact->returnType = BOOLEAN_TYPE;
109 onTransact->name = "onTransact";
110 onTransact->parameters.push_back(this->transact_code);
111 onTransact->parameters.push_back(this->transact_data);
112 onTransact->parameters.push_back(this->transact_reply);
113 onTransact->parameters.push_back(this->transact_flags);
114 onTransact->statements = new StatementBlock;
115 onTransact->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
116 this->elements.push_back(onTransact);
117 this->transact_switch = new SwitchStatement(this->transact_code);
118
119 onTransact->statements->Add(this->transact_switch);
120 MethodCall* superCall = new MethodCall(SUPER_VALUE, "onTransact", 4,
121 this->transact_code, this->transact_data,
122 this->transact_reply, this->transact_flags);
123 onTransact->statements->Add(new ReturnStatement(superCall));
124}
125
126StubClass::~StubClass()
127{
128}
129
130void
131StubClass::make_as_interface(Type *interfaceType)
132{
133 Variable* obj = new Variable(IBINDER_TYPE, "obj");
134
135 Method* m = new Method;
136 m->comment = "/**\n * Cast an IBinder object into an ";
Adrian Taylor75427062009-02-13 09:52:17 +0000137 m->comment += interfaceType->QualifiedName();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700138 m->comment += " interface,\n";
139 m->comment += " * generating a proxy if needed.\n */";
140 m->modifiers = PUBLIC | STATIC;
141 m->returnType = interfaceType;
142 m->name = "asInterface";
143 m->parameters.push_back(obj);
144 m->statements = new StatementBlock;
145
146 IfStatement* ifstatement = new IfStatement();
147 ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
148 ifstatement->statements = new StatementBlock;
149 ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
150 m->statements->Add(ifstatement);
151
152 // IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
153 MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
154 queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
155 IInterfaceType* iinType = new IInterfaceType();
156 Variable *iin = new Variable(iinType, "iin");
157 VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface, iinType);
158 m->statements->Add(iinVd);
159
160 // Ensure the instance type of the local object is as expected.
161 // One scenario where this is needed is if another package (with a
162 // different class loader) runs in the same process as the service.
163
164 // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>) iin;
165 Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
166 Comparison* instOfCheck = new Comparison(iin, " instanceof ",
167 new LiteralExpression(interfaceType->QualifiedName()));
168 IfStatement* instOfStatement = new IfStatement();
169 instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
170 instOfStatement->statements = new StatementBlock;
171 instOfStatement->statements->Add(new ReturnStatement(new Cast(interfaceType, iin)));
172 m->statements->Add(instOfStatement);
173
174 string proxyType = interfaceType->QualifiedName();
175 proxyType += ".Stub.Proxy";
176 NewExpression* ne = new NewExpression(NAMES.Find(proxyType));
177 ne->arguments.push_back(obj);
178 m->statements->Add(new ReturnStatement(ne));
179
180 this->elements.push_back(m);
181}
182
183
184
185// =================================================
186class ProxyClass : public Class
187{
188public:
189 ProxyClass(Type* type, InterfaceType* interfaceType);
190 virtual ~ProxyClass();
191
192 Variable* mRemote;
193 bool mOneWay;
194};
195
196ProxyClass::ProxyClass(Type* type, InterfaceType* interfaceType)
197 :Class()
198{
199 this->modifiers = PRIVATE | STATIC;
200 this->what = Class::CLASS;
201 this->type = type;
202 this->interfaces.push_back(interfaceType);
203
204 mOneWay = interfaceType->OneWay();
205
206 // IBinder mRemote
207 mRemote = new Variable(IBINDER_TYPE, "mRemote");
208 this->elements.push_back(new Field(PRIVATE, mRemote));
209
210 // Proxy()
211 Variable* remote = new Variable(IBINDER_TYPE, "remote");
212 Method* ctor = new Method;
213 ctor->name = "Proxy";
214 ctor->statements = new StatementBlock;
215 ctor->parameters.push_back(remote);
216 ctor->statements->Add(new Assignment(mRemote, remote));
217 this->elements.push_back(ctor);
218
219 // IBinder asBinder()
220 Method* asBinder = new Method;
221 asBinder->modifiers = PUBLIC;
222 asBinder->returnType = IBINDER_TYPE;
223 asBinder->name = "asBinder";
224 asBinder->statements = new StatementBlock;
225 asBinder->statements->Add(new ReturnStatement(mRemote));
226 this->elements.push_back(asBinder);
227}
228
229ProxyClass::~ProxyClass()
230{
231}
232
233// =================================================
234static string
235gather_comments(extra_text_type* extra)
236{
237 string s;
238 while (extra) {
239 if (extra->which == SHORT_COMMENT) {
240 s += extra->data;
241 }
242 else if (extra->which == LONG_COMMENT) {
243 s += "/*";
244 s += extra->data;
245 s += "*/";
246 }
247 extra = extra->next;
248 }
249 return s;
250}
251
252static string
253append(const char* a, const char* b)
254{
255 string s = a;
256 s += b;
257 return s;
258}
259
260static void
261generate_new_array(Type* t, StatementBlock* addTo, Variable* v,
262 Variable* parcel)
263{
264 Variable* len = new Variable(INT_TYPE, v->name + "_length");
265 addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
266 IfStatement* lencheck = new IfStatement();
267 lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
268 lencheck->statements->Add(new Assignment(v, NULL_VALUE));
269 lencheck->elseif = new IfStatement();
270 lencheck->elseif->statements->Add(new Assignment(v,
271 new NewArrayExpression(t, len)));
272 addTo->Add(lencheck);
273}
274
275static void
276generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v,
277 Variable* parcel, int flags)
278{
279 if (v->dimension == 0) {
280 t->WriteToParcel(addTo, v, parcel, flags);
281 }
282 if (v->dimension == 1) {
283 t->WriteArrayToParcel(addTo, v, parcel, flags);
284 }
285}
286
287static void
288generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
Elliott Hughesb9eba862011-07-13 12:10:30 -0700289 Variable* parcel, Variable** cl)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700290{
291 if (v->dimension == 0) {
Elliott Hughesb9eba862011-07-13 12:10:30 -0700292 t->CreateFromParcel(addTo, v, parcel, cl);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700293 }
294 if (v->dimension == 1) {
Elliott Hughesb9eba862011-07-13 12:10:30 -0700295 t->CreateArrayFromParcel(addTo, v, parcel, cl);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700296 }
297}
298
299static void
300generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
Elliott Hughesb9eba862011-07-13 12:10:30 -0700301 Variable* parcel, Variable** cl)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700302{
303 if (v->dimension == 0) {
Elliott Hughesb9eba862011-07-13 12:10:30 -0700304 t->ReadFromParcel(addTo, v, parcel, cl);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700305 }
306 if (v->dimension == 1) {
Elliott Hughesb9eba862011-07-13 12:10:30 -0700307 t->ReadArrayFromParcel(addTo, v, parcel, cl);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700308 }
309}
310
311
312static void
313generate_method(const method_type* method, Class* interface,
314 StubClass* stubClass, ProxyClass* proxyClass, int index)
315{
316 arg_type* arg;
317 int i;
318 bool hasOutParams = false;
319
320 const bool oneway = proxyClass->mOneWay || method->oneway;
321
322 // == the TRANSACT_ constant =============================================
323 string transactCodeName = "TRANSACTION_";
324 transactCodeName += method->name.data;
325
326 char transactCodeValue[50];
Adrian Taylor75427062009-02-13 09:52:17 +0000327 sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700328
329 Field* transactCode = new Field(STATIC | FINAL,
330 new Variable(INT_TYPE, transactCodeName));
331 transactCode->value = transactCodeValue;
332 stubClass->elements.push_back(transactCode);
333
334 // == the declaration in the interface ===================================
335 Method* decl = new Method;
336 decl->comment = gather_comments(method->comments_token->extra);
337 decl->modifiers = PUBLIC;
338 decl->returnType = NAMES.Search(method->type.type.data);
339 decl->returnTypeDimension = method->type.dimension;
340 decl->name = method->name.data;
341
342 arg = method->args;
343 while (arg != NULL) {
344 decl->parameters.push_back(new Variable(
345 NAMES.Search(arg->type.type.data), arg->name.data,
346 arg->type.dimension));
347 arg = arg->next;
348 }
349
350 decl->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
351
352 interface->elements.push_back(decl);
353
354 // == the stub method ====================================================
355
356 Case* c = new Case(transactCodeName);
357
358 MethodCall* realCall = new MethodCall(THIS_VALUE, method->name.data);
359
360 // interface token validation is the very first thing we do
361 c->statements->Add(new MethodCall(stubClass->transact_data,
362 "enforceInterface", 1, new LiteralExpression("DESCRIPTOR")));
363
364 // args
Elliott Hughesb9eba862011-07-13 12:10:30 -0700365 Variable* cl = NULL;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700366 VariableFactory stubArgs("_arg");
367 arg = method->args;
368 while (arg != NULL) {
369 Type* t = NAMES.Search(arg->type.type.data);
370 Variable* v = stubArgs.Get(t);
371 v->dimension = arg->type.dimension;
372
373 c->statements->Add(new VariableDeclaration(v));
374
375 if (convert_direction(arg->direction.data) & IN_PARAMETER) {
376 generate_create_from_parcel(t, c->statements, v,
Elliott Hughesb9eba862011-07-13 12:10:30 -0700377 stubClass->transact_data, &cl);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700378 } else {
379 if (arg->type.dimension == 0) {
380 c->statements->Add(new Assignment(
381 v, new NewExpression(v->type)));
382 }
383 else if (arg->type.dimension == 1) {
384 generate_new_array(v->type, c->statements, v,
385 stubClass->transact_data);
386 }
387 else {
388 fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
389 __LINE__);
390 }
391 }
392
393 realCall->arguments.push_back(v);
394
395 arg = arg->next;
396 }
397
398 // the real call
399 Variable* _result = NULL;
400 if (0 == strcmp(method->type.type.data, "void")) {
401 c->statements->Add(realCall);
402
403 if (!oneway) {
404 // report that there were no exceptions
405 MethodCall* ex = new MethodCall(stubClass->transact_reply,
406 "writeNoException", 0);
407 c->statements->Add(ex);
408 }
409 } else {
410 _result = new Variable(decl->returnType, "_result",
411 decl->returnTypeDimension);
412 c->statements->Add(new VariableDeclaration(_result, realCall));
413
414 if (!oneway) {
415 // report that there were no exceptions
416 MethodCall* ex = new MethodCall(stubClass->transact_reply,
417 "writeNoException", 0);
418 c->statements->Add(ex);
419 }
420
421 // marshall the return value
422 generate_write_to_parcel(decl->returnType, c->statements, _result,
423 stubClass->transact_reply,
424 Type::PARCELABLE_WRITE_RETURN_VALUE);
425 }
426
427 // out parameters
428 i = 0;
429 arg = method->args;
430 while (arg != NULL) {
431 Type* t = NAMES.Search(arg->type.type.data);
432 Variable* v = stubArgs.Get(i++);
433
434 if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
435 generate_write_to_parcel(t, c->statements, v,
436 stubClass->transact_reply,
437 Type::PARCELABLE_WRITE_RETURN_VALUE);
438 hasOutParams = true;
439 }
440
441 arg = arg->next;
442 }
443
444 // return true
445 c->statements->Add(new ReturnStatement(TRUE_VALUE));
446 stubClass->transact_switch->cases.push_back(c);
447
448 // == the proxy method ===================================================
449 Method* proxy = new Method;
450 proxy->comment = gather_comments(method->comments_token->extra);
451 proxy->modifiers = PUBLIC;
452 proxy->returnType = NAMES.Search(method->type.type.data);
453 proxy->returnTypeDimension = method->type.dimension;
454 proxy->name = method->name.data;
455 proxy->statements = new StatementBlock;
456 arg = method->args;
457 while (arg != NULL) {
458 proxy->parameters.push_back(new Variable(
459 NAMES.Search(arg->type.type.data), arg->name.data,
460 arg->type.dimension));
461 arg = arg->next;
462 }
463 proxy->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
464 proxyClass->elements.push_back(proxy);
465
466 // the parcels
467 Variable* _data = new Variable(PARCEL_TYPE, "_data");
468 proxy->statements->Add(new VariableDeclaration(_data,
469 new MethodCall(PARCEL_TYPE, "obtain")));
470 Variable* _reply = NULL;
471 if (!oneway) {
472 _reply = new Variable(PARCEL_TYPE, "_reply");
473 proxy->statements->Add(new VariableDeclaration(_reply,
474 new MethodCall(PARCEL_TYPE, "obtain")));
475 }
476
477 // the return value
478 _result = NULL;
479 if (0 != strcmp(method->type.type.data, "void")) {
480 _result = new Variable(proxy->returnType, "_result",
481 method->type.dimension);
482 proxy->statements->Add(new VariableDeclaration(_result));
483 }
484
485 // try and finally
486 TryStatement* tryStatement = new TryStatement();
487 proxy->statements->Add(tryStatement);
488 FinallyStatement* finallyStatement = new FinallyStatement();
489 proxy->statements->Add(finallyStatement);
490
491 // the interface identifier token: the DESCRIPTOR constant, marshalled as a string
492 tryStatement->statements->Add(new MethodCall(_data, "writeInterfaceToken",
493 1, new LiteralExpression("DESCRIPTOR")));
494
495 // the parameters
496 arg = method->args;
497 while (arg != NULL) {
498 Type* t = NAMES.Search(arg->type.type.data);
499 Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
500 int dir = convert_direction(arg->direction.data);
501 if (dir == OUT_PARAMETER && arg->type.dimension != 0) {
502 IfStatement* checklen = new IfStatement();
503 checklen->expression = new Comparison(v, "==", NULL_VALUE);
504 checklen->statements->Add(new MethodCall(_data, "writeInt", 1,
505 new LiteralExpression("-1")));
506 checklen->elseif = new IfStatement();
507 checklen->elseif->statements->Add(new MethodCall(_data, "writeInt",
508 1, new FieldVariable(v, "length")));
509 tryStatement->statements->Add(checklen);
510 }
511 else if (dir & IN_PARAMETER) {
512 generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
513 }
514 arg = arg->next;
515 }
516
517 // the transact call
518 MethodCall* call = new MethodCall(proxyClass->mRemote, "transact", 4,
519 new LiteralExpression("Stub." + transactCodeName),
520 _data, _reply ? _reply : NULL_VALUE,
521 new LiteralExpression(
Adrian Taylor75427062009-02-13 09:52:17 +0000522 oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700523 tryStatement->statements->Add(call);
524
525 // throw back exceptions.
526 if (_reply) {
527 MethodCall* ex = new MethodCall(_reply, "readException", 0);
528 tryStatement->statements->Add(ex);
529 }
530
531 // returning and cleanup
532 if (_reply != NULL) {
533 if (_result != NULL) {
534 generate_create_from_parcel(proxy->returnType,
Elliott Hughesb9eba862011-07-13 12:10:30 -0700535 tryStatement->statements, _result, _reply, &cl);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700536 }
537
538 // the out/inout parameters
539 arg = method->args;
540 while (arg != NULL) {
541 Type* t = NAMES.Search(arg->type.type.data);
542 Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
543 if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
544 generate_read_from_parcel(t, tryStatement->statements,
Elliott Hughesb9eba862011-07-13 12:10:30 -0700545 v, _reply, &cl);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700546 }
547 arg = arg->next;
548 }
549
550 finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
551 }
552 finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
553
554 if (_result != NULL) {
555 proxy->statements->Add(new ReturnStatement(_result));
556 }
557}
558
559static void
560generate_interface_descriptors(StubClass* stub, ProxyClass* proxy)
561{
562 // the interface descriptor transaction handler
563 Case* c = new Case("INTERFACE_TRANSACTION");
564 c->statements->Add(new MethodCall(stub->transact_reply, "writeString",
565 1, new LiteralExpression("DESCRIPTOR")));
566 c->statements->Add(new ReturnStatement(TRUE_VALUE));
567 stub->transact_switch->cases.push_back(c);
568
569 // and the proxy-side method returning the descriptor directly
570 Method* getDesc = new Method;
571 getDesc->modifiers = PUBLIC;
572 getDesc->returnType = STRING_TYPE;
573 getDesc->returnTypeDimension = 0;
574 getDesc->name = "getInterfaceDescriptor";
575 getDesc->statements = new StatementBlock;
576 getDesc->statements->Add(new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
577 proxy->elements.push_back(getDesc);
578}
579
580static Class*
581generate_interface_class(const interface_type* iface)
582{
583 InterfaceType* interfaceType = static_cast<InterfaceType*>(
584 NAMES.Find(iface->package, iface->name.data));
585
586 // the interface class
587 Class* interface = new Class;
588 interface->comment = gather_comments(iface->comments_token->extra);
589 interface->modifiers = PUBLIC;
590 interface->what = Class::INTERFACE;
591 interface->type = interfaceType;
592 interface->interfaces.push_back(IINTERFACE_TYPE);
593
594 // the stub inner class
595 StubClass* stub = new StubClass(
596 NAMES.Find(iface->package, append(iface->name.data, ".Stub").c_str()),
597 interfaceType);
598 interface->elements.push_back(stub);
599
600 // the proxy inner class
601 ProxyClass* proxy = new ProxyClass(
602 NAMES.Find(iface->package,
603 append(iface->name.data, ".Stub.Proxy").c_str()),
604 interfaceType);
605 stub->elements.push_back(proxy);
606
607 // stub and proxy support for getInterfaceDescriptor()
608 generate_interface_descriptors(stub, proxy);
609
610 // all the declared methods of the interface
611 int index = 0;
612 interface_item_type* item = iface->interface_items;
613 while (item != NULL) {
614 if (item->item_type == METHOD_TYPE) {
615 generate_method((method_type*)item, interface, stub, proxy, index);
616 }
617 item = item->next;
618 index++;
619 }
620
621 return interface;
622}
623
624int
625generate_java(const string& filename, const string& originalSrc,
626 interface_type* iface)
627{
628 Document* document = new Document;
629 document->comment = "";
630 if (iface->package) document->package = iface->package;
631 document->originalSrc = originalSrc;
632 document->classes.push_back(generate_interface_class(iface));
633
634// printf("outputting... filename=%s\n", filename.c_str());
635 FILE* to;
636 if (filename == "-") {
637 to = stdout;
638 } else {
639 /* open file in binary mode to ensure that the tool produces the
640 * same output on all platforms !!
641 */
642 to = fopen(filename.c_str(), "wb");
643 if (to == NULL) {
644 fprintf(stderr, "unable to open %s for write\n", filename.c_str());
645 return 1;
646 }
647 }
648
649 document->Write(to);
650
651 fclose(to);
652 return 0;
653}
654