<commit_before>/* valacodewriter.vala
 *
 * Copyright (C) 2006-2010  <NAME>
 * Copyright (C) 2006-2008  <NAME>
 *
 * 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:
 * 	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 */


/**
 * Code visitor generating Vala API file for the public interface.
 */
public class Vala.CodeWriter : CodeVisitor {
	private CodeContext context;
	
	FileStream stream;
	
	int indent;
	/* at begin of line */
	bool bol = true;

	Scope current_scope;

	CodeWriterType type;

	string? override_header = null;
	string? header_to_override = null;

	public CodeWriter (CodeWriterType type = CodeWriterType.EXTERNAL) {
		this.type = type;
	}

	/**
	 * Allows overriding of a specific cheader in the output
	 * @param original orignal cheader to override
	 * @param replacement cheader to replace original with
	 */
	public void set_cheader_override (string original, string replacement)
	{
		header_to_override = original;
		override_header = replacement;
	}

	/**
	 * Writes the public interface of the specified code context into the
	 * specified file.
	 *
	 * @param context  a code context
	 * @param filename a relative or absolute filename
	 */
	public void write_file (CodeContext context, string filename) {
		var file_exists = FileUtils.test (filename, FileTest.EXISTS);
		var temp_filename = filename + ".valatmp";
		this.context = context;

		if (file_exists) {
			stream = FileStream.open (temp_filename, "w");
		} else {
			stream = FileStream.open (filename, "w");
		}

		if (stream == null) {
			Report.error (null, "unable to open `%s' for writing".printf (filename));
			return;
		}

		var header = context.version_header ?
			"/* %s generated by %s %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname (), Config.BUILD_VERSION) :
			"/* %s generated by %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname ());
		write_string (header);
		write_newline ();
		write_newline ();

		current_scope = context.root.scope;

		context.accept (this);

		current_scope = null;

		stream = null;

		if (file_exists) {
			var changed = true;

			try {
				var old_file = new MappedFile (filename, false);
				var new_file = new MappedFile (temp_filename, false);
				var len = old_file.get_length ();
				if (len == new_file.get_length ()) {
					if (Memory.cmp (old_file.get_contents (), new_file.get_contents (), len) == 0) {
						changed = false;
					}
				}
				old_file = null;
				new_file = null;
			} catch (FileError e) {
				// assume changed if mmap comparison doesn't work
			}

			if (changed) {
				FileUtils.rename (temp_filename, filename);
			} else {
				FileUtils.unlink (temp_filename);
			}
		}

	}

	public override void visit_using_directive (UsingDirective ns) {
		if (type == CodeWriterType.FAST) {
			write_string ("using %s;\n".printf (ns.namespace_symbol.name));
		}
	}

