TurtleDB
A mini distributed database system
|
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 }