1/* node.vala
2 *
3 * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois
4 * Copyright (C) 2011 Florian Brosch
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19 *
20 * Author:
21 * 	Didier 'Ptitjes Villevalois <ptitjes@free.fr>
22 */
23
24
25/**
26 * Represents a node in the api tree.
27 */
28public abstract class Valadoc.Api.Node : Item, Documentation {
29	protected bool do_document = false;
30	private SourceFile file;
31
32	/**
33	 * The name of the node
34	 */
35	public string? name {
36		private set;
37		get;
38	}
39
40	public SourceFile get_source_file () {
41		return file;
42	}
43
44	/**
45	 * Returns the type of this node
46	 */
47	public abstract NodeType node_type { get; }
48
49	private Vala.Map<string, Node> per_name_children;
50	private Vala.Map<NodeType, Vala.List<Node>> per_type_children;
51
52
53	protected Node (Node? parent, SourceFile? file, string? name, Vala.CodeNode? data) {
54		base (data);
55
56		per_name_children = new Vala.HashMap<string, Node> (str_hash, str_equal);
57		per_type_children = new Vala.HashMap<NodeType, Vala.List<Node>> ();
58
59		if (name != null && (Vala.Scanner.get_identifier_or_keyword (name, name.length) != Vala.TokenType.IDENTIFIER || name[0].isdigit ())) {
60			this.name = "@" + name;
61		} else {
62			this.name = name;
63		}
64
65		this.parent = parent;
66		this.file = file;
67	}
68
69	/**
70	 * Visits this node with the specified Visitor.
71	 *
72	 * @param visitor the visitor to be called while traversing
73	 */
74	public abstract void accept (Visitor visitor);
75
76	/**
77	 * {@inheritDoc}
78	 */
79	// TODO: rename to is_visible
80	public abstract bool is_browsable (Settings settings);
81
82	/**
83	 * {@inheritDoc}
84	 */
85	public string? get_filename () {
86		if (file == null) {
87			return null;
88		}
89
90		return file.relative_path;
91	}
92
93	public void add_child (Symbol child) {
94		if (child.name != null) {
95			if (child.name[0] == '@') {
96				per_name_children.set (child.name.next_char (), child);
97			} else {
98				per_name_children.set (child.name, child);
99			}
100		} else {
101			// Special case for the root namespace
102			per_name_children.set ("", child);
103		}
104
105		Vala.List<Node> children = per_type_children.get (child.node_type);
106		if (children == null) {
107			children = new Vala.ArrayList<Node> ();
108			per_type_children.set (child.node_type, children);
109		}
110
111		children.add (child);
112	}
113
114	/**
115	 * {@inheritDoc}
116	 */
117	internal override void parse_comments (Settings settings, DocumentationParser parser) {
118		do_document = true;
119
120		foreach (Node node in per_name_children.get_values ()) {
121			if (this.parent == node) {
122				continue;
123			}
124			if (node.is_browsable (settings)) {
125				node.parse_comments (settings, parser);
126			}
127		}
128	}
129
130	/**
131	 * {@inheritDoc}
132	 */
133	internal override void check_comments (Settings settings, DocumentationParser parser) {
134
135		foreach (Node node in per_name_children.get_values ()) {
136			if (this.parent == node) {
137				continue;
138			}
139			if (node.is_browsable (settings)) {
140				node.check_comments (settings, parser);
141			}
142		}
143	}
144
145
146	/**
147	 * Specifies whether this node has at least one visible child with the given type
148	 *
149	 * @param type a node type
150	 */
151	public bool has_visible_children_by_type (NodeType type, Settings settings) {
152		Vala.List<Node>? all_children = per_type_children.get (type);
153		if (all_children != null) {
154			foreach (Node node in all_children) {
155				if (node.is_browsable (settings)) {
156					return true;
157				}
158			}
159		}
160
161		return false;
162	}
163
164	/**
165	 * Specifies whether this node has at least one visible child with the given types
166	 *
167	 * @param types a list of node types
168	 */
169	public bool has_visible_children_by_types (NodeType[] types, Settings settings) {
170		foreach (NodeType type in types) {
171			if (has_visible_children_by_type (type, settings)) {
172				return true;
173			}
174		}
175
176		return false;
177	}
178
179	/**
180	 * Specifies whether this node has at least one visible child
181	 */
182	public bool has_visible_children (Settings settings) {
183		return has_visible_children_by_types (per_type_children.get_keys ().to_array (), settings);
184	}
185
186	/**
187	 * Specifies whether this node has at least one child with the given type
188	 *
189	 * @param type a node type
190	 */
191	public bool has_children_by_type (NodeType type) {
192		Vala.List<Node>? all_children = per_type_children.get (type);
193		return all_children != null && !all_children.is_empty;
194	}
195
196	/**
197	 * Specifies whether this node has at least one child with the given types
198	 *
199	 * @param types a list of node types
200	 */
201	public bool has_children (NodeType[] types) {
202		foreach (NodeType type in types) {
203			if (has_children_by_type (type)) {
204				return true;
205			}
206		}
207		return false;
208	}
209
210	/**
211	 * Returns a list of all children with the given type.
212	 *
213	 * @param type a node type
214	 * @param filtered specifies whether nodes which are not browsable should appear in the list
215	 */
216	public Vala.List<Node> get_children_by_type (NodeType type, bool filtered = true) {
217		var children = new Vala.ArrayList<Node> ();
218
219		Vala.List<Node> all_children = per_type_children.get (type);
220		if (all_children != null) {
221			foreach (Node node in all_children) {
222				if (node.do_document || !filtered) {
223					children.add (node);
224				}
225			}
226		}
227
228		return children;
229	}
230
231	/**
232	 * Returns a list of all children with the given types.
233	 *
234	 * @param types a list of node types
235	 * @param filtered specifies whether nodes which are not browsable should appear in the list
236	 */
237	public Vala.List<Node> get_children_by_types (NodeType[] types, bool filtered = true) {
238		var children = new Vala.ArrayList<Node> ();
239
240		foreach (NodeType type in types) {
241			children.add_all (get_children_by_type (type, filtered));
242		}
243
244		return children;
245	}
246
247	/**
248	 * Visits all children of this node with the given type with the specified Visitor.
249	 *
250	 * @param type a node type
251	 * @param visitor the visitor to be called while traversing
252	 * @param filtered specifies whether nodes which are not browsable should appear in the list
253	 */
254	public void accept_children_by_type (NodeType type, Visitor visitor, bool filtered = true) {
255		Vala.List<Node> all_children = per_type_children.get (type);
256		if (all_children != null) {
257			foreach (Node node in all_children) {
258				if (node.do_document || !filtered) {
259					node.accept (visitor);
260				}
261			}
262		}
263	}
264
265	/**
266	 * Visits all children of this node with the given types with the specified Visitor.
267	 *
268	 * @param types a list of node types
269	 * @param visitor the visitor to be called while traversing
270	 * @param filtered specifies whether nodes which are not browsable should appear in the list
271	 */
272	public void accept_children (NodeType[] types, Visitor visitor, bool filtered = true) {
273		foreach (NodeType type in types) {
274			accept_children_by_type (type, visitor, filtered);
275		}
276	}
277
278	/**
279	 * Visits all children of this node with the specified Visitor.
280	 *
281	 * @param visitor the visitor to be called while traversing
282	 * @param filtered specifies whether nodes which are not browsable should appear in the list
283	 */
284	public void accept_all_children (Visitor visitor, bool filtered = true) {
285		foreach (Vala.List<Node> children in per_type_children.get_values ()) {
286			if (this.parent == children[0]) {
287				continue;
288			}
289			foreach (Node node in children) {
290				if (node.do_document || !filtered) {
291					node.accept (visitor);
292				}
293			}
294		}
295	}
296
297	public Node? find_by_name (string name) {
298		if (name[0] == '@') {
299			return per_name_children.get (name.next_char ());
300		} else {
301			return per_name_children.get (name);
302		}
303	}
304
305	private Namespace? _nspace = null;
306	private Package? _package = null;
307	private string _full_name = null;
308
309	/**
310	 * The corresponding namespace
311	 */
312	public Namespace? nspace {
313		get {
314			if (this._nspace == null) {
315				Api.Item ast = this;
316				while (ast is Valadoc.Api.Namespace == false) {
317					ast = ast.parent;
318					if (ast == null) {
319						return null;
320					}
321				}
322				this._nspace = (Valadoc.Api.Namespace)ast;
323			}
324			return this._nspace;
325		}
326	}
327
328	/**
329	 * The corresponding package such as a vapi or gir file
330	 */
331	public Package? package {
332		get {
333			if (this._package == null) {
334				Api.Item ast = this;
335				while (ast is Valadoc.Api.Package == false) {
336					ast = ast.parent;
337					if (ast == null) {
338						return null;
339					}
340				}
341				this._package = (Valadoc.Api.Package)ast;
342			}
343			return this._package;
344		}
345	}
346
347	public Content.Comment? documentation {
348		internal set;
349		get;
350	}
351
352	/**
353	 * Returns canonicalized absolute name (GLib.FileStream for instance)
354	 */
355	public string? get_full_name () {
356		if (this._full_name == null) {
357			if (this.name == null) {
358				return null;
359			}
360
361			GLib.StringBuilder full_name = new GLib.StringBuilder (this.name);
362
363			if (this.parent != null) {
364				for (Item pos = this.parent; pos is Package == false ; pos = pos.parent) {
365					string name = ((Node)pos).name;
366					if (name != null) {
367						full_name.prepend_unichar ('.');
368						full_name.prepend (name);
369					}
370				}
371			}
372			this._full_name = full_name.str;
373		}
374		return this._full_name;
375	}
376
377	/**
378	 * A comparison function used to sort nodes in alphabetical order
379	 */
380	public int compare_to (Node node) {
381		return strcmp (name, node.name);
382	}
383}
384
385