	public override void visit_namespace (Namespace ns) {
		if (ns.external_package) {
			return;
		}

		if (ns.name == null)  {
			ns.accept_children (this);
			return;
		}

		write_indent ();
		write_string ("[CCode (cprefix = \"%s\", lower_case_cprefix = \"%s\"".printf (ns.get_cprefix (), ns.get_lower_case_cprefix ()));

		if (ns.source_reference != null) {
			if (ns.source_reference.file.gir_namespace != null) {
				write_string (", ");
				write_string ("gir_namespace = \"%s\"".printf (ns.source_reference.file.gir_namespace));
			}
			if (ns.source_reference.file.gir_version != null) {
				write_string(", ");
				write_string ("gir_version = \"%s\"".printf (ns.source_reference.file.gir_version));
			}
		}

		write_string (")]");
		write_newline ();

		write_attributes (ns);

		write_indent ();
		write_string ("namespace ");
		write_identifier (ns.name);
		write_begin_block ();

		current_scope = ns.scope;

		visit_sorted (ns.get_namespaces ());
		visit_sorted (ns.get_classes ());
		visit_sorted (ns.get_interfaces ());
		visit_sorted (ns.get_structs ());
		visit_sorted (ns.get_enums ());
		visit_sorted (ns.get_error_domains ());
		visit_sorted (ns.get_delegates ());
		visit_sorted (ns.get_fields ());
		visit_sorted (ns.get_constants ());
		visit_sorted (ns.get_methods ());

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	private string get_cheaders (Symbol cl) {
		bool first = true;
		string cheaders = "";
		if (type != CodeWriterType.FAST) {
			foreach (string cheader in cl.get_cheader_filenames ()) {
				if (header_to_override != null &&
				    cheader == header_to_override) {
					cheader = override_header;
				}
				if (first) {
					cheaders = cheader;
					first = false;
				} else {
					cheaders = "%s,%s".printf (cheaders, cheader);
				}
			}
		}
		return cheaders;
	}

	private void emit_deprecated_attribute (Symbol symbol) {
		if (symbol.deprecated) {
			write_indent ();
			write_string ("[Deprecated");
			var since = symbol.deprecated_since;
			var replacement = symbol.replacement;

			if (since != null || replacement != null) {
				write_string (" (");
				if (since != null) {
					write_string ("since = \"%s\"".printf (since));
				}
				if (since != null && replacement != null) {
					write_string (", ");
				}
				if (replacement != null) {
					write_string ("replacement = \"%s\"".printf (replacement));
				}
				write_string (")");
			}
			write_string ("]");
		}
	}

	public override void visit_class (Class cl) {
		if (cl.external_package) {
			return;
		}

		if (!check_accessibility (cl)) {
			return;
		}

		if (cl.is_compact) {
			write_indent ();
			write_string ("[Compact]");
			write_newline ();
		}

		if (cl.is_immutable) {
			write_indent ();
			write_string ("[Immutable]");
			write_newline ();
		}

		emit_deprecated_attribute (cl);

		write_indent ();
		
		write_string ("[CCode (");

		if (cl.is_reference_counting () && type != CodeWriterType.FAST) {
			if (cl.base_class == null || cl.base_class.get_ref_function () == null || cl.base_class.get_ref_function () != cl.get_ref_function ()) {
				write_string ("ref_function = \"%s\", ".printf (cl.get_ref_function ()));
				if (cl.ref_function_void) {
					write_string ("ref_function_void = true, ");
				}
			}
			if (cl.base_class == null || cl.base_class.get_unref_function () == null || cl.base_class.get_unref_function () != cl.get_unref_function ()) {
				write_string ("unref_function = \"%s\", ".printf (cl.get_unref_function ()));
			}
		} else {
			if (cl.get_dup_function () != null) {
				write_string ("copy_function = \"%s\", ".printf (cl.get_dup_function ()));
			}
			if (cl.get_free_function () != cl.get_default_free_function ()) {
				write_string ("free_function = \"%s\", ".printf (cl.get_free_function ()));
			}
		}

		if (cl.get_cname () != cl.get_default_cname ()) {
			write_string ("cname = \"%s\", ".printf (cl.get_cname ()));
		}
		if (cl.const_cname != null) {
			write_string ("const_cname = \"%s\", ".printf (cl.const_cname));
		}

		if (cl.type_check_function != null) {
			write_string ("type_check_function = \"%s\", ".printf (cl.type_check_function ));
		}

		if (cl.get_type_id () != cl.get_default_type_id ()) {
			write_string ("type_id = \"%s\", ".printf (cl.get_type_id ()));
		}

		if (cl.get_param_spec_function () != cl.get_default_param_spec_function ()) {
			write_string ("param_spec_function = \"%s\", ".printf (cl.get_param_spec_function ()));
		}

		write_string ("cheader_filename = \"%s\")]".printf (get_cheaders(cl)));
		write_newline ();

		write_attributes (cl);
		
		write_indent ();
		write_accessibility (cl);
		if (cl.is_abstract) {
			write_string ("abstract ");
		}
		write_string ("class ");
		write_identifier (cl.name);

		var type_params = cl.get_type_parameters ();
		if (type_params.size > 0) {
			write_string ("<");
			bool first = true;
			foreach (TypeParameter type_param in type_params) {
				if (first) {
					first = false;
				} else {
					write_string (",");
				}
				write_identifier (type_param.name);
			}
			write_string (">");
		}

		var base_types = cl.get_base_types ();
		if (base_types.size > 0) {
			write_string (" : ");
		
			bool first = true;
			foreach (DataType base_type in base_types) {
				if (!first) {
					write_string (", ");
				} else {
					first = false;
				}
				write_type (base_type);
			}
		}
		write_begin_block ();

		current_scope = cl.scope;

		visit_sorted (cl.get_classes ());
		visit_sorted (cl.get_structs ());
		visit_sorted (cl.get_enums ());
		visit_sorted (cl.get_delegates ());
		visit_sorted (cl.get_fields ());
		visit_sorted (cl.get_constants ());
		visit_sorted (cl.get_methods ());
		visit_sorted (cl.get_properties ());
		visit_sorted (cl.get_signals ());

		if (cl.constructor != null) {
			cl.constructor.accept (this);
		}

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	void visit_sorted (List<Symbol> symbols) {
		if (type != CodeWriterType.EXTERNAL) {
			// order of virtual methods matters for fast vapis
			foreach (Symbol sym in symbols) {
				sym.accept (this);
			}
			return;
		}

		var sorted_symbols = new ArrayList<Symbol> ();
		foreach (Symbol sym in symbols) {
			int left = 0;
			int right = sorted_symbols.size - 1;
			if (left > right || sym.name < sorted_symbols[left].name) {
				sorted_symbols.insert (0, sym);
			} else if (sym.name > sorted_symbols[right].name) {
				sorted_symbols.add (sym);
			} else {
				while (right - left > 1) {
					int i = (right + left) / 2;
					if (sym.name > sorted_symbols[i].name) {
						left = i;
					} else {
						right = i;
					}
				}
				sorted_symbols.insert (left + 1, sym);
			}
		}
		foreach (Symbol sym in sorted_symbols) {
			sym.accept (this);
		}
	}

	public override void visit_struct (Struct st) {
		if (st.external_package) {
			return;
		}

		if (!check_accessibility (st)) {
			return;
		}
		
		if (st.is_immutable) {
			write_indent ();
			write_string ("[Immutable]");
			write_newline ();
		}

		emit_deprecated_attribute (st);

		write_indent ();

		write_string ("[CCode (");

		if (st.get_cname () != st.get_default_cname ()) {
			write_string ("cname = \"%s\", ".printf (st.get_cname ()));
		}

		if (!st.has_type_id) {
			write_string ("has_type_id = false, ");
		} else if (!st.is_simple_type () && st.get_type_id () != "G_TYPE_POINTER") {
			write_string ("type_id = \"%s\", ".printf (st.get_type_id ()));
		}

		if (!st.has_copy_function) {
			write_string ("has_copy_function = false, ");
		}

		if (!st.has_destroy_function) {
			write_string ("has_destroy_function = false, ");
		}

		write_string ("cheader_filename = \"%s\")]".printf (get_cheaders(st)));
		write_newline ();

		if (st.is_simple_type ()) {
			write_indent ();
			write_string ("[SimpleType]");
			write_newline ();
		}

		if (st.is_integer_type ()) {
			write_indent ();
			write_string ("[IntegerType (rank = %d)]".printf (st.get_rank ()));
			write_newline ();
		}

		if (st.is_floating_type ()) {
			write_indent ();
			write_string ("[FloatingType (rank = %d)]".printf (st.get_rank ()));
			write_newline ();
		}

		write_attributes (st);

		write_indent ();
		write_accessibility (st);
		write_string ("struct ");
		write_identifier (st.name);

		if (st.base_type != null) {
			write_string (" : ");
			write_type (st.base_type);
		}

		write_begin_block ();

		current_scope = st.scope;

		foreach (Field field in st.get_fields ()) {
			field.accept (this);
		}
		visit_sorted (st.get_constants ());
		visit_sorted (st.get_methods ());
		visit_sorted (st.get_properties ());

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	public override void visit_interface (Interface iface) {
		if (iface.external_package) {
			return;
		}

		if (!check_accessibility (iface)) {
			return;
		}

		emit_deprecated_attribute (iface);

		write_indent ();

		write_string ("[CCode (cheader_filename = \"%s\"".printf (get_cheaders(iface)));
		if (iface.get_lower_case_csuffix () != iface.get_default_lower_case_csuffix ())
			write_string (", lower_case_csuffix = \"%s\"".printf (iface.get_lower_case_csuffix ()));

		write_string (")]");
		write_newline ();

		write_attributes (iface);

		write_indent ();
		write_accessibility (iface);
		write_string ("interface ");
		write_identifier (iface.name);

		var type_params = iface.get_type_parameters ();
		if (type_params.size > 0) {
			write_string ("<");
			bool first = true;
			foreach (TypeParameter type_param in type_params) {
				if (first) {
					first = false;
				} else {
					write_string (",");
				}
				write_identifier (type_param.name);
			}
			write_string (">");
		}

		var prerequisites = iface.get_prerequisites ();
		if (prerequisites.size > 0) {
			write_string (" : ");
		
			bool first = true;
			foreach (DataType prerequisite in prerequisites) {
				if (!first) {
					write_string (", ");
				} else {
					first = false;
				}
				write_type (prerequisite);
			}
		}
		write_begin_block ();

		current_scope = iface.scope;

		visit_sorted (iface.get_classes ());
		visit_sorted (iface.get_structs ());
		visit_sorted (iface.get_enums ());
		visit_sorted (iface.get_delegates ());
		visit_sorted (iface.get_fields ());
		visit_sorted (iface.get_constants ());
		visit_sorted (iface.get_methods ());
		visit_sorted (iface.get_properties ());
		visit_sorted (iface.get_signals ());

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	public override void visit_enum (Enum en) {
		if (en.external_package) {
			return;
		}

		if (!check_accessibility (en)) {
			return;
		}

		emit_deprecated_attribute (en);

		write_indent ();

		write_string ("[CCode (cprefix = \"%s\", ".printf (en.get_cprefix ()));

		if (!en.has_type_id) {
			write_string ("has_type_id = false, ");
		}

		write_string ("cheader_filename = \"%s\")]".printf (get_cheaders(en)));

		if (en.is_flags) {
			write_indent ();
			write_string ("[Flags]");
		}

		write_attributes (en);

		write_indent ();
		write_accessibility (en);
		write_string ("enum ");
		write_identifier (en.name);
		write_begin_block ();

		bool first = true;
		foreach (EnumValue ev in en.get_values ()) {
			if (first) {
				first = false;
			} else {
				write_string (",");
				write_newline ();
			}

			if (ev.get_cname () != ev.get_default_cname ()) {
				write_indent ();
				write_string ("[CCode (cname = \"%s\")]".printf (ev.get_cname ()));
			}
			write_indent ();
			write_identifier (ev.name);

			if (type == CodeWriterType.FAST && ev.value != null) {
				write_string(" = ");
				ev.value.accept (this);
			}
		}

		if (!first) {
			if (en.get_methods ().size > 0 || en.get_constants ().size > 0) {
				write_string (";");
			}
			write_newline ();
		}

		current_scope = en.scope;
		foreach (Method m in en.get_methods ()) {
			m.accept (this);
		}
		foreach (Constant c in en.get_constants ()) {
			c.accept (this);
		}
		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	public override void visit_error_domain (ErrorDomain edomain) {
		if (edomain.external_package) {
			return;
		}

		if (!check_accessibility (edomain)) {
			return;
		}

		emit_deprecated_attribute (edomain);

		write_indent ();

		write_string ("[CCode (cprefix = \"%s\", cheader_filename = \"%s\")]".printf (edomain.get_cprefix (), get_cheaders(edomain)));

		write_attributes (edomain);

		write_indent ();
		write_accessibility (edomain);
		write_string ("errordomain ");
		write_identifier (edomain.name);
		write_begin_block ();

		edomain.accept_children (this);

		write_end_block ();
		write_newline ();
	}

	public override void visit_error_code (ErrorCode ecode) {
		write_indent ();
		write_identifier (ecode.name);
		write_string (",");
		write_newline ();
	}

	public override void visit_constant (Constant c) {
		if (c.external_package) {
			return;
		}

		if (!check_accessibility (c)) {
			return;
		}

		emit_deprecated_attribute (c);

		bool custom_cname = (c.get_cname () != c.get_default_cname ());
		bool custom_cheaders = (c.parent_symbol is Namespace);
		if (custom_cname || custom_cheaders) {
			write_indent ();
			write_string ("[CCode (");

			if (custom_cname) {
				write_string ("cname = \"%s\"".printf (c.get_cname ()));
			}

			if (custom_cheaders) {
				if (custom_cname) {
					write_string (", ");
				}

				write_string ("cheader_filename = \"%s\"".printf (get_cheaders(c)));
			}

			write_string (")]");
		}

		write_indent ();
		write_accessibility (c);
		write_string ("const ");

		write_type (c.type_reference);
			
		write_string (" ");
		write_identifier (c.name);
		if (type == CodeWriterType.FAST && c.value != null) {
			write_string(" = ");
			c.value.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_field (Field f) {
		if (f.external_package) {
			return;
		}

		if (!check_accessibility (f)) {
			return;
		}

		emit_deprecated_attribute (f);

		bool custom_cname = (f.get_cname () != f.get_default_cname ());
		bool custom_ctype = (f.get_ctype () != null);
		bool custom_cheaders = (f.parent_symbol is Namespace);
		bool custom_array_length_cname = (f.get_array_length_cname () != null);
		bool custom_array_length_type = (f.array_length_type != null);
		if (custom_cname || custom_ctype || custom_cheaders || custom_array_length_cname || custom_array_length_type || (f.no_array_length && f.variable_type is ArrayType)) {
			write_indent ();
			write_string ("[CCode (");

			if (custom_cname) {
				write_string ("cname = \"%s\"".printf (f.get_cname ()));
			}

			if (custom_ctype) {
				if (custom_cname) {
					write_string (", ");
				}

				write_string ("type = \"%s\"".printf (f.get_ctype ()));
			}

			if (custom_cheaders) {
				if (custom_cname || custom_ctype) {
					write_string (", ");
				}

				write_string ("cheader_filename = \"%s\"".printf (get_cheaders(f)));
			}

			if (f.variable_type is ArrayType) {
				if (f.no_array_length) {
					if (custom_cname || custom_ctype || custom_cheaders) {
						write_string (", ");
					}

					write_string ("array_length = false");

					if (f.array_null_terminated) {
						write_string (", array_null_terminated = true");
					}
				} else {
					if (custom_array_length_cname) {
						if (custom_cname || custom_ctype || custom_cheaders) {
							write_string (", ");
						}

						write_string ("array_length_cname = \"%s\"".printf (f.get_array_length_cname ()));
					}

					if (custom_array_length_type) {
						if (custom_cname || custom_ctype || custom_cheaders || custom_array_length_cname) {
							write_string (", ");
						}

						write_string ("array_length_type = \"%s\"".printf (f.array_length_type));
					}
				}
			}

			write_string (")]");
		}

		write_indent ();
		write_accessibility (f);

		if (f.binding == MemberBinding.STATIC) {
			write_string ("static ");
		} else if (f.binding == MemberBinding.CLASS) {
			write_string ("class ");
		}

		if (f.variable_type.is_weak ()) {
			write_string ("weak ");
		}

		write_type (f.variable_type);
			
		write_string (" ");
		write_identifier (f.name);
		write_string (";");
		write_newline ();
	}
	
	private void write_error_domains (List<DataType> error_domains) {
		if (error_domains.size > 0) {
			write_string (" throws ");

			bool first = true;
			foreach (DataType type in error_domains) {
				if (!first) {
					write_string (", ");
				} else {
					first = false;
				}

				write_type (type);
			}
		}
	}

	// equality comparison with 3 digit precision
	private bool float_equal (double d1, double d2) {
		return ((int) (d1 * 1000)) == ((int) (d2 * 1000));
	}

	private void write_params (List<FormalParameter> params) {
		write_string ("(");

		int i = 1;
		foreach (FormalParameter param in params) {
			if (i > 1) {
				write_string (", ");
			}
			
			if (param.ellipsis) {
				write_string ("...");
				continue;
			}
			

			var ccode_params = new StringBuilder ();
			var separator = "";

			if (!float_equal (param.cparameter_position, i)) {
				ccode_params.append_printf ("%spos = %g", separator, param.cparameter_position);
				separator = ", ";
			}
			if (param.ctype != null) {
				ccode_params.append_printf ("%stype = \"%s\"", separator, param.ctype);
				separator = ", ";
			}
			if (param.no_array_length && param.variable_type is ArrayType) {
				ccode_params.append_printf ("%sarray_length = false", separator);
				separator = ", ";
			}
			if (param.array_length_type != null && param.variable_type is ArrayType) {
				ccode_params.append_printf ("%sarray_length_type = \"%s\"", separator, param.array_length_type);
				separator = ", ";
			}
			if (param.get_array_length_cname () != null && param.variable_type is ArrayType) {
				ccode_params.append_printf ("%sarray_length_cname = \"%s\"", separator, param.get_array_length_cname ());
				separator = ", ";
			}
			if (!float_equal (param.carray_length_parameter_position, i + 0.1)) {
				ccode_params.append_printf ("%sarray_length_pos = %g", separator, param.carray_length_parameter_position);
				separator = ", ";
			}
			if (!float_equal (param.cdelegate_target_parameter_position, i + 0.1)) {
				ccode_params.append_printf ("%sdelegate_target_pos = %g", separator, param.cdelegate_target_parameter_position);
				separator = ", ";
			}

			if (ccode_params.len > 0) {
				write_string ("[CCode (%s)] ".printf (ccode_params.str));
			}

			if (param.params_array) {
				write_string ("params ");
			}

			if (param.direction == ParameterDirection.IN) {
				if (param.variable_type.value_owned) {
					write_string ("owned ");
				}
			} else {
				if (param.direction == ParameterDirection.REF) {
					write_string ("ref ");
				} else if (param.direction == ParameterDirection.OUT) {
					write_string ("out ");
				}
				if (param.variable_type.is_weak ()) {
					write_string ("unowned ");
				}
			}

			write_type (param.variable_type);

			write_string (" ");
			write_identifier (param.name);
			
			if (param.initializer != null) {
				write_string (" = ");
				param.initializer.accept (this);
			}

			i++;
		}

		write_string (")");
	}

	public override void visit_delegate (Delegate cb) {
		if (cb.external_package) {
			return;
		}

		if (!check_accessibility (cb)) {
			return;
		}

		emit_deprecated_attribute (cb);

		write_indent ();

		write_string ("[CCode (cheader_filename = \"%s\"".printf (get_cheaders(cb)));

		if (!cb.has_target) {
			write_string (", has_target = false");
		} else if (!float_equal (cb.cinstance_parameter_position, -2)) {
			write_string (", instance_pos = %g".printf (cb.cinstance_parameter_position));
		}

		write_string (")]");

		write_indent ();

		write_accessibility (cb);
		write_string ("delegate ");
		
		write_return_type (cb.return_type);
		
		write_string (" ");
		write_identifier (cb.name);

		var type_params = cb.get_type_parameters ();
		if (type_params.size > 0) {
			write_string ("<");
			bool first = true;
			foreach (TypeParameter type_param in type_params) {
				if (first) {
					first = false;
				} else {
					write_string (",");
				}
				write_identifier (type_param.name);
			}
			write_string (">");
		}

		write_string (" ");
		
		write_params (cb.get_parameters ());

		write_error_domains (cb.get_error_types ());

		write_string (";");

		write_newline ();
	}

	public override void visit_constructor (Constructor c) {
		if (type != CodeWriterType.DUMP) {
			return;
		}

		write_indent ();
		write_string ("construct");
		write_code_block (c.body);
		write_newline ();
	}

	public override void visit_method (Method m) {
		if (m.external_package) {
			return;
		}

		// don't write interface implementation unless it's an abstract or virtual method
		if (!check_accessibility (m) || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
			if (type != CodeWriterType.DUMP) {
				return;
			}
		}

		if (m.get_attribute ("NoWrapper") != null) {
			write_indent ();
			write_string ("[NoWrapper]");
		}
		if (m.get_attribute ("NoThrow") != null) {
			write_indent ();
			write_string ("[NoThrow]");
		}
		if (m.returns_modified_pointer) {
			write_indent ();
			write_string ("[ReturnsModifiedPointer]");
		}
		if (m.printf_format) {
			write_indent ();
			write_string ("[PrintfFormat]");
		}
		if (m.scanf_format) {
			write_indent ();
			write_string ("[ScanfFormat]");
		}
		if (m.get_attribute ("Print") != null) {
			write_indent ();
			write_string ("[Print]");
		}
		if (m.get_attribute ("NoReturn") != null) {
			write_indent ();
			write_string ("[NoReturn]");
		}

		emit_deprecated_attribute (m);

		var ccode_params = new StringBuilder ();
		var separator = "";

		if (m.get_cname () != m.get_default_cname ()) {
			ccode_params.append_printf ("%scname = \"%s\"", separator, m.get_cname ());
			separator = ", ";
		}
		if (m.parent_symbol is Namespace) {
			ccode_params.append_printf ("%scheader_filename = \"%s\"", separator, get_cheaders(m));
			separator = ", ";
		}
		if (!float_equal (m.cinstance_parameter_position, 0)) {
			ccode_params.append_printf ("%sinstance_pos = %g", separator, m.cinstance_parameter_position);
			separator = ", ";
		}
		if (m.no_array_length && m.return_type is ArrayType) {
			ccode_params.append_printf ("%sarray_length = false", separator);
			separator = ", ";
		}
		if (!float_equal (m.carray_length_parameter_position, -3)) {
			ccode_params.append_printf ("%sarray_length_pos = %g", separator, m.carray_length_parameter_position);
			separator = ", ";
		}
		if (m.array_null_terminated && m.return_type is ArrayType) {
			ccode_params.append_printf ("%sarray_null_terminated = true", separator);
			separator = ", ";
		}
		if (m.array_length_type != null && m.return_type is ArrayType) {
			ccode_params.append_printf ("%sarray_length_type = \"%s\"", separator, m.array_length_type);
			separator = ", ";
		}
		if (!float_equal (m.cdelegate_target_parameter_position, -3)) {
			ccode_params.append_printf ("%sdelegate_target_pos = %g", separator, m.cdelegate_target_parameter_position);
			separator = ", ";
		}
		if (m.vfunc_name != m.name) {
			ccode_params.append_printf ("%svfunc_name = \"%s\"", separator, m.vfunc_name);
			separator = ", ";
		}
		if (m.coroutine && m.get_finish_cname () != m.get_default_finish_cname ()) {
			ccode_params.append_printf ("%sfinish_name = \"%s\"", separator, m.get_finish_cname ());
			separator = ", ";
		}
		if (m.sentinel != m.DEFAULT_SENTINEL) {
			ccode_params.append_printf ("%ssentinel = \"%s\"", separator, m.sentinel);
			separator = ", ";
		}
		var cm = m as CreationMethod;
		if (cm != null) {
			if (cm.custom_return_type_cname != null) {
				ccode_params.append_printf ("%stype = \"%s\"", separator, cm.custom_return_type_cname);
				separator = ", ";
			}
			if (!m.has_new_function) {
				ccode_params.append_printf ("%shas_new_function = false", separator);
				separator = ", ";
			}
			if (!m.has_construct_function) {
				ccode_params.append_printf ("%shas_construct_function = false", separator);
				separator = ", ";
			} else if (m.name == ".new" && m.get_real_cname () != cm.get_default_construct_function ()) {
				ccode_params.append_printf ("%sconstruct_function = \"%s\"", separator, m.get_real_cname ());
				separator = ", ";
			}
		}

		if (ccode_params.len > 0) {
			write_indent ();
			write_string ("[CCode (%s)]".printf (ccode_params.str));
		}
		
		write_indent ();
		write_accessibility (m);
		
		if (m is CreationMethod) {
			if (m.coroutine) {
				write_string ("async ");
			}

			var datatype = (TypeSymbol) m.parent_symbol;
			write_identifier (datatype.name);
			if (m.name != ".new") {
				write_string (".");
				write_identifier (m.name);
			}
			write_string (" ");
		} else {
			if (m.binding == MemberBinding.STATIC) {
				write_string ("static ");
			} else if (m.binding == MemberBinding.CLASS) {
				write_string ("class ");
			} else if (m.is_abstract) {
				write_string ("abstract ");
			} else if (m.is_virtual) {
				write_string ("virtual ");
			} else if (m.overrides) {
				write_string ("override ");
			}

			if (m.hides) {
				write_string ("new ");
			}

			if (m.coroutine) {
				write_string ("async ");
			}
		
			write_return_type (m.return_type);
			write_string (" ");

			write_identifier (m.name);

			var type_params = m.get_type_parameters ();
			if (type_params.size > 0) {
				write_string ("<");
				bool first = true;
				foreach (TypeParameter type_param in type_params) {
					if (first) {
						first = false;
					} else {
						write_string (",");
					}
					write_identifier (type_param.name);
				}
				write_string (">");
			}

			write_string (" ");
		}
		
		write_params (m.get_parameters ());

		if (context.profile != Profile.DOVA) {
			write_error_domains (m.get_error_types ());
		}

		write_code_block (m.body);

		write_newline ();
	}

	public override void visit_creation_method (CreationMethod m) {
		visit_method (m);
	}

	public override void visit_property (Property prop) {
		if (!check_accessibility (prop) || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
			return;
		}

		emit_deprecated_attribute (prop);

		if (prop.no_accessor_method) {
			write_indent ();
			write_string ("[NoAccessorMethod]");
		}
		if (prop.property_type is ArrayType && prop.no_array_length) {
			write_indent ();
			write_string ("[CCode (array_length = false");

			if (prop.array_null_terminated) {
				write_string (", array_null_terminated = true");
			}

			write_string (")]");
		}

		write_indent ();
		write_accessibility (prop);

		if (prop.binding == MemberBinding.STATIC) {
			write_string ("static ");
		} else  if (prop.is_abstract) {
			write_string ("abstract ");
		} else if (prop.is_virtual) {
			write_string ("virtual ");
		} else if (prop.overrides) {
			write_string ("override ");
		}

		write_type (prop.property_type);

		write_string (" ");
		write_identifier (prop.name);
		write_string (" {");
		if (prop.get_accessor != null) {
			var ccode_params = new StringBuilder ();
			var separator = "";

			if (prop.get_accessor.get_cname () != prop.get_accessor.get_default_cname ()) {
				ccode_params.append_printf ("%scname = \"%s\"", separator, prop.get_accessor.get_cname ());
				separator = ", ";
			}
			if (ccode_params.len > 0) {
				write_indent ();
				write_string ("[CCode (%s)]".printf (ccode_params.str));
			}

			if (context.profile != Profile.DOVA && prop.get_accessor.value_type.is_disposable ()) {
				write_string (" owned");
			}

			write_string (" get");
			write_code_block (prop.get_accessor.body);
		}
		if (prop.set_accessor != null) {
			var ccode_params = new StringBuilder ();
			var separator = "";

			if (prop.set_accessor.get_cname () != prop.set_accessor.get_default_cname ()) {
				ccode_params.append_printf ("%scname = \"%s\"", separator, prop.set_accessor.get_cname ());
				separator = ", ";
			}
			if (ccode_params.len > 0) {
				write_indent ();
				write_string ("[CCode (%s)]".printf (ccode_params.str));
			}

			if (context.profile != Profile.DOVA && prop.set_accessor.value_type.value_owned) {
				write_string (" owned");
			}

			if (prop.set_accessor.writable) {
				write_string (" set");
			}
			if (prop.set_accessor.construction) {
				write_string (" construct");
			}
			write_code_block (prop.set_accessor.body);
		}
		write_string (" }");
		write_newline ();
	}

	public override void visit_signal (Signal sig) {
		if (!check_accessibility (sig)) {
			return;
		}
		
		if (sig.has_emitter) {
			write_indent ();
			write_string ("[HasEmitter]");
		}

		emit_deprecated_attribute (sig);
		
		write_indent ();
		write_accessibility (sig);

		if (sig.is_virtual) {
			write_string ("virtual ");
		}

		write_string ("signal ");
		
		write_return_type (sig.return_type);
		
		write_string (" ");
		write_identifier (sig.name);
		
		write_string (" ");
		
		write_params (sig.get_parameters ());

		write_string (";");

		write_newline ();
	}

	public override void visit_block (Block b) {
		write_begin_block ();

		foreach (Statement stmt in b.get_statements ()) {
			stmt.accept (this);
		}

		write_end_block ();
	}

	public override void visit_empty_statement (EmptyStatement stmt) {
	}

	public override void visit_declaration_statement (DeclarationStatement stmt) {
		write_indent ();
		stmt.declaration.accept (this);
		write_string (";");
		write_newline ();
	}

	public override void visit_local_variable (LocalVariable local) {
		write_type (local.variable_type);
		write_string (" ");
		write_identifier (local.name);
		if (local.initializer != null) {
			write_string (" = ");
			local.initializer.accept (this);
		}
	}

	public override void visit_initializer_list (InitializerList list) {
		write_string ("{");

		bool first = true;
		foreach (Expression initializer in list.get_initializers ()) {
			if (!first) {
				write_string (", ");
			} else {
				write_string (" ");
			}
			first = false;
			initializer.accept (this);
		}
		write_string (" }");
	}

	public override void visit_expression_statement (ExpressionStatement stmt) {
		write_indent ();
		stmt.expression.accept (this);
		write_string (";");
		write_newline ();
	}

	public override void visit_if_statement (IfStatement stmt) {
		write_indent ();
		write_string ("if (");
		stmt.condition.accept (this);
		write_string (")");
		stmt.true_statement.accept (this);
		if (stmt.false_statement != null) {
			write_string (" else");
			stmt.false_statement.accept (this);
		}
		write_newline ();
	}

	public override void visit_switch_statement (SwitchStatement stmt) {
		write_indent ();
		write_string ("switch (");
		stmt.expression.accept (this);
		write_string (") {");
		write_newline ();

		foreach (SwitchSection section in stmt.get_sections ()) {
			section.accept (this);
		}

		write_indent ();
		write_string ("}");
		write_newline ();
	}

	public override void visit_switch_section (SwitchSection section) {
		foreach (SwitchLabel label in section.get_labels ()) {
			label.accept (this);
		}

		visit_block (section);
	}

	public override void visit_switch_label (SwitchLabel label) {
		if (label.expression != null) {
			write_indent ();
			write_string ("case ");
			label.expression.accept (this);
			write_string (":");
			write_newline ();
		} else {
			write_indent ();
			write_string ("default:");
			write_newline ();
		}
	}

	public override void visit_loop (Loop stmt) {
		write_indent ();
		write_string ("loop");
		stmt.body.accept (this);
		write_newline ();
	}

	public override void visit_while_statement (WhileStatement stmt) {
		write_indent ();
		write_string ("while (");
		stmt.condition.accept (this);
		write_string (")");
		stmt.body.accept (this);
		write_newline ();
	}

	public override void visit_do_statement (DoStatement stmt) {
		write_indent ();
		write_string ("do");
		stmt.body.accept (this);
		write_string ("while (");
		stmt.condition.accept (this);
		write_string (");");
		write_newline ();
	}

	public override void visit_for_statement (ForStatement stmt) {
		write_indent ();
		write_string ("for (");

		bool first = true;
		foreach (Expression initializer in stmt.get_initializer ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;
			initializer.accept (this);
		}
		write_string ("; ");

		stmt.condition.accept (this);
		write_string ("; ");

		first = true;
		foreach (Expression iterator in stmt.get_iterator ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;
			iterator.accept (this);
		}

		write_string (")");
		stmt.body.accept (this);
		write_newline ();
	}

	public override void visit_foreach_statement (ForeachStatement stmt) {
	}

	public override void visit_break_statement (BreakStatement stmt) {
		write_indent ();
		write_string ("break;");
		write_newline ();
	}

	public override void visit_continue_statement (ContinueStatement stmt) {
		write_indent ();
		write_string ("continue;");
		write_newline ();
	}

	public override void visit_return_statement (ReturnStatement stmt) {
		write_indent ();
		write_string ("return");
		if (stmt.return_expression != null) {
			write_string (" ");
			stmt.return_expression.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_yield_statement (YieldStatement y) {
		write_indent ();
		write_string ("yield");
		if (y.yield_expression != null) {
			write_string (" ");
			y.yield_expression.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_throw_statement (ThrowStatement stmt) {
		write_indent ();
		write_string ("throw");
		if (stmt.error_expression != null) {
			write_string (" ");
			stmt.error_expression.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_try_statement (TryStatement stmt) {
		write_indent ();
		write_string ("try");
		stmt.body.accept (this);
		foreach (var clause in stmt.get_catch_clauses ()) {
			clause.accept (this);
		}
		if (stmt.finally_body != null) {
			write_string (" finally");
			stmt.finally_body.accept (this);
		}
		write_newline ();
	}

	public override void visit_catch_clause (CatchClause clause) {
		var type_name = clause.error_type == null ? "GLib.Error" : clause.error_type.to_string ();
		var var_name = clause.variable_name == null ? "_" : clause.variable_name;
		write_string (" catch (%s %s)".printf (type_name, var_name));
		clause.body.accept (this);
	}

	public override void visit_lock_statement (LockStatement stmt) {
		write_indent ();
		write_string ("lock (");
		stmt.resource.accept (this);
		write_string (")");
		if (stmt.body == null) {
			write_string (";");
		} else {
			stmt.body.accept (this);
		}
		write_newline ();
	}

	public override void visit_delete_statement (DeleteStatement stmt) {
		write_indent ();
		write_string ("delete ");
		stmt.expression.accept (this);
		write_string (";");
		write_newline ();
	}

	public override void visit_array_creation_expression (ArrayCreationExpression expr) {
		write_string ("new ");
		write_type (expr.element_type);
		write_string ("[");

		bool first = true;
		foreach (Expression size in expr.get_sizes ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			size.accept (this);
		}

		write_string ("]");

		if (expr.initializer_list != null) {
			write_string (" ");
			expr.initializer_list.accept (this);
		}
	}

	public override void visit_boolean_literal (BooleanLiteral lit) {
		write_string (lit.value.to_string ());
	}

	public override void visit_character_literal (CharacterLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_integer_literal (IntegerLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_real_literal (RealLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_string_literal (StringLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_null_literal (NullLiteral lit) {
		write_string ("null");
	}

	public override void visit_member_access (MemberAccess expr) {
		if (expr.inner != null) {
			expr.inner.accept (this);
			write_string (".");
		}
		write_identifier (expr.member_name);
	}

	public override void visit_method_call (MethodCall expr) {
		expr.call.accept (this);
		write_string (" (");

		bool first = true;
		foreach (Expression arg in expr.get_argument_list ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			arg.accept (this);
		}

		write_string (")");
	}
	
	public override void visit_element_access (ElementAccess expr) {
		expr.container.accept (this);
		write_string ("[");

		bool first = true;
		foreach (Expression index in expr.get_indices ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			index.accept (this);
		}

		write_string ("]");
	}

	public override void visit_slice_expression (SliceExpression expr) {
		expr.container.accept (this);
		write_string ("[");
		expr.start.accept (this);
		write_string (":");
		expr.stop.accept (this);
		write_string ("]");
	}

	public override void visit_base_access (BaseAccess expr) {
		write_string ("base");
	}

	public override void visit_postfix_expression (PostfixExpression expr) {
		expr.inner.accept (this);
		if (expr.increment) {
			write_string ("++");
		} else {
			write_string ("--");
		}
	}

	public override void visit_object_creation_expression (ObjectCreationExpression expr) {
		if (!expr.struct_creation) {
			write_string ("new ");
		}

		write_type (expr.type_reference);

		if (expr.constructor.name != ".new") {
			write_string (".");
			write_string (expr.constructor.name);
		}

		write_string (" (");

		bool first = true;
		foreach (Expression arg in expr.get_argument_list ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			arg.accept (this);
		}

		write_string (")");
	}

	public override void visit_sizeof_expression (SizeofExpression expr) {
		write_string ("sizeof (");
		write_type (expr.type_reference);
		write_string (")");
	}

	public override void visit_typeof_expression (TypeofExpression expr) {
		write_string ("typeof (");
		write_type (expr.type_reference);
		write_string (")");
	}

	public override void visit_unary_expression (UnaryExpression expr) {
		switch (expr.operator) {
		case UnaryOperator.PLUS:
			write_string ("+");
			break;
		case UnaryOperator.MINUS:
			write_string ("-");
			break;
		case UnaryOperator.LOGICAL_NEGATION:
			write_string ("!");
			break;
		case UnaryOperator.BITWISE_COMPLEMENT:
			write_string ("~");
			break;
		case UnaryOperator.INCREMENT:
			write_string ("++");
			break;
		case UnaryOperator.DECREMENT:
			write_string ("--");
			break;
		case UnaryOperator.REF:
			write_string ("ref ");
			break;
		case UnaryOperator.OUT:
			write_string ("out ");
			break;
		default:
			assert_not_reached ();
		}
		expr.inner.accept (this);
	}

	public override void visit_cast_expression (CastExpression expr) {
		if (expr.is_non_null_cast) {
			write_string ("(!) ");
			expr.inner.accept (this);
			return;
		}

		if (!expr.is_silent_cast) {
			write_string ("(");
			write_type (expr.type_reference);
			write_string (") ");
		}

		expr.inner.accept (this);

		if (expr.is_silent_cast) {
			write_string (" as ");
			write_type (expr.type_reference);
		}
	}

	public override void visit_pointer_indirection (PointerIndirection expr) {
		write_string ("*");
		expr.inner.accept (this);
	}

	public override void visit_addressof_expression (AddressofExpression expr) {
		write_string ("&");
		expr.inner.accept (this);
	}

	public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
		write_string ("(owned) ");
		expr.inner.accept (this);
	}

	public override void visit_binary_expression (BinaryExpression expr) {
		expr.left.accept (this);

		switch (expr.operator) {
		case BinaryOperator.PLUS:
			write_string (" + ");
			break;
		case BinaryOperator.MINUS:
			write_string (" - ");
			break;
		case BinaryOperator.MUL:
			write_string (" * ");
			break;
		case BinaryOperator.DIV:
			write_string (" / ");
			break;
		case BinaryOperator.MOD:
			write_string (" % ");
			break;
		case BinaryOperator.SHIFT_LEFT:
			write_string (" << ");
			break;
		case BinaryOperator.SHIFT_RIGHT:
			write_string (" >> ");
			break;
		case BinaryOperator.LESS_THAN:
			write_string (" < ");
			break;
		case BinaryOperator.GREATER_THAN:
			write_string (" > ");
			break;
		case BinaryOperator.LESS_THAN_OR_EQUAL:
			write_string (" <= ");
			break;
		case BinaryOperator.GREATER_THAN_OR_EQUAL:
			write_string (" >= ");
			break;
		case BinaryOperator.EQUALITY:
			write_string (" == ");
			break;
		case BinaryOperator.INEQUALITY:
			write_string (" != ");
			break;
		case BinaryOperator.BITWISE_AND:
			write_string (" & ");
			break;
		case BinaryOperator.BITWISE_OR:
			write_string (" | ");
			break;
		case BinaryOperator.BITWISE_XOR:
			write_string (" ^ ");
			break;
		case BinaryOperator.AND:
			write_string (" && ");
			break;
		case BinaryOperator.OR:
			write_string (" || ");
			break;
		case BinaryOperator.IN:
			write_string (" in ");
			break;
		case BinaryOperator.COALESCE:
			write_string (" ?? ");
			break;
		default:
			assert_not_reached ();
		}

		expr.right.accept (this);
	}

	public override void visit_type_check (TypeCheck expr) {
		expr.expression.accept (this);
		write_string (" is ");
		write_type (expr.type_reference);
	}

	public override void visit_conditional_expression (ConditionalExpression expr) {
		expr.condition.accept (this);
		write_string ("?");
		expr.true_expression.accept (this);
		write_string (":");
		expr.false_expression.accept (this);
	}

	public override void visit_lambda_expression (LambdaExpression expr) {
		write_string ("(");
		var params = expr.get_parameters ();
		if (params.size != 0) {
			for (var i = 0; i < params.size - 1; ++ i) {
				write_string (params[i]);
				write_string (", ");
			}
			write_string (params[params.size - 1]);
		}
		write_string (") =>");
		if (expr.statement_body != null) {
			expr.statement_body.accept (this);
		} else if (expr.expression_body != null) {
			expr.expression_body.accept (this);
		}
	}

	public override void visit_assignment (Assignment a) {
		a.left.accept (this);
		write_string (" = ");
		a.right.accept (this);
	}

	private void write_indent () {
		int i;
		
		if (!bol) {
			stream.putc ('\n');
		}
		
		for (i = 0; i < indent; i++) {
			stream.putc ('\t');
		}
		
		bol = false;
	}
	
	private void write_identifier (string s) {
		char* id = (char*)s;
		int id_length = (int)s.length;
		if (context.profile != Profile.DOVA &&
		    (Vala.Scanner.get_identifier_or_keyword (id, id_length) != Vala.TokenType.IDENTIFIER ||
		     s.get_char ().isdigit ())) {
			stream.putc ('@'); 
		}
		write_string (s);
	}

	private void write_return_type (DataType type) {
		if (type.is_weak ()) {
			write_string ("unowned ");
		}

		write_type (type);
	}

	private void write_type (DataType type) {
		write_string (type.to_qualified_string (current_scope));
	}

	private void write_string (string s) {
		stream.printf ("%s", s);
		bol = false;
	}
	
	private void write_newline () {
		stream.putc ('\n');
		bol = true;
	}
	
	void write_code_block (Block? block) {
		if (block == null || type != CodeWriterType.DUMP) {
			write_string (";");
			return;
		}

		block.accept (this);
	}

	private void write_begin_block () {
		if (!bol) {
			stream.putc (' ');
		} else {
			write_indent ();
		}
		stream.putc ('{');
		write_newline ();
		indent++;
	}
	
	private void write_end_block () {
		indent--;
		write_indent ();
		stream.printf ("}");
	}

	private bool check_accessibility (Symbol sym) {
		switch (type) {
		case CodeWriterType.EXTERNAL:
			return sym.access == SymbolAccessibility.PUBLIC ||
			       sym.access == SymbolAccessibility.PROTECTED;

		case CodeWriterType.INTERNAL:
		case CodeWriterType.FAST:
			return sym.access == SymbolAccessibility.INTERNAL ||
			       sym.access == SymbolAccessibility.PUBLIC ||
			       sym.access == SymbolAccessibility.PROTECTED;

		case CodeWriterType.DUMP:
			return true;

		default:
			assert_not_reached ();
		}
	}

	private void write_attributes (CodeNode node) {
		foreach (Attribute attr in node.attributes) {
			if (!filter_attribute (attr)) {
				write_indent ();
				stream.printf ("[%s", attr.name);

				var keys = attr.args.get_keys ();
				if (keys.size != 0) {
					stream.printf (" (");

					string separator = "";
					foreach (string arg_name in keys) {
						stream.printf ("%s%s = %s", separator, arg_name, attr.args.get (arg_name));
						separator = ", ";
					}

					stream.printf (")");
				}
				stream.printf ("]");
				write_newline ();
			}
		}
	}

	private bool filter_attribute (Attribute attr) {
		if (attr.name == "CCode"
		    || attr.name == "Compact" || attr.name == "Immutable"
		    || attr.name == "SimpleType" || attr.name == "IntegerType" || attr.name == "FloatingType"
		    || attr.name == "Flags") {
			return true;
		}
		return false;
	}

	private void write_accessibility (Symbol sym) {
		if (sym.access == SymbolAccessibility.PUBLIC) {
			write_string ("public ");
		} else if (sym.access == SymbolAccessibility.PROTECTED) {
			write_string ("protected ");
		} else if (sym.access == SymbolAccessibility.INTERNAL) {
			write_string ("internal ");
		} else if (sym.access == SymbolAccessibility.PRIVATE) {
			write_string ("private ");
		}

		if (type != CodeWriterType.EXTERNAL && sym.external && !sym.external_package) {
			write_string ("extern ");
		}
	}
}

public enum Vala.CodeWriterType {<commit_msg>CodeWriter: Write accessibility for property accessors<commit_after>/* valacodewriter.vala
 *
 * Copyright (C) 2006-2010  <NAME>
 * Copyright (C) 2006-2008  <NAME>
 *
 * 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:
 * 	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 */


/**
 * Code visitor generating Vala API file for the public interface.
 */
public class Vala.CodeWriter : CodeVisitor {
	private CodeContext context;
	
	FileStream stream;
	
	int indent;
	/* at begin of line */
	bool bol = true;

	Scope current_scope;

	CodeWriterType type;

	string? override_header = null;
	string? header_to_override = null;

	public CodeWriter (CodeWriterType type = CodeWriterType.EXTERNAL) {
		this.type = type;
	}

	/**
	 * Allows overriding of a specific cheader in the output
	 * @param original orignal cheader to override
	 * @param replacement cheader to replace original with
	 */
	public void set_cheader_override (string original, string replacement)
	{
		header_to_override = original;
		override_header = replacement;
	}

	/**
	 * Writes the public interface of the specified code context into the
	 * specified file.
	 *
	 * @param context  a code context
	 * @param filename a relative or absolute filename
	 */
	public void write_file (CodeContext context, string filename) {
		var file_exists = FileUtils.test (filename, FileTest.EXISTS);
		var temp_filename = filename + ".valatmp";
		this.context = context;

		if (file_exists) {
			stream = FileStream.open (temp_filename, "w");
		} else {
			stream = FileStream.open (filename, "w");
		}

		if (stream == null) {
			Report.error (null, "unable to open `%s' for writing".printf (filename));
			return;
		}

		var header = context.version_header ?
			"/* %s generated by %s %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname (), Config.BUILD_VERSION) :
			"/* %s generated by %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname ());
		write_string (header);
		write_newline ();
		write_newline ();

		current_scope = context.root.scope;

		context.accept (this);

		current_scope = null;

		stream = null;

		if (file_exists) {
			var changed = true;

			try {
				var old_file = new MappedFile (filename, false);
				var new_file = new MappedFile (temp_filename, false);
				var len = old_file.get_length ();
				if (len == new_file.get_length ()) {
					if (Memory.cmp (old_file.get_contents (), new_file.get_contents (), len) == 0) {
						changed = false;
					}
				}
				old_file = null;
				new_file = null;
			} catch (FileError e) {
				// assume changed if mmap comparison doesn't work
			}

			if (changed) {
				FileUtils.rename (temp_filename, filename);
			} else {
				FileUtils.unlink (temp_filename);
			}
		}

	}

	public override void visit_using_directive (UsingDirective ns) {
		if (type == CodeWriterType.FAST) {
			write_string ("using %s;\n".printf (ns.namespace_symbol.name));
		}
	}

	public override void visit_namespace (Namespace ns) {
		if (ns.external_package) {
			return;
		}

		if (ns.name == null)  {
			ns.accept_children (this);
			return;
		}

		write_indent ();
		write_string ("[CCode (cprefix = \"%s\", lower_case_cprefix = \"%s\"".printf (ns.get_cprefix (), ns.get_lower_case_cprefix ()));

		if (ns.source_reference != null) {
			if (ns.source_reference.file.gir_namespace != null) {
				write_string (", ");
				write_string ("gir_namespace = \"%s\"".printf (ns.source_reference.file.gir_namespace));
			}
			if (ns.source_reference.file.gir_version != null) {
				write_string(", ");
				write_string ("gir_version = \"%s\"".printf (ns.source_reference.file.gir_version));
			}
		}

		write_string (")]");
		write_newline ();

		write_attributes (ns);

		write_indent ();
		write_string ("namespace ");
		write_identifier (ns.name);
		write_begin_block ();

		current_scope = ns.scope;

		visit_sorted (ns.get_namespaces ());
		visit_sorted (ns.get_classes ());
		visit_sorted (ns.get_interfaces ());
		visit_sorted (ns.get_structs ());
		visit_sorted (ns.get_enums ());
		visit_sorted (ns.get_error_domains ());
		visit_sorted (ns.get_delegates ());
		visit_sorted (ns.get_fields ());
		visit_sorted (ns.get_constants ());
		visit_sorted (ns.get_methods ());

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	private string get_cheaders (Symbol cl) {
		bool first = true;
		string cheaders = "";
		if (type != CodeWriterType.FAST) {
			foreach (string cheader in cl.get_cheader_filenames ()) {
				if (header_to_override != null &&
				    cheader == header_to_override) {
					cheader = override_header;
				}
				if (first) {
					cheaders = cheader;
					first = false;
				} else {
					cheaders = "%s,%s".printf (cheaders, cheader);
				}
			}
		}
		return cheaders;
	}

	private void emit_deprecated_attribute (Symbol symbol) {
		if (symbol.deprecated) {
			write_indent ();
			write_string ("[Deprecated");
			var since = symbol.deprecated_since;
			var replacement = symbol.replacement;

			if (since != null || replacement != null) {
				write_string (" (");
				if (since != null) {
					write_string ("since = \"%s\"".printf (since));
				}
				if (since != null && replacement != null) {
					write_string (", ");
				}
				if (replacement != null) {
					write_string ("replacement = \"%s\"".printf (replacement));
				}
				write_string (")");
			}
			write_string ("]");
		}
	}

	public override void visit_class (Class cl) {
		if (cl.external_package) {
			return;
		}

		if (!check_accessibility (cl)) {
			return;
		}

		if (cl.is_compact) {
			write_indent ();
			write_string ("[Compact]");
			write_newline ();
		}

		if (cl.is_immutable) {
			write_indent ();
			write_string ("[Immutable]");
			write_newline ();
		}

		emit_deprecated_attribute (cl);

		write_indent ();
		
		write_string ("[CCode (");

		if (cl.is_reference_counting () && type != CodeWriterType.FAST) {
			if (cl.base_class == null || cl.base_class.get_ref_function () == null || cl.base_class.get_ref_function () != cl.get_ref_function ()) {
				write_string ("ref_function = \"%s\", ".printf (cl.get_ref_function ()));
				if (cl.ref_function_void) {
					write_string ("ref_function_void = true, ");
				}
			}
			if (cl.base_class == null || cl.base_class.get_unref_function () == null || cl.base_class.get_unref_function () != cl.get_unref_function ()) {
				write_string ("unref_function = \"%s\", ".printf (cl.get_unref_function ()));
			}
		} else {
			if (cl.get_dup_function () != null) {
				write_string ("copy_function = \"%s\", ".printf (cl.get_dup_function ()));
			}
			if (cl.get_free_function () != cl.get_default_free_function ()) {
				write_string ("free_function = \"%s\", ".printf (cl.get_free_function ()));
			}
		}

		if (cl.get_cname () != cl.get_default_cname ()) {
			write_string ("cname = \"%s\", ".printf (cl.get_cname ()));
		}
		if (cl.const_cname != null) {
			write_string ("const_cname = \"%s\", ".printf (cl.const_cname));
		}

		if (cl.type_check_function != null) {
			write_string ("type_check_function = \"%s\", ".printf (cl.type_check_function ));
		}

		if (cl.get_type_id () != cl.get_default_type_id ()) {
			write_string ("type_id = \"%s\", ".printf (cl.get_type_id ()));
		}

		if (cl.get_param_spec_function () != cl.get_default_param_spec_function ()) {
			write_string ("param_spec_function = \"%s\", ".printf (cl.get_param_spec_function ()));
		}

		write_string ("cheader_filename = \"%s\")]".printf (get_cheaders(cl)));
		write_newline ();

		write_attributes (cl);
		
		write_indent ();
		write_accessibility (cl);
		if (cl.is_abstract) {
			write_string ("abstract ");
		}
		write_string ("class ");
		write_identifier (cl.name);

		var type_params = cl.get_type_parameters ();
		if (type_params.size > 0) {
			write_string ("<");
			bool first = true;
			foreach (TypeParameter type_param in type_params) {
				if (first) {
					first = false;
				} else {
					write_string (",");
				}
				write_identifier (type_param.name);
			}
			write_string (">");
		}

		var base_types = cl.get_base_types ();
		if (base_types.size > 0) {
			write_string (" : ");
		
			bool first = true;
			foreach (DataType base_type in base_types) {
				if (!first) {
					write_string (", ");
				} else {
					first = false;
				}
				write_type (base_type);
			}
		}
		write_begin_block ();

		current_scope = cl.scope;

		visit_sorted (cl.get_classes ());
		visit_sorted (cl.get_structs ());
		visit_sorted (cl.get_enums ());
		visit_sorted (cl.get_delegates ());
		visit_sorted (cl.get_fields ());
		visit_sorted (cl.get_constants ());
		visit_sorted (cl.get_methods ());
		visit_sorted (cl.get_properties ());
		visit_sorted (cl.get_signals ());

		if (cl.constructor != null) {
			cl.constructor.accept (this);
		}

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	void visit_sorted (List<Symbol> symbols) {
		if (type != CodeWriterType.EXTERNAL) {
			// order of virtual methods matters for fast vapis
			foreach (Symbol sym in symbols) {
				sym.accept (this);
			}
			return;
		}

		var sorted_symbols = new ArrayList<Symbol> ();
		foreach (Symbol sym in symbols) {
			int left = 0;
			int right = sorted_symbols.size - 1;
			if (left > right || sym.name < sorted_symbols[left].name) {
				sorted_symbols.insert (0, sym);
			} else if (sym.name > sorted_symbols[right].name) {
				sorted_symbols.add (sym);
			} else {
				while (right - left > 1) {
					int i = (right + left) / 2;
					if (sym.name > sorted_symbols[i].name) {
						left = i;
					} else {
						right = i;
					}
				}
				sorted_symbols.insert (left + 1, sym);
			}
		}
		foreach (Symbol sym in sorted_symbols) {
			sym.accept (this);
		}
	}

	public override void visit_struct (Struct st) {
		if (st.external_package) {
			return;
		}

		if (!check_accessibility (st)) {
			return;
		}
		
		if (st.is_immutable) {
			write_indent ();
			write_string ("[Immutable]");
			write_newline ();
		}

		emit_deprecated_attribute (st);

		write_indent ();

		write_string ("[CCode (");

		if (st.get_cname () != st.get_default_cname ()) {
			write_string ("cname = \"%s\", ".printf (st.get_cname ()));
		}

		if (!st.has_type_id) {
			write_string ("has_type_id = false, ");
		} else if (!st.is_simple_type () && st.get_type_id () != "G_TYPE_POINTER") {
			write_string ("type_id = \"%s\", ".printf (st.get_type_id ()));
		}

		if (!st.has_copy_function) {
			write_string ("has_copy_function = false, ");
		}

		if (!st.has_destroy_function) {
			write_string ("has_destroy_function = false, ");
		}

		write_string ("cheader_filename = \"%s\")]".printf (get_cheaders(st)));
		write_newline ();

		if (st.is_simple_type ()) {
			write_indent ();
			write_string ("[SimpleType]");
			write_newline ();
		}

		if (st.is_integer_type ()) {
			write_indent ();
			write_string ("[IntegerType (rank = %d)]".printf (st.get_rank ()));
			write_newline ();
		}

		if (st.is_floating_type ()) {
			write_indent ();
			write_string ("[FloatingType (rank = %d)]".printf (st.get_rank ()));
			write_newline ();
		}

		write_attributes (st);

		write_indent ();
		write_accessibility (st);
		write_string ("struct ");
		write_identifier (st.name);

		if (st.base_type != null) {
			write_string (" : ");
			write_type (st.base_type);
		}

		write_begin_block ();

		current_scope = st.scope;

		foreach (Field field in st.get_fields ()) {
			field.accept (this);
		}
		visit_sorted (st.get_constants ());
		visit_sorted (st.get_methods ());
		visit_sorted (st.get_properties ());

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	public override void visit_interface (Interface iface) {
		if (iface.external_package) {
			return;
		}

		if (!check_accessibility (iface)) {
			return;
		}

		emit_deprecated_attribute (iface);

		write_indent ();

		write_string ("[CCode (cheader_filename = \"%s\"".printf (get_cheaders(iface)));
		if (iface.get_lower_case_csuffix () != iface.get_default_lower_case_csuffix ())
			write_string (", lower_case_csuffix = \"%s\"".printf (iface.get_lower_case_csuffix ()));

		write_string (")]");
		write_newline ();

		write_attributes (iface);

		write_indent ();
		write_accessibility (iface);
		write_string ("interface ");
		write_identifier (iface.name);

		var type_params = iface.get_type_parameters ();
		if (type_params.size > 0) {
			write_string ("<");
			bool first = true;
			foreach (TypeParameter type_param in type_params) {
				if (first) {
					first = false;
				} else {
					write_string (",");
				}
				write_identifier (type_param.name);
			}
			write_string (">");
		}

		var prerequisites = iface.get_prerequisites ();
		if (prerequisites.size > 0) {
			write_string (" : ");
		
			bool first = true;
			foreach (DataType prerequisite in prerequisites) {
				if (!first) {
					write_string (", ");
				} else {
					first = false;
				}
				write_type (prerequisite);
			}
		}
		write_begin_block ();

		current_scope = iface.scope;

		visit_sorted (iface.get_classes ());
		visit_sorted (iface.get_structs ());
		visit_sorted (iface.get_enums ());
		visit_sorted (iface.get_delegates ());
		visit_sorted (iface.get_fields ());
		visit_sorted (iface.get_constants ());
		visit_sorted (iface.get_methods ());
		visit_sorted (iface.get_properties ());
		visit_sorted (iface.get_signals ());

		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	public override void visit_enum (Enum en) {
		if (en.external_package) {
			return;
		}

		if (!check_accessibility (en)) {
			return;
		}

		emit_deprecated_attribute (en);

		write_indent ();

		write_string ("[CCode (cprefix = \"%s\", ".printf (en.get_cprefix ()));

		if (!en.has_type_id) {
			write_string ("has_type_id = false, ");
		}

		write_string ("cheader_filename = \"%s\")]".printf (get_cheaders(en)));

		if (en.is_flags) {
			write_indent ();
			write_string ("[Flags]");
		}

		write_attributes (en);

		write_indent ();
		write_accessibility (en);
		write_string ("enum ");
		write_identifier (en.name);
		write_begin_block ();

		bool first = true;
		foreach (EnumValue ev in en.get_values ()) {
			if (first) {
				first = false;
			} else {
				write_string (",");
				write_newline ();
			}

			if (ev.get_cname () != ev.get_default_cname ()) {
				write_indent ();
				write_string ("[CCode (cname = \"%s\")]".printf (ev.get_cname ()));
			}
			write_indent ();
			write_identifier (ev.name);

			if (type == CodeWriterType.FAST && ev.value != null) {
				write_string(" = ");
				ev.value.accept (this);
			}
		}

		if (!first) {
			if (en.get_methods ().size > 0 || en.get_constants ().size > 0) {
				write_string (";");
			}
			write_newline ();
		}

		current_scope = en.scope;
		foreach (Method m in en.get_methods ()) {
			m.accept (this);
		}
		foreach (Constant c in en.get_constants ()) {
			c.accept (this);
		}
		current_scope = current_scope.parent_scope;

		write_end_block ();
		write_newline ();
	}

	public override void visit_error_domain (ErrorDomain edomain) {
		if (edomain.external_package) {
			return;
		}

		if (!check_accessibility (edomain)) {
			return;
		}

		emit_deprecated_attribute (edomain);

		write_indent ();

		write_string ("[CCode (cprefix = \"%s\", cheader_filename = \"%s\")]".printf (edomain.get_cprefix (), get_cheaders(edomain)));

		write_attributes (edomain);

		write_indent ();
		write_accessibility (edomain);
		write_string ("errordomain ");
		write_identifier (edomain.name);
		write_begin_block ();

		edomain.accept_children (this);

		write_end_block ();
		write_newline ();
	}

	public override void visit_error_code (ErrorCode ecode) {
		write_indent ();
		write_identifier (ecode.name);
		write_string (",");
		write_newline ();
	}

	public override void visit_constant (Constant c) {
		if (c.external_package) {
			return;
		}

		if (!check_accessibility (c)) {
			return;
		}

		emit_deprecated_attribute (c);

		bool custom_cname = (c.get_cname () != c.get_default_cname ());
		bool custom_cheaders = (c.parent_symbol is Namespace);
		if (custom_cname || custom_cheaders) {
			write_indent ();
			write_string ("[CCode (");

			if (custom_cname) {
				write_string ("cname = \"%s\"".printf (c.get_cname ()));
			}

			if (custom_cheaders) {
				if (custom_cname) {
					write_string (", ");
				}

				write_string ("cheader_filename = \"%s\"".printf (get_cheaders(c)));
			}

			write_string (")]");
		}

		write_indent ();
		write_accessibility (c);
		write_string ("const ");

		write_type (c.type_reference);
			
		write_string (" ");
		write_identifier (c.name);
		if (type == CodeWriterType.FAST && c.value != null) {
			write_string(" = ");
			c.value.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_field (Field f) {
		if (f.external_package) {
			return;
		}

		if (!check_accessibility (f)) {
			return;
		}

		emit_deprecated_attribute (f);

		bool custom_cname = (f.get_cname () != f.get_default_cname ());
		bool custom_ctype = (f.get_ctype () != null);
		bool custom_cheaders = (f.parent_symbol is Namespace);
		bool custom_array_length_cname = (f.get_array_length_cname () != null);
		bool custom_array_length_type = (f.array_length_type != null);
		if (custom_cname || custom_ctype || custom_cheaders || custom_array_length_cname || custom_array_length_type || (f.no_array_length && f.variable_type is ArrayType)) {
			write_indent ();
			write_string ("[CCode (");

			if (custom_cname) {
				write_string ("cname = \"%s\"".printf (f.get_cname ()));
			}

			if (custom_ctype) {
				if (custom_cname) {
					write_string (", ");
				}

				write_string ("type = \"%s\"".printf (f.get_ctype ()));
			}

			if (custom_cheaders) {
				if (custom_cname || custom_ctype) {
					write_string (", ");
				}

				write_string ("cheader_filename = \"%s\"".printf (get_cheaders(f)));
			}

			if (f.variable_type is ArrayType) {
				if (f.no_array_length) {
					if (custom_cname || custom_ctype || custom_cheaders) {
						write_string (", ");
					}

					write_string ("array_length = false");

					if (f.array_null_terminated) {
						write_string (", array_null_terminated = true");
					}
				} else {
					if (custom_array_length_cname) {
						if (custom_cname || custom_ctype || custom_cheaders) {
							write_string (", ");
						}

						write_string ("array_length_cname = \"%s\"".printf (f.get_array_length_cname ()));
					}

					if (custom_array_length_type) {
						if (custom_cname || custom_ctype || custom_cheaders || custom_array_length_cname) {
							write_string (", ");
						}

						write_string ("array_length_type = \"%s\"".printf (f.array_length_type));
					}
				}
			}

			write_string (")]");
		}

		write_indent ();
		write_accessibility (f);

		if (f.binding == MemberBinding.STATIC) {
			write_string ("static ");
		} else if (f.binding == MemberBinding.CLASS) {
			write_string ("class ");
		}

		if (f.variable_type.is_weak ()) {
			write_string ("weak ");
		}

		write_type (f.variable_type);
			
		write_string (" ");
		write_identifier (f.name);
		write_string (";");
		write_newline ();
	}
	
	private void write_error_domains (List<DataType> error_domains) {
		if (error_domains.size > 0) {
			write_string (" throws ");

			bool first = true;
			foreach (DataType type in error_domains) {
				if (!first) {
					write_string (", ");
				} else {
					first = false;
				}

				write_type (type);
			}
		}
	}

	// equality comparison with 3 digit precision
	private bool float_equal (double d1, double d2) {
		return ((int) (d1 * 1000)) == ((int) (d2 * 1000));
	}

	private void write_params (List<FormalParameter> params) {
		write_string ("(");

		int i = 1;
		foreach (FormalParameter param in params) {
			if (i > 1) {
				write_string (", ");
			}
			
			if (param.ellipsis) {
				write_string ("...");
				continue;
			}
			

			var ccode_params = new StringBuilder ();
			var separator = "";

			if (!float_equal (param.cparameter_position, i)) {
				ccode_params.append_printf ("%spos = %g", separator, param.cparameter_position);
				separator = ", ";
			}
			if (param.ctype != null) {
				ccode_params.append_printf ("%stype = \"%s\"", separator, param.ctype);
				separator = ", ";
			}
			if (param.no_array_length && param.variable_type is ArrayType) {
				ccode_params.append_printf ("%sarray_length = false", separator);
				separator = ", ";
			}
			if (param.array_length_type != null && param.variable_type is ArrayType) {
				ccode_params.append_printf ("%sarray_length_type = \"%s\"", separator, param.array_length_type);
				separator = ", ";
			}
			if (param.get_array_length_cname () != null && param.variable_type is ArrayType) {
				ccode_params.append_printf ("%sarray_length_cname = \"%s\"", separator, param.get_array_length_cname ());
				separator = ", ";
			}
			if (!float_equal (param.carray_length_parameter_position, i + 0.1)) {
				ccode_params.append_printf ("%sarray_length_pos = %g", separator, param.carray_length_parameter_position);
				separator = ", ";
			}
			if (!float_equal (param.cdelegate_target_parameter_position, i + 0.1)) {
				ccode_params.append_printf ("%sdelegate_target_pos = %g", separator, param.cdelegate_target_parameter_position);
				separator = ", ";
			}

			if (ccode_params.len > 0) {
				write_string ("[CCode (%s)] ".printf (ccode_params.str));
			}

			if (param.params_array) {
				write_string ("params ");
			}

			if (param.direction == ParameterDirection.IN) {
				if (param.variable_type.value_owned) {
					write_string ("owned ");
				}
			} else {
				if (param.direction == ParameterDirection.REF) {
					write_string ("ref ");
				} else if (param.direction == ParameterDirection.OUT) {
					write_string ("out ");
				}
				if (param.variable_type.is_weak ()) {
					write_string ("unowned ");
				}
			}

			write_type (param.variable_type);

			write_string (" ");
			write_identifier (param.name);
			
			if (param.initializer != null) {
				write_string (" = ");
				param.initializer.accept (this);
			}

			i++;
		}

		write_string (")");
	}

	public override void visit_delegate (Delegate cb) {
		if (cb.external_package) {
			return;
		}

		if (!check_accessibility (cb)) {
			return;
		}

		emit_deprecated_attribute (cb);

		write_indent ();

		write_string ("[CCode (cheader_filename = \"%s\"".printf (get_cheaders(cb)));

		if (!cb.has_target) {
			write_string (", has_target = false");
		} else if (!float_equal (cb.cinstance_parameter_position, -2)) {
			write_string (", instance_pos = %g".printf (cb.cinstance_parameter_position));
		}

		write_string (")]");

		write_indent ();

		write_accessibility (cb);
		write_string ("delegate ");
		
		write_return_type (cb.return_type);
		
		write_string (" ");
		write_identifier (cb.name);

		var type_params = cb.get_type_parameters ();
		if (type_params.size > 0) {
			write_string ("<");
			bool first = true;
			foreach (TypeParameter type_param in type_params) {
				if (first) {
					first = false;
				} else {
					write_string (",");
				}
				write_identifier (type_param.name);
			}
			write_string (">");
		}

		write_string (" ");
		
		write_params (cb.get_parameters ());

		write_error_domains (cb.get_error_types ());

		write_string (";");

		write_newline ();
	}

	public override void visit_constructor (Constructor c) {
		if (type != CodeWriterType.DUMP) {
			return;
		}

		write_indent ();
		write_string ("construct");
		write_code_block (c.body);
		write_newline ();
	}

	public override void visit_method (Method m) {
		if (m.external_package) {
			return;
		}

		// don't write interface implementation unless it's an abstract or virtual method
		if (!check_accessibility (m) || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
			if (type != CodeWriterType.DUMP) {
				return;
			}
		}

		if (m.get_attribute ("NoWrapper") != null) {
			write_indent ();
			write_string ("[NoWrapper]");
		}
		if (m.get_attribute ("NoThrow") != null) {
			write_indent ();
			write_string ("[NoThrow]");
		}
		if (m.returns_modified_pointer) {
			write_indent ();
			write_string ("[ReturnsModifiedPointer]");
		}
		if (m.printf_format) {
			write_indent ();
			write_string ("[PrintfFormat]");
		}
		if (m.scanf_format) {
			write_indent ();
			write_string ("[ScanfFormat]");
		}
		if (m.get_attribute ("Print") != null) {
			write_indent ();
			write_string ("[Print]");
		}
		if (m.get_attribute ("NoReturn") != null) {
			write_indent ();
			write_string ("[NoReturn]");
		}

		emit_deprecated_attribute (m);

		var ccode_params = new StringBuilder ();
		var separator = "";

		if (m.get_cname () != m.get_default_cname ()) {
			ccode_params.append_printf ("%scname = \"%s\"", separator, m.get_cname ());
			separator = ", ";
		}
		if (m.parent_symbol is Namespace) {
			ccode_params.append_printf ("%scheader_filename = \"%s\"", separator, get_cheaders(m));
			separator = ", ";
		}
		if (!float_equal (m.cinstance_parameter_position, 0)) {
			ccode_params.append_printf ("%sinstance_pos = %g", separator, m.cinstance_parameter_position);
			separator = ", ";
		}
		if (m.no_array_length && m.return_type is ArrayType) {
			ccode_params.append_printf ("%sarray_length = false", separator);
			separator = ", ";
		}
		if (!float_equal (m.carray_length_parameter_position, -3)) {
			ccode_params.append_printf ("%sarray_length_pos = %g", separator, m.carray_length_parameter_position);
			separator = ", ";
		}
		if (m.array_null_terminated && m.return_type is ArrayType) {
			ccode_params.append_printf ("%sarray_null_terminated = true", separator);
			separator = ", ";
		}
		if (m.array_length_type != null && m.return_type is ArrayType) {
			ccode_params.append_printf ("%sarray_length_type = \"%s\"", separator, m.array_length_type);
			separator = ", ";
		}
		if (!float_equal (m.cdelegate_target_parameter_position, -3)) {
			ccode_params.append_printf ("%sdelegate_target_pos = %g", separator, m.cdelegate_target_parameter_position);
			separator = ", ";
		}
		if (m.vfunc_name != m.name) {
			ccode_params.append_printf ("%svfunc_name = \"%s\"", separator, m.vfunc_name);
			separator = ", ";
		}
		if (m.coroutine && m.get_finish_cname () != m.get_default_finish_cname ()) {
			ccode_params.append_printf ("%sfinish_name = \"%s\"", separator, m.get_finish_cname ());
			separator = ", ";
		}
		if (m.sentinel != m.DEFAULT_SENTINEL) {
			ccode_params.append_printf ("%ssentinel = \"%s\"", separator, m.sentinel);
			separator = ", ";
		}
		var cm = m as CreationMethod;
		if (cm != null) {
			if (cm.custom_return_type_cname != null) {
				ccode_params.append_printf ("%stype = \"%s\"", separator, cm.custom_return_type_cname);
				separator = ", ";
			}
			if (!m.has_new_function) {
				ccode_params.append_printf ("%shas_new_function = false", separator);
				separator = ", ";
			}
			if (!m.has_construct_function) {
				ccode_params.append_printf ("%shas_construct_function = false", separator);
				separator = ", ";
			} else if (m.name == ".new" && m.get_real_cname () != cm.get_default_construct_function ()) {
				ccode_params.append_printf ("%sconstruct_function = \"%s\"", separator, m.get_real_cname ());
				separator = ", ";
			}
		}

		if (ccode_params.len > 0) {
			write_indent ();
			write_string ("[CCode (%s)]".printf (ccode_params.str));
		}
		
		write_indent ();
		write_accessibility (m);
		
		if (m is CreationMethod) {
			if (m.coroutine) {
				write_string ("async ");
			}

			var datatype = (TypeSymbol) m.parent_symbol;
			write_identifier (datatype.name);
			if (m.name != ".new") {
				write_string (".");
				write_identifier (m.name);
			}
			write_string (" ");
		} else {
			if (m.binding == MemberBinding.STATIC) {
				write_string ("static ");
			} else if (m.binding == MemberBinding.CLASS) {
				write_string ("class ");
			} else if (m.is_abstract) {
				write_string ("abstract ");
			} else if (m.is_virtual) {
				write_string ("virtual ");
			} else if (m.overrides) {
				write_string ("override ");
			}

			if (m.hides) {
				write_string ("new ");
			}

			if (m.coroutine) {
				write_string ("async ");
			}
		
			write_return_type (m.return_type);
			write_string (" ");

			write_identifier (m.name);

			var type_params = m.get_type_parameters ();
			if (type_params.size > 0) {
				write_string ("<");
				bool first = true;
				foreach (TypeParameter type_param in type_params) {
					if (first) {
						first = false;
					} else {
						write_string (",");
					}
					write_identifier (type_param.name);
				}
				write_string (">");
			}

			write_string (" ");
		}
		
		write_params (m.get_parameters ());

		if (context.profile != Profile.DOVA) {
			write_error_domains (m.get_error_types ());
		}

		write_code_block (m.body);

		write_newline ();
	}

	public override void visit_creation_method (CreationMethod m) {
		visit_method (m);
	}

	public override void visit_property (Property prop) {
		if (!check_accessibility (prop) || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
			return;
		}

		emit_deprecated_attribute (prop);

		if (prop.no_accessor_method) {
			write_indent ();
			write_string ("[NoAccessorMethod]");
		}
		if (prop.property_type is ArrayType && prop.no_array_length) {
			write_indent ();
			write_string ("[CCode (array_length = false");

			if (prop.array_null_terminated) {
				write_string (", array_null_terminated = true");
			}

			write_string (")]");
		}

		write_indent ();
		write_accessibility (prop);

		if (prop.binding == MemberBinding.STATIC) {
			write_string ("static ");
		} else  if (prop.is_abstract) {
			write_string ("abstract ");
		} else if (prop.is_virtual) {
			write_string ("virtual ");
		} else if (prop.overrides) {
			write_string ("override ");
		}

		write_type (prop.property_type);

		write_string (" ");
		write_identifier (prop.name);
		write_string (" {");
		if (prop.get_accessor != null) {
			var ccode_params = new StringBuilder ();
			var separator = "";

			if (prop.get_accessor.get_cname () != prop.get_accessor.get_default_cname ()) {
				ccode_params.append_printf ("%scname = \"%s\"", separator, prop.get_accessor.get_cname ());
				separator = ", ";
			}
			if (ccode_params.len > 0) {
				write_indent ();
				write_string ("[CCode (%s)]".printf (ccode_params.str));
			}

			write_property_accessor_accessibility (prop.get_accessor);

			if (context.profile != Profile.DOVA && prop.get_accessor.value_type.is_disposable ()) {
				write_string (" owned");
			}

			write_string (" get");
			write_code_block (prop.get_accessor.body);
		}
		if (prop.set_accessor != null) {
			var ccode_params = new StringBuilder ();
			var separator = "";

			if (prop.set_accessor.get_cname () != prop.set_accessor.get_default_cname ()) {
				ccode_params.append_printf ("%scname = \"%s\"", separator, prop.set_accessor.get_cname ());
				separator = ", ";
			}
			if (ccode_params.len > 0) {
				write_indent ();
				write_string ("[CCode (%s)]".printf (ccode_params.str));
			}

			write_property_accessor_accessibility (prop.set_accessor);

			if (context.profile != Profile.DOVA && prop.set_accessor.value_type.value_owned) {
				write_string (" owned");
			}

			if (prop.set_accessor.writable) {
				write_string (" set");
			}
			if (prop.set_accessor.construction) {
				write_string (" construct");
			}
			write_code_block (prop.set_accessor.body);
		}
		write_string (" }");
		write_newline ();
	}

	public override void visit_signal (Signal sig) {
		if (!check_accessibility (sig)) {
			return;
		}
		
		if (sig.has_emitter) {
			write_indent ();
			write_string ("[HasEmitter]");
		}

		emit_deprecated_attribute (sig);
		
		write_indent ();
		write_accessibility (sig);

		if (sig.is_virtual) {
			write_string ("virtual ");
		}

		write_string ("signal ");
		
		write_return_type (sig.return_type);
		
		write_string (" ");
		write_identifier (sig.name);
		
		write_string (" ");
		
		write_params (sig.get_parameters ());

		write_string (";");

		write_newline ();
	}

	public override void visit_block (Block b) {
		write_begin_block ();

		foreach (Statement stmt in b.get_statements ()) {
			stmt.accept (this);
		}

		write_end_block ();
	}

	public override void visit_empty_statement (EmptyStatement stmt) {
	}

	public override void visit_declaration_statement (DeclarationStatement stmt) {
		write_indent ();
		stmt.declaration.accept (this);
		write_string (";");
		write_newline ();
	}

	public override void visit_local_variable (LocalVariable local) {
		write_type (local.variable_type);
		write_string (" ");
		write_identifier (local.name);
		if (local.initializer != null) {
			write_string (" = ");
			local.initializer.accept (this);
		}
	}

	public override void visit_initializer_list (InitializerList list) {
		write_string ("{");

		bool first = true;
		foreach (Expression initializer in list.get_initializers ()) {
			if (!first) {
				write_string (", ");
			} else {
				write_string (" ");
			}
			first = false;
			initializer.accept (this);
		}
		write_string (" }");
	}

	public override void visit_expression_statement (ExpressionStatement stmt) {
		write_indent ();
		stmt.expression.accept (this);
		write_string (";");
		write_newline ();
	}

	public override void visit_if_statement (IfStatement stmt) {
		write_indent ();
		write_string ("if (");
		stmt.condition.accept (this);
		write_string (")");
		stmt.true_statement.accept (this);
		if (stmt.false_statement != null) {
			write_string (" else");
			stmt.false_statement.accept (this);
		}
		write_newline ();
	}

	public override void visit_switch_statement (SwitchStatement stmt) {
		write_indent ();
		write_string ("switch (");
		stmt.expression.accept (this);
		write_string (") {");
		write_newline ();

		foreach (SwitchSection section in stmt.get_sections ()) {
			section.accept (this);
		}

		write_indent ();
		write_string ("}");
		write_newline ();
	}

	public override void visit_switch_section (SwitchSection section) {
		foreach (SwitchLabel label in section.get_labels ()) {
			label.accept (this);
		}

		visit_block (section);
	}

	public override void visit_switch_label (SwitchLabel label) {
		if (label.expression != null) {
			write_indent ();
			write_string ("case ");
			label.expression.accept (this);
			write_string (":");
			write_newline ();
		} else {
			write_indent ();
			write_string ("default:");
			write_newline ();
		}
	}

	public override void visit_loop (Loop stmt) {
		write_indent ();
		write_string ("loop");
		stmt.body.accept (this);
		write_newline ();
	}

	public override void visit_while_statement (WhileStatement stmt) {
		write_indent ();
		write_string ("while (");
		stmt.condition.accept (this);
		write_string (")");
		stmt.body.accept (this);
		write_newline ();
	}

	public override void visit_do_statement (DoStatement stmt) {
		write_indent ();
		write_string ("do");
		stmt.body.accept (this);
		write_string ("while (");
		stmt.condition.accept (this);
		write_string (");");
		write_newline ();
	}

	public override void visit_for_statement (ForStatement stmt) {
		write_indent ();
		write_string ("for (");

		bool first = true;
		foreach (Expression initializer in stmt.get_initializer ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;
			initializer.accept (this);
		}
		write_string ("; ");

		stmt.condition.accept (this);
		write_string ("; ");

		first = true;
		foreach (Expression iterator in stmt.get_iterator ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;
			iterator.accept (this);
		}

		write_string (")");
		stmt.body.accept (this);
		write_newline ();
	}

	public override void visit_foreach_statement (ForeachStatement stmt) {
	}

	public override void visit_break_statement (BreakStatement stmt) {
		write_indent ();
		write_string ("break;");
		write_newline ();
	}

	public override void visit_continue_statement (ContinueStatement stmt) {
		write_indent ();
		write_string ("continue;");
		write_newline ();
	}

	public override void visit_return_statement (ReturnStatement stmt) {
		write_indent ();
		write_string ("return");
		if (stmt.return_expression != null) {
			write_string (" ");
			stmt.return_expression.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_yield_statement (YieldStatement y) {
		write_indent ();
		write_string ("yield");
		if (y.yield_expression != null) {
			write_string (" ");
			y.yield_expression.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_throw_statement (ThrowStatement stmt) {
		write_indent ();
		write_string ("throw");
		if (stmt.error_expression != null) {
			write_string (" ");
			stmt.error_expression.accept (this);
		}
		write_string (";");
		write_newline ();
	}

	public override void visit_try_statement (TryStatement stmt) {
		write_indent ();
		write_string ("try");
		stmt.body.accept (this);
		foreach (var clause in stmt.get_catch_clauses ()) {
			clause.accept (this);
		}
		if (stmt.finally_body != null) {
			write_string (" finally");
			stmt.finally_body.accept (this);
		}
		write_newline ();
	}

	public override void visit_catch_clause (CatchClause clause) {
		var type_name = clause.error_type == null ? "GLib.Error" : clause.error_type.to_string ();
		var var_name = clause.variable_name == null ? "_" : clause.variable_name;
		write_string (" catch (%s %s)".printf (type_name, var_name));
		clause.body.accept (this);
	}

	public override void visit_lock_statement (LockStatement stmt) {
		write_indent ();
		write_string ("lock (");
		stmt.resource.accept (this);
		write_string (")");
		if (stmt.body == null) {
			write_string (";");
		} else {
			stmt.body.accept (this);
		}
		write_newline ();
	}

	public override void visit_delete_statement (DeleteStatement stmt) {
		write_indent ();
		write_string ("delete ");
		stmt.expression.accept (this);
		write_string (";");
		write_newline ();
	}

	public override void visit_array_creation_expression (ArrayCreationExpression expr) {
		write_string ("new ");
		write_type (expr.element_type);
		write_string ("[");

		bool first = true;
		foreach (Expression size in expr.get_sizes ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			size.accept (this);
		}

		write_string ("]");

		if (expr.initializer_list != null) {
			write_string (" ");
			expr.initializer_list.accept (this);
		}
	}

	public override void visit_boolean_literal (BooleanLiteral lit) {
		write_string (lit.value.to_string ());
	}

	public override void visit_character_literal (CharacterLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_integer_literal (IntegerLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_real_literal (RealLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_string_literal (StringLiteral lit) {
		write_string (lit.value);
	}

	public override void visit_null_literal (NullLiteral lit) {
		write_string ("null");
	}

	public override void visit_member_access (MemberAccess expr) {
		if (expr.inner != null) {
			expr.inner.accept (this);
			write_string (".");
		}
		write_identifier (expr.member_name);
	}

	public override void visit_method_call (MethodCall expr) {
		expr.call.accept (this);
		write_string (" (");

		bool first = true;
		foreach (Expression arg in expr.get_argument_list ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			arg.accept (this);
		}

		write_string (")");
	}
	
	public override void visit_element_access (ElementAccess expr) {
		expr.container.accept (this);
		write_string ("[");

		bool first = true;
		foreach (Expression index in expr.get_indices ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			index.accept (this);
		}

		write_string ("]");
	}

	public override void visit_slice_expression (SliceExpression expr) {
		expr.container.accept (this);
		write_string ("[");
		expr.start.accept (this);
		write_string (":");
		expr.stop.accept (this);
		write_string ("]");
	}

	public override void visit_base_access (BaseAccess expr) {
		write_string ("base");
	}

	public override void visit_postfix_expression (PostfixExpression expr) {
		expr.inner.accept (this);
		if (expr.increment) {
			write_string ("++");
		} else {
			write_string ("--");
		}
	}

	public override void visit_object_creation_expression (ObjectCreationExpression expr) {
		if (!expr.struct_creation) {
			write_string ("new ");
		}

		write_type (expr.type_reference);

		if (expr.constructor.name != ".new") {
			write_string (".");
			write_string (expr.constructor.name);
		}

		write_string (" (");

		bool first = true;
		foreach (Expression arg in expr.get_argument_list ()) {
			if (!first) {
				write_string (", ");
			}
			first = false;

			arg.accept (this);
		}

		write_string (")");
	}

	public override void visit_sizeof_expression (SizeofExpression expr) {
		write_string ("sizeof (");
		write_type (expr.type_reference);
		write_string (")");
	}

	public override void visit_typeof_expression (TypeofExpression expr) {
		write_string ("typeof (");
		write_type (expr.type_reference);
		write_string (")");
	}

	public override void visit_unary_expression (UnaryExpression expr) {
		switch (expr.operator) {
		case UnaryOperator.PLUS:
			write_string ("+");
			break;
		case UnaryOperator.MINUS:
			write_string ("-");
			break;
		case UnaryOperator.LOGICAL_NEGATION:
			write_string ("!");
			break;
		case UnaryOperator.BITWISE_COMPLEMENT:
			write_string ("~");
			break;
		case UnaryOperator.INCREMENT:
			write_string ("++");
			break;
		case UnaryOperator.DECREMENT:
			write_string ("--");
			break;
		case UnaryOperator.REF:
			write_string ("ref ");
			break;
		case UnaryOperator.OUT:
			write_string ("out ");
			break;
		default:
			assert_not_reached ();
		}
		expr.inner.accept (this);
	}

	public override void visit_cast_expression (CastExpression expr) {
		if (expr.is_non_null_cast) {
			write_string ("(!) ");
			expr.inner.accept (this);
			return;
		}

		if (!expr.is_silent_cast) {
			write_string ("(");
			write_type (expr.type_reference);
			write_string (") ");
		}

		expr.inner.accept (this);

		if (expr.is_silent_cast) {
			write_string (" as ");
			write_type (expr.type_reference);
		}
	}

	public override void visit_pointer_indirection (PointerIndirection expr) {
		write_string ("*");
		expr.inner.accept (this);
	}

	public override void visit_addressof_expression (AddressofExpression expr) {
		write_string ("&");
		expr.inner.accept (this);
	}

	public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
		write_string ("(owned) ");
		expr.inner.accept (this);
	}

	public override void visit_binary_expression (BinaryExpression expr) {
		expr.left.accept (this);

		switch (expr.operator) {
		case BinaryOperator.PLUS:
			write_string (" + ");
			break;
		case BinaryOperator.MINUS:
			write_string (" - ");
			break;
		case BinaryOperator.MUL:
			write_string (" * ");
			break;
		case BinaryOperator.DIV:
			write_string (" / ");
			break;
		case BinaryOperator.MOD:
			write_string (" % ");
			break;
		case BinaryOperator.SHIFT_LEFT:
			write_string (" << ");
			break;
		case BinaryOperator.SHIFT_RIGHT:
			write_string (" >> ");
			break;
		case BinaryOperator.LESS_THAN:
			write_string (" < ");
			break;
		case BinaryOperator.GREATER_THAN:
			write_string (" > ");
			break;
		case BinaryOperator.LESS_THAN_OR_EQUAL:
			write_string (" <= ");
			break;
		case BinaryOperator.GREATER_THAN_OR_EQUAL:
			write_string (" >= ");
			break;
		case BinaryOperator.EQUALITY:
			write_string (" == ");
			break;
		case BinaryOperator.INEQUALITY:
			write_string (" != ");
			break;
		case BinaryOperator.BITWISE_AND:
			write_string (" & ");
			break;
		case BinaryOperator.BITWISE_OR:
			write_string (" | ");
			break;
		case BinaryOperator.BITWISE_XOR:
			write_string (" ^ ");
			break;
		case BinaryOperator.AND:
			write_string (" && ");
			break;
		case BinaryOperator.OR:
			write_string (" || ");
			break;
		case BinaryOperator.IN:
			write_string (" in ");
			break;
		case BinaryOperator.COALESCE:
			write_string (" ?? ");
			break;
		default:
			assert_not_reached ();
		}

		expr.right.accept (this);
	}

	public override void visit_type_check (TypeCheck expr) {
		expr.expression.accept (this);
		write_string (" is ");
		write_type (expr.type_reference);
	}

	public override void visit_conditional_expression (ConditionalExpression expr) {
		expr.condition.accept (this);
		write_string ("?");
		expr.true_expression.accept (this);
		write_string (":");
		expr.false_expression.accept (this);
	}

	public override void visit_lambda_expression (LambdaExpression expr) {
		write_string ("(");
		var params = expr.get_parameters ();
		if (params.size != 0) {
			for (var i = 0; i < params.size - 1; ++ i) {
				write_string (params[i]);
				write_string (", ");
			}
			write_string (params[params.size - 1]);
		}
		write_string (") =>");
		if (expr.statement_body != null) {
			expr.statement_body.accept (this);
		} else if (expr.expression_body != null) {
			expr.expression_body.accept (this);
		}
	}

	public override void visit_assignment (Assignment a) {
		a.left.accept (this);
		write_string (" = ");
		a.right.accept (this);
	}

	private void write_indent () {
		int i;
		
		if (!bol) {
			stream.putc ('\n');
		}
		
		for (i = 0; i < indent; i++) {
			stream.putc ('\t');
		}
		
		bol = false;
	}
	
	private void write_identifier (string s) {
		char* id = (char*)s;
		int id_length = (int)s.length;
		if (context.profile != Profile.DOVA &&
		    (Vala.Scanner.get_identifier_or_keyword (id, id_length) != Vala.TokenType.IDENTIFIER ||
		     s.get_char ().isdigit ())) {
			stream.putc ('@'); 
		}
		write_string (s);
	}

	private void write_return_type (DataType type) {
		if (type.is_weak ()) {
			write_string ("unowned ");
		}

		write_type (type);
	}

	private void write_type (DataType type) {
		write_string (type.to_qualified_string (current_scope));
	}

	private void write_string (string s) {
		stream.printf ("%s", s);
		bol = false;
	}
	
	private void write_newline () {
		stream.putc ('\n');
		bol = true;
	}
	
	void write_code_block (Block? block) {
		if (block == null || type != CodeWriterType.DUMP) {
			write_string (";");
			return;
		}

		block.accept (this);
	}

	private void write_begin_block () {
		if (!bol) {
			stream.putc (' ');
		} else {
			write_indent ();
		}
		stream.putc ('{');
		write_newline ();
		indent++;
	}
	
	private void write_end_block () {
		indent--;
		write_indent ();
		stream.printf ("}");
	}

	private bool check_accessibility (Symbol sym) {
		switch (type) {
		case CodeWriterType.EXTERNAL:
			return sym.access == SymbolAccessibility.PUBLIC ||
			       sym.access == SymbolAccessibility.PROTECTED;

		case CodeWriterType.INTERNAL:
		case CodeWriterType.FAST:
			return sym.access == SymbolAccessibility.INTERNAL ||
			       sym.access == SymbolAccessibility.PUBLIC ||
			       sym.access == SymbolAccessibility.PROTECTED;

		case CodeWriterType.DUMP:
			return true;

		default:
			assert_not_reached ();
		}
	}

	private void write_attributes (CodeNode node) {
		foreach (Attribute attr in node.attributes) {
			if (!filter_attribute (attr)) {
				write_indent ();
				stream.printf ("[%s", attr.name);

				var keys = attr.args.get_keys ();
				if (keys.size != 0) {
					stream.printf (" (");

					string separator = "";
					foreach (string arg_name in keys) {
						stream.printf ("%s%s = %s", separator, arg_name, attr.args.get (arg_name));
						separator = ", ";
					}

					stream.printf (")");
				}
				stream.printf ("]");
				write_newline ();
			}
		}
	}

	private bool filter_attribute (Attribute attr) {
		if (attr.name == "CCode"
		    || attr.name == "Compact" || attr.name == "Immutable"
		    || attr.name == "SimpleType" || attr.name == "IntegerType" || attr.name == "FloatingType"
		    || attr.name == "Flags") {
			return true;
		}
		return false;
	}

	private void write_accessibility (Symbol sym) {
		if (sym.access == SymbolAccessibility.PUBLIC) {
			write_string ("public ");
		} else if (sym.access == SymbolAccessibility.PROTECTED) {
			write_string ("protected ");
		} else if (sym.access == SymbolAccessibility.INTERNAL) {
			write_string ("internal ");
		} else if (sym.access == SymbolAccessibility.PRIVATE) {
			write_string ("private ");
		}

		if (type != CodeWriterType.EXTERNAL && sym.external && !sym.external_package) {
			write_string ("extern ");
		}
	}

	void write_property_accessor_accessibility (Symbol sym) {
		if (sym.access == SymbolAccessibility.PROTECTED) {
			write_string (" protected");
		} else if (sym.access == SymbolAccessibility.INTERNAL) {
			write_string (" internal");
		} else if (sym.access == SymbolAccessibility.PRIVATE) {
			write_string (" private");
		}
	}
}

public enum Vala.CodeWriterType {
{'fraction_non_alphanumeric': 0.14824362865001472, 'fraction_numerical': 0.0015833223380393193, 'mean_word_length': 3.507434008875063, 'pattern_counts': {'":': 6, '<': 49, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5291758', 'n_tokens_mistral': 39370, 'n_tokens_neox': 33142, 'n_words': 6894}