.
1 /*
2 Copyright (C) 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 /**
16 * Tools originally written for the BirdFont project.
17 */
18 namespace Bird {
19
20 /** Log levels */
21 internal const int NONE = 0;
22 internal const int WARNINGS = 1;
23
24 /**
25 * XML parser
26 *
27 * A tiny XML parser written in Vala.
28 *
29 * Example:
30 * {{{
31 *
32 * // Print all tags and attributes in an XML document.
33 * // Expected output:
34 * // tag1
35 * // tag2
36 * // attribute1
37 * public static int main (string[] arg) {
38 * Tag root;
39 * XmlParser parser;
40 *
41 * parser = new XmlParser ("""<tag1><tag2 attribute1=""/></tag1>""");
42 *
43 * if (parser.validate ()) {
44 * root = parser.get_root_tag ();
45 * print_tags (root);
46 * }
47 * }
48 *
49 *
50 * void print_tags (Tag tag) {
51 * print (tag.get_name ());
52 * print ("\n");
53 * print_attributes (tag);
54 *
55 * foreach (Tag t in tag) {
56 * print_tags (t);
57 * }
58 * }
59 *
60 * void print_attributes (Tag tag) {
61 * Attributes attributes = tag.get_attributes ();
62 * foreach (Attribute attribute in attributes) {
63 * print (attribute.get_name ());
64 * print ("\n");
65 * }
66 * }
67 *
68 * }}}
69 *
70 */
71 public class XmlParser : GLib.Object {
72 Tag root;
73 XmlData data;
74 string input;
75 bool error;
76
77 /**
78 * Create a new xml parser.
79 * @param data valid xml data
80 */
81 public XmlParser (string data) {
82 this.input = data;
83 this.data = new XmlData (data, data.length);
84 reparse (NONE);
85 }
86
87 /**
88 * Determine if the document can be parsed.
89 * @return true if the xml document is valid xml.
90 */
91 public bool validate () {
92 if (this.data.error) {
93 error = true;
94 return false;
95 }
96
97 reparse (NONE);
98
99 if (error) {
100 return false;
101 }
102
103 validate_tags (root);
104
105 reparse (NONE);
106 return !error;
107 }
108
109 void validate_tags (Tag tag) {
110 Attributes attributes = tag.get_attributes ();
111
112 foreach (Attribute a in attributes) {
113 if (tag.has_failed () || a.name.length == 0) {
114 error = true;
115 return;
116 }
117 }
118
119 foreach (Tag t in tag) {
120 if (tag.has_failed ()) {
121 error = true;
122 return;
123 }
124
125 validate_tags (t);
126 }
127 }
128
129 /**
130 * Obtain the root tag.
131 * @return the root tag.
132 */
133 public Tag get_root_tag () {
134 reparse (WARNINGS);
135 return root;
136 }
137
138 /**
139 * Reset the parser and start from the beginning of the XML document.
140 */
141 internal void reparse (int log_level) {
142 int root_index;
143 Tag container;
144 XmlString content;
145 XmlString empty;
146
147 error = false;
148 empty = new XmlString ("", 0);
149
150 root_index = find_root_tag ();
151 if (root_index == -1) {
152 if (log_level == WARNINGS) {
153 XmlParser.warning ("No root tag found.");
154 }
155 error = true;
156 root = new Tag.empty ();
157 } else {
158 content = data.substring (root_index);
159 container = new Tag (empty, empty, content, log_level, data);
160 root = container.get_next_tag ();
161 }
162 }
163
164 int find_root_tag () {
165 int index = 0;
166 int prev_index = 0;
167 int modifier = 0;
168 unichar c;
169
170 while (true) {
171 prev_index = index;
172 if (!data.get_next_ascii_char (ref index, out c)) {
173 break;
174 }
175
176 if (c == '<') {
177 modifier = index;
178 data.get_next_ascii_char (ref modifier, out c);
179 if (c != '?' && c != '[' && c != '!') {
180 return prev_index;
181 }
182 }
183 }
184
185 return -1;
186 }
187
188 /** Print a warning message. */
189 public static void warning (string message) {
190 print ("XML error: ");
191 print (message);
192 print ("\n");
193 }
194
195 /** Replace escaped character with plain text characters.
196 * & will be replaced with & etc.
197 */
198 public static string decode (string s) {
199 string t;
200 t = s.replace (""", "\"");
201 t = t.replace ("'", "'");
202 t = t.replace ("<", "<");
203 t = t.replace (">", ">");
204 t = t.replace ("&", "&");
205 return t;
206 }
207
208 public static string encode (string s) {
209 string t;
210 t = s.replace ("\"", """);
211 t = t.replace ("'", "'");
212 t = t.replace ("<", "<");
213 t = t.replace (">", ">");
214 t = t.replace ("&", "&");
215 return t;
216 }
217
218 }
219
220 }
221