Improve string tests
[vala-lang.git] / vala / valasourcefile.vala
blobcd511916d4c491c6b26e5468d34355a2f273ef75
1 /* valasourcefile.vala
3 * Copyright (C) 2006-2008 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
24 using Gee;
26 /**
27 * Represents a Vala source or VAPI package file.
29 public class Vala.SourceFile {
30 /**
31 * The name of this source file.
33 public string filename { get; set; }
35 /**
36 * The header comment of this source file.
38 public string comment { get; set; }
40 /**
41 * Specifies whether this file is a VAPI package file.
43 public bool external_package { get; set; }
45 /**
46 * Specifies the dependency cycle this source file is member of. If this
47 * source file is in a cycle, all type definitions of that cycle will
48 * only be written to the C header file of the cycle head.
50 public SourceFileCycle cycle { get; set; }
52 /**
53 * Specifies whether this source file is the head of the cycle, if it is
54 * in a cycle at all.
56 public bool is_cycle_head { get; set; }
58 /**
59 * Mark used for cycle detection.
61 * 0: not yet visited
62 * 1: currently visiting
63 * 2: already visited
65 public int mark { get; set; }
67 /**
68 * The context this source file belongs to.
70 public weak CodeContext context { get; set; }
72 public string? content {
73 get { return this._content; }
74 set {
75 this._content = value;
76 this.source_array = null;
80 private Gee.List<UsingDirective> using_directives = new ArrayList<UsingDirective> ();
82 private Gee.List<CodeNode> nodes = new ArrayList<CodeNode> ();
84 private string cheader_filename = null;
85 private string csource_filename = null;
86 private string cinclude_filename = null;
88 private Gee.List<string> header_external_includes = new ArrayList<string> ();
89 private Gee.List<string> header_internal_includes = new ArrayList<string> ();
90 private Gee.List<string> source_external_includes = new ArrayList<string> ();
91 private Gee.List<string> source_internal_includes = new ArrayList<string> ();
93 private Gee.List<weak SourceFile> header_internal_full_dependencies = new ArrayList<weak SourceFile> ();
94 private Gee.List<weak SourceFile> header_internal_dependencies = new ArrayList<weak SourceFile> ();
96 private Gee.Set<Symbol> source_symbol_dependencies = new HashSet<Symbol> ();
98 private Gee.ArrayList<string> source_array = null;
100 private MappedFile mapped_file = null;
102 private string? _content = null;
105 * Creates a new source file.
107 * @param filename source file name
108 * @param pkg true if this is a VAPI package file
109 * @return newly created source file
111 public SourceFile (CodeContext context, string filename, bool pkg = false, string? content = null) {
112 this.filename = filename;
113 this.external_package = pkg;
114 this.context = context;
115 this.content = content;
119 * Adds a new using directive with the specified namespace.
121 * @param ns reference to namespace
123 public void add_using_directive (UsingDirective ns) {
124 foreach (UsingDirective using_directive in using_directives) {
125 if (same_symbol (using_directive.namespace_symbol, ns.namespace_symbol)) {
126 // ignore duplicates
127 return;
130 using_directives.add (ns);
133 public void clear_using_directives () {
134 using_directives.clear ();
137 bool same_symbol (Symbol? sym1, Symbol? sym2) {
138 if (sym1 == sym2) {
139 return true;
142 var unresolved_symbol1 = sym1 as UnresolvedSymbol;
143 var unresolved_symbol2 = sym2 as UnresolvedSymbol;
144 if (unresolved_symbol1 != null && unresolved_symbol2 != null) {
145 if (same_symbol (unresolved_symbol1.inner, unresolved_symbol2.inner)) {
146 return (unresolved_symbol1.name == unresolved_symbol2.name);
150 return false;
154 * Returns a copy of the list of using directives.
156 * @return using directive list
158 public Gee.List<UsingDirective> get_using_directives () {
159 return new ReadOnlyList<UsingDirective> (using_directives);
163 * Adds the specified code node to this source file.
165 * @param node a code node
167 public void add_node (CodeNode node) {
168 nodes.add (node);
171 public void remove_node (CodeNode node) {
172 nodes.remove (node);
176 * Returns a copy of the list of code nodes.
178 * @return code node list
180 public Gee.List<CodeNode> get_nodes () {
181 return new ReadOnlyList<CodeNode> (nodes);
184 public void accept (CodeVisitor visitor) {
185 visitor.visit_source_file (this);
188 public void accept_children (CodeVisitor visitor) {
189 foreach (UsingDirective ns_ref in using_directives) {
190 ns_ref.accept (visitor);
193 foreach (CodeNode node in nodes) {
194 node.accept (visitor);
198 private string get_subdir () {
199 if (context.basedir == null) {
200 return "";
203 // filename and basedir are already canonicalized
204 if (filename.has_prefix (context.basedir)) {
205 var basename = Path.get_basename (filename);
206 var subdir = filename.substring (context.basedir.len (), filename.len () - context.basedir.len () - basename.len ());
207 while (subdir[0] == '/') {
208 subdir = subdir.offset (1);
210 return subdir;
212 return "";
215 private string get_destination_directory () {
216 if (context.directory == null) {
217 return get_subdir ();
219 return "%s/%s".printf (context.directory, get_subdir ());
222 private string get_basename () {
223 long dot = filename.pointer_to_offset (filename.rchr (-1, '.'));
224 return Path.get_basename (filename.substring (0, dot));
227 public string get_relative_filename () {
228 return get_subdir () + Path.get_basename (filename);
232 * Returns the filename to use when generating C header files.
234 * @return generated C header filename
236 public string get_cheader_filename () {
237 if (cheader_filename == null) {
238 cheader_filename = "%s%s.h".printf (get_destination_directory (), get_basename ());
240 return cheader_filename;
244 * Returns the filename to use when generating C source files.
246 * @return generated C source filename
248 public string get_csource_filename () {
249 if (csource_filename == null) {
250 csource_filename = "%s%s.c".printf (get_destination_directory (), get_basename ());
252 return csource_filename;
256 * Returns the filename to use when including the generated C header
257 * file.
259 * @return C header filename to include
261 public string get_cinclude_filename () {
262 if (cinclude_filename == null) {
263 cinclude_filename = "%s%s.h".printf (get_subdir (), get_basename ());
265 return cinclude_filename;
269 * Adds the specified symbol to the list of symbols code in this source
270 * file depends on.
272 * TODO Move source and header file dependency analysis to
273 * code generator.
275 * @param sym a symbol
276 * @param dep_type type of dependency
278 public void add_symbol_dependency (Symbol? sym, SourceFileDependencyType dep_type) {
279 if (external_package) {
280 return;
283 Symbol s;
285 if (sym is ErrorCode) {
286 s = sym.parent_symbol;
287 } else if (sym is TypeSymbol ||
288 sym is Method ||
289 sym is Field ||
290 sym is Property ||
291 sym is Constant) {
292 s = sym;
293 } else if (sym is FormalParameter) {
294 var fp = (FormalParameter) sym;
295 s = fp.parameter_type.data_type;
296 if (s == null) {
297 /* generic type parameter */
298 return;
300 } else {
301 return;
304 if (dep_type == SourceFileDependencyType.SOURCE) {
305 source_symbol_dependencies.add (s);
306 if (s.external_package) {
307 foreach (string fn in s.get_cheader_filenames ()) {
308 source_external_includes.add (fn);
310 } else {
311 foreach (string fn in s.get_cheader_filenames ()) {
312 source_internal_includes.add (fn);
315 return;
318 if (s.external_package) {
319 /* external package */
320 foreach (string fn in s.get_cheader_filenames ()) {
321 header_external_includes.add (fn);
323 return;
326 if (dep_type == SourceFileDependencyType.HEADER_FULL) {
327 foreach (string fn in s.get_cheader_filenames ()) {
328 header_internal_includes.add (fn);
330 header_internal_full_dependencies.add (s.source_reference.file);
333 header_internal_dependencies.add (s.source_reference.file);
337 * Adds the symbols that define the specified type to the list of
338 * symbols code in this source file depends on.
340 * TODO Move source and header file dependency analysis to
341 * code generator.
343 * @param type a data type
344 * @param dep_type type of dependency
346 public void add_type_dependency (DataType type, SourceFileDependencyType dep_type) {
347 foreach (Symbol type_symbol in type.get_symbols ()) {
348 add_symbol_dependency (type_symbol, dep_type);
353 * Returns the list of external includes the generated C header file
354 * requires.
356 * @return external include list for C header file
358 public Gee.List<string> get_header_external_includes () {
359 return new ReadOnlyList<string> (header_external_includes);
363 * Adds the specified filename to the list of package-internal includes
364 * the generated C header file requires.
366 * @param include internal include for C header file
368 public void add_header_internal_include (string include) {
369 /* skip includes to self */
370 if (include != get_cinclude_filename ()) {
371 header_internal_includes.add (include);
376 * Returns the list of package-internal includes the generated C header
377 * file requires.
379 * @return internal include list for C header file
381 public Gee.List<string> get_header_internal_includes () {
382 return new ReadOnlyList<string> (header_internal_includes);
386 * Returns the list of external includes the generated C source file
387 * requires.
389 * @return include list for C source file
391 public Gee.List<string> get_source_external_includes () {
392 return new ReadOnlyList<string> (source_external_includes);
396 * Returns the list of package-internal includes the generated C source
397 * file requires.
399 * @return include list for C source file
401 public Gee.List<string> get_source_internal_includes () {
402 return new ReadOnlyList<string> (source_internal_includes);
406 * Returns the list of source files the generated C header file requires
407 * definitely.
409 * @return definite source file dependencies
411 public Gee.List<weak SourceFile> get_header_internal_full_dependencies () {
412 return new ReadOnlyList<weak SourceFile> (header_internal_full_dependencies);
416 * Returns the list of source files the generated C header file loosely
417 * depends on.
419 * @return loose source file dependencies
421 public Gee.List<weak SourceFile> get_header_internal_dependencies () {
422 return new ReadOnlyList<weak SourceFile> (header_internal_dependencies);
425 public Gee.Set<Symbol> get_source_symbol_dependencies () {
426 return new ReadOnlySet<Symbol> (source_symbol_dependencies);
430 * Returns the requested line from this file, loading it if needed.
432 * @param lineno 1-based line number
433 * @return the specified source line
435 public string? get_source_line (int lineno) {
436 if (source_array == null) {
437 if (content != null) {
438 read_source_lines (content);
439 } else {
440 read_source_file ();
443 if (lineno < 1 || lineno > source_array.size) {
444 return null;
446 return source_array.get (lineno - 1);
450 * Parses the input file into ::source_array.
452 private void read_source_file () {
453 string cont;
454 try {
455 FileUtils.get_contents (filename, out cont);
456 } catch (FileError fe) {
457 return;
459 read_source_lines (cont);
462 private void read_source_lines (string cont)
464 source_array = new Gee.ArrayList<string> ();
465 string[] lines = cont.split ("\n", 0);
466 int idx;
467 for (idx = 0; lines[idx] != null; ++idx) {
468 source_array.add (lines[idx]);
472 public char* get_mapped_contents () {
473 if (content != null) {
474 return content;
477 if (mapped_file == null) {
478 try {
479 mapped_file = new MappedFile (filename, false);
480 } catch (FileError e) {
481 Report.error (null, "Unable to map file `%s': %s".printf (filename, e.message));
482 return null;
486 return mapped_file.get_contents ();
489 public size_t get_mapped_length () {
490 if (content != null) {
491 return content.length;
494 return mapped_file.get_length ();
497 public bool check (SemanticAnalyzer analyzer) {
498 foreach (CodeNode node in nodes) {
499 node.check (analyzer);
501 return true;
505 public enum Vala.SourceFileDependencyType {
506 HEADER_FULL,
507 HEADER_SHALLOW,
508 SOURCE