1 /*
2  * This file is part of gir-to-d.
3  *
4  * gir-to-d is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation, either version 3
7  * of the License, or (at your option) any later version.
8  *
9  * gir-to-d is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with gir-to-d.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 module gtd.GirStruct;
19 
20 import std.algorithm: among, sort, uniq, startsWith, endsWith, canFind;
21 import std.array : replace;
22 import std.conv;
23 import std.file : write;
24 import std.path: buildNormalizedPath;
25 import std.uni: toUpper, toLower;
26 import std.range;
27 import std.string: capitalize, splitLines, strip, chomp;
28 
29 import gtd.GirConstant;
30 import gtd.GirField;
31 import gtd.GirFunction;
32 import gtd.GirPackage;
33 import gtd.GirType;
34 import gtd.GirVersion;
35 import gtd.GirWrapper;
36 import gtd.IndentedStringBuilder;
37 import gtd.LinkedHasMap: Map = LinkedHashMap;
38 import gtd.Log;
39 import gtd.XMLReader;
40 
41 enum GirStructType : string
42 {
43 	Class = "class",
44 	Interface = "interface",
45 	Record = "record",
46 	Union = "union"
47 }
48 
49 final class GirStruct
50 {
51 	string name;
52 	GirStructType type;
53 	string doc;
54 	string cType;
55 	string parent;
56 	string libVersion;
57 
58 	bool lookupClass = false;
59 	bool lookupInterface = false;
60 	bool lookupParent = false;  /// is the parent set with the lookup file.
61 	bool noCode = false;        /// Only generate the C declarations.
62 	bool noDeclaration = false; /// Don't generate a Declaration of the C struct.
63 	bool noExternal = false;    /// Don't generate a Declaration of the C struct. And don't generate the C function declarations.
64 	bool noNamespace = false;   /// Generate the functions as global functions.
65 	string[string] structWrap;
66 	string[string] aliases;
67 	string[] lookupCode;
68 	string[] lookupInterfaceCode;
69 
70 	string[] implements;
71 	string[] imports;
72 	GirField[] fields;
73 	string[] virtualFunctions;
74 	Map!(string, GirFunction) functions;
75 	bool disguised = false;
76 
77 	GirWrapper wrapper;
78 	GirPackage pack;
79 
80 	private GirStruct parentStruct;
81 
82 	this(GirWrapper wrapper, GirPackage pack)
83 	{
84 		this.wrapper = wrapper;
85 		this.pack = pack;
86 	}
87 
88 	GirStruct dup()
89 	{
90 		GirStruct copy = new GirStruct(wrapper, pack);
91 
92 		foreach ( i, field; this.tupleof )
93 			copy.tupleof[i] = field;
94 
95 		return copy;
96 	}
97 
98 	pure string getFullyQualifiedDName() {
99 		return "D"~ parentStruct.pack.name.capitalize() ~ parentStruct.name;
100 	}
101 
102 	void parse(T)(XMLReader!T reader)
103 	{
104 		name = reader.front.attributes["name"];
105 		type = cast(GirStructType)reader.front.value;
106 
107 		if ( "c:type" in reader.front.attributes )
108 			cType = reader.front.attributes["c:type"];
109 		else if ( "glib:type-name" in reader.front.attributes )
110 			cType = reader.front.attributes["glib:type-name"];
111 
112 		if ( "parent" in reader.front.attributes )
113 			parent = reader.front.attributes["parent"];
114 		if ( "version" in reader.front.attributes )
115 		{
116 			libVersion = reader.front.attributes["version"];
117 			pack.checkVersion(libVersion);
118 		}
119 
120 		if ( !parent.empty )
121 		{
122 			if ( parent == "GObject.InitiallyUnowned" )
123 				parent = "GObject.Object";
124 			else if ( parent == "InitiallyUnowned" )
125 				parent = "Object";
126 		}
127 
128 		if ( pack && pack.name != "glib" && "glib:get-type" in reader.front.attributes && reader.front.attributes["glib:get-type"].endsWith("_get_type") )
129 			functions["get_type"] = getTypeFunction(reader.front.attributes["glib:get-type"]);
130 		if ( auto disg = "disguised" in reader.front.attributes )
131 			disguised = *disg == "1";
132 
133 		if ( reader.front.type == XMLNodeType.EmptyTag )
134 			return;
135 
136 		reader.popFront();
137 
138 		while( !reader.empty && !reader.endTag("class", "interface", "record", "union") )
139 		{
140 			switch(reader.front.value)
141 			{
142 				case "attribute":
143 					//TODO: Do we need these attibutes?
144 					//dbus.name ccode.ordering deprecated replacement.
145 					reader.skipTag();
146 					break;
147 				case "constant":
148 					GirConstant constant = new GirConstant(wrapper, pack);
149 					constant.parse(reader);
150 					pack.collectedConstants[constant.name] = constant;
151 					constant.name = name.toUpper() ~"_"~ constant.name;
152 					break;
153 				case "doc":
154 					reader.popFront();
155 					doc ~= reader.front.value;
156 					reader.popFront();
157 					break;
158 				case "doc-deprecated":
159 					reader.popFront();
160 					doc ~= "\n\nDeprecated: "~ reader.front.value;
161 					reader.popFront();
162 					break;
163 				case "field":
164 					GirField field = new GirField(wrapper, this);
165 					field.parse(reader);
166 					fields ~= field;
167 					break;
168 				case "record":
169 					GirField field = new GirField(wrapper, this);
170 					GirStruct strct = new GirStruct(wrapper, null);
171 					strct.parse(reader);
172 					strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
173 					field.gtkStruct = strct;
174 					fields ~= field;
175 					break;
176 				case "union":
177 					GirField field = new GirField(wrapper, this);
178 					GirUnion uni = new GirUnion(wrapper);
179 					uni.parse(reader);
180 					field.gtkUnion = uni;
181 					fields ~= field;
182 					break;
183 				case "callback":
184 					GirFunction callback = new GirFunction(wrapper, null);
185 					callback.parse(reader);
186 					pack.collectedCallbacks[callback.name] = callback;
187 					callback.name = name.toUpper() ~ callback.name;
188 					break;
189 				case "constructor":
190 				case "method":
191 				case "glib:signal":
192 					if ( type == GirStructType.Record )
193 						type = GirStructType.Class;
194 					goto case "function";
195 				case "function":
196 					GirFunction func = new GirFunction(wrapper, this);
197 					func.parse(reader);
198 					if ( func.type == GirFunctionType.Signal )
199 						functions[func.name~"-signal"] = func;
200 					else
201 						functions[func.name] = func;
202 					break;
203 				case "virtual-method":
204 					// Virtual methods in the gir file are mirrored
205 					// as regular methods, so we only collect whitch are virtual;
206 					virtualFunctions ~= reader.front.attributes["name"];
207 					reader.skipTag();
208 					break;
209 				case "implements":
210 					implements ~= reader.front.attributes["name"];
211 					break;
212 				case "prerequisite": // Determines whitch base class the implementor of an interface must implement.
213 				case "property":
214 				case "source-position":
215 					reader.skipTag();
216 					break;
217 				default:
218 					warning("Unexpected tag: ", reader.front.value, " in GirStruct: ", name, reader);
219 			}
220 
221 			reader.popFront();
222 		}
223 
224 		foreach( func; virtualFunctions )
225 		{
226 			if ( auto vFunc = func in functions )
227 				vFunc.virtual = true;
228 		}
229 
230 		if ( type == GirStructType.Union )
231 		{
232 			GirField field = new GirField(wrapper, this);
233 			GirUnion uni = new GirUnion(wrapper);
234 			uni.fields = fields;
235 			field.gtkUnion = uni;
236 			fields = [field];
237 
238 			//special case for "_Value__data__union"
239 			if ( cType.empty )
240 				cType = name;
241 
242 			type = GirStructType.Record;
243 
244 			foreach ( funct; functions )
245 			{
246 				if ( funct.type != GirFunctionType.Function )
247 					type = GirStructType.Class;
248 			}
249 		}
250 	}
251 
252 	GirStruct getParent()
253 	{
254 		if ( !parentStruct )
255 			parentStruct = pack.getStruct(parent);
256 
257 		return parentStruct;
258 	}
259 
260 	string[] getStructDeclaration()
261 	{
262 		if ( noExternal || cType.empty )
263 			return null;
264 
265 		string[] buff;
266 
267 		if ( doc !is null && wrapper.includeComments && type == GirStructType.Record )
268 		{
269 			buff ~= "/**";
270 			foreach ( line; doc.splitLines() )
271 				buff ~= " * "~ line.strip();
272 
273 			if ( libVersion )
274 			{
275 				buff ~= " *";
276 				buff ~= " * Since: "~ libVersion;
277 			}
278 
279 			buff ~= " */";
280 		}
281 
282 		if ( !fields.empty )
283 		{
284 			buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses, false);
285 			buff ~= "{";
286 			buff ~= GirField.getFieldDeclarations(fields, wrapper);
287 			buff ~= "}";
288 		}
289 		else
290 		{
291 			buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses, false) ~";";
292 		}
293 
294 		return buff;
295 	}
296 
297 	void writeClass()
298 	{
299 		if ( noCode )
300 			return;
301 
302 		if ( type == GirStructType.Record && !(lookupClass || lookupInterface) && (functions.empty && lookupCode.empty ) )
303 			return;
304 
305 		parentStruct = pack.getStruct(parent);
306 		resolveImports();
307 
308 		if ( type == GirStructType.Record && !(lookupClass || lookupInterface) && !("get_type" in functions && isSimpleStruct()) )
309 		{
310 			writeDStruct();
311 			return;
312 		}
313 
314 		if ( isInterface() )
315 			writeInterface();
316 
317 		string buff = wrapper.licence;
318 		auto indenter = new IndentedStringBuilder();
319 
320 		if ( isInterface() )
321 			buff ~= "module "~ pack.name ~"."~ name ~"T;\n\n";
322 		else
323 			buff ~= "module "~ pack.name ~"."~ name ~";\n\n";
324 
325 		writeImports(buff, isInterface() );
326 		writeDocs(buff);
327 
328 		if ( isInterface() )
329 			buff ~= "public template "~ name ~"T(TStruct)";
330 		else if ( isSimpleStruct() )
331 			buff ~= "public final class "~ name;
332 		else
333 			buff ~= "public class "~ name;
334 
335 		if ( lookupParent && !parentStruct )
336 			buff ~= " : "~ parent;
337 		else if ( parentStruct && parentStruct.name != name )
338 			buff ~= " : "~ parentStruct.name;
339 		else if ( parentStruct )
340 			buff ~= " : "~ getFullyQualifiedDName();
341 
342 		bool first = !parentStruct;
343 
344 		foreach ( interf; implements )
345 		{
346 			if ( parentStruct && parentStruct.implements.canFind(interf) )
347 				continue;
348 
349 			// If the parentStruct is in an different package compare without package name.
350 			if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) )
351 				continue;
352 
353 			GirStruct strct = pack.getStruct(interf);
354 
355 			if ( strct && first )
356 			{
357 				buff ~= " :";
358 				first = false;
359 			}
360 			else if ( strct )
361 				buff ~= ",";
362 
363 			if ( strct )
364 				buff ~= " "~ strct.name ~"IF";
365 		}
366 
367 		buff ~= "\n";
368 		buff ~= indenter.format("{");
369 
370 		if ( !cType.empty )
371 		{
372 			if ( !isInterface() )
373 			{
374 				buff ~= indenter.format("/** the main Gtk struct */");
375 				buff ~= indenter.format("protected "~ cType ~"* "~ getHandleVar() ~";");
376 
377 				if ( !parentStruct )
378 				{
379 					buff ~= indenter.format("protected bool ownedRef;");
380 				}
381 																																												
382 				buff ~= "\n";
383 			}
384 			buff ~= indenter.format("/** Get the main Gtk struct */");
385 			buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"(bool transferOwnership = false)");
386 			buff ~= indenter.format("{");
387 			buff ~= indenter.format("if (transferOwnership)");
388 			buff ~= indenter.format("ownedRef = false;");
389 
390 			if ( isInterface() )
391 				buff ~= indenter.format("return cast("~ cType ~"*)getStruct();");
392 			else
393 				buff ~= indenter.format("return "~ getHandleVar ~";");
394 
395 			buff ~= indenter.format("}");
396 			buff ~= "\n";
397 
398 			if ( !isInterface() )
399 			{
400 				buff ~= indenter.format("/** the main Gtk struct as a void* */");
401 
402 				if ( parentStruct )
403 					buff ~= indenter.format("protected override void* getStruct()");
404 				else
405 					buff ~= indenter.format("protected void* getStruct()");
406 
407 				buff ~= indenter.format("{");
408 				buff ~= indenter.format("return cast(void*)"~ getHandleVar ~";");
409 				buff ~= indenter.format("}");
410 				buff ~= "\n";
411 			}
412 
413 			if ( !isInterface() && !hasDefaultConstructor() )
414 			{
415 				buff ~= indenter.format("/**");
416 				buff ~= indenter.format(" * Sets our main struct and passes it to the parent class.");
417 				buff ~= indenter.format(" */");
418 																																												
419 				buff ~= indenter.format("public this ("~ cType ~"* "~ getHandleVar() ~", bool ownedRef = false)");
420 				buff ~= indenter.format("{");
421 				buff ~= indenter.format("this."~ getHandleVar() ~" = "~ getHandleVar() ~";");
422 
423 				if ( parentStruct )
424 					buff ~= indenter.format("super(cast("~ parentStruct.cType ~"*)"~ getHandleVar() ~", ownedRef);");
425 				else
426 					buff ~= indenter.format("this.ownedRef = ownedRef;");
427 
428 				buff ~= indenter.format("}");
429 				buff ~= "\n";
430 
431 				if ( shouldFree() )
432 				{
433 					buff ~= indenter.format("~this ()");
434 					buff ~= indenter.format("{");
435 
436 					if ( wrapper.useRuntimeLinker )
437 						buff ~= indenter.format("if ( Linker.isLoaded(LIBRARY_"~ pack.name.replace(".","").toUpper() ~") && ownedRef )");
438 					else
439 						buff ~= indenter.format("if ( ownedRef )");
440 
441 					if ( "unref" in functions )
442 						buff ~= indenter.format(functions["unref"].cType ~"("~ getHandleVar ~");");
443 					else
444 						buff ~= indenter.format(functions["free"].cType ~"("~ getHandleVar ~");");
445 
446 					buff ~= indenter.format("}");
447 					buff ~= "\n";
448 				}
449 				else if ( isSimpleStruct() )
450 				{
451 					buff ~= indenter.format("~this ()");
452 					buff ~= indenter.format("{");
453 
454 					if ( wrapper.useRuntimeLinker )
455 						buff ~= indenter.format("if ( Linker.isLoaded(LIBRARY_"~ pack.name.replace(".","").toUpper() ~") && ownedRef )");
456 					else
457 						buff ~= indenter.format("if ( ownedRef )");
458 
459 					buff ~= indenter.format("sliceFree("~ getHandleVar ~");");
460 					buff ~= indenter.format("}");
461 					buff ~= "\n";
462 				}
463 			}
464 
465 			foreach ( interf; implements )
466 			{
467 				if ( parentStruct && parentStruct.implements.canFind(interf) )
468 					continue;
469 
470 				if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) )
471 					continue;
472 
473 				GirStruct strct = pack.getStruct(interf);
474 
475 				if ( strct )
476 				{
477 					buff ~= indenter.format("// add the "~ strct.name ~" capabilities");
478 					buff ~= indenter.format("mixin "~ strct.name ~"T!("~ cType.chomp("*") ~");");
479 					buff ~= "\n";
480 				}
481 			}
482 
483 		}
484 
485 		if ( !lookupCode.empty )
486 		{
487 			buff ~= indenter.format(lookupCode);
488 			buff ~= "\n";
489 
490 			buff ~= indenter.format(["/**", "*/"]);
491 		}
492 
493 		if ( isSimpleStruct() )
494 		{
495 			foreach( field; fields )
496 			{
497 				if ( field.name.startsWith("dummy") )
498 					continue;
499 
500 				buff ~= "\n";
501 				buff ~= indenter.format(field.getProperty());
502 			}
503 		}
504 
505 		foreach ( func; functions )
506 		{
507 			if ( func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback )
508 				continue;
509 
510 			if ( isInterface() && func.type == GirFunctionType.Constructor )
511 				continue;
512 
513 			if ( isInterface() && func.isStatic() )
514 				continue;
515 
516 			if ( func.type == GirFunctionType.Signal )
517 			{
518 				buff ~= "\n";
519 				buff ~= indenter.format(func.getAddListenerDeclaration());
520 				buff ~= indenter.format(func.getAddListenerBody());
521 
522 				foreach ( param; func.params )
523 				{
524 					if ( param.type.name.startsWith("Gdk.Event") && param.type.name != "Gdk.Event" )
525 					{
526 						buff ~= "\n";
527 						buff ~= indenter.format(getGenericEventSignal(func));
528 
529 						break;
530 					}
531 				}
532 			}
533 			else
534 			{
535 				buff ~= "\n";
536 
537 				if ( func.name.among("delete", "export", "foreach", "union") )
538 					buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
539 				else if ( func.name == "ref" )
540 					buff ~= indenter.format("alias doref = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
541 
542 				buff ~= indenter.format(func.getDeclaration());
543 				buff ~= indenter.format("{");
544 				buff ~= indenter.format(func.getBody());
545 				buff ~= indenter.format("}");
546 			}
547 		}
548 
549 		buff ~= indenter.format("}");
550 
551 		if ( isInterface() )
552 			wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~"T.d"), buff);
553 		else
554 			wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~".d"), buff);
555 	}
556 
557 	void writeInterface()
558 	{
559 		string buff = wrapper.licence;
560 		auto indenter = new IndentedStringBuilder();
561 
562 		buff ~= "module "~ pack.name ~"."~ name ~"IF;\n\n";
563 
564 		writeImports(buff);
565 		writeDocs(buff);
566 
567 		buff ~= "public interface "~ name ~"IF";
568 		buff ~= indenter.format("{");
569 
570 		if ( cType )
571 		{
572 			buff ~= indenter.format("/** Get the main Gtk struct */");
573 			buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"(bool transferOwnership = false);");
574 			buff ~= "\n";
575 
576 			buff ~= indenter.format("/** the main Gtk struct as a void* */");
577 			buff ~= indenter.format("protected void* getStruct();");
578 			buff ~= "\n";
579 
580 			if ( !lookupInterfaceCode.empty )
581 			{
582 				buff ~= indenter.format(lookupInterfaceCode);
583 				buff ~= "\n";
584 
585 				buff ~= indenter.format(["/**", "*/"]);
586 			}
587 
588 			foreach ( func; functions )
589 			{
590 				if ( func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback || func.type == GirFunctionType.Constructor )
591 					continue;
592 
593 				if ( func.type == GirFunctionType.Signal )
594 				{
595 					string[] dec = func.getAddListenerDeclaration();
596 					dec[$-1] ~= ";";
597 
598 					buff ~= "\n";
599 					buff ~= indenter.format(dec);
600 				}
601 				else if ( !func.isStatic() )
602 				{
603 					string[] dec = func.getDeclaration();
604 					dec[$-1] = dec[$-1].replace("override ", "");
605 					dec[$-1] ~= ";";
606 
607 					buff ~= "\n";
608 
609 					if ( func.name.among("delete", "export", "foreach", "union") )
610 						buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
611 
612 					buff ~= indenter.format(dec);
613 				}
614 				else
615 				{
616 					buff ~= "\n";
617 					buff ~= indenter.format(func.getDeclaration());
618 					buff ~= indenter.format("{");
619 					buff ~= indenter.format(func.getBody());
620 					buff ~= indenter.format("}");
621 				}
622 			}
623 
624 			buff ~= indenter.format("}");
625 		}
626 
627 		wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~"IF.d"), buff);
628 	}
629 
630 	void writeDStruct()
631 	{
632 		string buff = wrapper.licence;
633 		auto indenter = new IndentedStringBuilder();
634 
635 		buff ~= "module "~ pack.name ~"."~ name ~";\n\n";
636 
637 		writeImports(buff);
638 		writeDocs(buff);
639 
640 		if ( !noNamespace )
641 		{
642 			buff ~= "public struct "~ name ~"\n";
643 			buff ~= indenter.format("{");
644 		}
645 
646 		if ( !lookupCode.empty )
647 		{
648 			buff ~= indenter.format(lookupCode);
649 			buff ~= "\n";
650 
651 			buff ~= indenter.format(["/**", "*/"]);
652 		}
653 
654 		foreach ( func; functions )
655 		{
656 			if ( func.noCode || func.isVariadic() || !( func.type == GirFunctionType.Function || func.type == GirFunctionType.Method ) )
657 				continue;
658 
659 			buff ~= "\n";
660 
661 			if ( func.name.among("delete", "export", "foreach", "union") )
662 					buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
663 
664 			buff ~= indenter.format(func.getDeclaration());
665 			buff ~= indenter.format("{");
666 			buff ~= indenter.format(func.getBody());
667 			buff ~= indenter.format("}");
668 		}
669 
670 		if ( !noNamespace )
671 			buff ~= indenter.format("}");
672 
673 		wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~".d"), buff);
674 	}
675 
676 	/**
677 	 * Return the variable name the c type is stored in.
678 	 */
679 	string getHandleVar()
680 	{
681 		if (cType.length == 0)
682 			return "";
683 
684 		string p = to!string(toLower(cType[0]));
685 		if ( cType.endsWith("_t") )
686 		{
687 			return p ~ cType[1 .. $ - 2];
688 		} else {
689 			return p ~ cType[1 .. $];
690 		}
691 	}
692 
693 	/**
694 	 * Returns the name of the function that returns the cType.
695 	 */
696 	string getHandleFunc()
697 	{
698 		if ( parent && !parentStruct )
699 			parentStruct = getParent();
700 
701 		if ( parentStruct && parentStruct.name == name )
702 			return "get"~ cast(char)pack.name[0].toUpper ~ pack.name[1..$] ~ name ~"Struct";
703 		else
704 			return "get"~ name ~"Struct";
705 	}
706 
707 	bool isInterface()
708 	{
709 		if ( lookupInterface )
710 			return true;
711 		if ( lookupClass )
712 			return false;
713 		if ( type == GirStructType.Interface )
714 			return true;
715 
716 		return false;
717 	}
718 
719 	bool isNamespace()
720 	{
721 		return type == GirStructType.Record && !(lookupClass || lookupInterface) && !noNamespace;
722 	}
723 
724 	void merge(GirStruct mergeStruct)
725 	{
726 		foreach ( func; mergeStruct.functions )
727 		{
728 			func.strct = this;
729 			functions[func.name] = func;
730 		}
731 	}
732 
733 	GirStruct getAncestor()
734 	{
735 		if ( parent.empty )
736 			return this;
737 
738 		if ( !parentStruct )
739 			parentStruct = pack.getStruct(parent);
740 
741 		return parentStruct.getAncestor();
742 	}
743 
744 	bool hasFunction(string funct)
745 	{
746 		if ( funct in functions )
747 			return true;
748 
749 		if ( parent.empty )
750 			return false;
751 
752 		if ( !parentStruct )
753 			parentStruct = pack.getStruct(parent);
754 
755 		if ( !parentStruct )
756 			return false;
757 
758 		return parentStruct.hasFunction(funct);
759 	}
760 
761 	private bool hasDefaultConstructor()
762 	{
763 		foreach ( line; lookupCode )
764 		{
765 			//TODO: Whitespace differences?
766 			if ( line.strip == "public this ("~ cType ~"* "~ getHandleVar() ~", bool ownedRef = false)" )
767 				return true;
768 		}
769 
770 		return false;
771 	}
772 
773 	bool shouldFree()
774 	{
775 		if ( !parent.empty && parent != "Boxed" )
776 			return false;
777 		if ( name.among("Object", "Boxed") )
778 			return false;
779 
780 		if ( auto u = "unref" in functions )
781 		{
782 			if ( u.noCode == false && u.params.empty )
783 				return true;
784 		}
785 
786 		if ( auto f = "free" in functions )
787 		{
788 			if ( f.noCode == false && f.params.empty )
789 				return true;
790 		}
791 		return false;
792 	}
793 
794 	bool isSimpleStruct()
795 	{
796 		//TODO: don't use this workaround.
797 		//TODO: For TestLogMsg, GArray and GByteArray implement array properties that are not zero terminated. 
798 		if ( cType == "PangoAttribute" || cType == "GTestLogMsg" || cType == "GArray" || cType == "GByteArray" || cType == "GtkTreeIter" )
799 			return false;
800 
801 		if ( pack.name == "cairo" )
802 			return false;
803 
804 		if ( lookupClass || lookupInterface || noDeclaration || noNamespace )
805 			return false;
806 
807 		if ( disguised || fields.length == 0 )
808 			return false;
809 
810 		if ( !fields.empty && fields[0].type )
811 		{
812 			// If the first field is wraped as a D class and isn't declared
813 			// as a pointer we assume its the parent instance.
814 			GirStruct dStruct = pack.getStruct(fields[0].type.name);
815 			if ( dStruct && dStruct.isDClass() && !fields[0].type.cType.endsWith("*") )
816 				return false;
817 		}
818 
819 		foreach ( field; fields )
820 		{
821 			if ( !field.writable )
822 				return false;
823 		}
824 
825 		return true;
826 	}
827 
828 	bool isDClass()
829 	{
830 		if ( type.among(GirStructType.Class, GirStructType.Interface) )
831 			return true;
832 		if ( type == GirStructType.Record && (lookupClass || lookupInterface) )
833 			return true;
834 		if ( "get_type" in functions && isSimpleStruct() )
835 			return true;
836 
837 		return false;
838 	}
839 
840 	string[] usedNamespaces()
841 	{
842 		string[] namespaces;
843 
844 		string getNamespace(GirType type)
845 		{
846 			if ( type.isArray() )
847 				type = type.elementType;
848 
849 			if ( type.cType in wrapper.aliasses || type.cType in aliases )
850 				return null;
851 
852 			if ( type.name.canFind(".") )
853 				return type.name.split(".")[0];
854 
855 			return null;
856 		}
857 
858 		if ( parent.canFind(".") )
859 				namespaces ~= parent.split(".")[0];
860 
861 		foreach ( func; functions )
862 		{
863 			namespaces ~= getNamespace(func.returnType);
864 			if ( func.instanceParam )
865 				namespaces ~= getNamespace(func.instanceParam.type);
866 			foreach ( param; func.params )
867 				namespaces ~= getNamespace(param.type);
868 		}
869 
870 		return namespaces.sort().uniq.array;
871 	}
872 
873 	private void resolveImports()
874 	{
875 		if ( parentStruct && parentStruct.name != name)
876 		{
877 			imports ~= parentStruct.pack.name ~"."~ parentStruct.name;
878 		}
879 		else if ( parentStruct )
880 		{
881 			string QParent = getFullyQualifiedDName();
882 			imports ~= parentStruct.pack.name ~"."~ parentStruct.name ~" : "~ QParent ~" = "~ parentStruct.name;
883 			structWrap[parent] = QParent;
884 		}
885 
886 		imports ~= pack.name ~".c.functions";
887 		imports ~= pack.name ~".c.types";
888 
889 		//Temporarily import the old bindDir.*types modules for backwards compatibility.
890 		const string[string] bindDirs = ["atk": "gtkc", "cairo": "gtkc", "gdk": "gtkc", "gdkpixbuf": "gtkc",
891 			"gio": "gtkc", "glib": "gtkc", "gobject": "gtkc", "gtk": "gtkc", "pango": "gtkc", "gsv": "gsvc",
892 			"vte": "vtec", "gstinterfaces": "gstreamerc", "gstreamer": "gstreamerc"];
893 
894 		if ( pack.wrapper.useBindDir )
895 		{
896 			if ( auto dir = pack.name in bindDirs )
897 				imports ~= *dir ~"."~ pack.name ~"types";
898 		}
899 
900 		if ( isSimpleStruct() )
901 			imports ~= "glib.MemorySlice";
902 
903 		if ( wrapper.useRuntimeLinker && (shouldFree() || isSimpleStruct()) )
904 			imports ~= "gtkd.Loader";
905 
906 		if ( isSimpleStruct() )
907 		{
908 			foreach ( field; fields ) 
909 			{
910 				if ( field.type.name in structWrap || field.type.name in aliases )
911 					continue;
912 
913 				GirStruct dType;
914 				
915 				if ( field.type.isArray() )
916 					dType = pack.getStruct(field.type.elementType.name);
917 				else
918 					dType = pack.getStruct(field.type.name);
919 
920 				if ( dType is this )
921 					continue;
922 			
923 				if ( dType && dType.isDClass() )
924 				{
925 					if ( !dType.pack.name.among("cairo", "glib", "gthread") )
926 						imports ~= "gobject.ObjectG";
927 
928 					if ( dType.type == GirStructType.Interface || dType.lookupInterface )
929 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
930 					else
931 						imports ~= dType.pack.name ~"."~ dType.name;
932 				}
933 				else if ( field.type.isString() || (field.type.isArray() && field.type.elementType.isString())  ) {
934 					imports ~= "glib.Str";
935 					imports ~= "glib.c.functions";
936 				}
937 			}
938 		}
939 
940 		foreach( func; functions )
941 		{
942 			if ( func.noCode )
943 				continue;
944 
945 			if ( func.throws )
946 			{
947 				imports ~= "glib.ErrorG";
948 				imports ~= "glib.GException";
949 			}
950 
951 			void getReturnImport(GirType type)
952 			{
953 				if ( type.name in structWrap || type.name in aliases )
954 					return;
955 
956 				GirStruct dType = pack.getStruct(type.name);
957 
958 				if ( dType && dType.isDClass() )
959 				{
960 					if ( !dType.pack.name.among("cairo", "glib", "gthread") )
961 						imports ~= "gobject.ObjectG";
962 
963 					if ( dType.type == GirStructType.Interface && func.name.startsWith("new") )
964 						return;
965 
966 					if ( dType is this && dType.type != GirStructType.Interface )
967 						return;
968 
969 					if ( dType.type == GirStructType.Interface || dType.lookupInterface )
970 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
971 					else
972 						imports ~= dType.pack.name ~"."~ dType.name;
973 				}
974 				else if ( type.name.among("utf8", "filename") || type.cType.among("guchar**") ) {
975 					imports ~= "glib.Str";
976 					imports ~= "glib.c.functions";
977 				}
978 			}
979 
980 			if ( func.returnType && func.returnType.cType !in structWrap )
981 			{
982 				getReturnImport(func.returnType);
983 
984 				if ( func.returnType.isArray() )
985 					getReturnImport(func.returnType.elementType);
986 			}
987 
988 			void getParamImport(GirType type)
989 			{
990 				if ( type.name in structWrap || type.name in aliases )
991 					return;
992 
993 				GirStruct dType = pack.getStruct(type.name);
994 
995 				if ( dType is this )
996 					return;
997 			
998 				if ( func.type == GirFunctionType.Signal && type.name.startsWith("Gdk.Event") )
999 					imports ~= "gdk.Event";
1000 
1001 				if ( dType && dType.isDClass() )
1002 				{
1003 					if ( dType.type == GirStructType.Interface || dType.lookupInterface )
1004 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
1005 					else
1006 						imports ~= dType.pack.name ~"."~ dType.name;
1007 				}
1008 				else if ( type.isString() || (type.isArray() && type.elementType.isString()) )
1009 					imports ~= "glib.Str";
1010 			}
1011 
1012 			foreach ( param; func.params )
1013 			{
1014 				if ( param.type.cType in structWrap )
1015 					continue;
1016 
1017 				getParamImport(param.type);
1018 
1019 				if ( param.type.elementType )
1020 					getParamImport(param.type.elementType);
1021 
1022 				if ( param.direction != GirParamDirection.Default )
1023 					getReturnImport(param.type);
1024 
1025 				if ( param.direction == GirParamDirection.Out
1026 						&& !param.type.cType.endsWith("**")
1027 						&& pack.getStruct(param.type.name) !is null
1028 						&& pack.getStruct(param.type.name).isDClass() )
1029 					imports ~= "glib.MemorySlice"; 
1030 
1031 				if ( param.direction == GirParamDirection.Out 
1032 						&& param.type.elementType
1033 						&& pack.getStruct(param.type.elementType.name) !is null
1034 						&& pack.getStruct(param.type.elementType.name).isDClass()
1035 						&& param.type.size > 0 )
1036 					imports ~= "glib.MemorySlice";
1037 			}
1038 
1039 			if ( func.type == GirFunctionType.Signal )
1040 			{
1041 				imports ~= "std.algorithm";
1042 				imports ~= "gobject.Signals";
1043 			}
1044 
1045 			if ( func.type == GirFunctionType.Constructor )
1046 				imports ~= "glib.ConstructionException";
1047 		}
1048 
1049 		foreach ( interf; implements )
1050 		{
1051 			if ( parentStruct && parentStruct.implements.canFind(interf) )
1052 				continue;
1053 
1054 			GirStruct strct = pack.getStruct(interf);
1055 
1056 			if ( strct )
1057 			{
1058 				imports ~= strct.pack.name ~"."~ strct.name ~"IF";
1059 				imports ~= strct.pack.name ~"."~ strct.name ~"T";
1060 			}
1061 		}
1062 
1063 		imports = uniq(sort(imports)).array;
1064 	}
1065 
1066 	private void writeImports(ref string buff, bool _public = false)
1067 	{
1068 		foreach ( imp; imports )
1069 		{
1070 			if ( _public || imp.endsWith("types") )
1071 				buff ~= "public  import "~ imp ~";\n";
1072 			else
1073 				buff ~= "private import "~ imp ~";\n";
1074 		}
1075 
1076 		buff ~= "\n\n";
1077 	}
1078 
1079 	private void writeDocs(ref string buff)
1080 	{
1081 		if ( doc !is null && wrapper.includeComments )
1082 		{
1083 			buff ~= "/**\n";
1084 			foreach ( line; doc.splitLines() )
1085 				buff ~= " * "~ line.strip() ~"\n";
1086 
1087 			if ( libVersion )
1088 			{
1089 				buff ~= " *\n * Since: "~ libVersion ~"\n";
1090 			}
1091 
1092 			buff ~= " */\n";
1093 		}
1094 		else if ( wrapper.includeComments )
1095 		{
1096 			buff ~= "/** */\n";
1097 		}
1098 	}
1099 
1100 	private GirFunction getTypeFunction(string cIdentifier)
1101 	{
1102 		GirType returnType = new GirType(wrapper);
1103 		returnType.name = "GObject.GType";
1104 		returnType.cType = "GType";
1105 
1106 		GirFunction func = new GirFunction(wrapper, this);
1107 		func.type = GirFunctionType.Function;
1108 		func.name = "get_type";
1109 		func.cType = cIdentifier;
1110 		func.returnType = returnType;
1111 
1112 		return func;
1113 	}
1114 
1115 	/**
1116 	 * Get an overload of events that accept an generic Gdk Event
1117 	 * instead of the spesific type listed in the gir files.
1118 	 * 
1119 	 * This for backwards compatibility with the documentation based wrapper.
1120 	 */
1121 	private string[] getGenericEventSignal(GirFunction func)
1122 	{
1123 		GirFunction signal = func.dup();
1124 		string[] buff;
1125 		
1126 		for ( size_t i; i < signal.params.length; i++ )
1127 		{
1128 			if ( signal.params[i].type.name.startsWith("Gdk.Event") )
1129 			{
1130 				GirType eventType = new GirType(wrapper);
1131 				eventType.name = "Gdk.Event";
1132 				
1133 				GirParam newParam = new GirParam(wrapper);
1134 				newParam.name = signal.params[i].name;
1135 				newParam.doc  = signal.params[i].doc;
1136 				newParam.type = eventType;
1137 				
1138 				signal.params[i] = newParam;
1139 				
1140 				break;
1141 			}
1142 		}
1143 
1144 		buff ~= signal.getAddListenerDeclaration();
1145 		buff ~= signal.getAddListenerBody();
1146 		
1147 		return buff;
1148 	}
1149 }
1150 
1151 final class GirUnion
1152 {
1153 	string name;
1154 	string doc;
1155 	GirField[] fields;
1156 
1157 	GirWrapper wrapper;
1158 
1159 	this(GirWrapper wrapper)
1160 	{
1161 		this.wrapper = wrapper;
1162 	}
1163 
1164 	void parse(T)(XMLReader!T reader)
1165 	{
1166 		if ( "name" in reader.front.attributes )
1167 			name = reader.front.attributes["name"];
1168 
1169 		reader.popFront();
1170 
1171 		while( !reader.empty && !reader.endTag("union") )
1172 		{
1173 			switch(reader.front.value)
1174 			{
1175 				case "doc":
1176 					reader.popFront();
1177 					doc ~= reader.front.value;
1178 					reader.popFront();
1179 					break;
1180 				case "doc-deprecated":
1181 					reader.popFront();
1182 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1183 					reader.popFront();
1184 					break;
1185 				case "field":
1186 					GirField field = new GirField(wrapper, null);
1187 					field.parse(reader);
1188 					fields ~= field;
1189 					break;
1190 				case "record":
1191 					GirField field = new GirField(wrapper, null);
1192 					GirStruct strct = new GirStruct(wrapper, null);
1193 					strct.parse(reader);
1194 					strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
1195 					field.gtkStruct = strct;
1196 					fields ~= field;
1197 					break;
1198 				case "source-position":
1199 					reader.skipTag();
1200 					break;
1201 				default:
1202 					warning("Unexpected tag: ", reader.front.value, " in GirUnion: ", name, reader);
1203 			}
1204 			reader.popFront();
1205 		}
1206 	}
1207 
1208 	string[] getUnionDeclaration()
1209 	{
1210 		string[] buff;
1211 		if ( doc !is null && wrapper.includeComments )
1212 		{
1213 			buff ~= "/**";
1214 			foreach ( line; doc.splitLines() )
1215 				buff ~= " * "~ line.strip();
1216 			buff ~= " */";
1217 		}
1218 
1219 		if ( name )
1220 			buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses);
1221 		else
1222 			buff ~= "union";
1223 
1224 		buff ~= "{";
1225 		buff ~= GirField.getFieldDeclarations(fields, wrapper);
1226 		buff ~= "}";
1227 
1228 		if ( name )
1229 			buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";";
1230 
1231 		return buff;
1232 	}
1233 }