X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Fmarina%2FProjectLoader.vala;fp=src%2Fmarina%2FProjectLoader.vala;h=7a9ffdfa821c6b3c274fb1ae41c76302aa07cf04;hb=a712cd772f4f3db8bed7037bb95c4de94767b230;hp=0000000000000000000000000000000000000000;hpb=2f0296582bf5d3f51db40d299f434fc8240ca6a5;p=fillmore diff --git a/src/marina/ProjectLoader.vala b/src/marina/ProjectLoader.vala new file mode 100644 index 0000000..7a9ffdf --- /dev/null +++ b/src/marina/ProjectLoader.vala @@ -0,0 +1,353 @@ +/* Copyright 2009 Yorba Foundation + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +using Logging; + +namespace Model { + +public class LoaderHandler : Object { + public signal void load_error(string error_message); + public signal void complete(); + + public LoaderHandler() { + } + + public virtual bool commit_library(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_marina(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_track(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_clip(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_clipfile(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_time_signature_entry(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_tempo_entry(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_click(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual bool commit_library_preference(string[] attr_names, string[] attr_values) { + return true; + } + + public virtual void leave_library() { + } + + public virtual void leave_marina() { + } + + public virtual void leave_track() { + } + + public virtual void leave_clip() { + } + + public virtual void leave_clipfile() { + + } +} + +// TODO: Move these classes into separate file +public class XmlTreeLoader { + XmlElement current_element = null; + public XmlElement root = null; + + public XmlTreeLoader(string document) { + MarkupParser parser = { xml_start_element, xml_end_element, null, null }; + MarkupParseContext context = + new MarkupParseContext(parser, (MarkupParseFlags) 0, this, null); + try { + context.parse(document, document.length); + } catch (MarkupError e) { + } + + } + + void xml_start_element(GLib.MarkupParseContext c, string name, + string[] attr_names, string[] attr_values) { + Model.XmlElement new_element = new Model.XmlElement(name, attr_names, attr_values, current_element); + if (root == null) { + root = new_element; + } else { + assert(current_element != null); + current_element.add_child(new_element); + } + + current_element = new_element; + } + + void xml_end_element(GLib.MarkupParseContext c, string name) { + assert(current_element != null); + current_element = current_element.parent; + } +} + +// ProjectBuilder is responsible for verifying the structure of the XML document. +// Subclasses of this class will be responsible for checking the attributes of +// any particular element. +class ProjectBuilder : Object { + LoaderHandler handler; + + public signal void error_occurred(string error); + + public ProjectBuilder(LoaderHandler handler) { + this.handler = handler; + } + + bool check_name(string expected_name, XmlElement node) { + if (node.name == expected_name) { + return true; + } + + error_occurred("expected %s, got %s".printf(expected_name, node.name)); + return false; + } + + void handle_clip(XmlElement clip) { + if (check_name("clip", clip)) { + if (handler.commit_clip(clip.attribute_names, clip.attribute_values)) { + if (clip.children.size != 0) { + error_occurred("clip cannot have children"); + } + handler.leave_clip(); + } + } + } + + void handle_track(XmlElement track) { + if (check_name("track", track)) { + emit(this, Facility.LOADING, Level.VERBOSE, "loading track"); + if (handler.commit_track(track.attribute_names, track.attribute_values)) { + foreach (XmlElement child in track.children) { + handle_clip(child); + } + handler.leave_track(); + } + } + } + + void handle_preference(XmlElement preference) { + if ("click" == preference.name) { + handler.commit_click(preference.attribute_names, preference.attribute_values); + } else if ("library" == preference.name) { + handler.commit_library_preference( + preference.attribute_names, preference.attribute_values); + } else { + error_occurred("Unknown preference: %s".printf(preference.name)); + } + } + + void handle_time_signature(XmlElement time_signature) { + foreach (XmlElement child in time_signature.children) { + if (check_name("entry", child)) { + if (!handler.commit_time_signature_entry(child.attribute_names, + child.attribute_values)) { + error_occurred("Improper time signature node"); + } + } + } + } + + void handle_tempo(XmlElement tempo) { + foreach (XmlElement child in tempo.children) { + if (check_name("entry", child)) { + if (!handler.commit_tempo_entry(child.attribute_names, child.attribute_values)) { + error_occurred("Improper tempo node"); + } + } + } + } + + void handle_map(XmlElement map) { + switch (map.name) { + case "tempo": + handle_tempo(map); + break; + case "time_signature": + handle_time_signature(map); + break; + default: + error_occurred("improper map node"); + break; + } + } + + void handle_library(XmlElement library) { + if (handler.commit_library(library.attribute_names, library.attribute_values)) { + foreach (XmlElement child in library.children) { + if (!handler.commit_clipfile(child.attribute_names, child.attribute_values)) + error_occurred("Improper library node"); + } + handler.leave_library(); + } + } + + void handle_tracks(XmlElement tracks) { + foreach (XmlElement child in tracks.children) { + handle_track(child); + } + } + + void handle_preferences(XmlElement preferences) { + foreach (XmlElement child in preferences.children) { + handle_preference(child); + } + } + void handle_maps(XmlElement maps) { + foreach (XmlElement child in maps.children) { + handle_map(child); + } + } + public bool check_project(XmlElement? root) { + if (root == null) { + error_occurred("Invalid XML file!"); + return false; + } + + if (check_name("marina", root) && + handler.commit_marina(root.attribute_names, root.attribute_values)) { + if (root.children.size != 3 && root.children.size != 4) { + error_occurred("Improper number of children!"); + return false; + } + + if (!check_name("library", root.children[0]) || + !check_name("tracks", root.children[1]) || + !check_name("preferences", root.children[2])) + return false; + + if (root.children.size == 4 && !check_name("maps", root.children[3])) { + return false; + } + } else + return false; + return true; + } + + public void build_project(XmlElement? root) { + handle_library(root.children[0]); + handle_tracks(root.children[1]); + handle_preferences(root.children[2]); + if (root.children.size == 4) { + handle_maps(root.children[3]); + } + + handler.leave_marina(); + } +} + +public class XmlElement { + public string name { get; private set; } + + public string[] attribute_names; + + public string[] attribute_values; + + public Gee.ArrayList children { get { return _children; } } + + public weak XmlElement? parent { get; private set; } + + private Gee.ArrayList _children; + public XmlElement(string name, string[] attribute_names, string[] attribute_values, + XmlElement? parent) { + this.name = name; + + this.attribute_names = copy_array(attribute_names); + this.attribute_values = copy_array(attribute_values); + this.parent = parent; + this._children = new Gee.ArrayList(); + } + + public void add_child(XmlElement child_element) { + _children.add(child_element); + } +} + +public class ProjectLoader : Object { + string? file_name; + LoaderHandler loader_handler; + string text; + size_t text_len; + bool project_load_completed = false; + bool load_completed_fired = false; + bool handler_completed = false; + + public signal void load_started(string filename); + public signal void load_complete(); + public signal void load_error(string error); + + public ProjectLoader(LoaderHandler loader_handler, string? file_name) { + this.file_name = file_name; + this.loader_handler = loader_handler; + loader_handler.load_error.connect(on_load_error); + loader_handler.complete.connect(on_handler_complete); + } + + void on_load_error(string error) { + emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error"); + load_error(error); + } + + void on_handler_complete() { + emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_handler_complete"); + handler_completed = true; + if (project_load_completed && !load_completed_fired) { + load_completed_fired = true; + load_complete(); + } + } + + public void load() { + try { + FileUtils.get_contents(file_name, out text, out text_len); + } catch (FileError e) { + emit(this, Facility.LOADING, Level.MEDIUM, + "error loading %s: %s".printf(file_name, e.message)); + load_error(e.message); + load_complete(); + return; + } + emit(this, Facility.LOADING, Level.VERBOSE, "Building tree for %s".printf(file_name)); + XmlTreeLoader tree_loader = new XmlTreeLoader(text); + + ProjectBuilder builder = new ProjectBuilder(loader_handler); + builder.error_occurred.connect(on_load_error); + + if (builder.check_project(tree_loader.root)) { + emit(this, Facility.LOADING, Level.VERBOSE, "project checked out. starting load"); + load_started(file_name); + builder.build_project(tree_loader.root); + project_load_completed = true; + if (handler_completed && !load_completed_fired) { + load_completed_fired = true; + load_complete(); + } + } + else { + emit(this, Facility.LOADING, Level.INFO, "project did not check out. stopping."); + load_complete(); + } + } +} +}