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.GirType;
19 
20 import std.algorithm: among, canFind, startsWith;
21 import std.array: replace;
22 import std.conv: to;
23 import std.range: empty;
24 
25 import gtd.GirWrapper;
26 import gtd.XMLReader;
27 
28 /**
29  * Represent sthe type of an field or a parameter.
30  */
31 final class GirType
32 {
33 	string name;
34 	string cType;
35 	string dType;
36 	string doc;
37 	bool constType;
38 
39 	int size = -1;   /// The size of a fixed size array.
40 	int length = -1; /// The index of the param representing the length, not counting the instance param.
41 	bool zeroTerminated;   /// Is this array zero-terminated.
42 	bool girArray = false; /// The gir file specifies this as an array. Use isArray to check if this is actually an array.
43 	GirType elementType;   /// The type of the array elements, also set for Glib.List, Glib.SList Glib.Array and GLib.HashTable.
44 	GirType keyType;       /// The key type of a HashTable;
45 
46 	GirWrapper wrapper;
47 
48 	this(GirWrapper wrapper)
49 	{
50 		this.wrapper = wrapper;
51 	}
52 
53 	void parse(T)(XMLReader!T reader)
54 	{
55 		if ( reader.front.value == "array" )
56 			girArray = true;
57 
58 		if ( "c:type" in reader.front.attributes )
59 			cType = reader.front.attributes["c:type"];
60 		if ( "length" in reader.front.attributes )
61 			length = to!int(reader.front.attributes["length"]);
62 		if ( "zero-terminated" in reader.front.attributes )
63 			zeroTerminated = to!int(reader.front.attributes["zero-terminated"]) == 1;
64 		if ( "fixed-size" in reader.front.attributes )
65 			size = to!int(reader.front.attributes["fixed-size"]);
66 		if ( "name" in reader.front.attributes )
67 			name = reader.front.attributes["name"];
68 
69 		if ( cType is null && name is null )
70 		{
71 			name = "none";
72 		}
73 
74 		// Some GIR files have a none type name.
75 		if (name == "none") {
76 			cType = "void";
77 		}
78 
79 		if ( cType.canFind(" const") || cType.canFind("const ") )
80 		{
81 			constType = true;
82 			fixType();
83 		}
84 
85 		if ( cType.canFind("unsigned ") )
86 		{
87 			cType = cType.replace("unsigned ", "u");
88 		}
89 
90 		if ( name == "long double" )
91 		{
92 			name = "greal";
93 			cType = "greal";
94 		}
95 
96 		cType = cType.replace("volatile ", "");
97 
98 		if ( cType == "unsigned" )
99 			cType = name;
100 
101 		removeInitialyUnowned();
102 
103 		if ( cType is null && (name == "filename" || name == "utf8") )
104 			cType = "gchar*";
105 
106 		if ( reader.front.type == XMLNodeType.EmptyTag )
107 			return;
108 
109 		reader.popFront();
110 
111 		while ( !reader.empty && !reader.endTag("type", "array") )
112 		{
113 			if ( elementType )
114 				keyType = elementType;
115 
116 			elementType = new GirType(wrapper);
117 			elementType.parse(reader);
118 
119 			reader.popFront();
120 		}
121 
122 		if ( cType == elementType.cType && !cType.among("void*", "gpointer", "gconstpointer") && size < 0 )
123 			cType ~= "*";
124 
125 		if ( isArray() && (cType == "void" || cType.empty) )
126 		{
127 			if ( size > 0 )
128 				cType = elementType.cType;
129 			else
130 				cType = elementType.cType ~"*";
131 		}
132 	}
133 
134 	bool isString()
135 	{
136 		if ( cType.startsWith("gchar*", "char*", "const(char)*") )
137 			return true;
138 		if ( name.among("utf8", "filename") )
139 			return true;
140 		if ( isArray() && elementType.cType.startsWith("gchar", "char", "const(char)") )
141 			return true;
142 
143 		return false;
144 	}
145 
146 	bool isArray()
147 	{
148 		if ( elementType is null )
149 			return false;
150 
151 		// The GLib Arrays are in the GIR files as arrays but they shouldn't be wrapped as such.
152 		if ( name.among("GLib.Array", "Array", "GLib.ByteArray", "ByteArray", "GLib.PtrArray", "PtrArray") )
153 			return false;
154 
155 		if ( girArray )
156 			return true;
157 
158 		return false;
159 	}
160 
161 	private void fixType()
162 	{
163 		if ( name == "utf8" && !cType.canFind("**") )
164 		{
165 			cType = "const(char)*";
166 			return;
167 		}
168 
169 		cType = cType.replace("const ", "").replace(" const", "");
170 	}
171 
172 	private void removeInitialyUnowned()
173 	{
174 		if ( name.among("GObject.InitiallyUnowned", "InitiallyUnowned") )
175 		{
176 			if ( name == "GObject.InitiallyUnowned" )
177 				name = "GObject.Object";
178 			else if ( name == "InitiallyUnowned" )
179 				name = "Object";
180 
181 			if ( cType == "GInitiallyUnowned" )
182 				cType = "GObject";
183 			else if ( cType == "GInitiallyUnowned*" )
184 				cType = "GObject*";
185 		}
186 		else if ( name.among("GObject.InitiallyUnownedClass", "InitiallyUnownedClass") )
187 		{
188 			if ( name == "GObject.InitiallyUnownedClass" )
189 				name = "GObject.ObjectClass";
190 			else if ( name == "InitiallyUnownedClass" )
191 				name = "ObjectClass";
192 
193 			if ( cType == "GInitiallyUnownedClass" )
194 				cType = "GObjectClass";
195 			else if ( cType == "GInitiallyUnownedClass*" )
196 				cType = "GObjectClass*";
197 		}
198 	}
199 }