The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

Tag.vala in libbirdxml

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdxml/Tag.vala.
Give press event method a name
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 namespace Bird { 16 17 /** 18 * Representation of one XML tag. 19 */ 20 public class Tag : GLib.Object { 21 22 public int tag_index; 23 public int attribute_index; 24 25 public bool has_tags; 26 public bool has_attributes; 27 28 public XmlString name; 29 public XmlString data; 30 public XmlString attributes; 31 32 public Tag? next_tag = null; 33 public Attribute? next_attribute = null; 34 35 public bool error = false; 36 public int log_level = WARNINGS; 37 38 public int refcount = 1; 39 40 internal Tag (XmlString name, XmlString attributes, XmlString content, int log_level) { 41 this.log_level = log_level; 42 this.name = name; 43 this.data = content; 44 this.attributes = attributes; 45 reparse (); 46 reparse_attributes (); 47 } 48 49 internal Tag.empty () { 50 data = new XmlString ("", 0); 51 attributes = new XmlString ("", 0); 52 name = new XmlString ("", 0); 53 } 54 55 /** 56 * Get tag attributes for this tag. 57 * @return a container with all the attributes 58 */ 59 public Attributes get_attributes () { 60 return new Attributes (this); 61 } 62 63 /** 64 * Iterate over all tags inside of this tag. 65 */ 66 public Iterator iterator () { 67 return new Iterator(this); 68 } 69 70 /** 71 * Reset the parser and start from the beginning XML tag. 72 */ 73 public void reparse () { 74 tag_index = 0; 75 next_tag = obtain_next_tag (); 76 } 77 78 internal void reparse_attributes () { 79 attribute_index = 0; 80 next_attribute = obtain_next_attribute (); 81 } 82 83 /** 84 * Obtain the name of the tag. 85 * @return the name of this tag. 86 */ 87 public string get_name () { 88 return name.to_string (); 89 } 90 91 /** 92 * Obtain tag content. 93 * @return data between the start and end tags. 94 */ 95 public string get_content () { 96 return data.to_string (); 97 } 98 99 /** 100 * @return true if there is one more tags left 101 */ 102 internal bool has_more_tags () { 103 return has_tags; 104 } 105 106 /** @return the next tag. **/ 107 internal Tag get_next_tag () { 108 Tag r = next_tag == null ? new Tag.empty () : (!) next_tag; 109 next_tag = obtain_next_tag (); 110 return r; 111 } 112 113 /** @return true is there is one or more attributes to obtain with get_next_attribute */ 114 internal bool has_more_attributes () { 115 return has_attributes; 116 } 117 118 /** @return next attribute. */ 119 internal Attribute get_next_attribute () { 120 Attribute r = next_attribute == null ? new Attribute.empty () : (!) next_attribute; 121 next_attribute = obtain_next_attribute (); 122 return r; 123 } 124 125 internal bool has_failed () { 126 return error; 127 } 128 129 Tag obtain_next_tag () { 130 int end_tag_index; 131 Tag tag; 132 133 tag = find_next_tag (tag_index, out end_tag_index); 134 135 if (end_tag_index != -1) { 136 tag_index = end_tag_index; 137 has_tags = true; 138 return tag; 139 } 140 141 has_tags = false; 142 return new Tag.empty (); 143 } 144 145 Tag find_next_tag (int start, out int end_tag_index) { 146 int index; 147 unichar c; 148 int separator; 149 int end; 150 int closing_tag; 151 XmlString? d; 152 153 XmlString name; 154 XmlString attributes; 155 XmlString content; 156 157 end_tag_index = -1; 158 159 if (start < 0) { 160 warn ("Negative index."); 161 return new Tag.empty (); 162 } 163 164 index = start; 165 166 d = data; 167 if (d == null) { 168 warn ("No data in xml string."); 169 return new Tag.empty (); 170 } 171 172 while (data.get_next_char (ref index, out c)) { 173 if (c == '<') { 174 separator = find_next_separator (index); 175 176 if (separator < 0) { 177 error = true; 178 warn ("Expecting a separator."); 179 return new Tag.empty (); 180 } 181 182 name = data.substring (index, separator - index); 183 184 if (name.has_prefix ("!")) { 185 continue; 186 } 187 188 end = data.index_of (">", start); 189 attributes = data.substring (separator, end - separator); 190 191 if (attributes.has_suffix ("/")) { 192 content = new XmlString ("", 0); 193 end_tag_index = data.index_of (">", index); 194 data.get_next_char (ref end_tag_index, out c); 195 } else { 196 if (!data.get_next_char (ref end, out c)) {; // skip > 197 warn ("Unexpected end of data."); 198 error = true; 199 break; 200 } 201 202 if (c != '>') { 203 warn ("Expecting '>'"); 204 error = true; 205 break; 206 } 207 208 closing_tag = find_closing_tag (name, end); 209 210 if (closing_tag == -1) { 211 warn ("No closing tag."); 212 error = true; 213 break; 214 } 215 216 content = data.substring (end, closing_tag - end); 217 end_tag_index = data.index_of (">", closing_tag); 218 data.get_next_char (ref end_tag_index, out c); 219 } 220 221 return new Tag (name, attributes, content, log_level); 222 } 223 } 224 225 return new Tag.empty (); 226 } 227 228 int find_next_separator (int start) { 229 int index = start; 230 int previous_index = start; 231 unichar c; 232 233 while (true) { 234 235 previous_index = index; 236 if (!data.get_next_char (ref index, out c)) { 237 break; 238 } 239 240 if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '>' || c == '/') { 241 return previous_index; 242 } 243 } 244 245 return -1; 246 } 247 248 int find_closing_tag (XmlString name, int start) { 249 int index = start; 250 int slash_index = start; 251 int previous_index; 252 unichar c, slash; 253 int start_count = 1; 254 255 if (name.length == 0) { 256 error = true; 257 warn ("No name for tag."); 258 return -1; 259 } 260 261 while (true) { 262 previous_index = index; 263 if (!data.get_next_char (ref index, out c)) { 264 warn (@"Unexpected end of file"); 265 break; 266 } 267 268 if (c == '<') { 269 slash_index = index; 270 data.get_next_char (ref slash_index, out slash); 271 if (slash == '/' && is_tag (name, slash_index)) { 272 if (start_count == 1) { 273 return previous_index; 274 } else { 275 start_count--; 276 if (start_count == 0) { 277 return previous_index; 278 } 279 } 280 } else if (is_tag (name, index)) { 281 start_count++; 282 } 283 } 284 } 285 286 error = true; 287 warn (@"No closing tag for $(name.to_string ())"); 288 289 return -1; 290 } 291 292 bool is_tag (XmlString name, int start) { 293 int index = 0; 294 int data_index = start; 295 unichar c; 296 unichar c_data; 297 298 while (name.get_next_char (ref index, out c)) { 299 if (data.get_next_char (ref data_index, out c_data)) { 300 if (c_data != c) { 301 return false; 302 } 303 } 304 } 305 306 if (data.get_next_char (ref data_index, out c_data)) { 307 return c_data == '>' || c_data == ' ' || c_data == '\t' 308 || c_data == '\n' || c_data == '\r' || c_data == '/'; 309 } 310 311 return false; 312 } 313 314 internal Attribute obtain_next_attribute () { 315 int previous_index; 316 int index = attribute_index; 317 int name_start; 318 XmlString attribute_name; 319 XmlString ns; 320 XmlString content; 321 int ns_separator; 322 int content_start; 323 int content_stop; 324 unichar quote; 325 unichar c; 326 327 // skip space and other separators 328 while (true) { 329 previous_index = index; 330 331 if (!attributes.get_next_char (ref index, out c)) { 332 has_attributes = false; 333 return new Attribute.empty (); 334 } 335 336 if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '/')) { 337 break; 338 } 339 } 340 341 name_start = previous_index; 342 343 // read attribute name 344 while (true) { 345 previous_index = index; 346 if (!attributes.get_next_char (ref index, out c)) { 347 error = true; 348 warn (@"Unexpected end of attributes in tag $(this.name)"); 349 has_attributes = false; 350 return new Attribute.empty (); 351 } 352 353 if (c == ' ' || c == '\t' || c == '=' || c == '\n' || c == '\r') { 354 break; 355 } 356 } 357 358 attribute_name = attributes.substring (name_start, previous_index - name_start); 359 index = name_start + attribute_name.length; 360 ns = new XmlString ("", 0); 361 ns_separator = attribute_name.index_of (":"); 362 if (ns_separator != -1) { 363 ns = attribute_name.substring (0, ns_separator); 364 attribute_name = attribute_name.substring (ns_separator + 1); 365 } 366 367 // equal sign and space around it 368 while (attributes.get_next_char (ref index, out c)) { 369 if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) { 370 if (c == '=') { 371 break; 372 } else { 373 has_attributes = false; 374 error = true; 375 warn (@"Expecting equal sign for attribute $(attribute_name)."); 376 return new Attribute.empty (); 377 } 378 } 379 } 380 381 while (attributes.get_next_char (ref index, out c)) { 382 if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) { 383 if (c == '"' || c == '\'') { 384 break; 385 } else { 386 has_attributes = false; 387 error = true; 388 warn (@"Expecting quote for attribute $(attribute_name)."); 389 return new Attribute.empty (); 390 } 391 } 392 } 393 394 quote = c; 395 content_start = index; 396 397 while (true) { 398 if (!attributes.get_next_char (ref index, out c)) { 399 has_attributes = false; 400 error = true; 401 warn (@"Expecting end quote for attribute $(attribute_name)."); 402 return new Attribute.empty (); 403 } 404 405 if (c == quote) { 406 break; 407 } 408 } 409 410 content_stop = index - 1; 411 content = attributes.substring (content_start, content_stop - content_start); 412 413 has_attributes = true; 414 415 attribute_index = content_stop + 1; 416 return new Attribute (ns, attribute_name, content); 417 } 418 419 public class Iterator : GLib.Object { 420 public Tag tag; 421 public Tag? next_tag = null; 422 public int iterator_efcount = 1; 423 424 internal Iterator (Tag t) { 425 tag = t; 426 tag.reparse (); 427 } 428 429 public bool next () { 430 if (tag.error) { 431 return false; 432 } 433 434 if (tag.has_more_tags ()) { 435 next_tag = tag.get_next_tag (); 436 } else { 437 next_tag = null; 438 } 439 440 return next_tag != null; 441 } 442 443 public new Tag get () { 444 if (next_tag == null) { 445 XmlParser.warning ("No tag is parsed yet."); 446 return new Tag.empty (); 447 } 448 return (!) next_tag; 449 } 450 } 451 452 internal void warn (string message) { 453 if (log_level == WARNINGS) { 454 XmlParser.warning (message); 455 } 456 } 457 } 458 459 } 460