.
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 public Tag root;
73 public XmlString data;
74 public string input;
75 public bool error;
76 public int refcount = 1;
77
78 /**
79 * Create a new xml parser.
80 * @param data valid xml data
81 */
82 public XmlParser (string data) {
83 this.input = data;
84 this.data = new XmlString (data, data.length);
85 reparse (NONE);
86 }
87
88 /**
89 * Determine if the document can be parsed.
90 * @return true if the xml document is valid xml.
91 */
92 public bool validate () {
93 reparse (NONE);
94
95 if (error) {
96 return false;
97 }
98
99 validate_tags (root);
100
101 reparse (NONE);
102 return !error;
103 }
104
105 void validate_tags (Tag tag) {
106 Attributes attributes = tag.get_attributes ();
107
108 foreach (Attribute a in attributes) {
109 if (tag.has_failed () || a.name.length == 0) {
110 error = true;
111 return;
112 }
113 }
114
115 foreach (Tag t in tag) {
116 if (tag.has_failed ()) {
117 error = true;
118 return;
119 }
120
121 validate_tags (t);
122 }
123 }
124
125 /**
126 * Obtain the root tag.
127 * @return the root tag.
128 */
129 public Tag get_root_tag () {
130 reparse (WARNINGS);
131 return root;
132 }
133
134 /**
135 * Reset the parser and start from the beginning of the XML document.
136 */
137 internal void reparse (int log_level) {
138 int root_index;
139 Tag container;
140 XmlString content;
141
142 error = false;
143
144 root_index = find_root_tag ();
145 if (root_index == -1) {
146 if (log_level == WARNINGS) {
147 XmlParser.warning ("No root tag found.");
148 }
149 error = true;
150 root = new Tag.empty ();
151 } else {
152 content = data.substring (root_index);
153 container = new Tag (new XmlString ("", 0), new XmlString ("", 0), content, log_level);
154 root = container.get_next_tag ();
155 }
156 }
157
158 int find_root_tag () {
159 int index = 0;
160 int prev_index = 0;
161 int modifier = 0;
162 unichar c;
163
164 while (true) {
165 prev_index = index;
166 if (!data.get_next_char (ref index, out c)) {
167 break;
168 }
169
170 if (c == '<') {
171 modifier = index;
172 data.get_next_char (ref modifier, out c);
173 if (c != '?' && c != '[' && c != '!') {
174 return prev_index;
175 }
176 }
177 }
178
179 return -1;
180 }
181
182 /** Print a warning message. */
183 public static void warning (string message) {
184 print ("XML error: ");
185 print (message);
186 print ("\n");
187 }
188
189 /** Replace escaped character with plain text characters.
190 * & will be replaced with & etc.
191 */
192 public static string decode (string s) {
193 string t;
194 t = s.replace (""", "\"");
195 t = t.replace ("'", "'");
196 t = t.replace ("<", "<");
197 t = t.replace (">", ">");
198 t = t.replace ("&", "&");
199 return t;
200 }
201
202 public static string encode (string s) {
203 string t;
204 t = s.replace ("\"", """);
205 t = t.replace ("'", "'");
206 t = t.replace ("<", "<");
207 t = t.replace (">", ">");
208 t = t.replace ("&", "&");
209 return t;
210 }
211
212 }
213
214 }
215