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.GirEnum;
19 
20 import std.array : split;
21 import std.algorithm;
22 import std.range : back, empty;
23 import std..string : splitLines, strip, toUpper;
24 import std.uni : isNumber;
25 
26 import gtd.GirPackage;
27 import gtd.GirWrapper;
28 import gtd.Log;
29 import gtd.XMLReader;
30 
31 final class GirEnum
32 {
33 	string name;
34 	string cName;
35 	string libVersion;
36 	string doc;
37 
38 	GirEnumMember[] members;
39 	GirWrapper wrapper;
40 	GirPackage pack;
41 
42 	this(GirWrapper wrapper, GirPackage pack)
43 	{
44 		this.wrapper = wrapper;
45 		this.pack = pack;
46 	}
47 
48 	void parse(T)(XMLReader!T reader)
49 	{
50 		name = reader.front.attributes["name"];
51 		cName = reader.front.attributes["c:type"];
52 
53 		if ( "version" in reader.front.attributes )
54 			libVersion = reader.front.attributes["version"];
55 		reader.popFront();
56 
57 		while ( !reader.empty && !reader.endTag("bitfield", "enumeration") )
58 		{
59 			switch (reader.front.value)
60 			{
61 				case "doc":
62 					reader.popFront();
63 					doc ~= reader.front.value;
64 					reader.popFront();
65 					break;
66 				case "doc-deprecated":
67 					reader.popFront();
68 					doc ~= "\n\nDeprecated: "~ reader.front.value;
69 					reader.popFront();
70 					break;
71 				case "member":
72 					if ( reader.front.attributes["name"].startsWith("2bu", "2bi", "3bu") )
73 					{
74 						reader.skipTag();
75 						break;
76 					}
77 
78 					GirEnumMember member = GirEnumMember(wrapper);
79 					member.parse(reader);
80 					members ~= member;
81 					break;
82 				case "function":
83 					//Skip these functions for now
84 					//as they are also availabe as global functions.
85 					//pack.parseFunction(reader);
86 					reader.skipTag();
87 					break;
88 				case "source-position":
89 					reader.skipTag();
90 					break;
91 				default:
92 					warning("Unexpected tag: ", reader.front.value, " in GirEnum: ", name, reader);
93 			}
94 			reader.popFront();
95 		}
96 	}
97 
98 	string[] getEnumDeclaration()
99 	{
100 		string[] buff;
101 		if ( doc !is null && wrapper.includeComments )
102 		{
103 			buff ~= "/**";
104 			foreach ( line; doc.splitLines() )
105 				buff ~= " * "~ line.strip();
106 
107 			if ( libVersion )
108 			{
109 				buff ~= " *";
110 				buff ~= " * Since: "~ libVersion;
111 			}
112 
113 			buff ~= " */";
114 		}
115 
116 		buff ~= "public enum "~ cName ~(name.among("ParamFlags", "MessageType") ? " : uint" : "");
117 		buff ~= "{";
118 
119 		foreach ( member; members )
120 		{
121 			buff ~= member.getEnumMemberDeclaration();
122 		}
123 
124 		buff ~= "}";
125 		if ( name !is null && pack.name.among("glgdk", "glgtk") )
126 			buff ~= "alias "~ cName ~" GL"~ name ~";";
127 		else if ( name !is null && pack.name != "pango" )
128 			buff ~= "alias "~ cName ~" "~ name ~";";
129 
130 		return buff;
131 	}
132 }
133 
134 struct GirEnumMember
135 {
136 	string name;
137 	string value;
138 	string doc;
139 
140 	GirWrapper wrapper;
141 
142 	@disable this();
143 
144 	this(GirWrapper wrapper)
145 	{
146 		this.wrapper = wrapper;
147 	}
148 
149 	void parse(T)(XMLReader!T reader)
150 	{
151 		name = reader.front.attributes["name"];
152 		value = reader.front.attributes["value"];
153 
154 		if ( name.empty )
155 			name = reader.front.attributes["c:identifier"].split("_").back;
156 		
157 		if ( reader.front.type == XMLNodeType.EmptyTag )
158 			return;
159 
160 		reader.popFront();
161 
162 		while ( !reader.empty && !reader.endTag("member", "constant") )
163 		{
164 			switch (reader.front.value)
165 			{
166 				case "doc":
167 					reader.popFront();
168 					doc ~= reader.front.value;
169 					reader.popFront();
170 					break;
171 				case "doc-deprecated":
172 					reader.popFront();
173 					doc ~= "\n\nDeprecated: "~ reader.front.value;
174 					reader.popFront();
175 					break;
176 				case "source-position":
177 					reader.skipTag();
178 					break;
179 				case "type":
180 					if ( reader.front.attributes["name"] == "utf8" )
181 						value = "\""~ value ~"\"";
182 					break;
183 				default:
184 					warning("Unexpected tag: ", reader.front.value, " in GirEnumMember: ", name, reader);
185 			}
186 			reader.popFront();
187 		}
188 	}
189 
190 	string[] getEnumMemberDeclaration()
191 	{
192 		string[] buff;
193 		if ( doc !is null && wrapper.includeComments )
194 		{
195 			buff ~= "/**";
196 			foreach ( line; doc.splitLines() )
197 				buff ~= " * "~ line.strip();
198 			buff ~= " */";
199 		}
200 
201 		if ( name[0].isNumber && name !in wrapper.aliasses )
202 			buff ~= "_"~ name.toUpper() ~" = "~ value ~",";
203 		else
204 			buff ~= tokenToGtkD(name.toUpper(), wrapper.aliasses, false) ~" = "~ value ~",";
205 
206 		return buff;
207 	}
208 }