[valadoc] gir: basic reading support



commit dc6edfc736bb630b805d0c3f7afc6ca4768e605c
Author: Florian Brosch <flo brosch gmail com>
Date:   Mon Nov 28 17:11:49 2011 +0100

    gir: basic reading support

 src/doclets/htm/doclet.vala                        |    4 +-
 src/driver/0.10.x/treebuilder.vala                 |    2 +-
 src/driver/0.11.0/treebuilder.vala                 |    2 +-
 src/driver/0.11.x/treebuilder.vala                 |    2 +-
 src/driver/0.12.x/treebuilder.vala                 |    2 +-
 src/driver/0.13.x/treebuilder.vala                 |    2 +-
 src/driver/0.14.x/Makefile.am                      |    2 -
 src/driver/0.14.x/treebuilder.vala                 |    2 +-
 src/driver/0.16.x/treebuilder.vala                 |   45 +-
 src/libvaladoc/Makefile.am                         |    3 +
 src/libvaladoc/api/girsourcecomment.vala           |   55 ++
 src/libvaladoc/api/sourcefile.vala                 |    8 +-
 src/libvaladoc/api/tree.vala                       |   14 +-
 src/libvaladoc/ctyperesolver.vala                  |   97 ++-
 .../documentation/documentationparser.vala         |   11 +-
 .../documentation/gtkdoccommentparser.vala         |  942 ++++++++++++++++++++
 .../documentation/gtkdoccommentscanner.vala        |  644 +++++++++++++
 .../importer/valadocdocumentationimporter.vala     |    2 +-
 src/libvaladoc/taglets/tagletlink.vala             |    6 +-
 src/libvaladoc/taglets/tagletsee.vala              |    2 +-
 20 files changed, 1802 insertions(+), 45 deletions(-)
---
diff --git a/src/doclets/htm/doclet.vala b/src/doclets/htm/doclet.vala
index 23dec68..7613f17 100755
--- a/src/doclets/htm/doclet.vala
+++ b/src/doclets/htm/doclet.vala
@@ -74,9 +74,9 @@ public class Valadoc.HtmlDoclet : Valadoc.Html.BasicDoclet {
 		string path = GLib.Path.build_filename ( this.settings.path, pkg_name );
 
 		var rt = DirUtils.create (path, 0777);
-		rt = DirUtils.create (GLib.Path.build_filename ( path, "img" ), 0777);
+		rt = DirUtils.create (GLib.Path.build_filename (path, "img"), 0777);
 
-		GLib.FileStream file = GLib.FileStream.open (GLib.Path.build_filename ( path, "index.htm" ), "w");
+		GLib.FileStream file = GLib.FileStream.open (GLib.Path.build_filename (path, "index.htm"), "w");
 		writer = new Html.MarkupWriter (file);
 		_renderer.set_writer (writer);
 		write_file_header (this.css_path, this.js_path, pkg_name);
diff --git a/src/driver/0.10.x/treebuilder.vala b/src/driver/0.10.x/treebuilder.vala
index c3543a5..6bc66b4 100644
--- a/src/driver/0.10.x/treebuilder.vala
+++ b/src/driver/0.10.x/treebuilder.vala
@@ -271,7 +271,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	}
 
 	private SourceFile register_source_file (PackageMetaData meta_data, Vala.SourceFile source_file) {
-		SourceFile file = new SourceFile (source_file.get_relative_filename (), source_file.get_csource_filename ());
+		SourceFile file = new SourceFile (meta_data.package, source_file.get_relative_filename (), source_file.get_csource_filename ());
 		files.set (source_file, file);
 
 		meta_data.register_source_file (source_file);
diff --git a/src/driver/0.11.0/treebuilder.vala b/src/driver/0.11.0/treebuilder.vala
index 6ebc871..ba51196 100644
--- a/src/driver/0.11.0/treebuilder.vala
+++ b/src/driver/0.11.0/treebuilder.vala
@@ -273,7 +273,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	}
 
 	private SourceFile register_source_file (PackageMetaData meta_data, Vala.SourceFile source_file) {
-		SourceFile file = new SourceFile (source_file.get_relative_filename (), source_file.get_csource_filename ());
+		SourceFile file = new SourceFile (meta_data.package, source_file.get_relative_filename (), source_file.get_csource_filename ());
 		files.set (source_file, file);
 
 		meta_data.register_source_file (source_file);
diff --git a/src/driver/0.11.x/treebuilder.vala b/src/driver/0.11.x/treebuilder.vala
index f297a1e..130a3ca 100644
--- a/src/driver/0.11.x/treebuilder.vala
+++ b/src/driver/0.11.x/treebuilder.vala
@@ -224,7 +224,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	}
 
 	private SourceFile register_source_file (PackageMetaData meta_data, Vala.SourceFile source_file) {
-		SourceFile file = new SourceFile (source_file.get_relative_filename (), source_file.get_csource_filename ());
+		SourceFile file = new SourceFile (meta_data.package, source_file.get_relative_filename (), source_file.get_csource_filename ());
 		files.set (source_file, file);
 
 		meta_data.register_source_file (source_file);
diff --git a/src/driver/0.12.x/treebuilder.vala b/src/driver/0.12.x/treebuilder.vala
index 455a0e6..ac91568 100644
--- a/src/driver/0.12.x/treebuilder.vala
+++ b/src/driver/0.12.x/treebuilder.vala
@@ -273,7 +273,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	}
 
 	private SourceFile register_source_file (PackageMetaData meta_data, Vala.SourceFile source_file) {
-		SourceFile file = new SourceFile (source_file.get_relative_filename (), source_file.get_csource_filename ());
+		SourceFile file = new SourceFile (meta_data.package, source_file.get_relative_filename (), source_file.get_csource_filename ());
 		files.set (source_file, file);
 
 		meta_data.register_source_file (source_file);
diff --git a/src/driver/0.13.x/treebuilder.vala b/src/driver/0.13.x/treebuilder.vala
index 9739d92..9cfac52 100644
--- a/src/driver/0.13.x/treebuilder.vala
+++ b/src/driver/0.13.x/treebuilder.vala
@@ -392,7 +392,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	}
 
 	private SourceFile register_source_file (PackageMetaData meta_data, Vala.SourceFile source_file) {
-		SourceFile file = new SourceFile (source_file.get_relative_filename (), source_file.get_csource_filename ());
+		SourceFile file = new SourceFile (meta_data.package, source_file.get_relative_filename (), source_file.get_csource_filename ());
 		files.set (source_file, file);
 
 		meta_data.register_source_file (source_file);
diff --git a/src/driver/0.14.x/Makefile.am b/src/driver/0.14.x/Makefile.am
index a2c24a1..b8bb596 100755
--- a/src/driver/0.14.x/Makefile.am
+++ b/src/driver/0.14.x/Makefile.am
@@ -2,8 +2,6 @@ NULL =
 
 VERSIONED_VAPI_DIR=`pkg-config libvala-0.14 --variable vapidir`
 
-
-
 AM_CFLAGS =  -g \
 	-DPACKAGE_ICONDIR=\"$(datadir)/valadoc/icons/\" \
 	-I ../../libvaladoc/ \
diff --git a/src/driver/0.14.x/treebuilder.vala b/src/driver/0.14.x/treebuilder.vala
index 691fadd..daff9b2 100644
--- a/src/driver/0.14.x/treebuilder.vala
+++ b/src/driver/0.14.x/treebuilder.vala
@@ -326,7 +326,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	}
 
 	private SourceFile register_source_file (PackageMetaData meta_data, Vala.SourceFile source_file) {
-		SourceFile file = new SourceFile (source_file.get_relative_filename (), source_file.get_csource_filename ());
+		SourceFile file = new SourceFile (meta_data.package, source_file.get_relative_filename (), source_file.get_csource_filename ());
 		files.set (source_file, file);
 
 		meta_data.register_source_file (source_file);
diff --git a/src/driver/0.16.x/treebuilder.vala b/src/driver/0.16.x/treebuilder.vala
index 7d52395..512d7b1 100644
--- a/src/driver/0.16.x/treebuilder.vala
+++ b/src/driver/0.16.x/treebuilder.vala
@@ -82,7 +82,11 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 				foreach (Vala.Comment c in vns.get_comments()) {
 					if (c.source_reference.file == vns.source_reference.file) {
 						Vala.SourceReference pos = c.source_reference;
-						comment = new SourceComment (c.content, file, pos.first_line, pos.first_column, pos.last_line, pos.last_column);
+						if (c is Vala.GirComment) {
+							comment = new GirSourceComment (c.content, file, pos.first_line, pos.first_column, pos.last_line, pos.last_column);
+						} else {
+							comment = new SourceComment (c.content, file, pos.first_line, pos.first_column, pos.last_line, pos.last_column);
+						}
 						break;
 					}
 				}
@@ -290,7 +294,24 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 		if (comment != null) {
 			Vala.SourceReference pos = comment.source_reference;
 			SourceFile file = files.get (pos.file);
-			return new SourceComment (comment.content, file, pos.first_line, pos.first_column, pos.last_line, pos.last_column);
+			if (comment is Vala.GirComment) {
+				var tmp = new GirSourceComment (comment.content, file, pos.first_line, pos.first_column, pos.last_line, pos.last_column);
+				if (((Vala.GirComment) comment).return_content != null) {
+					Vala.SourceReference return_pos = ((Vala.GirComment) comment).return_content.source_reference;
+					tmp.return_comment = new SourceComment (((Vala.GirComment) comment).return_content.content, file, return_pos.first_line, return_pos.first_column, return_pos.last_line, return_pos.last_column);
+				}
+
+				Vala.MapIterator<string, Vala.Comment> it = ((Vala.GirComment) comment).parameter_iterator ();
+				while (it.next ()) {
+					Vala.Comment vala_param = it.get_value ();
+					Vala.SourceReference param_pos = vala_param.source_reference;
+					var param_comment = new SourceComment (vala_param.content, file, param_pos.first_line, param_pos.first_column, param_pos.last_line, param_pos.last_column);
+					tmp.add_parameter_content (it.get_key (), param_comment);
+				}
+				return tmp;
+			} else {
+				return new SourceComment (comment.content, file, pos.first_line, pos.first_column, pos.last_line, pos.last_column);
+			}
 		}
 
 		return null;
@@ -326,7 +347,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	}
 
 	private SourceFile register_source_file (PackageMetaData meta_data, Vala.SourceFile source_file) {
-		SourceFile file = new SourceFile (source_file.get_relative_filename (), source_file.get_csource_filename ());
+		SourceFile file = new SourceFile (meta_data.package, source_file.get_relative_filename (), source_file.get_csource_filename ());
 		files.set (source_file, file);
 
 		meta_data.register_source_file (source_file);
@@ -340,7 +361,6 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 		}
 
 		SourceFile? file = files.get (source_ref.file);
