linux: Add constants from program_invocation_name(3)
[vala-gnome.git] / libvaladoc / markupwriter.vala
blob28a61ffe72ad61d8eb7f8fd1e74b0e582403a336
1 /* markupwriter.vala
3 * Copyright (C) 2008-2009 Florian Brosch, Didier Villevalois
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 * Didier 'Ptitjes Villevalois <ptitjes@free.fr>
24 /**
25 * Writes markups and text to a file.
27 public class Valadoc.MarkupWriter {
28 protected WriteFunc write;
29 protected int indent;
31 protected long current_column = 0;
32 protected bool last_was_tag;
33 private bool wrap = true;
35 public static string escape (string txt) {
36 StringBuilder builder = new StringBuilder ();
37 unowned string start = txt;
38 unowned string pos;
39 unichar c;
41 for (pos = txt; (c = pos.get_char ()) != '\0'; pos = pos.next_char ()) {
42 switch (c) {
43 case '"':
44 builder.append_len (start, (ssize_t) ((char*) pos - (char*) start));
45 builder.append ("&quot;");
46 start = pos.next_char ();
47 break;
49 case '<':
50 builder.append_len (start, (ssize_t) ((char*) pos - (char*) start));
51 builder.append ("&lt;");
52 start = pos.next_char ();
53 break;
55 case '>':
56 builder.append_len (start, (ssize_t) ((char*) pos - (char*) start));
57 builder.append ("&gt;");
58 start = pos.next_char ();
59 break;
61 case '&':
62 builder.append_len (start, (ssize_t) ((char*) pos - (char*) start));
63 builder.append ("&amp;");
64 start = pos.next_char ();
65 break;
67 case '\'':
68 builder.append_len (start, (ssize_t) ((char*) pos - (char*) start));
69 builder.append ("&apos;");
70 start = pos.next_char ();
71 break;
75 if (&txt == &start) {
76 return txt;
77 } else {
78 builder.append_len (start, (ssize_t) ((char*) pos - (char*) start));
79 return (owned) builder.str;
83 /**
84 * Writes text to a desination like a {@link GLib.StringBuilder} or a {@link GLib.FileStream}
86 public delegate void WriteFunc (string text);
88 private const int MAX_COLUMN = 150;
90 /**
91 * Initializes a new instance of the MarkupWriter
93 * @param write stream a WriteFunc
94 * @param xml_declaration specifies whether this file starts with an xml-declaration
96 public MarkupWriter (owned WriteFunc write, bool xml_declaration = true) {
97 this.write = (owned) write;
98 if (xml_declaration) {
99 do_write ("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
101 indent = -1;
102 last_was_tag = true;
106 * Writes an start tag of a markup element to the file
108 * @param name the name of the markup
109 * @param attributes a list of name/value pairs
110 * @return this
112 public MarkupWriter start_tag (string name, string[]? attributes=null) {
113 indent++;
114 check_column (name);
116 if (attributes.length % 2 != 0) {
117 attributes.resize (attributes.length+1);
118 attributes[attributes.length-1] = "";
121 var content = new StringBuilder ("<");
122 content.append (name);
123 for (int i = 0; i < attributes.length; i=i+2) {
124 if (attributes[i+1] != null) {
125 content.append_printf (" %s=\"%s\"", attributes[i], attributes[i+1]);
128 content.append (">");
130 do_write (content.str);
131 last_was_tag = true;
132 return this;
136 * Writes a simple tag (<name />) to the file
138 * @param name the name of the markup
139 * @param attributes a list of name/value pairs
140 * @return this
142 public MarkupWriter simple_tag (string name, string[]? attributes=null) {
143 indent++;
144 check_column (name);
146 if (attributes.length % 2 != 0) {
147 attributes.resize (attributes.length+1);
148 attributes[attributes.length-1] = "";
151 var content = new StringBuilder ("<");
152 content.append (name);
153 for (int i = 0; i < attributes.length; i=i+2) {
154 if (attributes[i+1] != null) {
155 content.append_printf (" %s=\"%s\"", attributes[i], attributes[i+1]);
158 content.append ("/>");
160 do_write (content.str);
161 indent--;
162 last_was_tag = true;
163 return this;
167 * Writes an end tag of a markup element to the file
169 * @param name the name of the markup
170 * @return this
172 public MarkupWriter end_tag (string name) {
173 check_column (name, true);
174 do_write ("</%s>".printf (name));
175 indent--;
176 last_was_tag = true;
177 return this;
181 * Writes the specified string to the output stream
183 * @see raw_text
184 * @return this
186 public MarkupWriter text (string text) {
187 if (wrap && text.length + current_column > MAX_COLUMN) {
188 long wrote = 0;
189 while (wrote < text.length) {
190 long space_pos = -1;
191 for (long i = wrote + 1; i < text.length; i++) {
192 if (text[i] == ' ') {
193 if (i - wrote + current_column > MAX_COLUMN) {
194 break;
196 space_pos = i;
199 if (text.length - wrote + current_column <= MAX_COLUMN) {
200 do_write (text.substring (wrote));
201 wrote = text.length + 1;
202 } else if (space_pos == -1) {
203 // Force line break
204 } else {
205 do_write (text.substring (wrote, space_pos - wrote));
206 wrote = space_pos + 1;
208 if (wrote < text.length) {
209 break_line ();
210 do_write (" ");
213 } else {
214 do_write (text);
216 last_was_tag = false;
217 return this;
221 * Writes the specified string to the output stream
223 * @see text
224 * @return this
226 public MarkupWriter raw_text (string text) {
227 do_write (text);
228 last_was_tag = false;
229 return this;
232 public void set_wrap (bool wrap) {
233 this.wrap = wrap;
236 private void break_line () {
237 write ("\n");
238 write (string.nfill (indent * 2, ' '));
239 current_column = indent * 2;
242 protected void do_write (string text) {
243 if (wrap && current_column + text.length > MAX_COLUMN) {
244 break_line ();
246 write (text);
247 current_column += text.length;
250 private void check_column (string name, bool end_tag = false) {
251 if (!wrap) {
252 return;
253 } else if (!end_tag && inline_element (name) /*&& !last_was_tag*/) {
254 return;
255 } else if (end_tag && content_inline_element (name)) {
256 return;
257 } else if (end_tag && !last_was_tag) {
258 return;
260 break_line ();
263 protected virtual bool inline_element (string name) {
264 return false;
267 protected virtual bool content_inline_element (string name) {
268 return true;