banshee r2962 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Hyena src/Core/Hyena/Hyena.Data.Query



Author: gburt
Date: Thu Jan 10 21:40:14 2008
New Revision: 2962
URL: http://svn.gnome.org/viewvc/banshee?rev=2962&view=rev

Log:
2008-01-10  Gabriel Burt  <gabriel burt gmail com>

	* src/Core/Hyena/Hyena.Data.Query/QueryNode.cs: Add ToUserQuery method to
	rebuild what a user would type in the search bar from the object tree.
	Add AppendXml and AppendUserQuery abstract methods.

	* src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs:
	For testing purposes, when a user searches, parse the query into the
	QueryNode tree, generate XML from that, and then parse the XML.

	* src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs: Implement
	AppendUserQuery.

	* src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs: Add Operator class and
	implement AppendUserQuery and AppendXml.  Generate XESAM-like XML (with
	the goal of being compliant).

	* src/Core/Hyena/Hyena.Data.Query/QueryParser.cs:
	* src/Core/Hyena/Hyena.Data.Query/UserQueryParser.cs: Update copyright.

	* src/Core/Hyena/Makefile.am:
	* src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs: New file for parsing
	XML queries into QueryNode trees.


Added:
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryParser.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs
   trunk/banshee/src/Core/Hyena/Hyena.Data.Query/UserQueryParser.cs
   trunk/banshee/src/Core/Hyena/Makefile.am

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	Thu Jan 10 21:40:14 2008
@@ -83,6 +83,12 @@
                 //n.Dump ();
                 Console.WriteLine ("Xml for Query: {0}", n.ToXml ());
                 Console.WriteLine ("Sql for Query: {0}", filter_query);
+                Hyena.Data.Query.QueryParser qp2 = new XmlQueryParser (n.ToXml ());
+                QueryNode n2 = qp2.BuildTree ();
+                if (n2 != null) {
+                    Console.WriteLine ("User query for Xml: {0}", n2.ToUserQuery ());
+                } else
+                    Console.WriteLine ("n2 is null");
 
                 if (filter_query.Length == 0)
                     filter_query = null;

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryListNode.cs	Thu Jan 10 21:40:14 2008
@@ -3,8 +3,9 @@
 //
 // Author:
 //   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2007-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -159,7 +160,7 @@
             if (ChildCount == 0)
                 return;
 
-            XmlElement node = doc.CreateElement (Keyword.ToString ());
+            XmlElement node = doc.CreateElement (Keyword.ToString ().ToLower ());
             parent.AppendChild (node);
             foreach (QueryNode child in Children)
                 child.AppendXml (doc, node);
@@ -170,6 +171,34 @@
             return String.Format("<{0}>", Keyword);
         }
 
