TurtleDB
A mini distributed database system
src/ca/uqac/dim/turtledb/XmlQueryParser.java
Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002     Simple distributed database engine
00003     Copyright (C) 2012  Sylvain Hallé
00004 
00005     This program is free software: you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation, either version 3 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  -------------------------------------------------------------------------*/
00018 package ca.uqac.dim.turtledb;
00019 
00020 import java.io.*;
00021 import java.util.*;
00022 
00023 import javax.xml.parsers.*;
00024 import org.w3c.dom.*;
00025 import org.xml.sax.InputSource;
00026 import org.xml.sax.SAXException;
00027 
00038 public class XmlQueryParser
00039 {
00045   public static Relation parse(String s) throws XmlQueryParser.ParseException
00046   {
00047     DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
00048     DocumentBuilder builder = null;
00049     try
00050     {
00051       builder = builderFactory.newDocumentBuilder();
00052     }
00053     catch (ParserConfigurationException e)
00054     {
00055       e.printStackTrace();  
00056     }
00057     try
00058     {
00059       Document document = builder.parse(new InputSource(new StringReader(s)));
00060       return parse(document);
00061     }
00062     catch (SAXException e)
00063     {
00064       e.printStackTrace();
00065     }
00066     catch (IOException e)
00067     {
00068       e.printStackTrace();
00069     }
00070     return null;
00071   }
00072   
00078   public static Relation parse(Document doc) throws XmlQueryParser.ParseException
00079   {
00080     return parse(doc.getDocumentElement());
00081   }
00082   
00095   protected static Relation parse(Node e) throws XmlQueryParser.ParseException
00096   {
00097     NodeList nl = e.getChildNodes();
00098     String name = "";
00099     for (int i = 0; i < nl.getLength(); i++)
00100     {
00101       Node n = nl.item(i);
00102       if (n.getNodeType() != Node.ELEMENT_NODE)
00103           continue;
00104       Element elem = (Element) n;
00105       name = elem.getTagName();
00106       if (name == null)
00107           continue;
00108       name = name.toLowerCase();
00109       if (name.compareTo("selection") == 0)
00110       {
00111         return parseSelection(n);
00112       }
00113       else if (name.compareTo("projection") == 0)
00114       {
00115         return parseProjection(n);
00116       }
00117       else if (name.compareTo("union") == 0)
00118       {
00119         return parseUnion(n);
00120       }
00121       else if (name.compareTo("intersection") == 0)
00122       {
00123         return parseIntersection(n);
00124       }
00125       else if (name.compareTo("join") == 0)
00126       {
00127         return parseJoin(n);
00128       }
00129       else if (name.compareTo("product") == 0)
00130       {
00131         return parseProduct(n);
00132       }
00133       else if (name.compareTo("vartable") == 0)
00134       {
00135         return parseVariableTable(n);
00136       }
00137       else if (name.compareTo("table") == 0)
00138       {
00139         return parseTable(n);
00140       }
00141     }
00142     // If we get here, we did not recognize any operand we know
00143     throw new XmlQueryParser.ParseException("Unrecognized operand: " + name);
00144   }
00145   
00157   protected static Relation parseSelection(Node e) throws XmlQueryParser.ParseException
00158   {
00159     Condition c = null;
00160     Relation r = null;
00161     NodeList nl = e.getChildNodes();
00162     for (int i = 0; i < nl.getLength(); i++)
00163     {
00164       Node n = nl.item(i);
00165       if (n.getNodeName().compareToIgnoreCase("condition") == 0)
00166         c = parseCondition(n);
00167       if (n.getNodeName().compareToIgnoreCase("operand") == 0)
00168       {
00169         r = parse(n);
00170       }
00171     }
00172     if (c == null)
00173       throw new XmlQueryParser.ParseException("Missing condition in selection");
00174     if (r == null)
00175       throw new XmlQueryParser.ParseException("Missing operand in selection");
00176     Selection sel = new Selection(c, r);
00177     return sel;
00178   }
00179   
00191   protected static Relation parseProjection(Node e) throws XmlQueryParser.ParseException
00192   {
00193     Schema s = null;
00194     Relation r = null;
00195     NodeList nl = e.getChildNodes();
00196     for (int i = 0; i < nl.getLength(); i++)
00197     {
00198       Node n = nl.item(i);
00199       if (n.getNodeName().compareToIgnoreCase("schema") == 0)
00200         s = parseSchema(n);
00201       if (n.getNodeName().compareToIgnoreCase("operand") == 0)
00202       {
00203         r = parse(n);
00204       }
00205     }
00206     if (s == null)
00207       throw new XmlQueryParser.ParseException("Missing schema in projection");
00208     if (r == null)
00209       throw new XmlQueryParser.ParseException("Missing operand in projection");
00210     Projection pro = new Projection(s, r);
00211     return pro;
00212   }
00213   
00226   protected static Relation parseUnion(Node e) throws XmlQueryParser.ParseException
00227   {
00228     Union u = new Union();
00229     NodeList nl = e.getChildNodes();
00230     for (int i = 0; i < nl.getLength(); i++)
00231     {
00232       Node n = nl.item(i);
00233       if (n.getNodeName().compareToIgnoreCase("operand") == 0)
00234       {
00235         Relation r = parse(n);
00236         u.addOperand(r);
00237       }
00238     }
00239     if (u.m_relations.size() < 2)
00240       throw new XmlQueryParser.ParseException("Union of less than 2 relations");
00241     return u;
00242   }
00243   
00256   protected static Relation parseIntersection(Node e) throws XmlQueryParser.ParseException
00257   {
00258     Intersection u = new Intersection();
00259     NodeList nl = e.getChildNodes();
00260     for (int i = 0; i < nl.getLength(); i++)
00261     {
00262       Node n = nl.item(i);
00263       if (n.getNodeName().compareToIgnoreCase("operand") == 0)
00264       {
00265         Relation r = parse(n);
00266         u.addOperand(r);
00267       }
00268     }
00269     if (u.m_relations.size() < 2)
00270       throw new XmlQueryParser.ParseException("Intersection of less than 2 relations");
00271     return u;
00272   }
00273   
00286   protected static Relation parseProduct(Node e) throws XmlQueryParser.ParseException
00287   {
00288     Product p = new Product();
00289     NodeList nl = e.getChildNodes();
00290     for (int i = 0; i < nl.getLength(); i++)
00291     {
00292       Node n = nl.item(i);
00293       if (n.getNodeName().compareToIgnoreCase("operand") == 0)
00294       {
00295         Relation r = parse(n);
00296         p.addOperand(r);
00297       }
00298     }
00299     if (p.m_relations.size() < 2)
00300       throw new XmlQueryParser.ParseException("Product of less than 2 relations");
00301     return p;
00302   }
00303   
00316   protected static Relation parseJoin(Node e) throws XmlQueryParser.ParseException
00317   {
00318     Join j = new Join();
00319     NodeList nl = e.getChildNodes();
00320     for (int i = 0; i < nl.getLength(); i++)
00321     {
00322       Node n = nl.item(i);
00323       if (n.getNodeName().compareToIgnoreCase("operand") == 0)
00324       {
00325         Relation r = parse(n);
00326         if (j.m_left == null)
00327           j.setLeft(r);
00328         else if (j.m_right == null)
00329           j.setRight(r);
00330         else
00331           throw new XmlQueryParser.ParseException("Join of more than 2 relations");
00332       }
00333       else if (n.getNodeName().compareToIgnoreCase("condition") == 0)
00334       {
00335         Condition c = parseCondition(n);
00336         j.m_condition = c;
00337       }
00338     }
00339     if (j.m_left == null || j.m_right == null)
00340       throw new XmlQueryParser.ParseException("Join of less than 2 relations");
00341     if (j.m_condition == null)
00342       throw new XmlQueryParser.ParseException("Join without a join condition");
00343     return j;
00344   }
00345   
00358   protected static Condition parseCondition(Node e) throws XmlQueryParser.ParseException
00359   {
00360     NodeList nl = e.getChildNodes();
00361     for (int i = 0; i < nl.getLength(); i++)
00362     {
00363       Node n = nl.item(i);
00364       if (n.getNodeName().compareToIgnoreCase("equals") == 0)
00365       {
00366         return parseEquality(n);
00367       }
00368       if (n.getNodeName().compareToIgnoreCase("and") == 0)
00369       {
00370         return parseNAryCondition(new LogicalAnd(), n);
00371       }
00372       if (n.getNodeName().compareToIgnoreCase("or") == 0)
00373       {
00374         return parseNAryCondition(new LogicalOr(), n);
00375       }
00376     }
00377     // If we get here, we did not recognize any condition we know
00378     throw new XmlQueryParser.ParseException("No condition recognized");
00379   }
00380   
00381   protected static Condition parseNAryCondition(NAryCondition c, Node e) throws XmlQueryParser.ParseException
00382   {
00383     NodeList nl = e.getChildNodes();
00384     for (int i = 0; i < nl.getLength(); i++)
00385     {
00386       Node n = nl.item(i);
00387       if (n.getNodeName().compareToIgnoreCase("condition") == 0)
00388       {
00389         Condition c_in = parseCondition(n);
00390         c.addCondition(c_in);
00391       }
00392     }
00393     return c;
00394   }
00395   
00409   protected static Equality parseEquality(Node e) throws XmlQueryParser.ParseException
00410   {
00411     NodeList nl = e.getChildNodes();
00412     Literal left = null, right = null;
00413     for (int i = 0; i < nl.getLength(); i++)
00414     {
00415       Node n = nl.item(i);
00416       Literal a = null;
00417       if (n.getNodeName().compareToIgnoreCase("attribute") == 0)
00418       {
00419         a = parseAttribute(n);
00420       }
00421       else if (n.getNodeName().compareToIgnoreCase("value") == 0)
00422       {
00423         a = parseValue(n);
00424       }
00425       else
00426         continue;
00427       if (left == null)
00428         left = a;
00429       else
00430         right = a;
00431     }
00432     if (left == null || right == null)
00433       throw new XmlQueryParser.ParseException("Missing operand in condition 'equals'");
00434     return new Equality(left, right);
00435   }
00436 
00445   protected static Attribute parseAttribute(Node e) throws XmlQueryParser.ParseException
00446   {
00447     Attribute a = new Attribute();
00448     NodeList nl = e.getChildNodes();
00449     for (int i = 0; i < nl.getLength(); i++)
00450     {
00451       Node n = nl.item(i);
00452       if (n.getNodeName().compareToIgnoreCase("name") == 0)
00453       {
00454         a.setName(n.getTextContent().trim());
00455       }
00456       if (n.getNodeName().compareToIgnoreCase("table") == 0)
00457       {
00458         a.setTableName(n.getTextContent().trim());
00459       }
00460     }
00461     if (a.getName().isEmpty())
00462       throw new XmlQueryParser.ParseException("Empty attribute name");
00463     return a;
00464   }
00465   
00474   protected static VariableTable parseVariableTable(Node e) throws XmlQueryParser.ParseException
00475   {
00476     VariableTable vt = new VariableTable();
00477     NodeList nl = e.getChildNodes();
00478     for (int i = 0; i < nl.getLength(); i++)
00479     {
00480       Node n = nl.item(i);
00481       if (n.getNodeName().compareToIgnoreCase("name") == 0)
00482       {
00483         vt.setName(n.getTextContent().trim());
00484       }
00485       if (n.getNodeName().compareToIgnoreCase("site") == 0)
00486       {
00487         vt.setSite(n.getTextContent().trim());
00488       }
00489       if (n.getNodeName().compareToIgnoreCase("operand") == 0)
00490       {
00491         Relation r = parse(n);
00492         vt.setRelation(r);
00493       }
00494     }
00495     if (vt.getName().isEmpty())
00496       throw new XmlQueryParser.ParseException("Empty vartable name");
00497     return vt;
00498   }
00499   
00508   protected static Table parseTable(Node e) throws XmlQueryParser.ParseException
00509   {
00510     Schema s = null;
00511     String table_name = "";
00512     List<Tuple> tuples = new LinkedList<Tuple>();
00513     NodeList nl = e.getChildNodes();
00514     for (int i = 0; i < nl.getLength(); i++)
00515     {
00516       Node n = nl.item(i);
00517       if (n.getNodeName().compareToIgnoreCase("schema") == 0)
00518       {
00519         s = parseSchema(n);
00520       }
00521       if (n.getNodeName().compareToIgnoreCase("name") == 0)
00522       {
00523         table_name = n.getTextContent().trim();
00524       }
00525       if (n.getNodeName().compareToIgnoreCase("tuple") == 0)
00526       {
00527         Tuple t = parseTuple(n);
00528         tuples.add(t);
00529       }
00530     }
00531     Table tab = new Table(table_name);
00532     tab.setSchema(s);
00533     tab.putAll(tuples);
00534     if (s == null)
00535       throw new XmlQueryParser.ParseException("Missing schema in projection");
00536     return tab;
00537   }
00538   
00547   protected static Value parseValue(Node e) throws XmlQueryParser.ParseException
00548   {
00549     NodeList nl = e.getChildNodes();
00550     if (nl.getLength() == 0)
00551       throw new XmlQueryParser.ParseException("Empty value");
00552     Node n = nl.item(0);
00553     return new Value(n.getTextContent().trim());
00554   }
00555   
00568   protected static Tuple parseTuple(Node e) throws XmlQueryParser.ParseException
00569   {
00570     Tuple t = new Tuple();
00571     NodeList nl = e.getChildNodes();
00572     for (int i = 0; i < nl.getLength(); i++)
00573     {
00574       Node n = nl.item(i);
00575       if (n.getNodeType() == Node.ELEMENT_NODE)
00576       {
00577         String name = n.getNodeName();
00578         String value = n.getTextContent().trim();
00579         t.put(new Attribute(name), new Value(value));
00580       }
00581     }
00582     if (t.size() == 0)
00583       throw new XmlQueryParser.ParseException("Empty tuple");
00584     return t;
00585   }
00586 
00599   protected static Schema parseSchema(Node e) throws XmlQueryParser.ParseException
00600   {
00601     NodeList nl = e.getChildNodes();
00602     Schema s = new Schema();
00603     for (int i = 0; i < nl.getLength(); i++)
00604     {
00605       Node n = nl.item(i);
00606       if (n.getNodeName().compareToIgnoreCase("attribute") == 0)
00607       {
00608         Attribute a = parseAttribute(n);
00609         s.add(a);
00610       }
00611     }
00612     if (s.size() == 0)
00613       throw new ParseException("Empty schema");
00614     return s;
00615   }
00616   
00621   public static class ParseException extends Exception
00622   {
00623     protected String m_message = "ParseException";
00624     private static final long serialVersionUID = 1L;
00625     
00626     public ParseException(String msg)
00627     {
00628       m_message = msg;
00629     }
00630     
00631     @Override
00632     public String toString()
00633     {
00634       return m_message;
00635     }
00636   }
00637 }