-
 		assert (file != null);
 		return file;
 	}
@@ -534,6 +554,11 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 	// Vala tree creation:
 	//
 
+	private string get_package_name (string path) {
+		string file_name = Path.get_basename (path);
+		return file_name.substring (0, file_name.last_index_of_char ('.'));
+	}
+
 	private bool add_package (Vala.CodeContext context, string pkg) {
 		// ignore multiple occurences of the same package
 		if (context.has_package (pkg)) {
@@ -636,8 +661,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 
 					context.add_source_file (source_file);
 				} else if (source.has_suffix (".vapi") || source.has_suffix (".gir")) {
-					string file_name = Path.get_basename (source);
-					file_name = file_name.substring (0, file_name.last_index_of_char ('.'));
+					string file_name = get_package_name (source);
 
 					var vfile = new Vala.SourceFile (context, Vala.SourceFileType.PACKAGE, rpath);
 					context.add_source_file (vfile);
@@ -1126,6 +1150,15 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor {
 			return null;
 		}
 
+		// TODO: Register all packages here
+		// register packages included by gir-files
+		foreach (Vala.SourceFile vfile in context.get_source_files ()) {
+			if (vfile.file_type == Vala.SourceFileType.PACKAGE && vfile.get_nodes ().size > 0 && files.contains (vfile) == false) {
+				Package vdpkg = new Package (get_package_name (vfile.filename), true, null);
+				register_source_file (register_package (vdpkg), vfile);
+			}
+		}
+
 		context.accept(this);
 
 		return (reporter.errors == 0)? tree : null;
diff --git a/src/libvaladoc/Makefile.am b/src/libvaladoc/Makefile.am
index 9fba548..14a8bfa 100755
--- a/src/libvaladoc/Makefile.am
+++ b/src/libvaladoc/Makefile.am
@@ -36,11 +36,14 @@ libvaladoc_la_VALASOURCES = \
 	documentation/documentationparser.vala \
 	documentation/wiki.vala \
 	documentation/wikiscanner.vala \
+	documentation/gtkdoccommentparser.vala \
+	documentation/gtkdoccommentscanner.vala \
 	importer/documentationimporter.vala \
 	importer/valadocdocumentationimporter.vala \
 	importer/valadocdocumentationimporterscanner.vala \
 	api/symbolaccessibility.vala \
 	api/sourcecomment.vala \
+	api/girsourcecomment.vala \
 	api/attributeargument.vala \
 	api/attribute.vala \
 	api/array.vala \
diff --git a/src/libvaladoc/api/girsourcecomment.vala b/src/libvaladoc/api/girsourcecomment.vala
new file mode 100644
index 0000000..e554092
--- /dev/null
+++ b/src/libvaladoc/api/girsourcecomment.vala
@@ -0,0 +1,55 @@
+/* sourcecomment.vala
+ *
+ * Copyright (C) 2011  Florian Brosch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	Florian Brosch <flo brosch gmail com>
+ */
+
+
+using Gee;
+
+
+/**
+ * A documentation comment used by valadoc
+ */
+public class Valadoc.Api.GirSourceComment : SourceComment {
+	private Map<string, SourceComment> parameters = new HashMap<string, SourceComment> ();
+
+	public SourceComment return_comment { set; get; }
+
+	public MapIterator<string, SourceComment> parameter_iterator () {
+		return parameters.map_iterator ();
+	}
+
+	public void add_parameter_content (string param_name, SourceComment comment) {
+		this.parameters.set (param_name, comment);
+	}
+
+	public SourceComment? get_parameter_comment (string param_name) {
+		if (parameters == null) {
+			return null;
+		}
+
+		return parameters.get (param_name);
+	}
+
+	public GirSourceComment (string content, SourceFile file, int first_line, int first_column, int last_line, int last_column) {
+		base (content, file, first_line, first_column, last_line, last_column);
+	}
+}
+
diff --git a/src/libvaladoc/api/sourcefile.vala b/src/libvaladoc/api/sourcefile.vala
index 6ac6720..09b821f 100644
--- a/src/libvaladoc/api/sourcefile.vala
+++ b/src/libvaladoc/api/sourcefile.vala
@@ -24,6 +24,11 @@
  * Represents a source file
  */
 public class Valadoc.Api.SourceFile : Object {
+	public Package package {
+		private set;
+		get;
+	}
+
 	public string relative_path {
 		private set;
 		get;
@@ -38,9 +43,10 @@ public class Valadoc.Api.SourceFile : Object {
 		return Path.get_basename (relative_path);
 	}
 
-	public SourceFile (string relative_path, string? relative_c_path) {
+	public SourceFile (Package package, string relative_path, string? relative_c_path) {
 		this.relative_c_path = relative_c_path;
 		this.relative_path = relative_path;
+		this.package = package;
 	}
 }
 
diff --git a/src/libvaladoc/api/tree.vala b/src/libvaladoc/api/tree.vala
index bddb1bf..1298d06 100755
--- a/src/libvaladoc/api/tree.vala
+++ b/src/libvaladoc/api/tree.vala
@@ -143,21 +143,25 @@ public class Valadoc.Api.Tree {
 		// absolute
 		foreach (Package package in packages) {
 			// search in root namespace
-			node = search_relative_to (package.find_by_name (""), path);
-			if (node != null) {
-				return node;
+
+			Node? global = package.find_by_name ("");
+			if (global != null) {
+				node = search_relative_to (global, path);
+				if (node != null) {
+					return node;
+				}
 			}
 		}
 
 		return null;
 	}
 
-	public Node? search_symbol_cstr (string cname) {
+	public Node? search_symbol_cstr (Node? element, string cname) {
 		if (_cresolver == null) {
 			_cresolver = new CTypeResolver (this);
 		}
 
-		return _cresolver.resolve_symbol (cname);
+		return _cresolver.resolve_symbol (element, cname);
 	}
 
 	public Node? search_symbol_str (Node? element, string symname) {
diff --git a/src/libvaladoc/ctyperesolver.vala b/src/libvaladoc/ctyperesolver.vala
index 87fc6f3..a7adac8 100755
--- a/src/libvaladoc/ctyperesolver.vala
+++ b/src/libvaladoc/ctyperesolver.vala
@@ -34,40 +34,99 @@ public class Valadoc.CTypeResolver : Visitor {
 		tree.accept (this);
 	}
 
+
+	private string convert_array_to_camelcase (string[] elements) {
+		StringBuilder builder = new StringBuilder ();
+
+		foreach (string element in elements) {
+			builder.append_c (((char[])element)[0].toupper ());
+			builder.append (element.next_char ().down ());
+		}
+
+		return (owned) builder.str;
+	}
+
+	private bool is_capitalized_and_underscored (string name) {
+		unowned string pos;
+
+		unichar c = name.get_char ();
+
+
+		if (c < 'A' || c > 'Z') {
+			return false;
+		}
+
+		bool last_was_underscore = false;
+		for (c = (pos = name).get_char (); c != '\0' ; c = (pos = pos.next_char ()).get_char ()) {
+			if ((c != '_'  && !(c >= 'A' && c <= 'Z')) || (last_was_underscore && c == '_')) {
+				return false;
+			}
+
+			last_was_underscore = (c == '_');
+		}
+
+		return !last_was_underscore;
+	}
+
+	private string? translate_cname (string name) {
+		if (is_capitalized_and_underscored (name)) {
+			string[] segments = name.split ("_");
+			unowned string last_segment = segments[segments.length - 1];
+			if (last_segment == "ERROR") {
+			} else if (last_segment == "TYPE") {
+				segments.resize (segments.length - 1);
+			} else {
+				return null;
+			}
+
+			return convert_array_to_camelcase (segments);
+		}
+
+		int length = name.length;
+		if (length > 5 && name.has_suffix ("Iface")) {
+			return name.substring (0, length - 5);
+		} else if (length > 5 && name.has_suffix ("Class")) {
+			return name.substring (0, length - 5);
+		}
+
+		return null;
+	}
+
 	/**
 	 * Resolves symbols by C-names
 	 *
 	 * @param name a C-name
 	 * @return the resolved node or null
 	 */
-	public Api.Node? resolve_symbol (string _name) {
+	public Api.Node? resolve_symbol (Api.Node? element, string _name) {
 		string name = _name.replace ("-", "_");
 
+		if (element != null && _name.has_prefix (":")) {
+			Item parent = element;
+			while (parent != null && !(parent is Class || parent is Interface)) {
+				parent = parent.parent;
+			}
+
+			if (parent is Class && ((Class) parent).get_cname () != null) {
+				name = ((Class) parent).get_cname () + name;
+			} else if (parent is Interface && ((Interface) parent).get_cname () != null) {
+				name = ((Interface) parent).get_cname () + name;
+			} else {
+				return null;
+			}
+
+		}
+
 		Api.Node? node = nodes.get (name);
 		if (node != null) {
 			return node;
 		}
 
-		var name_length = name.length;
-		if (name_length > 5 && name.has_suffix ("Class")) {
-			return nodes.get (name.substring (0, name_length - 5));
+		string? alternative = translate_cname (_name);
+		if (alternative != null) {
+			return nodes.get (alternative);
 		}
 
-		/*
-		for (int i = 0; name[i] != '\0' ; i++) {
-			if (name[i] == ':' && name[i+1] == ':') {
-				string first_part = name.substring (0, i - 1);
-				string second_part = name.substring (i + 2, -1);
-				string nick = first_part + ":" + second_part;
-				return nodes.get (nick);
-			} else if (name[i] == ':') {
-				string first_part = name.substring (0, i);
-				string second_part = name.substring (i + 1, -1);
-				string nick = first_part + "::" + second_part;
-				return nodes.get (nick);
-			}
-		} */
-
 		return null;
 	}
 
diff --git a/src/libvaladoc/documentation/documentationparser.vala b/src/libvaladoc/documentation/documentationparser.vala
index 92aa698..b2a96f0 100755
--- a/src/libvaladoc/documentation/documentationparser.vala
+++ b/src/libvaladoc/documentation/documentationparser.vala
@@ -43,9 +43,13 @@ public class Valadoc.DocumentationParser : Object, ResourceLocator {
 		_comment_parser = new Parser (_settings, _comment_scanner, _reporter);
 		_comment_scanner.set_parser (_comment_parser);
 
+		gtkdoc_parser = new Gtkdoc.Parser (settings, reporter, tree, modules);
+
 		init_valadoc_rules ();
 	}
 
+	private Gtkdoc.Parser gtkdoc_parser;
+
 	private Settings _settings;
 	private ErrorReporter _reporter;
 	private Api.Tree _tree;
@@ -61,7 +65,12 @@ public class Valadoc.DocumentationParser : Object, ResourceLocator {
 	private Scanner _scanner;
 
 	public Comment? parse (Api.Node element, Api.SourceComment comment) {
-		return parse_comment_str (element, comment.content, comment.file.get_name (), comment.first_line, comment.first_column);
+		if (comment is Api.GirSourceComment) {
+			Comment doc_comment = gtkdoc_parser.parse (element, (Api.GirSourceComment) comment);
+			return doc_comment;
+		} else {
+			return parse_comment_str (element, comment.content, comment.file.get_name (), comment.first_line, comment.first_column);
+		}
 	}
 
 	public Comment? parse_comment_str (Api.Node element, string content, string filename, int first_line, int first_column) {
diff --git a/src/libvaladoc/documentation/gtkdoccommentparser.vala b/src/libvaladoc/documentation/gtkdoccommentparser.vala
new file mode 100644
index 0000000..48e5227
--- /dev/null
+++ b/src/libvaladoc/documentation/gtkdoccommentparser.vala
@@ -0,0 +1,942 @@
+/* gtkcommentparser.vala
+ *
+ * Copyright (C) 2011  Florian Brosch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	Florian Brosch <flo brosch gmail com>
+ */
+
+
+using Valadoc.Content;
+using Valadoc.Gtkdoc;
+using Gee;
+
+
+
+public class Valadoc.Gtkdoc.Parser : Object, ResourceLocator {
+	private Scanner scanner = new Scanner ();
+	private Token current;
+
+	private LinkedList<string> stack = new LinkedList<string> ();
+
+	private ContentFactory factory;
+	private ErrorReporter reporter;
+	private Settings settings;
+	private Api.Tree tree;
+
+	private bool show_warnings;
+	private Api.SourceComment comment;
+
+	private string[]? comment_lines;
+
+	private void reset (Api.SourceComment comment) {
+		this.scanner.reset (comment.content);
+		this.show_warnings = !comment.file.package.is_package || settings.verbose;
+		this.comment_lines = null;
+		this.comment = comment;
+		current = null;
+		stack.clear ();
+	}
+
+	private void report_unexpected_token (Token got, string expected) {
+		if (this.show_warnings) {
+			return ;
+		}
+
+		int startpos = (got.line == 0)? comment.first_column + got.first_column : got.first_column;
+		int endpos = (got.line == 0)? comment.first_column + got.last_column : got.last_column;
+
+		if (this.comment_lines == null) {
+			this.comment_lines = this.comment.content.split ("\n");
+		}
+
+		this.reporter.warning (this.comment.file.get_name (), comment.first_line + got.line, startpos + 1, endpos + 1, this.comment_lines[got.line], "Unexpected Token: %s (Expected: %s)", got.to_string (), expected);
+	}
+
+	public Parser (Settings settings, ErrorReporter reporter, Api.Tree tree, ModuleLoader modules) {
+		this.factory = new ContentFactory (settings, this, modules);
+		this.reporter = reporter;
+		this.settings = settings;
+		this.tree = tree;
+	}
+
+	public Comment? parse (Api.Node element, Api.GirSourceComment gir_comment) {
+		Comment? comment = this.parse_main_content (gir_comment);
+		if (comment == null) {
+			return null;
+		}
+
+		if (gir_comment.return_comment != null) {
+			Taglet? taglet = this.parse_block_taglet (gir_comment.return_comment, "return");
+			if (taglet == null) {
+				return null;
+			}
+
+			comment.taglets.add (taglet);
+		}
+
+		MapIterator<string, Api.SourceComment> iter = gir_comment.parameter_iterator ();
+		for (bool has_next = iter.first (); has_next; has_next = iter.next ()) {
+			Taglets.Param? taglet = this.parse_block_taglet (iter.get_value (), "param") as Taglets.Param;
+			if (taglet == null) {
+				return null;
+			}
+
+			taglet.parameter_name = iter.get_key ();
+			comment.taglets.add (taglet);
+		}
+
+		comment.check (tree, element, reporter, settings);
+		return comment;
+	}
+
+	private Taglet? parse_block_taglet (Api.SourceComment gir_comment, string taglet_name) {
+		this.reset (gir_comment);
+		current = null;
+		next ();
+
+		parse_docbook_spaces (false);
+		var ic = parse_inline_content ();
+		parse_docbook_spaces (false);
+
+		if (current.type != TokenType.EOF) {
+			this.report_unexpected_token (current, "<EOF>");
+			return null;
+		}
+
+		InlineContent? taglet = factory.create_taglet (taglet_name) as InlineContent;
+		assert (taglet != null);
+		taglet.content.add (ic);
+		return taglet as Taglet;
+	}
+
+	private Comment? parse_main_content (Api.GirSourceComment gir_comment) {
+		this.reset (gir_comment);
+		current = null;
+
+		next ();
+
+		Token tmp = null;
+		parse_docbook_spaces (false);
+
+		Comment comment = factory.create_comment ();
+		while (current.type != TokenType.EOF && tmp != current) {
+			tmp = current;
+			var ic = parse_inline_content ();
+			if (ic != null && ic.content.size > 0) {
+				Paragraph p = factory.create_paragraph ();
+				p.content.add (ic);
+				comment.content.add (p);
+			}
+
+			var bc = parse_block_content ();
+			if (bc != null && bc.size > 0) {
+				comment.content.add_all (bc);
+			}
+		}
+
+		if (current.type != TokenType.EOF) {
+			this.report_unexpected_token (current, "<INLINE|BLOCK>");
+			return null;
+		}
+
+		return comment;
+	}
+
+
+
+	//
+	// Common:
+	//
+
+	private Token next () {
+		current = scanner.next ();
+		return current;
+	}
+
+	private bool ignore_current_xml_close () {
+		if (current.type != TokenType.XML_CLOSE) {
+			return false;
+		}
+
+		string name = current.content;
+		if ((name in stack) == false) {
+			return true;
+		}
+
+		return false;
+	}
+
+	private bool check_xml_open_tag (string tagname) {
+		if (current.type == TokenType.XML_OPEN && current.content != tagname) {
+			return false;
+		}
+
+		stack.offer_head (tagname);
+		return true;
+	}
+
+	private bool check_xml_close_tag (string tagname) {
+		if (current.type == TokenType.XML_CLOSE && current.content != tagname) {
+			return false;
+		}
+
+		if (stack.poll_head () == tagname) {
+			stack.peek_head ();
+		}
+
+		return true;
+	}
+
+
+	private void parse_docbook_spaces (bool accept_paragraphs = true) {
+		while (true) {
+			if (current.type == TokenType.SPACE) {
+				next ();
+			} else if (current.type == TokenType.NEWLINE) {
+				next ();
+			} else if (accept_paragraphs && current.type == TokenType.GTKDOC_PARAGRAPH) {
+				next ();
+			} else {
+				break;
+			}
+		}
+	}
+
+
+
+	//
+	// Rules, Ground:
+	//
+
+	private Inline? parse_docbook_link_tempalte (string tagname) {
+		if (!check_xml_open_tag (tagname)) {
+			this.report_unexpected_token (current, "<%s>".printf (tagname));
+			return null;
+		}
+
+		StringBuilder builder = new StringBuilder ();
+		string url = current.attributes.get ("linkend");
+		next ();
+
+		// TODO: check xml
+		while (current.type != TokenType.XML_CLOSE && current.content != tagname && current.type != TokenType.EOF) {
+			if (current.type == TokenType.XML_OPEN) {
+			} else if (current.type == TokenType.XML_CLOSE) {
+			} else if (current.type == TokenType.XML_COMMENT) {
+			} else {
+				builder.append (current.content);
+			}
+
+			next ();
+		}
+
+		var link = factory.create_link ();
+		link.url = url;
+
+		if (builder.len == 0) {
+			link.content.add (factory.create_text (url));
+		} else {
+			link.content.add (factory.create_text (builder.str));
+		}
+
+		if (!check_xml_close_tag (tagname)) {
+			this.report_unexpected_token (current, "</%s>".printf (tagname));
+			return link;
+		}
+
+		next ();
+		return link;
+	}
+
+	private InlineTaglet? parse_symbol_link (string tagname) {
+		if (!check_xml_open_tag (tagname)) {
+			this.report_unexpected_token (current, "<%s>".printf (tagname));
+			return null;
+		}
+
+		if (next ().type == TokenType.SPACE) {
+			next ();
+		}
+
+		InlineTaglet? taglet = null;
+
+		if (current.type == TokenType.GTKDOC_FUNCTION || current.type == TokenType.GTKDOC_CONST || current.type == TokenType.GTKDOC_TYPE || current.type == TokenType.WORD || current.type == TokenType.GTKDOC_PROPERTY || current.type == TokenType.GTKDOC_SIGNAL) {
+			taglet = this.create_type_link (current.content) as InlineTaglet;
+			assert (taglet != null);
+		}
+
+		if (next ().type == TokenType.SPACE) {
+			next ();
+		}
+
+		if (!check_xml_close_tag (tagname)) {
+			this.report_unexpected_token (current, "</%s>".printf (tagname));
+			return taglet;
+		}
+
+		next ();
+		return taglet;
+	}
+
+	private void parse_anchor () {
+		if (!check_xml_open_tag ("anchor")) {
+			this.report_unexpected_token (current, "<anchor>");
+			return;
+		}
+
+		string id = current.attributes.get ("id");
+		next ();
+		// TODO register xref
+
+		if (!check_xml_close_tag ("anchor")) {
+			this.report_unexpected_token (current, "</anchor>");
+			return;
+		}
+
+		next ();
+	}
+
+	private void parse_xref () {
+		if (!check_xml_open_tag ("xref")) {
+			this.report_unexpected_token (current, "<xref>");
+			return;
+		}
+
+		string linkend = current.attributes.get ("linkend");
+		next ();
+		// TODO register xref
+
+		if (!check_xml_close_tag ("xref")) {
+			this.report_unexpected_token (current, "</xref>");
+			return;
+		}
+
+		next ();
+	}
+
+	private Run? parse_highlighted_template (string tag_name, Run.Style style) {
+		if (!check_xml_open_tag (tag_name)) {
+			this.report_unexpected_token (current, "<%s>".printf (tag_name));
+			return null;
+		}
+
+		next ();
+		Run run = parse_inline_content ();
+		if (run.style != Run.Style.NONE && run.style != style) {
+			Run tmp = factory.create_run (style);
+			tmp.content.add (run);
+			run = tmp;
+		} else {
+			run.style = style;
+		}
+
+		if (!check_xml_close_tag (tag_name)) {
+			this.report_unexpected_token (current, "</%s>".printf (tag_name));
+			return run;
+		}
+
+		next ();
+		return run;
+	}
+
+	private ListItem?  parse_docbook_listitem () {
+		if (!check_xml_open_tag ("listitem")) {
+			this.report_unexpected_token (current, "<listitem>");
+			return null;
+		}
+
+		next ();
+
+		ListItem item = factory.create_list_item ();
+	
+		while (current.type != TokenType.XML_CLOSE && current.type != TokenType.EOF) {
+			if (current.type == TokenType.XML_OPEN && current.content == "para") {
+				foreach (Block block in parse_docbook_para ()) {
+					if (block is Paragraph) {
+						if (item.content.size > 0) {
+							item.content.add (factory.create_text ("\n"));
+						}
+
+						item.content.add_all (((Paragraph) block).content);
+					} else {
+						// TODO: extend me
+						this.report_unexpected_token (current, "<para>|</listitem>");
+						return null;
+					}
+				}
+			} else {
+				Token tmp_t = current;
+				parse_inline_content ();
+				if (tmp_t == current) {
+					break;
+				}
+			}
+		}
+
+		if (!check_xml_close_tag ("listitem")) {
+			this.report_unexpected_token (current, "</listitem>");
+			return item;
+		}
+
+		next ();
+		return item;
+	}
+
+	private LinkedList<Block>? parse_docbook_information_box_template (string tagname) {
+		if (!check_xml_open_tag (tagname)) {
+			this.report_unexpected_token (current, "<%s>".printf (tagname));
+			return null;
+		}
+
+		next ();
+		parse_docbook_spaces ();
+
+		LinkedList<Block> content = new LinkedList<Block> ();
+
+		var header_run = factory.create_run (Run.Style.BOLD);
+		header_run.content.add (factory.create_text ("Note:"));
+
+		while (current.type != TokenType.XML_CLOSE && current.type != TokenType.EOF) {
+			if (current.type == TokenType.XML_OPEN && current.content == "para") {
+				var paragraphs = parse_docbook_para ();
+				if (header_run != null) {
+					content.add_all (paragraphs);
+				} else {
+					Paragraph fp = paragraphs.first ();
+					fp.content.insert (0, factory.create_text (" "));
+					fp.content.insert (0, header_run);
+				}
+			} else {
+				Token tmp_t = current;
+
+				Run? inline_run = parse_inline_content ();
+				Paragraph p = factory.create_paragraph ();
+	
+				if (content != null) {
+					p.content.add (header_run);
+					p.content.add (factory.create_text (" "));
+					header_run = null;
+				}
+
+				p.content.add (inline_run);
+				content.add (p);
+
+				if (tmp_t == current) {
+					break;
+				}
+			}
+		}
+
+		//parse_block_content ();
+		parse_docbook_spaces ();
+
+		if (!check_xml_close_tag (tagname)) {
+			this.report_unexpected_token (current, "</%s>".printf (tagname));
+			return content;
+		}
+
+		next ();
+		return content;
+	}
+
+	private LinkedList<Block>? parse_docbook_note () {
+		return parse_docbook_information_box_template ("note");
+	}
+
+	private LinkedList<Block>? parse_docbook_warning () {
+		return parse_docbook_information_box_template ("warning");
+	}
+
+	private Content.List? parse_docbook_itemizedlist () {
+		if (!check_xml_open_tag ("itemizedlist")) {
+			this.report_unexpected_token (current, "<itemizedlist>");
+			return null;
+		}
+
+		next ();
+
+		parse_docbook_spaces ();
+
+		Content.List list = factory.create_list ();
+		list.bullet = Content.List.Bullet.UNORDERED;
+
+		while (current.type == TokenType.XML_OPEN) {
+			if (current.content == "listitem") {
+				list.items.add (parse_docbook_listitem ());
+			} else {
+				break;
+			}
+
+			parse_docbook_spaces ();
+		}
+
+		if (!check_xml_close_tag ("itemizedlist")) {
+			this.report_unexpected_token (current, "</itemizedlist>");
+			return list;
+		}
+
+		next ();
+		return list;
+	}
+
+	private Paragraph? parse_gtkdoc_paragraph () {
+		if (current.type != TokenType.GTKDOC_PARAGRAPH) {
+			this.report_unexpected_token (current, "<GTKDOC-PARAGRAPH>");
+			return null;
+		}
+
+		next ();
+
+		Paragraph p = factory.create_paragraph ();
+
+		Run? run = parse_inline_content ();
+		p.content.add (run);
+		return p;
+	}
+
+	private LinkedList<Paragraph>? parse_docbook_para () {
+		if (!check_xml_open_tag ("para")) {
+			this.report_unexpected_token (current, "<para>");
+			return null;
+		}
+
+		next ();
+
+		LinkedList<Paragraph> content = new LinkedList<Paragraph> ();
+
+		Token tmp = null;
+		while (tmp != current) {
+			tmp = current;
+			parse_docbook_spaces ();
+
+			Run? run = parse_inline_content ();
+			if (run != null && run.content.size > 0) {
+				Paragraph p = factory.create_paragraph ();
+				p.content.add (run);
+				content.add (p);
+				continue;
+			}
+
+			LinkedList<Block> lst = parse_block_content ();
+			if (lst != null && run.content.size > 0) {
+				content.add_all (lst);
+				continue;
+			}
+		}
+
+		if (!check_xml_close_tag ("para")) {
+			this.report_unexpected_token (current, "</para>");
+			return content;
+		}
+
+		next ();
+		return content;
+	}
+
+	private Paragraph? parse_gtkdoc_source () {
+		if (current.type != TokenType.GTKDOC_SOURCE_OPEN) {
+			this.report_unexpected_token (current, "|[");
+			return null;
+		}
+
+
+		StringBuilder builder = new StringBuilder ();
+
+		for (next (); current.type != TokenType.EOF && current.type != TokenType.GTKDOC_SOURCE_CLOSE; next ()) {
+			if (current.type == TokenType.WORD) {
+				builder.append (current.content);
+			} else if (current.type != TokenType.XML_COMMENT) {
+				builder.append_len (current.start, current.length);
+			}
+		}
+
+		SourceCode src = factory.create_source_code ();
+		src.language = SourceCode.Language.C;
+		src.code = builder.str;
+
+		Paragraph p = factory.create_paragraph ();
+		p.content.add (src);
+
+		if (current.type != TokenType.GTKDOC_SOURCE_CLOSE) {
+			this.report_unexpected_token (current, "|]");
+			return p;
+		}
+
+		next ();
+		return p;
+	}
+
+	private Paragraph? parse_docbook_title () {
+		if (!check_xml_open_tag ("title")) {
+			this.report_unexpected_token (current, "<title>");
+			return null;
+		}
+
+		next ();
+
+		Paragraph p = factory.create_paragraph ();
+		Run content = parse_inline_content ();
+		content.content.add (factory.create_text (":"));
+		content.style = Run.Style.BOLD;
+		p.content.add (content);
+
+		if (!check_xml_close_tag ("title")) {
+			this.report_unexpected_token (current, "</title>");
+			return p;
+		}
+
+		next ();
+		return p;
+	}
+
+	private Embedded? parse_docbook_inlinegraphic () {
+		if (!check_xml_open_tag ("inlinegraphic")) {
+			this.report_unexpected_token (current, "<inlinegraphic>");
+			return null;
+		}
+
+		Embedded e = factory.create_embedded ();
+		e.url = current.attributes.get ("fileref");
+
+		next ();
+		parse_docbook_spaces ();
+
+		if (!check_xml_close_tag ("inlinegraphic")) {
+			this.report_unexpected_token (current, "</inlinegrapic>");
+			return e;
+		}
+
+		next ();
+		return e;
+	}
+
+	private Paragraph? parse_docbook_programlisting () {
+		if (!check_xml_open_tag ("programlisting")) {
+			this.report_unexpected_token (current, "<programlisting>");
+			return null;
+		}
+
+		StringBuilder builder = new StringBuilder ();
+
+		for (next (); current.type != TokenType.EOF && !(current.type == TokenType.XML_CLOSE && current.content == "programlisting"); next ()) {
+			if (current.type == TokenType.WORD) {
+				builder.append (current.content);
+			} else if (current.type != TokenType.XML_COMMENT) {
+				builder.append_len (current.start, current.length);
+			}
+		}
+
+		SourceCode src = factory.create_source_code ();
+		src.language = SourceCode.Language.C;
+		src.code = builder.str;
+
+		Paragraph p = factory.create_paragraph ();
+		p.content.add (src);
+
+		if (!check_xml_close_tag ("programlisting")) {
+			this.report_unexpected_token (current, "</programlisting>");
+			return p;
+		}
+
+		next ();
+		return p;
+	}
+
+	private LinkedList<Block>? parse_docbook_informalexample () {
+		if (!check_xml_open_tag ("informalexample")) {
+			this.report_unexpected_token (current, "<informalexample>");
+			return null;
+		}
+
+		next ();
+
+		parse_docbook_spaces ();
+
+		LinkedList<Block> content = new LinkedList<Block> ();
+
+		if (current.type == TokenType.XML_OPEN && current.content == "title") {
+			append_block_content_not_null (content, parse_docbook_title ());
+			parse_docbook_spaces ();
+		}
+
+		append_block_content_not_null (content, parse_docbook_programlisting ());
+
+		parse_docbook_spaces ();
+
+		if (!check_xml_close_tag ("informalexample")) {
+			this.report_unexpected_token (current, "</informalexample>");
+			return content;
+		}
+
+		next ();
+		return content;
+	}
+
+	private LinkedList<Block>? parse_docbook_example () {
+		if (!check_xml_open_tag ("example")) {
+			this.report_unexpected_token (current, "<example>");
+			return null;
+		}
+
+		next ();
+
+		parse_docbook_spaces ();
+
+		LinkedList<Block> content = new LinkedList<Block> ();
+
+		if (current.type == TokenType.XML_OPEN && current.content == "title") {
+			append_block_content_not_null (content, parse_docbook_title ());
+			parse_docbook_spaces ();
+		}
+
+		while (current.type == TokenType.XML_OPEN) {
+			if (current.content == "inlinegraphic") {
+				Paragraph p = factory.create_paragraph ();
+				while (current.type == TokenType.XML_OPEN && current.content == "inlinegraphic") {
+					p.content.add (parse_docbook_inlinegraphic ());
+					next ();
+					parse_docbook_spaces ();
+				}
+			} else if (current.content == "programlisting") {
+				append_block_content_not_null (content, parse_docbook_programlisting ());
+				next ();
+			} else {
+				break;
+			}
+
+			parse_docbook_spaces ();
+		}
+
+		if (!check_xml_close_tag ("example")) {
+			this.report_unexpected_token (current, "</example>");
+			return content;
+		}
+
+		next ();
+		return content;
+	}
+
+	private LinkedList<Block>? parse_docbook_refsect2 () {
+		if (!check_xml_open_tag ("refsect2")) {
+			this.report_unexpected_token (current, "<refsect2>");
+			return null;
+		}
+
+		// TODO: register id
+		string id = current.attributes.get ("id");
+		next ();
+
+		parse_docbook_spaces ();
+
+		LinkedList<Block> content = new LinkedList<Block> ();
+
+		if (current.type == TokenType.XML_OPEN && current.content == "title") {
+			append_block_content_not_null (content, parse_docbook_title ());
+			parse_docbook_spaces ();
+		}
+
+		this.append_block_content_not_null_all (content, parse_block_content ());
+
+		if (!check_xml_close_tag ("refsect2")) {
+			this.report_unexpected_token (current, "</refsect2>");
+			return content;
+		}
+
+		next ();
+		return content;
+	}
+
+	private inline void append_block_content_not_null_all (LinkedList<Block> run, LinkedList<Block>? elements) {
+		if (elements != null) {
+			run.add_all (elements);
+		}
+	}
+
+	private inline void append_block_content_not_null (LinkedList<Block> run, Block? element) {
+		if (element != null) {
+			run.add (element);
+		}
+	}
+
+	private LinkedList<Block> parse_block_content () {
+		LinkedList<Block> content = new LinkedList<Block> ();
+
+		while (current.type != TokenType.EOF) {
+			parse_docbook_spaces (false);
+
+			if (current.type == TokenType.XML_OPEN && current.content == "itemizedlist") {
+				this.append_block_content_not_null (content, parse_docbook_itemizedlist ());
+			} else if (current.type == TokenType.XML_OPEN && current.content == "para") {
+				this.append_block_content_not_null_all (content, parse_docbook_para ());
+			} else if (current.type == TokenType.XML_OPEN && current.content == "informalexample") {
+				this.append_block_content_not_null_all (content, parse_docbook_informalexample ());
+			} else if (current.type == TokenType.XML_OPEN && current.content == "example") {
+				this.append_block_content_not_null_all (content, parse_docbook_example ());
+			} else if (current.type == TokenType.XML_OPEN && current.content == "warning") {
+				this.append_block_content_not_null_all (content, parse_docbook_warning ());
+			} else if (current.type == TokenType.XML_OPEN && current.content == "note") {
+				this.append_block_content_not_null_all (content, parse_docbook_note ());
+			} else if (current.type == TokenType.XML_OPEN && current.content == "refsect2") {
+				this.append_block_content_not_null_all (content, parse_docbook_refsect2 ());
+			} else if (current.type == TokenType.GTKDOC_PARAGRAPH) {
+				this.append_block_content_not_null (content, parse_gtkdoc_paragraph ());
+			} else if (current.type == TokenType.GTKDOC_SOURCE_OPEN) {
+				this.append_block_content_not_null (content, parse_gtkdoc_source ());
+			} else {
+				break;
+			}
+		}
+
+		return content;
+	}
+
+	private void append_inline_content_string (Run run, string current) {
+		Text last_as_text = null;
+
+		if (run.content.size > 0) {
+			last_as_text = run.content.last () as Text;
+		}
+
+		if (last_as_text == null) {
+			run.content.add (factory.create_text (current));
+		} else if (current.has_prefix (" ") && last_as_text.content.has_suffix (" ")) {
+			last_as_text.content += current.chug ();
+		} else {
+			last_as_text.content += current;
+		}
+	}
+
+	private Inline create_type_link (string name) {
+		if (name == "TRUE" || name == "FALSE" || name == "NULL") {
+			var monospaced = factory.create_run (Run.Style.MONOSPACED);
+			monospaced.content.add (factory.create_text (name));
+			return monospaced;
+		} else {
+			Taglets.Link? taglet = factory.create_taglet ("link") as Taglets.Link;
+			assert (taglet != null);
+			taglet.symbol_name = "c::"+name;
+			return taglet;
+		}
+	}
+
+	private inline void append_inline_content_not_null (Run run, Inline element) {
+		if (element != null) {
+			run.content.add (element);
+		}
+	}
+
+	private Run parse_inline_content () {
+		Run run = factory.create_run (Run.Style.NONE);
+
+		while (current.type != TokenType.EOF) {
+			if (current.type == TokenType.XML_OPEN && current.content == "firstterm") {
+				append_inline_content_not_null (run, parse_highlighted_template ("firstterm", Run.Style.ITALIC));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "literal") {
+				append_inline_content_not_null (run, parse_highlighted_template ("literal", Run.Style.ITALIC));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "application") {
+				append_inline_content_not_null (run, parse_highlighted_template ("application", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "emphasis") {
+				append_inline_content_not_null (run, parse_highlighted_template ("emphasis", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "code") {
+				append_inline_content_not_null (run, parse_highlighted_template ("code", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "command") {
+				append_inline_content_not_null (run, parse_highlighted_template ("command", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "option") {
+				append_inline_content_not_null (run, parse_highlighted_template ("option", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "keycap") {
+				append_inline_content_not_null (run, parse_highlighted_template ("keycap", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "keycombo") {
+				append_inline_content_not_null (run, parse_highlighted_template ("keycombo", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "envar") {
+				append_inline_content_not_null (run, parse_highlighted_template ("envar", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "filename") {
+				append_inline_content_not_null (run, parse_highlighted_template ("filename", Run.Style.MONOSPACED));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "replaceable") {
+				append_inline_content_not_null (run, parse_highlighted_template ("replaceable", Run.Style.ITALIC));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "type") {
+				append_inline_content_not_null (run, parse_symbol_link ("type"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "function") {
+				append_inline_content_not_null (run, parse_symbol_link ("function"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "classname") {
+				append_inline_content_not_null (run, parse_symbol_link ("classname"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "structname") {
+				append_inline_content_not_null (run, parse_symbol_link ("structname"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "structfield") {
+				append_inline_content_not_null (run, parse_symbol_link ("structfield"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "errorcode") {
+				append_inline_content_not_null (run, parse_symbol_link ("errorcode"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "constant") {
+				append_inline_content_not_null (run, parse_symbol_link ("constant"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "anchor") {
+				parse_anchor ();
+			} else if (current.type == TokenType.XML_OPEN && current.content == "link") {
+				append_inline_content_not_null (run, parse_docbook_link_tempalte ("link"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "ulink") {
+				append_inline_content_not_null (run, parse_docbook_link_tempalte ("ulink"));
+			} else if (current.type == TokenType.XML_OPEN && current.content == "xref") {
+				parse_xref ();
+			} else if (current.type == TokenType.GTKDOC_FUNCTION) {
+				run.content.add (this.create_type_link (current.content));
+				next ();
+			} else if (current.type == TokenType.GTKDOC_PARAM) {
+				Run current_run = factory.create_run (Run.Style.MONOSPACED);
+				current_run.content.add (factory.create_text (current.content));
+				run.content.add (current_run);
+				next ();
+			} else if (current.type == TokenType.GTKDOC_SIGNAL) {
+				run.content.add (this.create_type_link ("::"+current.content));
+				next ();
+			} else if (current.type == TokenType.GTKDOC_PROPERTY) {
+				run.content.add (this.create_type_link (":"+current.content));
+				next ();
+			} else if (current.type == TokenType.GTKDOC_CONST) {
+				run.content.add (this.create_type_link (current.content));
+				next ();
+			} else if (current.type == TokenType.GTKDOC_TYPE) {
+				run.content.add (this.create_type_link (current.content));
+				next ();
+			} else if (current.type == TokenType.NEWLINE || current.type == TokenType.SPACE) {
+				append_inline_content_string (run, " ");
+				next ();
+			} else if (current.type == TokenType.WORD) {
+				append_inline_content_string (run, current.content);
+				next ();
+			} else if (current.type == TokenType.XML_CLOSE && ignore_current_xml_close ()) {
+				next ();
+			} else if (current.type == TokenType.XML_COMMENT) {
+				next ();
+			} else {
+				break;
+			}
+		}
+
+		return run;
+	}
+
+
+
+	//
+	// Resource Locator:
+	//
+
+	public string resolve (string path) {
+		return path;
+	}
+}
+
+
diff --git a/src/libvaladoc/documentation/gtkdoccommentscanner.vala b/src/libvaladoc/documentation/gtkdoccommentscanner.vala
new file mode 100644
index 0000000..ca03b15
--- /dev/null
+++ b/src/libvaladoc/documentation/gtkdoccommentscanner.vala
@@ -0,0 +1,644 @@
+/* gtkcommentscanner.vala
+ *
+ * Copyright (C) 2011  Florian Brosch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	Florian Brosch <flo brosch gmail com>
+ */
+
+
+using Valadoc.Gtkdoc;
+using Gee;
+
+
+
+public enum Valadoc.Gtkdoc.TokenType {
+	XML_OPEN,
+	XML_CLOSE,
+	XML_COMMENT,
+	GTKDOC_FUNCTION,
+	GTKDOC_CONST,
+	GTKDOC_TYPE,
+	GTKDOC_PARAM,
+	GTKDOC_SOURCE_OPEN,
+	GTKDOC_SOURCE_CLOSE,
+	GTKDOC_SIGNAL,
+	GTKDOC_PROPERTY,
+	GTKDOC_PARAGRAPH,
+	NEWLINE,
+	SPACE,
+	WORD,
+	EOF
+}
+
+
+public class Valadoc.Gtkdoc.Token {
+	public TokenType type;
+	public string content;
+	public HashMap<string, string>? attributes;
+	public unowned string start;
+	public int length;
+	public int line;
+	public int first_column;
+	public int last_column;
+
+	public Token (TokenType type, string content, HashMap<string, string>? attributes, string start, int length, int line, int first_column, int last_column) {
+		this.attributes = attributes;
+		this.content = content;
+		this.length = length;
+		this.start = start;
+		this.type = type;
+		this.line = line;
+		this.first_column = first_column;
+		this.last_column = last_column;
+	}
+
+	public string to_string () {
+		switch (this.type) {
+		case TokenType.XML_OPEN:
+			return "`<%s>'".printf (this.content);
+
+		case TokenType.XML_CLOSE:
+			return "`<%s>'".printf (this.content);
+
+		case TokenType.XML_COMMENT:
+			return "<XML-COMMENT>";
+
+		case TokenType.GTKDOC_FUNCTION:
+			return "`%s ()'".printf (this.content);
+
+		case TokenType.GTKDOC_CONST:
+			return "`%%%s'".printf (this.content);
+
+		case TokenType.GTKDOC_TYPE:
+			return "`#%s'".printf (this.content);
+
+		case TokenType.GTKDOC_PARAM:
+			return "<GTKDOC-PARAM>";
+
+		case TokenType.GTKDOC_SOURCE_OPEN:
+			return "[|";
+
+		case TokenType.GTKDOC_SOURCE_CLOSE:
+			return "|]";
+
+		case TokenType.GTKDOC_SIGNAL:
+			return "`::%s'".printf (this.content);
+
+		case TokenType.GTKDOC_PROPERTY:
+			return "`:%s'".printf (this.content);
+
+		case TokenType.GTKDOC_PARAGRAPH:
+			return "<GKTDOC-PARAGRAPH>";
+
+		case TokenType.NEWLINE:
+			return "<NEWLNIE>";
+
+		case TokenType.SPACE:
+			return "<SPACE>";
+
+		case TokenType.WORD:
+			return "`%s'".printf (this.content);
+
+		case TokenType.EOF:
+			return "<EOF>";
+
+		default:
+			assert_not_reached ();
+		}
+	}
+}
+
+
+public class Valadoc.Gtkdoc.Scanner {
+	private unowned string content;
+	private unowned string pos;
+	private int column;
+	private int line;
+	private Token tmp_token;
+
+	public Scanner () {
+	}
+
+	public void reset (string content) {
+		this.content = content;
+		this.tmp_token = null;
+		this.pos = content;
+		this.column = 0;
+		this.line = 0;
+	}
+
+	private inline unichar next_char () {
+		this.pos = this.pos.next_char ();
+		this.column++;
+
+		return this.pos.get_char ();
+	}
+
+	private inline unichar get () {
+		return this.pos.get_char ();
+	}
+
+	private inline bool letter (unichar c) {
+		return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+	}
+
+	private inline bool letter_or_number (unichar c) {
+		return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
+	}
+
+	private inline bool space (unichar c) {
+		return c == ' ' || c == '\t';
+	}
+
+	private inline bool space_or_newline (unichar c) {
+		if (c == '\n') {
+			this.line++;
+			this.column = 0;
+			return true;
+		}
+
+		return space (c);
+	}
+
+	private inline int offset (string a, string b) {
+		return (int) ((char*) a - (char*) b);
+	}
+
+	private inline int id_prefix () {
+		if (!letter (get ())) {
+			return 0;
+		}
+
+		int start = this.column;
+		unichar c;
+		while ((c = next_char ()) == '_' || letter_or_number (c));
+		return this.column - start;
+	}
+
+	private inline int g_id_prefix () {
+		unowned string start = this.pos;
+
+		if (!letter (get ())) {
+			return 0;
+		}
+
+		unichar c;
+		while ((c = next_char ()) == '_' || c == '-' || letter_or_number (c));
+		return offset (this.pos, start);
+	}
+
+	private inline int skip_spaces_and_newlines () {
+		unowned string start = this.pos;
+		if (space_or_newline (get ())) {
+			while (space_or_newline (next_char ()));
+		}
+
+		return offset (this.pos, start);
+	}
+
+	private inline Token? function_prefix () {
+		unowned string start = this.pos;
+		int column_start = this.column;
+		int id_len = 0;
+		if ((id_len = id_prefix ()) == 0) {
+			return null;
+		}
+
+		space_prefix ();
+
+		if (get () != '(') {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		next_char ();
+		space_prefix ();
+
+		if (get () != ')') {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		next_char ();
+		return new Token (TokenType.GTKDOC_FUNCTION, start.substring (0, id_len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	private inline Token? gtkdoc_symbolic_link_prefix (unichar c, TokenType type) {
+		if (get () != c) {
+			return null;
+		}
+
+		unowned string start = this.pos;
+		int column_start = this.column;
+		next_char ();		
+
+		int id_len = 0;
+
+		if ((id_len = id_prefix ()) == 0) {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		unowned string separator = this.pos;
+		if (get () == ':') {
+			int separator_len = 1;
+			if (next_char () == ':') {
+				next_char ();
+				separator_len++;
+			}
+
+			int id_len2;
+			if ((id_len2 = g_id_prefix ()) == 0) {
+				this.pos = separator;
+			} else {
+				id_len += id_len2 + separator_len;
+			}
+		}
+
+		return new Token (type, start.substring (1, id_len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	private inline Token? gtkdoc_property_prefix () {
+		if (get () != ':') {
+			return null;
+		}		
+
+		unowned string start = this.pos;
+		int column_start = this.column;
+		next_char ();		
+
+		int id_len = 0;
+
+		if ((id_len = g_id_prefix ()) == 0) {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		return new Token (TokenType.GTKDOC_PROPERTY, start.substring (1, id_len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	private inline Token? gtkdoc_signal_prefix () {
+		if (get () != ':') {
+			return null;
+		}		
+
+		unowned string start = this.pos;
+		int column_start = this.column;
+		if (next_char () != ':') {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+
+		start = this.pos;
+		next_char ();		
+
+		int id_len = 0;
+
+		if ((id_len = g_id_prefix ()) == 0) {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		return new Token (TokenType.GTKDOC_SIGNAL, start.substring (1, id_len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	private inline Token? gtkdoc_const_prefix () {
+		return gtkdoc_symbolic_link_prefix ('%', TokenType.GTKDOC_CONST);
+	}
+
+	private inline Token? gtkdoc_param_prefix () {
+		return gtkdoc_symbolic_link_prefix ('@', TokenType.GTKDOC_PARAM);
+	}
+
+	private inline Token? gtkdoc_type_prefix () {
+		return gtkdoc_symbolic_link_prefix ('#', TokenType.GTKDOC_TYPE);
+	}
+
+	private inline Token? xml_prefix () {
+		if (get () != '<') {
+			return null;
+		}
+
+		unowned string start = this.pos;
+		int line_start = this.line;
+		int column_start = this.column;
+		next_char ();
+
+		if (get () == '!') {
+			// comment
+			if (next_char () == '-') {
+				if (next_char () != '-') {
+					this.column = column_start;
+					this.pos = start;
+					return null;
+				}
+
+				for (unichar c = next_char (); c != '\0'; c = next_char ()) {
+					if (c == '\n') {
+						this.line++;
+						this.column = 0;
+					} else if (this.pos.has_prefix ("-->")) {
+						next_char ();
+						next_char ();
+						next_char ();
+						return new Token (TokenType.XML_COMMENT, "", null, start, offset (this.pos, start), this.line, column_start, this.column);
+					}
+				}
+			} else if (this.pos.has_prefix ("[CDATA[")) {
+				next_char ();
+				next_char ();
+				next_char ();
+				next_char ();
+				next_char ();
+				next_char ();
+
+				for (unichar c = next_char (); c != '\0'; c = next_char ()) {
+					if (c == '\n') {
+						this.line++;
+						this.column = 0;
+					} else if (this.pos.has_prefix ("]]>")) {
+						string content = start.substring (9, offset (this.pos, start) - 9);
+						next_char ();
+						next_char ();
+						next_char ();
+						return new Token (TokenType.WORD, content, null, start, offset (this.pos, start), this.line, column_start, this.column);
+					}
+				}
+			}
+
+			this.pos = start;
+			this.column = column_start;
+			this.line = line_start;
+			return null;
+		}
+
+		bool close = false;
+		if (get () == '/') {
+			next_char ();
+			close = true;
+		}
+
+		unowned string id_start = this.pos;
+
+		int id_len = 0;
+		if ((id_len = id_prefix ()) == 0) {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		HashMap<string, string> map = new HashMap<string, string> ();
+
+		while (close == false && skip_spaces_and_newlines () > 0) {
+			string name;
+			string val;
+
+			unowned string att_pos = this.pos;
+			int att_id_len = 0;
+			if ((att_id_len = id_prefix ()) == 0) {
+				break;
+			}
+
+			name = att_pos.substring (0, att_id_len);
+
+			if (get() != '=') {
+				break;
+			}
+
+			next_char ();
+			skip_spaces_and_newlines ();
+
+			if (get() != '"') {
+				break;
+			}
+
+			unichar c = next_char ();
+			att_pos = this.pos;
+			for (; c != '\0' && c != '\n' && c != '"' ; c = next_char ());
+
+			val = att_pos.substring (0, offset (this.pos, att_pos));
+
+			if (get() != '"') {
+				break;
+			}
+
+			next_char ();
+
+			map.set (name, val);
+		}
+
+		skip_spaces_and_newlines ();
+
+		bool open_and_close = false;
+
+		if (!close && get () == '/') {
+			open_and_close = true;
+			next_char ();
+		}
+
+		if (get () != '>') {
+			this.line = line_start;
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		next_char ();
+
+		if (open_and_close) {
+			this.tmp_token = new Token (TokenType.XML_CLOSE, id_start.substring (0, id_len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+		}
+
+		if (close) {
+			return new Token (TokenType.XML_CLOSE, id_start.substring (0, id_len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+		} else {
+			return new Token (TokenType.XML_OPEN, id_start.substring (0, id_len), map, start, offset (this.pos, start), this.line, column_start, this.column);
+		}
+	}
+
+	private Token? newline_prefix () {
+		if (get () != '\n') {
+			return null;
+		}
+
+		unowned string start = this.pos;
+		this.line++;
+		this.column = 0;
+
+		for (unichar c = next_char (); c == ' ' || c == '\t' ; c = next_char ());
+
+		if (get () == '\n') {
+			next_char ();
+			this.line++;
+			this.column = 0;
+			return new Token (TokenType.GTKDOC_PARAGRAPH, "\n\n", null, start, offset (this.pos, start), this.line, this.column, this.column);
+		} else {
+			return new Token (TokenType.NEWLINE, "\n", null, start, offset (this.pos, start), this.line, this.column, this.column);
+		}
+	}
+
+	private Token? eof_prefix () {
+		if (get () != '\0') {
+			return null;
+		}
+
+		return new Token (TokenType.EOF, "", null, this.pos, 1, this.line, this.column, this.column);
+	}
+
+	private Token? space_prefix () {
+		unowned string start = this.pos;
+		int column_start = this.column;
+		for (unichar c = get (); c == ' ' || c == '\t'; c = next_char ());
+		int len = offset (this.pos, start);
+		if (len == 0) {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		return new Token (TokenType.SPACE, start.substring (0, len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	private Token? word_prefix () {
+		unowned string start = this.pos;
+		int column_start = this.column;
+		if (get () == '<') {
+			next_char ();
+		}
+
+		for (unichar c = get (); c != ' ' && c != '\t' && c != '\n' && c != '\0' && c != '<'; c = next_char ());
+		int len = offset (this.pos, start);
+		if (len == 0) {
+			this.column = column_start;
+			this.pos = start;
+			return null;
+		}
+
+		return new Token (TokenType.WORD, start.substring (0, len), null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	private Token? gtkdoc_source_open_prefix () {
+		if (!this.pos.has_prefix ("|[")) {
+			return null;
+		}
+
+		unowned string start = this.pos;
+		int column_start = this.column;
+		next_char ();
+		next_char ();
+
+		return new Token (TokenType.GTKDOC_SOURCE_OPEN, "|[", null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	private Token? gtkdoc_source_close_prefix () {
+		if (!this.pos.has_prefix ("]|")) {
+			return null;
+		}
+
+		unowned string start = this.pos;
+		int column_start = this.column;
+		next_char ();
+		next_char ();
+
+		return new Token (TokenType.GTKDOC_SOURCE_CLOSE, "]|", null, start, offset (this.pos, start), this.line, column_start, this.column);
+	}
+
+	public Token next () {
+		if (tmp_token != null) {
+			var tmp = tmp_token;
+			tmp_token = null;
+			return tmp;
+		}
+
+		Token? token = function_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = xml_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = gtkdoc_param_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = gtkdoc_const_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = gtkdoc_type_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = space_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = newline_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = gtkdoc_signal_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = gtkdoc_property_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = gtkdoc_source_open_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = gtkdoc_source_close_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = eof_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		token = word_prefix ();
+		if (token != null) {
+			return token;
+		}
+
+		assert_not_reached ();
+	}
+}
+
+
diff --git a/src/libvaladoc/importer/valadocdocumentationimporter.vala b/src/libvaladoc/importer/valadocdocumentationimporter.vala
index d0cdb84..16bb108 100755
--- a/src/libvaladoc/importer/valadocdocumentationimporter.vala
+++ b/src/libvaladoc/importer/valadocdocumentationimporter.vala
@@ -116,7 +116,7 @@ public class Valadoc.Importer.ValadocDocumentationImporter : DocumentationImport
 		Api.Node? symbol = null;
 
 		if (symbol_name.has_prefix ("c::")) {
-			symbol = tree.search_symbol_cstr (symbol_name.substring (3));
+			symbol = tree.search_symbol_cstr (null, symbol_name.substring (3));
 		} else {
 			symbol = tree.search_symbol_str (null, symbol_name);
 		}
diff --git a/src/libvaladoc/taglets/tagletlink.vala b/src/libvaladoc/taglets/tagletlink.vala
index 93ba7ab..57b1474 100755
--- a/src/libvaladoc/taglets/tagletlink.vala
+++ b/src/libvaladoc/taglets/tagletlink.vala
@@ -47,7 +47,11 @@ public class Valadoc.Taglets.Link : InlineTaglet {
 	public override void check (Api.Tree api_root, Api.Node container, ErrorReporter reporter, Settings settings) {
 		if (symbol_name.has_prefix ("c::")) {
 			_symbol_name = _symbol_name.substring (3);
-			_symbol = api_root.search_symbol_cstr (symbol_name);
+			_symbol = api_root.search_symbol_cstr (container, symbol_name);
+			if (_symbol == null) {
+				
+			}
+
 			if (_symbol != null) {
 				symbol_name = _symbol.name;
 			}
diff --git a/src/libvaladoc/taglets/tagletsee.vala b/src/libvaladoc/taglets/tagletsee.vala
index eb4562e..d976688 100755
--- a/src/libvaladoc/taglets/tagletsee.vala
+++ b/src/libvaladoc/taglets/tagletsee.vala
@@ -41,7 +41,7 @@ public class Valadoc.Taglets.See : ContentElement, Taglet, Block {
 	public override void check (Api.Tree api_root, Api.Node container, ErrorReporter reporter, Settings settings) {
 		if (symbol_name.has_prefix ("c::")) {
 			symbol_name = symbol_name.substring (3);
-			symbol = api_root.search_symbol_cstr (symbol_name);
+			symbol = api_root.search_symbol_cstr (container, symbol_name);
 			if (symbol != null) {
 				symbol_name = _symbol.name;
 			}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]