+        public override void AppendUserQuery (StringBuilder sb)
+        {
+            if (ChildCount == 0)
+                return;
+
+            if (Keyword != Keyword.Not) {
+                if (ChildCount > 1 && Parent != null)
+                    sb.Append ("(");
+                bool first = true;
+                foreach (QueryNode child in Children) {
+                    if (!first) {
+                        if (Keyword == Keyword.Or)
+                            sb.Append (", ");
+                        else
+                            sb.Append (" ");
+                    } else {
+                        first = false;
+                    }
+                    child.AppendUserQuery (sb);
+                }
+                if (ChildCount > 1 && Parent != null)
+                    sb.Append (")");
+            } else {
+                sb.Append ("-");
+                Children [0].AppendUserQuery (sb);
+            }
+        }
+
         public override void AppendSql (StringBuilder sb, QueryFieldSet fieldSet)
         {
             if (ChildCount == 0)
@@ -179,10 +208,11 @@
                 sb.Append ("(");
                 bool first = true;
                 foreach (QueryNode child in Children) {
-                    if (!first)
+                    if (!first) {
                         sb.AppendFormat (" {0} ", Keyword);
-                    else
+                    } else {
                         first = false;
+                    }
                     child.AppendSql (sb, fieldSet);
                 }
                 sb.Append (")");

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs	Thu Jan 10 21:40:14 2008
@@ -3,8 +3,9 @@
 //
 // Author:
 //   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2007-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -66,16 +67,31 @@
 
         public abstract QueryNode Trim ();
 
-        public abstract void AppendXml (XmlDocument doc, XmlNode parent);
+        public string ToUserQuery ()
+        {
+            StringBuilder sb = new StringBuilder ();
+            AppendUserQuery (sb);
+            return sb.ToString ();
+        }
+
+        public abstract void AppendUserQuery (StringBuilder sb);
 
         public virtual string ToXml ()
         {
             XmlDocument doc = new XmlDocument ();
-            AppendXml (doc, doc);
+
+            XmlElement request = doc.CreateElement ("request");
+            doc.AppendChild (request);
+
+            XmlElement query = doc.CreateElement ("query");
+            query.SetAttribute ("banshee-version", "1");
+            request.AppendChild (query);
+
+            AppendXml (doc, query);
             return doc.OuterXml;
         }
 
-        public abstract void AppendSql (StringBuilder sb, QueryFieldSet fieldSet);
+        public abstract void AppendXml (XmlDocument doc, XmlNode parent);
 
         public virtual string ToSql (QueryFieldSet fieldSet)
         {
@@ -83,6 +99,8 @@
             AppendSql (sb, fieldSet);
             return sb.ToString ();
         }
+
+        public abstract void AppendSql (StringBuilder sb, QueryFieldSet fieldSet);
         
         public QueryListNode Parent {
             get { return parent; }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryParser.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryParser.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryParser.cs	Thu Jan 10 21:40:14 2008
@@ -3,6 +3,7 @@
 //
 // Author:
 //   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
 //
 // Copyright (C) 2007 Novell, Inc.
 //

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs	Thu Jan 10 21:40:14 2008
@@ -3,8 +3,9 @@
 //
 // Author:
 //   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2007-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -29,42 +30,76 @@
 using System;
 using System.Xml;
 using System.Text;
+using System.Collections.Generic;
 
 namespace Hyena.Data.Query
 {
+    public class Operator
+    {
+        public string Name;
+        public string UserOperator;
+        
+        private static List<Operator> operators = new List<Operator> ();
+        private static Dictionary<string, Operator> by_op = new Dictionary<string, Operator> ();
+        private static Dictionary<string, Operator> by_name = new Dictionary<string, Operator> ();
+
+        static Operator () {
+            // Note, order of these is important since if = was before ==, the value of the
+            // term would start with the second =, etc.
+            Add (new Operator ("equals", "=="));
+            Add (new Operator ("lessThanEquals", "<="));
+            Add (new Operator ("greaterThanEquals", ">="));
+            Add (new Operator ("startsWith", "="));
+            Add (new Operator ("contains", ":"));
+            Add (new Operator ("lessThan", "<"));
+            Add (new Operator ("greaterThan", ">"));
+        }
+
+        public static IEnumerable<Operator> Operators {
+            get { return operators; }
+        }
+
+        private static void Add (Operator op)
+        {
+            operators.Add (op);
+            by_op.Add (op.UserOperator, op);
+            by_name.Add (op.Name, op);
+        }
+
+        public static Operator GetByUserOperator (string op)
+        {
+            return (by_op.ContainsKey (op)) ? by_op [op] : null;
+        }
+
+        public static Operator GetByName (string name)
+        {
+            return (by_name.ContainsKey (name)) ? by_name [name] : null;
+        }
+
+        public Operator (string name, string userOp)
+        {
+            Name = name;
+            UserOperator = userOp;
+        }
+    }
+
     public class QueryTermNode : QueryNode
     {
         private string field;
-        private string value;
-        private string op;
+        private string field_value;
+        private Operator op;
+
+        //private static string [] operators = new string [] {":", "==", "<=", ">=", "=", "<", ">"};
 
-        /*public struct Operator
+        public QueryTermNode() : base ()
         {
-            public string [] UserQueryOperators;
-            public string Label;
-            public string Name;
-            public string SqlFormat;
-
-            public Operator (string name, string label, string sqlFormat, string [] operators)
-            {
-                Name = name;
-                Label = label;
-                SqlFormat = sqlFormat;
-                UserQueryOperators = operators;
-            }
-
-            public static Operator 
-        }*/
-
-        // Note, order of these is important since if = was before ==, the value of the
-        // term would start with the second =, etc.
-        private static string [] operators = new string [] {":", "==", "<=", ">=", "=", "<", ">"};
+        }
 
         public QueryTermNode(string value) : base()
         {
             int field_separator = 0;
-            foreach (string op in operators) {
-                field_separator = value.IndexOf (op);
+            foreach (Operator op in Operator.Operators) {
+                field_separator = value.IndexOf (op.UserOperator);
                 if (field_separator != -1) {
                     this.op = op;
                     break;
@@ -73,16 +108,16 @@
 
             if(field_separator > 0) {
                 field = value.Substring(0, field_separator);
-                this.value = value.Substring(field_separator + op.Length);
+                this.field_value = value.Substring(field_separator + op.UserOperator.Length);
             } else {
-                this.value = value;
-                this.op = ":";
+                this.field_value = value;
+                this.op = Operator.GetByUserOperator (":");
             }
         }
 
         public override QueryNode Trim ()
         {
-            if (value == null || value == String.Empty)
+            if ((field_value == null || field_value == String.Empty) && Parent != null)
                 Parent.RemoveChild (this);
             return this;
         }
@@ -90,20 +125,33 @@
         public override string ToString()
         {
             if(field != null) {
-                return String.Format("[{0}]=\"{1}\"", field, value);
+                return String.Format("[{0}]=\"{1}\"", field, field_value);
             } else {
                 return String.Format("\"{0}\"", Value);
             }
         }
 
-        public override void AppendXml (XmlDocument doc, XmlNode parent)
+        public override void AppendUserQuery (StringBuilder sb)
         {
-            XmlElement node = doc.CreateElement ("term");
             if (Field != null)
-                node.SetAttribute ("field", Field);
-            node.SetAttribute ("op", Operator);
-            node.SetAttribute ("value", Value);
-            parent.AppendChild (node);
+                sb.AppendFormat ("{0}{1}", Field, Operator.UserOperator);
+            sb.Append (Value);
+        }
+
+        public override void AppendXml (XmlDocument doc, XmlNode parent)
+        {
+            XmlElement op_node = doc.CreateElement (op.Name);
+            parent.AppendChild (op_node);
+
+            if (Field != null) {
+                XmlElement field = doc.CreateElement ("field");
+                field.SetAttribute ("name", Field);
+                op_node.AppendChild (field);
+            }
+
+            XmlElement val_node = doc.CreateElement ("string");
+            val_node.InnerText = Value;
+            op_node.AppendChild (val_node);
         }
 
         public override void AppendSql (StringBuilder sb, QueryFieldSet fieldSet)
@@ -145,7 +193,7 @@
             if (emit_or)
                 sb.Append (" OR ");
 
-            switch (Operator) {
+            switch (Operator.UserOperator) {
                 case "=": // Treat as starts with
                 case "==":
                     sb.AppendFormat("{0} LIKE '{1}%'", field, safe_value);
@@ -169,7 +217,7 @@
                 if (emit_or)
                     sb.Append (" OR ");
 
-                switch (Operator) {
+                switch (Operator.UserOperator) {
                     case ":":
                     case "=":
                     case "==":
@@ -177,7 +225,7 @@
                         break;
 
                     default:
-                        sb.AppendFormat("{0} {2} {1}", field, num, Operator);
+                        sb.AppendFormat("{0} {2} {1}", field, num, Operator.UserOperator);
                         break;
                 }
                 return true;
@@ -186,15 +234,18 @@
         }
         
         public string Value {
-            get { return value; }
+            get { return field_value; }
+            set { field_value = value; }
         }
 
-        public string Operator {
+        public Operator Operator {
             get { return op; }
+            set { op = value; }
         }
         
         public string Field {
             get { return field; }
+            set { field = value; }
         }
     }
 }

Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/UserQueryParser.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/UserQueryParser.cs	(original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/UserQueryParser.cs	Thu Jan 10 21:40:14 2008
@@ -3,8 +3,9 @@
 //
 // Author:
 //   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2007-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the

Added: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs	Thu Jan 10 21:40:14 2008
@@ -0,0 +1,115 @@
+//
+// XmlQueryParser.cs
+//
+// Author:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2007-2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Text;
+using System.Xml;
+
+namespace Hyena.Data.Query
+{
+    public class XmlQueryParser : QueryParser
+    {
+        private string str;
+
+        public XmlQueryParser () : base () {}
+        public XmlQueryParser (string str)
+        {
+            this.str = str;
+        }
+
+        public override QueryNode BuildTree ()
+        {
+            XmlDocument doc = new XmlDocument ();
+            try {
+                doc.LoadXml (str);
+                XmlElement request = doc.FirstChild as XmlElement;
+                if (request == null || request.Name != "request")
+                    throw new Exception ("Invalid request");
+
+                XmlElement query = request.FirstChild as XmlElement;
+                if (query == null || query.Name != "query" || query.GetAttribute ("banshee-version") != "1")
+                    throw new Exception ("Invalid query");
+
+                QueryNode node = Parse (query.FirstChild as XmlElement, null);
+                return (node != null) ? node.Trim () : null;
+            } catch (Exception e) {
+                Console.WriteLine ("Caught exception trying to parse query tree from XML: {0}", e.ToString ());
+            }
+            return null;
+        }
+
+        private QueryNode Parse (XmlElement node, QueryListNode parent)
+        {
+            if (node == null)
+                return null;
+
+            QueryListNode list = null;
+            Console.WriteLine ("Parsing node: {0}", node.Name);
+            switch (node.Name.ToLower ()) {
+                case "and":
+                    list = new QueryListNode (Keyword.And);
+                    break;
+                case "or":
+                    list = new QueryListNode (Keyword.Or);
+                    break;
+                case "not":
+                    list = new QueryListNode (Keyword.Not);
+                    break;
+                default:
+                    QueryTermNode term = new QueryTermNode ();
+                    term.Operator = Operator.GetByName (node.Name);
+
+                    if (node["field"] != null)
+                        term.Field = node["field"].GetAttribute ("name");
+
+                    if (node["string"] != null)
+                        term.Value = node["string"].InnerText;
+
+                    if (parent != null)
+                        parent.AddChild (term);
+                    return term;
+            }
+
+            if (list != null) {
+                if (parent != null)
+                    parent.AddChild (list);
+
+                foreach (XmlNode child in node.ChildNodes) {
+                    Parse (child as XmlElement, list);
+                }
+            }
+
+            return list;
+        }
+
+        public override void Reset ()
+        {
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Hyena/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Hyena/Makefile.am	(original)
+++ trunk/banshee/src/Core/Hyena/Makefile.am	Thu Jan 10 21:40:14 2008
@@ -18,6 +18,7 @@
 	Hyena.Data.Query/QueryParser.cs \
 	Hyena.Data.Query/QueryTermNode.cs \
 	Hyena.Data.Query/QueryToken.cs \
+	Hyena.Data.Query/XmlQueryParser.cs \
 	Hyena.Data.Query/UserQueryParser.cs \
 	Hyena.Data/CacheableModelAdapter.cs \
 	Hyena.Data/ColumnDescription.cs \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]