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 					error("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 			}
936 		}
937 
938 		foreach( func; functions )
939 		{
940 			if ( func.noCode )
941 				continue;
942 
943 			if ( func.throws )
944 			{
945 				imports ~= "glib.ErrorG";
946 				imports ~= "glib.GException";
947 			}
948 
949 			void getReturnImport(GirType type)
950 			{
951 				if ( type.name in structWrap || type.name in aliases )
952 					return;
953 
954 				GirStruct dType = pack.getStruct(type.name);
955 
956 				if ( dType && dType.isDClass() )
957 				{
958 					if ( !dType.pack.name.among("cairo", "glib", "gthread") )
959 						imports ~= "gobject.ObjectG";
960 
961 					if ( dType.type == GirStructType.Interface && func.name.startsWith("new") )
962 						return;
963 
964 					if ( dType is this && dType.type != GirStructType.Interface )
965 						return;
966 
967 					if ( dType.type == GirStructType.Interface || dType.lookupInterface )
968 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
969 					else
970 						imports ~= dType.pack.name ~"."~ dType.name;
971 				}
972 				else if ( type.name.among("utf8", "filename") || type.cType.among("guchar**") )
973 					imports ~= "glib.Str";
974 			}
975 
976 			if ( func.returnType && func.returnType.cType !in structWrap )
977 			{
978 				getReturnImport(func.returnType);
979 
980 				if ( func.returnType.isArray() )
981 					getReturnImport(func.returnType.elementType);
982 			}
983 
984 			void getParamImport(GirType type)
985 			{
986 				if ( type.name in structWrap || type.name in aliases )
987 					return;
988 
989 				GirStruct dType = pack.getStruct(type.name);
990 
991 				if ( dType is this )
992 					return;
993 			
994 				if ( func.type == GirFunctionType.Signal && type.name.startsWith("Gdk.Event") )
995 					imports ~= "gdk.Event";
996 
997 				if ( dType && dType.isDClass() )
998 				{
999 					if ( dType.type == GirStructType.Interface || dType.lookupInterface )
1000 						imports ~= dType.pack.name ~"."~ dType.name ~"IF";
1001 					else
1002 						imports ~= dType.pack.name ~"."~ dType.name;
1003 				}
1004 				else if ( type.isString() || (type.isArray() && type.elementType.isString()) )
1005 					imports ~= "glib.Str";
1006 			}
1007 
1008 			foreach ( param; func.params )
1009 			{
1010 				if ( param.type.cType in structWrap )
1011 					continue;
1012 
1013 				getParamImport(param.type);
1014 
1015 				if ( param.type.elementType )
1016 					getParamImport(param.type.elementType);
1017 
1018 				if ( param.direction != GirParamDirection.Default )
1019 					getReturnImport(param.type);
1020 
1021 				if ( param.direction == GirParamDirection.Out
1022 						&& !param.type.cType.endsWith("**")
1023 						&& pack.getStruct(param.type.name) !is null
1024 						&& pack.getStruct(param.type.name).isDClass() )
1025 					imports ~= "glib.MemorySlice"; 
1026 			}
1027 
1028 			if ( func.type == GirFunctionType.Signal )
1029 			{
1030 				imports ~= "std.algorithm";
1031 				imports ~= "gobject.Signals";
1032 			}
1033 
1034 			if ( func.type == GirFunctionType.Constructor )
1035 				imports ~= "glib.ConstructionException";
1036 		}
1037 
1038 		foreach ( interf; implements )
1039 		{
1040 			if ( parentStruct && parentStruct.implements.canFind(interf) )
1041 				continue;
1042 
1043 			GirStruct strct = pack.getStruct(interf);
1044 
1045 			if ( strct )
1046 			{
1047 				imports ~= strct.pack.name ~"."~ strct.name ~"IF";
1048 				imports ~= strct.pack.name ~"."~ strct.name ~"T";
1049 			}
1050 		}
1051 
1052 		imports = uniq(sort(imports)).array;
1053 	}
1054 
1055 	private void writeImports(ref string buff, bool _public = false)
1056 	{
1057 		foreach ( imp; imports )
1058 		{
1059 			if ( _public || imp.endsWith("types") )
1060 				buff ~= "public  import "~ imp ~";\n";
1061 			else
1062 				buff ~= "private import "~ imp ~";\n";
1063 		}
1064 
1065 		buff ~= "\n\n";
1066 	}
1067 
1068 	private void writeDocs(ref string buff)
1069 	{
1070 		if ( doc !is null && wrapper.includeComments )
1071 		{
1072 			buff ~= "/**\n";
1073 			foreach ( line; doc.splitLines() )
1074 				buff ~= " * "~ line.strip() ~"\n";
1075 
1076 			if ( libVersion )
1077 			{
1078 				buff ~= " *\n * Since: "~ libVersion ~"\n";
1079 			}
1080 
1081 			buff ~= " */\n";
1082 		}
1083 		else if ( wrapper.includeComments )
1084 		{
1085 			buff ~= "/** */\n";
1086 		}
1087 	}
1088 
1089 	private GirFunction getTypeFunction(string cIdentifier)
1090 	{
1091 		GirType returnType = new GirType(wrapper);
1092 		returnType.name = "GObject.GType";
1093 		returnType.cType = "GType";
1094 
1095 		GirFunction func = new GirFunction(wrapper, this);
1096 		func.type = GirFunctionType.Function;
1097 		func.name = "get_type";
1098 		func.cType = cIdentifier;
1099 		func.returnType = returnType;
1100 
1101 		return func;
1102 	}
1103 
1104 	/**
1105 	 * Get an overload of events that accept an generic Gdk Event
1106 	 * instead of the spesific type listed in the gir files.
1107 	 * 
1108 	 * This for backwards compatibility with the documentation based wrapper.
1109 	 */
1110 	private string[] getGenericEventSignal(GirFunction func)
1111 	{
1112 		GirFunction signal = func.dup();
1113 		string[] buff;
1114 		
1115 		for ( size_t i; i < signal.params.length; i++ )
1116 		{
1117 			if ( signal.params[i].type.name.startsWith("Gdk.Event") )
1118 			{
1119 				GirType eventType = new GirType(wrapper);
1120 				eventType.name = "Gdk.Event";
1121 				
1122 				GirParam newParam = new GirParam(wrapper);
1123 				newParam.name = signal.params[i].name;
1124 				newParam.doc  = signal.params[i].doc;
1125 				newParam.type = eventType;
1126 				
1127 				signal.params[i] = newParam;
1128 				
1129 				break;
1130 			}
1131 		}
1132 
1133 		buff ~= signal.getAddListenerDeclaration();
1134 		buff ~= signal.getAddListenerBody();
1135 		
1136 		return buff;
1137 	}
1138 }
1139 
1140 final class GirUnion
1141 {
1142 	string name;
1143 	string doc;
1144 	GirField[] fields;
1145 
1146 	GirWrapper wrapper;
1147 
1148 	this(GirWrapper wrapper)
1149 	{
1150 		this.wrapper = wrapper;
1151 	}
1152 
1153 	void parse(T)(XMLReader!T reader)
1154 	{
1155 		if ( "name" in reader.front.attributes )
1156 			name = reader.front.attributes["name"];
1157 
1158 		reader.popFront();
1159 
1160 		while( !reader.empty && !reader.endTag("union") )
1161 		{
1162 			switch(reader.front.value)
1163 			{
1164 				case "doc":
1165 					reader.popFront();
1166 					doc ~= reader.front.value;
1167 					reader.popFront();
1168 					break;
1169 				case "doc-deprecated":
1170 					reader.popFront();
1171 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1172 					reader.popFront();
1173 					break;
1174 				case "field":
1175 					GirField field = new GirField(wrapper, null);
1176 					field.parse(reader);
1177 					fields ~= field;
1178 					break;
1179 				case "record":
1180 					GirField field = new GirField(wrapper, null);
1181 					GirStruct strct = new GirStruct(wrapper, null);
1182 					strct.parse(reader);
1183 					strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
1184 					field.gtkStruct = strct;
1185 					fields ~= field;
1186 					break;
1187 				case "source-position":
1188 					reader.skipTag();
1189 					break;
1190 				default:
1191 					error("Unexpected tag: ", reader.front.value, " in GirUnion: ", name, reader);
1192 			}
1193 			reader.popFront();
1194 		}
1195 	}
1196 
1197 	string[] getUnionDeclaration()
1198 	{
1199 		string[] buff;
1200 		if ( doc !is null && wrapper.includeComments )
1201 		{
1202 			buff ~= "/**";
1203 			foreach ( line; doc.splitLines() )
1204 				buff ~= " * "~ line.strip();
1205 			buff ~= " */";
1206 		}
1207 
1208 		if ( name )
1209 			buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses);
1210 		else
1211 			buff ~= "union";
1212 
1213 		buff ~= "{";
1214 		buff ~= GirField.getFieldDeclarations(fields, wrapper);
1215 		buff ~= "}";
1216 
1217 		if ( name )
1218 			buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";";
1219 
1220 		return buff;
1221 	}
1222 }