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, split;
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 		//Vala libraries can have the package name in there name twice, befoer and after the dot.
107 		string[] arr = split(name, '.');
108 		if ( arr.length > 1 && arr[1].startsWith(arr[0]) )
109 		{
110 			name = arr[1][arr[0].length .. $];
111 		}
112 
113 		if ( reader.front.type == XMLNodeType.EmptyTag )
114 			return;
115 
116 		reader.popFront();
117 
118 		while ( !reader.empty && !reader.endTag("type", "array") )
119 		{
120 			if ( elementType )
121 				keyType = elementType;
122 
123 			elementType = new GirType(wrapper);
124 			elementType.parse(reader);
125 
126 			reader.popFront();
127 		}
128 
129 		if ( cType == elementType.cType && !cType.among("void*", "gpointer", "gconstpointer") && size < 0 )
130 			cType ~= "*";
131 
132 		if ( isArray() && (cType == "void" || cType.empty) )
133 		{
134 			if ( size > 0 )
135 				cType = elementType.cType;
136 			else
137 				cType = elementType.cType ~"*";
138 		}
139 	}
140 
141 	bool isString()
142 	{
143 		if ( cType.startsWith("gchar*", "char*", "const(char)*") )
144 			return true;
145 		if ( name.among("utf8", "filename") )
146 			return true;
147 		if ( isArray() && elementType.cType.startsWith("gchar", "char", "const(char)") )
148 			return true;
149 
150 		return false;
151 	}
152 
153 	bool isArray()
154 	{
155 		if ( elementType is null )
156 			return false;
157 
158 		// The GLib Arrays are in the GIR files as arrays but they shouldn't be wrapped as such.
159 		if ( name.among("GLib.Array", "Array", "GLib.ByteArray", "ByteArray", "GLib.PtrArray", "PtrArray") )
160 			return false;
161 
162 		if ( girArray )
163 			return true;
164 
165 		return false;
166 	}
167 
168 	private void fixType()
169 	{
170 		if ( name == "utf8" && !cType.canFind("**") )
171 		{
172 			cType = "const(char)*";
173 			return;
174 		}
175 
176 		cType = cType.replace("const ", "").replace(" const", "");
177 	}
178 
179 	private void removeInitialyUnowned()
180 	{
181 		if ( name.among("GObject.InitiallyUnowned", "InitiallyUnowned") )
182 		{
183 			if ( name == "GObject.InitiallyUnowned" )
184 				name = "GObject.Object";
185 			else if ( name == "InitiallyUnowned" )
186 				name = "Object";
187 
188 			if ( cType == "GInitiallyUnowned" )
189 				cType = "GObject";
190 			else if ( cType == "GInitiallyUnowned*" )
191 				cType = "GObject*";
192 		}
193 		else if ( name.among("GObject.InitiallyUnownedClass", "InitiallyUnownedClass") )
194 		{
195 			if ( name == "GObject.InitiallyUnownedClass" )
196 				name = "GObject.ObjectClass";
197 			else if ( name == "InitiallyUnownedClass" )
198 				name = "ObjectClass";
199 
200 			if ( cType == "GInitiallyUnownedClass" )
201 				cType = "GObjectClass";
202 			else if ( cType == "GInitiallyUnownedClass*" )
203 				cType = "GObjectClass*";
204 		}
205 	}
206 }